lxc-2.0.11/0000755061062106075000000000000013435013524007372 500000000000000lxc-2.0.11/MAINTAINERS0000644061062106075000000000110613435013473011010 00000000000000Before submitting your patches, check they are signed-off-by conforming with the DCO contained in the ./CONTRIBUTING file. Maintainer ---------- Committers : Serge Hallyn, Stéphane Graber, Dwight Engen, Christian Brauner and Wolfgang Bumiller Mail patches to : lxc-devel@lists.linuxcontainers.org Send pull requests at : https://github.com/lxc/lxc Mailing lists : lxc-devel@lists.linuxcontainers.org, lxc-users@lists.linuxcontainers.org Web page : https://linuxcontainers.org/lxc GIT location : git://github.com/lxc/lxc lxc-2.0.11/INSTALL0000644061062106075000000002275013435013473010354 00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== 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. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 0. If the sources are not coming from a package maintainer and the 'configure' file does not exist, you should run './autogen.sh' in the directory containing the package's source code in order to generate the 'configure' file from the 'configure.ac' file. 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. lxc-2.0.11/lxc.spec0000644061062106075000000002355113435013515010762 00000000000000# # lxc: linux Container library # # (C) Copyright IBM Corp. 2007, 2008 # # Authors: # Daniel Lezcano # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA %global with_python %{?_with_python: 1} %{?!_with_python: 0} %global with_lua %{?_with_lua: 1} %{?!_with_lua: 0} # Set with_systemd on distros that use it, so we can install the service # file, otherwise the sysvinit script will be installed %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210 %global with_systemd 1 %define init_script systemd # # BuildRequires systemd-units on fedora and rhel %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 BuildRequires: systemd-units %endif # # BuildRequires systemd on openSUSE and SUSE %if 0%{?suse_version} >= 1210 BuildRequires: systemd %endif %else %global with_systemd 0 %define init_script sysvinit %endif # Must use /var/run for runtime_path on older releases or dnsmasq in the # lxc-net script will not be able to write its pid in /run (selinux denial) %if 0%{?fedora} < 15 || 0%{?rhel} < 7 %define _with_runtime_path --with-runtime-path=/var/run %endif # RPM needs alpha/beta/rc in Release: not Version: to ensure smooth # package upgrades from alpha->beta->rc->release. For more info see: # http://fedoraproject.org/wiki/Packaging%3aNamingGuidelines#NonNumericRelease %if "x" != "x" %global beta_rel %global beta_dot .%{beta_rel} %else %global norm_rel 1 %endif Name: lxc Version: 2.0.11 Release: %{?beta_rel:0.1.%{beta_rel}}%{?!beta_rel:%{norm_rel}}%{?dist} URL: http://linuxcontainers.org Source: http://linuxcontainers.org/downloads/%{name}-%{version}%{?beta_dot}.tar.gz Summary: Linux Containers userspace tools Group: Applications/System License: LGPLv2+ BuildRoot: %{_tmppath}/%{name}-%{version}-build Requires: openssl rsync dnsmasq bridge-utils Requires: %{name}-libs = %{version}-%{release} Requires(pre): /usr/sbin/useradd Requires(postun): /usr/sbin/userdel %if 0%{?fedora} < 15 || 0%{?rhel} < 7 Requires: libcgroup %endif # Note for Suse. The "docbook2X" BuildRequires does properly # match docbook2x on Suse in a case insensitive manner BuildRequires: libcap libcap-devel docbook2X graphviz libxslt pkgconfig # # Additional packages for openSUSE and SUSE # %if 0%{?suse_version} >= 1210 PreReq: permissions BuildRequires: libapparmor-devel linux-glibc-devel lsb-release docbook-utils # # libseccomp-devel only needed on i386/i586/i686 and X86_64 # %ifarch %ix86 x86_64 BuildRequires: libseccomp-devel %endif %endif %if %{with_python} Requires: python3 BuildRequires: python3-devel %endif %description Containers are insulated areas inside a system, which have their own namespace for filesystem, network, PID, IPC, CPU and memory allocation and which can be created using the Control Group and Namespace features included in the Linux kernel. This package provides the lxc-* tools, which can be used to start a single daemon in a container, or to boot an entire "containerized" system, and to manage and debug your containers. %package libs Summary: Shared library files for %{name} Group: System Environment/Libraries %description libs The %{name}-libs package contains libraries for running %{name} applications. %package devel Summary: Development library for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release}, pkgconfig %description devel The %{name}-devel package contains header files and library needed for development of the Linux containers. %if %{with_lua} %package lua Summary: Lua bindings for %{name} Group: System Environment/Libraries Requires: lua-filesystem lua-alt-getopt BuildRequires: lua-devel %description lua The %{name}-lua package contains %{name} bindings for lua. %endif %prep %setup -q -n %{name}-%{version}%{?beta_dot} %build PATH=$PATH:/usr/sbin:/sbin %configure $args \ %if %{with_lua} --enable-lua \ %endif %if %{with_python} --enable-python \ %endif %if "x%{_unitdir}" != "x" --with-systemdsystemunitdir=%{_unitdir} \ %endif %{?_with_runtime_path} \ --disable-rpath \ --with-init-script=%{init_script} make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} find %{buildroot} -type f -name '*.la' -exec rm -f {} ';' %clean rm -rf %{buildroot} %pre # Ensure that lxc-dnsmasq uid & gid gets correctly allocated if getent passwd lxc-dnsmasq >/dev/null 2>&1 ; then : ; else \ /usr/sbin/useradd -M -r -s /sbin/nologin \ -c "LXC Networking Service" -d %_localstatedir/%name lxc-dnsmasq 2> /dev/null \ || exit 1 fi %post # This test should trigger a network configure on a new install. if [ ! -f /usr/local/etc/default/lxc-net ] || ! grep -q 'USE_LXC_BRIDGE=' /usr/local/etc/default/lxc-net then # Grab a random 10net subnet. Need to add test logic... while [ true ] do SUBNET=10.$(($RANDOM % 256)).$(($RANDOM % 256)) if ! ip -4 route ls | grep -q "^$SUBNET" then break fi done cat > /usr/local/etc/default/lxc-net </config) for any containers # already created using the default config to reflect the new bridge # name. # If you have the dnsmasq daemon installed, you'll also have to update # /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon. LXC_BRIDGE="lxcbr0" LXC_BRIDGE_MAC="00:16:3e:00:00:00" LXC_ADDR="$SUBNET.1" LXC_NETMASK="255.255.255.0" LXC_NETWORK="$SUBNET.0/24" LXC_DHCP_RANGE="$SUBNET.2,$SUBNET.254" LXC_DHCP_MAX="253" # Uncomment the next line if you'd like to use a conf-file for the lxcbr0 # dnsmasq. For instance, you can use 'dhcp-host=mail1,10.0.3.100' to have # container 'mail1' always get ip address 10.0.3.100. #LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf # Uncomment the next line if you want lxcbr0's dnsmasq to resolve the .lxc # domain. You can then add "server=/lxc/10.0.3.1' (or your actual $LXC_ADDR) # to /etc/dnsmasq.conf, after which 'container1.lxc' will resolve on your # host. #LXC_DOMAIN="lxc" EOF fi %postun /usr/sbin/userdel lxc-dnsmasq > /dev/null 2>&1 || : %post libs -p /sbin/ldconfig %postun libs -p /sbin/ldconfig %files %defattr(-,root,root) %{_bindir}/* # openSUSE/SUSE %if 0%{?suse_version} >= 1210 %dir %{_sysconfdir}/apparmor.d %dir %{_sysconfdir}/apparmor.d/abstractions %dir %{_sysconfdir}/apparmor.d/abstractions/%{name} %config %{_sysconfdir}/apparmor.d/abstractions/%{name}/container-base %config %{_sysconfdir}/apparmor.d/abstractions/%{name}/start-container %config %{_sysconfdir}/apparmor.d/%{name}-containers %dir %{_sysconfdir}/apparmor.d/%{name} %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default-with-mounting %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default-with-nesting %config %{_sysconfdir}/apparmor.d/usr.bin.%{name}-start %endif %{_mandir}/man1/lxc* %{_mandir}/man5/lxc* %{_mandir}/man7/lxc* # not openSUSE/SUSE %if %{undefined suse_version} %{_mandir}/ja/man1/lxc* %{_mandir}/ja/man5/lxc* %{_mandir}/ja/man7/lxc* %{_mandir}/ko/man1/lxc* %{_mandir}/ko/man5/lxc* %{_mandir}/ko/man7/lxc* %endif %{_datadir}/doc/* %{_datadir}/lxc/* %{_sysconfdir}/bash_completion.d %config(noreplace) %{_sysconfdir}/lxc/* %config(noreplace) %{_sysconfdir}/sysconfig/* %if %{with_systemd} %{_unitdir}/lxc-net.service %{_unitdir}/lxc.service %{_unitdir}/lxc@.service %else %{_sysconfdir}/rc.d/init.d/lxc %{_sysconfdir}/rc.d/init.d/lxc-net %endif %files libs %defattr(-,root,root) %{_sbindir}/* %{_libdir}/*.so.* %{_libdir}/%{name} %{_localstatedir}/* %{_libexecdir}/%{name}/hooks/unmount-namespace %{_libexecdir}/%{name}/lxc-apparmor-load %{_libexecdir}/%{name}/lxc-monitord %attr(4111,root,root) %{_libexecdir}/%{name}/lxc-user-nic %if %{with_systemd} %attr(555,root,root) %{_libexecdir}/%{name}/lxc-net %attr(555,root,root) %{_libexecdir}/%{name}/lxc-containers %endif %if %{with_python} %{python3_sitearch}/* %endif %if %{with_lua} %files lua %defattr(-,root,root) %{_datadir}/lua %{_libdir}/lua %endif %files devel %defattr(-,root,root) %{_includedir}/%{name}/* %{_libdir}/*.so %{_libdir}/pkgconfig/* %changelog * Tue Oct 22 2013 Dwight Engen - 1.0.0-0.1.alpha2 - fix some rpmlint warnings/errors - split lua bits into separate package * Mon Sep 10 2012 Dwight Engen - 0.8.0 - fix lxc-init moved to libexec - .pc moved to _libdir - package template files /usr/share/lxc/templates * Thu Sep 8 2011 Greg Kurz - 0.7.5.1 - fix installed files for rpmbuild - introduce lxc-libs package * Fri Jul 23 2010 Daniel Lezcano - 0.7.2 - set attribute for installed files - fix libraries installation * Tue Mar 24 2009 Daniel Lezcano - 0.6.1 - Removed capability setting, let the user to do that through "lxc-setcap" * Mon Feb 16 2009 Daniel Lezcano - 0.6.0 - Added more capabilities to the executables * Sun Jan 25 2009 Daniel Lezcano - 0.6.0 - Reduced spec file * Sun Aug 3 2008 Daniel Lezcano - 0.1.0 - Initial RPM release. # Local variables: # mode: shell-script # sh-shell: rpm # end: lxc-2.0.11/Makefile.am0000644061062106075000000000120413435013473011346 00000000000000# Makefile.am ACLOCAL_AMFLAGS = -I config LIBTOOL_DEPS = @LIBTOOL_DEPS@ SUBDIRS = config src templates doc hooks DIST_SUBDIRS = config src templates doc hooks EXTRA_DIST = \ autogen.sh \ lxc.spec \ CONTRIBUTING \ MAINTAINERS RPMARGS = if ENABLE_LUA RPMARGS += --with lua endif if ENABLE_PYTHON RPMARGS += --with python endif pcdatadir = $(libdir)/pkgconfig pcdata_DATA = lxc.pc libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status libtool install-data-local: $(MKDIR_P) $(DESTDIR)$(LXCPATH) $(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/lxc ChangeLog:: @touch ChangeLog rpm: dist rpmbuild --clean -ta ${distdir}.tar.gz $(RPMARGS) lxc-2.0.11/templates/0000755061062106075000000000000013435013523011367 500000000000000lxc-2.0.11/templates/lxc-cirros.in0000644061062106075000000002421713435013473013736 00000000000000#!/bin/bash # template script for generating ubuntu container for LXC # # This script consolidates and extends the existing lxc ubuntu scripts # # Copyright © 2013 Canonical Ltd. # Author: Scott Moser # # 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. # Detect use under userns (unsupported) # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin VERBOSITY=0 DOWNLOAD_URL="http://download.cirros-cloud.net/" UNAME_M=$(uname -m) ARCHES=( i386 x86_64 amd64 arm ) STREAMS=( released devel ) SOURCES=( nocloud none ) BUILD="standard" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" LXC_MAPPED_GID= LXC_MAPPED_UID= DEF_VERSION="released" DEF_SOURCE="nocloud" case "${UNAME_M}" in i?86) DEF_ARCH="i386";; x86_64) DEF_ARCH="x86_64";; arm*) DEF_ARCH="arm";; *) DEF_ARCH="i386";; esac am_in_userns() { [ -e /proc/self/uid_map ] || { echo no; return; } [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) [ "$line" = "0 0 4294967295" ] && { echo no; return; } echo yes } in_userns=0 [ $(am_in_userns) = "yes" ] && in_userns=1 # Allow the cache base to be set by environment variable if [ $(id -u) -eq 0 ]; then CACHE_D=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc/cirros"} else CACHE_D=${LXC_CACHE_PATH:-"$HOME/.cache/lxc/cirros"} fi error() { echo "$@" 1>&2; } inargs() { local needle="$1" x="" shift for x in "$@"; do [ "$needle" = "$x" ] && return 0 done return 1 } Usage() { cat <&2; [ $# -eq 0 ] || error "$@"; return 1; } debug() { local level=${1}; shift; [ "${level}" -gt "${VERBOSITY}" ] && return error "${@}" } jsondict() { local k="" v="" ret="{" for arg in "$@"; do k="${arg%%=*}" v="${arg#*=}" ret="${ret} \"${k}\": \"$v\"," done ret="${ret%,} }" echo "$ret" } copy_configuration() { local path=$1 rootfs=$2 name=$3 arch=$4 release=$5 cat >> "$path/config" <> $path/config fi } insert_ds_nocloud() { local root_d="$1" authkey="$2" udfile="$3" local sdir="$root_d/var/lib/cloud/seed/nocloud" mkdir -p "$sdir" || { error "failed to make datasource dir $sdir"; return 1; } rm -f "$sdir/meta-data" "$sdir/user-data" || { error "failed to clean old data from $sdir"; return 1; } iid="iid-local01" jsondict "instance-id=$iid" \ ${authkeys:+"public-keys=${authkeys}"} > "$sdir/meta-data" || { error "failed to write metadata to $sdir/meta-data"; return 1; } if [ -n "$udfile" ]; then cat "$udfile" > "$sdir/user-data" || { error "failed to write user-data to $sdir"; return 1; } else rm -f "$sdir/user-data" fi } insert_ds() { local dstype="$1" root_d="$2" authkey="$3" udfile="$4" case "$dstype" in nocloud) insert_ds_nocloud "$root_d" "$authkey" "$udfile" esac } extract_rootfs() { local tarball="$1" rootfs_d="$2" mkdir -p "${rootfs_d}" || { error "failed to make rootfs dir ${rootfs_d}"; return 1; } if [ $in_userns -eq 1 ]; then tar -C "${rootfs_d}" --anchored --exclude="dev/*" -Sxzf "${tarball}" || { error "failed to populate ${rootfs_d}"; return 1; } else tar -C "${rootfs_d}" -Sxzf "${tarball}" || { error "failed to populate ${rootfs_d}"; return 1; } fi return 0 } download_tarball() { local arch="$1" ver="$2" cached="$3" baseurl="$4" local out="" outd="" file="" dlpath="" file="cirros-$ver-$arch-lxc.tar.gz" dlpath="$ver/$file" outd="${cached}/${dlpath%/*}" if [ -f "$cached/$dlpath" ]; then _RET="$cached/$dlpath" return 0 fi mkdir -p "${outd}" || { error "failed to create ${outd}"; return 1; } debug 1 "downloading ${baseurl%/}/$dlpath" to "${cached}/$dlpath" wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" && mv "$cached/$dlpath.$$" "$cached/$dlpath" || { rm -f "$cached/$dlpath.$$"; error "failed to download $dlpath"; return 1; } _RET="$cached/$dlpath" } create_main() { local short_opts="a:hn:p:S:uvV" local long_opts="arch:,auth-key:,name:,path:,tarball:,userdata:,verbose,version:,rootfs:,mapped-uid:,mapped-gid:" local getopt_out="" getopt_out=$(getopt --name "${0##*/}" \ --options "${short_opts}" --long "${long_opts}" -- "$@") && eval set -- "${getopt_out}" || { bad_Usage; return; } local arch="${DEF_ARCH}" dsource="${DEF_SOURCE}" version="${DEF_VERSION}" local authkey_f="" authkeys="" userdata_f="" path="" tarball="" local cur="" next="" local rootfs_d="" while [ $# -ne 0 ]; do cur=$1; next=$2; case "$cur" in -a|--arch) arch="$next"; shift;; -h|--help) Usage ; return 0;; -n|--name) name="$next"; shift;; -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; -S|--auth-key) authkey_f="$next"; shift;; -p|--path) path=$next; shift;; -v|--version) version=$next; shift;; -u|--userdata) userdata_f="$next"; shift;; --tarball) tarball="$next"; shift;; --source) dsource="$next"; shift;; --rootfs) rootfs_d="$next"; shift;; --mapped-uid) LXC_MAPPED_UID=$next; shift;; --mapped-gid) LXC_MAPPED_GID=$next; shift;; --) shift; break;; esac shift; done [ -n "$rootfs_d" ] || rootfs_d="$path/rootfs" [ $# -eq 0 ] || { bad_Usage "unexpected arguments: $*"; return; } [ -n "$path" ] || { error "'path' parameter is required"; return 1; } if [ "$(id -u)" != "0" ]; then { error "must be run as root"; return 1; } fi case "$arch" in i?86) arch="i386";; amd64) arch="x86_64";; esac inargs "$arch" "${ARCHES[@]}" || { error "bad arch '$arch'. allowed: ${ARCHES[*]}"; return 1; } inargs "$dsource" "${SOURCES[@]}" || { error "bad source '$dsource'. allowed: ${SOURCES[*]}"; return 1; } if [ "$dsource" = "none" ] && [ -n "$userdata_f" -o -n "$authkey_f" ]; then error "userdata and authkey are incompatible with --source=none"; return 1; fi if [ -n "$authkey_f" ]; then if [ ! -f "$authkey_f" ]; then error "--auth-key=${authkey_f} must reference a file" return 1 fi authkeys=$(cat "$authkey_f") || { error "failed to read ${authkey_f}"; return 1; } fi if [ -n "$userdata_f" -a ! -f "${userdata_f}" ]; then error "${userdata_f}: --userdata arg not a file" return 1 fi if [ -z "$tarball" ]; then if inargs "$version" "${STREAMS[@]}"; then out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || { error "failed to convert 'version=$version'"; return 1; } version="$out" fi download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || return tarball="$_RET" fi extract_rootfs "${tarball}" "${rootfs_d}" || return if [ "$version" = "0.3.2~pre1" ]; then debug 1 "fixing console for lxc and '$version'" sed -i 's,^\(#console.* 115200 \)# /dev/console,\1 console,g' \ "$rootfs_d/etc/inittab" || { error "failed to fix console entry for $version"; return 1; } fi if [ "$dsource" != "none" ]; then insert_ds "$dsource" "$path/rootfs" "$authkeys" "$userdata_f" || { error "failed to insert userdata to $path/rootfs" return 1 } fi copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" return } create_main "$@" # vi: ts=4 expandtab lxc-2.0.11/templates/lxc-ubuntu-cloud.in0000644061062106075000000002672713435013473015073 00000000000000#!/bin/bash # template script for generating ubuntu container for LXC based on released # cloud images. # # Copyright © 2012 Serge Hallyn # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA set -e STATE_DIR="@LOCALSTATEDIR@" HOOK_DIR="@LXCHOOKDIR@" CLONE_HOOK_FN="$HOOK_DIR/ubuntu-cloud-prep" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" KNOWN_RELEASES="precise trusty xenial yakkety zesty" skip_arch_check=${UCTEMPLATE_SKIP_ARCH_CHECK:-0} # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin if [ -r /etc/default/lxc ]; then . /etc/default/lxc fi am_in_userns() { [ -e /proc/self/uid_map ] || { echo no; return; } [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) [ "$line" = "0 0 4294967295" ] && { echo no; return; } echo yes } in_userns=0 [ $(am_in_userns) = "yes" ] && in_userns=1 copy_configuration() { path=$1 rootfs=$2 name=$3 arch=$4 release=$5 if [ $arch = "i386" ]; then arch="i686" fi # if there is exactly one veth network entry, make sure it has an # associated hwaddr. nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` if [ $nics -eq 1 ]; then grep -q "^lxc.network.hwaddr" $path/config || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config fi # Generate the configuration file ## Relocate all the network config entries sed -i -e "/lxc.network/{w ${path}/config-network" -e "d}" $path/config ## Relocate any other config entries sed -i -e "/lxc./{w ${path}/config-auto" -e "d}" $path/config ## Add all the includes echo "" >> $path/config echo "# Common configuration" >> $path/config if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.common.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.common.conf" >> $path/config fi if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.${release}.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.${release}.conf" >> $path/config fi if [ $in_userns -eq 1 ] && [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" >> $path/config fi ## Add the container-specific config echo "" >> $path/config echo "# Container specific configuration" >> $path/config [ -e "$path/config-auto" ] && cat $path/config-auto >> $path/config && rm $path/config-auto grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.arch = $arch EOF ## Re-add the previously removed network config echo "" >> $path/config echo "# Network configuration" >> $path/config cat $path/config-network >> $path/config rm $path/config-network # Set initial timezone as on host if [ -f /etc/timezone ]; then cat /etc/timezone > $rootfs/etc/timezone chroot $rootfs dpkg-reconfigure -f noninteractive tzdata elif [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock echo $ZONE > $rootfs/etc/timezone chroot $rootfs dpkg-reconfigure -f noninteractive tzdata else echo "Timezone in container is not configured. Adjust it manually." fi # rmdir /dev/shm for containers that have /run/shm # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did # get bind mounted to the host's /run/shm. So try to rmdir # it, and in case that fails move it out of the way. # NOTE: This can only be removed once 12.04 goes out of support if [ ! -L $rootfs/dev/shm ] && [ -e $rootfs/dev/shm ]; then rmdir $rootfs/dev/shm 2>/dev/null || mv $rootfs/dev/shm $rootfs/dev/shm.bak ln -s /run/shm $rootfs/dev/shm fi return 0 } usage() { cat < ]: Release name of container, defaults to host [ --rootfs ]: Path in which rootfs will be placed [ -a | --arch ]: Architecture of container, defaults to host architecture [ -T | --tarball ]: Location of tarball [ -d | --debug ]: Run with 'set -x' to debug errors [ -s | --stream]: Use specified stream rather than 'tryreleased' Additionally, clone hooks can be passed through (ie, --userdata). For those, see: $CLONE_HOOK_FN --help EOF return 0 } options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata:,vendordata:,mapped-uid:,mapped-gid: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" mapped_uid=-1 mapped_gid=-1 # default release is trusty, or the systems release if recognized release=trusty if [ -f /etc/lsb-release ]; then . /etc/lsb-release rels=$(ubuntu-distro-info --supported 2>/dev/null) || rels="$KNOWN_RELEASES" for r in $rels; do [ "$DISTRIB_CODENAME" = "$r" ] && release="$r" done fi # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/dpkg --print-architecture` elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/udpkg --print-architecture` else arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then arch="amd64" elif [ "$arch" = "armv7l" ]; then # note: arm images don't exist before oneiric; are called armhf in # trusty and later; and are not supported by the query, so we don't actually # support them yet (see check later on). When Query2 is available, # we'll use that to enable arm images. arch="armhf" elif [ "$arch" = "aarch64" ]; then arch="arm64" elif [ "$arch" = "ppc64le" ]; then arch="ppc64el" fi fi debug=0 hostarch=$arch cloud=0 locales=1 flushcache=0 stream="tryreleased" cloneargs=() while true do case "$1" in -h|--help) usage $0 && exit 1;; -p|--path) path=$2; shift 2;; -n|--name) name=$2; shift 2;; -F|--flush-cache) flushcache=1; shift 1;; -r|--release) release=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -T|--tarball) tarball=$2; shift 2;; -d|--debug) debug=1; shift 1;; -s|--stream) stream=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -L|--no?locales) cloneargs[${#cloneargs[@]}]="--no-locales"; shift 1;; -i|--hostid) cloneargs[${#cloneargs[@]}]="--hostid=$2"; shift 2;; -u|--userdata) cloneargs[${#cloneargs[@]}]="--userdata=$2"; shift 2;; -V|--vendordata) cloneargs[${#cloneargs[@]}]="--vendordata=$2"; shift 2;; -C|--cloud) cloneargs[${#cloneargs[@]}]="--cloud"; shift 1;; -S|--auth-key) cloneargs[${#cloneargs[@]}]="--auth-key=$2"; shift 2;; --mapped-uid) mapped_uid=$2; shift 2;; --mapped-gid) mapped_gid=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done cloneargs=( "--name=$name" "${cloneargs[@]}" ) if [ $debug -eq 1 ]; then set -x fi if [ "$arch" = "i686" ]; then arch=i386 fi if [ "$skip_arch_check" = "0" ]; then case "$hostarch:$arch" in $arch:$arch) : ;; # the host == container amd64:i386) :;; # supported "cross" arm64:arm*) :;; # supported "cross" armel:armhf) :;; # supported "cross" armhf:armel) :;; # supported "cross" *) echo "cannot create '$arch' container on hostarch '$hostarch'"; exit 1;; esac fi if [ "$stream" != "daily" -a "$stream" != "released" -a "$stream" != "tryreleased" ]; then echo "Only 'daily' and 'released' and 'tryreleased' streams are supported" exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi type ubuntu-cloudimg-query type wget # determine the url, tarball, and directory names # download if needed # Allow the cache base to be set by environment variable cache=${LXC_CACHE_PATH:-"$STATE_DIR/cache/lxc"}/cloud-$release if [ $in_userns -eq 1 ]; then STATE_DIR="$HOME/.cache/lxc" cache=${LXC_CACHE_PATH:-"$STATE_DIR"}/cloud-$release fi mkdir -p $cache if [ "$stream" = "tryreleased" ]; then stream=released ubuntu-cloudimg-query $release $stream $arch 1>/dev/null 2>/dev/null || stream=daily fi if [ -n "$tarball" ]; then url2="$tarball" else if ! url1=`ubuntu-cloudimg-query $release $stream $arch --format "%{url}\n"`; then echo "There is no download available for release=$release, stream=$stream, arch=$arch" [ "$stream" = "daily" ] || echo "You may try with '--stream=daily'" exit 1 fi if [ "$release" = "precise" ] || [ "$release" = "trusty" ]; then url2=`echo $url1 | sed -e 's/.tar.gz/-root\0/' -e 's/.tar.gz/.tar.xz/'` else url2=`echo $url1 | sed -e 's/.tar.gz/.squashfs/'` fi fi filename=`basename $url2` wgetcleanup() { rm -f $filename } do_extract_rootfs() { cd $cache if [ $flushcache -eq 1 ]; then echo "Clearing the cached images" rm -f $filename fi trap wgetcleanup EXIT SIGHUP SIGINT SIGTERM if [ ! -f $filename ]; then wget $url2 fi trap EXIT trap SIGHUP trap SIGINT trap SIGTERM echo "Extracting container rootfs" mkdir -p $rootfs cd $rootfs if [ "${filename##*.}" = "squashfs" ]; then unsquashfs -n -f -d "$rootfs" "$cache/$filename" else if [ $in_userns -eq 1 ]; then tar --anchored --exclude="dev/*" --numeric-owner -xpf "$cache/$filename" mkdir -p $rootfs/dev/pts/ else tar --numeric-owner -xpf "$cache/$filename" fi fi } if [ -n "$tarball" ]; then do_extract_rootfs else mkdir -p "$STATE_DIR/lock/subsys/" ( flock -x 9 do_extract_rootfs ) 9>"$STATE_DIR/lock/subsys/lxc-ubuntu-cloud" fi copy_configuration $path $rootfs $name $arch $release "$CLONE_HOOK_FN" "${cloneargs[@]}" "$rootfs" if [ $mapped_uid -ne -1 ]; then chown $mapped_uid $path/config chown -R $mapped_uid $STATE_DIR chown -R $mapped_uid $cache fi if [ $mapped_gid -ne -1 ]; then chgrp $mapped_gid $path/config chgrp -R $mapped_gid $STATE_DIR chgrp -R $mapped_gid $cache fi echo "Container $name created." exit 0 # vi: ts=4 expandtab lxc-2.0.11/templates/lxc-sshd.in0000644061062106075000000001523613435013473013377 00000000000000#!/bin/bash # # lxc: linux Container library # Authors: # Daniel Lezcano # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin install_sshd() { rootfs=$1 tree="\ $rootfs/var/empty/sshd \ $rootfs/var/lib/empty/sshd \ $rootfs/etc/init.d \ $rootfs/etc/rc.d \ $rootfs/etc/ssh \ $rootfs/etc/sysconfig/network-scripts \ $rootfs/dev/shm \ $rootfs/run/sshd \ $rootfs/proc \ $rootfs/sys \ $rootfs/bin \ $rootfs/sbin \ $rootfs/usr \ $rootfs/tmp \ $rootfs/home \ $rootfs/root \ $rootfs/lib \ $rootfs/lib64" mkdir -p $tree if [ $? -ne 0 ]; then return 1 fi ln -s /run $rootfs/var/run if [ $? -ne 0 ]; then return 1 fi return 0 } configure_sshd() { rootfs=$1 cat < $rootfs/etc/passwd root:x:0:0:root:/root:/bin/bash sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin EOF cat < $rootfs/etc/group root:x:0:root sshd:x:74: EOF ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key # by default setup root password with no password cat < $rootfs/etc/ssh/sshd_config Port 22 Protocol 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key UsePrivilegeSeparation yes SyslogFacility AUTH LogLevel INFO LoginGraceTime 120 PermitRootLogin yes StrictModes yes PubkeyAuthentication yes IgnoreRhosts yes HostbasedAuthentication no PermitEmptyPasswords yes ChallengeResponseAuthentication no EOF if [ -n "$auth_key" -a -f "$auth_key" ]; then u_path="/root/.ssh" root_u_path="$rootfs/$u_path" mkdir -p $root_u_path cp $auth_key "$root_u_path/authorized_keys" chown -R 0:0 "$rootfs/$u_path" chmod 700 "$rootfs/$u_path" echo "Inserted SSH public key from $auth_key into $rootfs/$u_path" fi return 0 } copy_configuration() { path=$1 rootfs=$2 name=$3 init_path=$(realpath --relative-to=/ $(readlink -f /sbin/init)) grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.pts = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined lxc.mount.entry = /dev dev none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /bin bin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = tmpfs run/sshd tmpfs mode=0644 0 0 lxc.mount.entry = @LXCTEMPLATEDIR@/lxc-sshd $init_path none ro,bind 0 0 lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0 lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed EOF # Oracle Linux and Fedora need the following two bind mounted if [ -d /etc/sysconfig/network-scripts ]; then cat <> $path/config lxc.mount.entry = /etc/sysconfig/network-scripts etc/sysconfig/network-scripts none ro,bind 0 0 EOF fi if [ -d /etc/rc.d ]; then cat <> $path/config lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0 EOF fi # if no .ipv4 section in config, then have the container run dhcp grep -q "^lxc.network.ipv4" $path/config || touch $rootfs/run-dhcp if [ "$(uname -m)" = "x86_64" ]; then cat <> $path/config lxc.mount.entry = /lib64 lib64 none ro,bind 0 0 EOF fi } usage() { cat < [--rootfs=] EOF return 0 } check_for_cmd() { cmd_path=`type $1` if [ $? -ne 0 ]; then echo "The command '$1' $cmd_path is not accessible on the system" exit 1 fi # we use cut instead of awk because awk is alternatives symlink on ubuntu # and /etc/alternatives isn't bind mounted cmd_path=`echo $cmd_path |cut -d ' ' -f 3` } options=$(getopt -o hp:n:S: -l help,rootfs:,path:,name:,auth-key: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -S|--auth-key) auth_key=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ $0 = "/sbin/init" ]; then PATH="$PATH:/bin:/sbin:/usr/sbin" check_for_cmd @SBINDIR@/init.lxc check_for_cmd sshd sshd_path=$cmd_path # run dhcp? if [ -f /run-dhcp ]; then check_for_cmd dhclient check_for_cmd ifconfig touch /etc/fstab rm -f /dhclient.conf cat > /dhclient.conf << EOF send host-name = gethostname(); EOF ifconfig eth0 up dhclient eth0 -cf /dhclient.conf echo "Container IP address:" ifconfig eth0 |grep inet fi exec @SBINDIR@/init.lxc -- $sshd_path exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi install_sshd $rootfs if [ $? -ne 0 ]; then echo "failed to install sshd's rootfs" exit 1 fi configure_sshd $rootfs if [ $? -ne 0 ]; then echo "failed to configure sshd template" exit 1 fi copy_configuration $path $rootfs $name if [ $? -ne 0 ]; then echo "failed to write configuration file" exit 1 fi lxc-2.0.11/templates/lxc-opensuse.in0000644061062106075000000004076013435013473014277 00000000000000#!/bin/bash # # template script for generating OpenSUSE container for LXC # # # lxc: linux Container library # Authors: # Daniel Lezcano # Frederic Crozat # Michael H. Warfield # Johannes Kastl # Thomas Lamprecht # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin if [ -x /usr/bin/obs-build ]; then BUILD=/usr/bin/obs-build export BUILD_DIR=/usr/lib/obs-build else BUILD=/usr/bin/build export BUILD_DIR=/usr/lib/build fi configure_opensuse() { rootfs=$1 hostname=$2 # set first network adapter as dhcp. This is the most common config. cat < $rootfs/etc/sysconfig/network/ifcfg-eth0 STARTMODE='auto' BOOTPROTO='dhcp' EOF # create empty fstab touch $rootfs/etc/fstab # set the hostname cat < $rootfs/etc/HOSTNAME $hostname EOF # ensure /etc/hostname is available too ln -s -f HOSTNAME $rootfs/etc/hostname # do not use hostname from HOSTNAME variable cat <> $rootfs/etc/sysconfig/cron unset HOSTNAME EOF # set minimal hosts cat < $rootfs/etc/hosts 127.0.0.1 localhost $hostname EOF # disable yast->bootloader in container cat < $rootfs/etc/sysconfig/bootloader LOADER_TYPE=none LOADER_LOCATION=none EOF # set /dev/console as securetty cat << EOF >> $rootfs/etc/securetty console EOF cat <> $rootfs/etc/sysconfig/boot # disable root fsck ROOTFS_FSCK="0" ROOTFS_BLKDEV="/dev/null" EOF # remove pointless services in a container ln -s /dev/null $rootfs/etc/systemd/system/proc-sys-fs-binfmt_misc.automount ln -s /dev/null $rootfs/etc/systemd/system/console-shell.service ln -s /dev/null $rootfs/etc/systemd/system/systemd-vconsole-setup.service # enable getty and console services sed -e 's/ConditionPathExists=.*//' $rootfs/usr/lib/systemd/system/getty@.service > $rootfs/etc/systemd/system/getty@.service ln -s getty@.service $rootfs/etc/systemd/system/getty@tty1.service mkdir -p $rootfs/etc/systemd/system/getty.target.wants/ ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@console.service ln -s -f ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty1.service ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty2.service ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty3.service ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty4.service touch $rootfs/etc/sysconfig/kernel echo "Please change root-password !" return 0 } download_opensuse() { cache=$1 arch=$2 if [ ! -x ${BUILD} ]; then echo "Could not create openSUSE template :" echo "you need to install \"build\" package" return 1 fi # check the mini opensuse was not already downloaded mkdir -p "$cache/partial-$arch" if [ $? -ne 0 ]; then echo "Failed to create '$cache/partial-$arch' directory" return 1 fi # download a mini opensuse into a cache echo "Downloading opensuse minimal ..." mkdir -p "$cache/partial-$arch-packages" oss_repo_url="http://download.opensuse.org/distribution/$DISTRO/repo/oss/" if [[ $DISTRO == "tumbleweed" ]]; then oss_repo_url="http://download.opensuse.org/$DISTRO/repo/oss/" fi zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$oss_repo_url" repo-oss || return 1 update_repo_url="http://download.opensuse.org/update/$DISTRO/repo/oss" # Leap update repos were rearranged if [[ $DISTRO == "leap/4"* ]]; then update_repo_url="http://download.opensuse.org/update/$DISTRO/oss/" fi # tumbleweed has no update repo if [[ $DISTRO != "tumbleweed" ]]; then zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$update_repo_url" update || return 1 fi zypper --quiet --root $cache/partial-$arch-packages --non-interactive --gpg-auto-import-keys update || return 1 zypper --root $cache/partial-$arch-packages --non-interactive in --auto-agree-with-licenses --download-only zypper lxc patterns-openSUSE-base bash iputils sed tar rsyslog || return 1 cat > $cache/partial-$arch-packages/opensuse.conf << EOF Preinstall: aaa_base bash coreutils diffutils Preinstall: filesystem fillup glibc grep insserv-compat perl-base Preinstall: libbz2-1 pam Preinstall: permissions rpm sed tar libz1 libselinux1 Preinstall: liblzma5 libcap2 libacl1 libattr1 Preinstall: libpopt0 libelf1 Preinstall: libpcre1 RunScripts: aaa_base Support: zypper Support: patterns-openSUSE-base Support: lxc Support: ncurses-utils Support: iputils Support: udev Support: netcfg Support: hwinfo insserv-compat module-init-tools openSUSE-release openssh Support: pwdutils sysconfig Ignore: rpm:suse-build-key,build-key Ignore: systemd:systemd-presets-branding EOF if [ $DISTRO = "13.2" ] then echo "Support: python3-base" >> $cache/partial-$arch-packages/opensuse.conf fi if [[ $DISTRO == "tumbleweed" ]]; then echo "Preinstall: liblua5_3 libncurses6 libreadline7" >> $cache/partial-$arch-packages/opensuse.conf else echo "Preinstall: liblua5_1 libncurses5 libreadline6" >> $cache/partial-$arch-packages/opensuse.conf echo "Support: rpcbind" >> $cache/partial-$arch-packages/opensuse.conf fi # dhcpcd is not in the default repos since Leap 42.1, neither in tumbleweed if [[ $DISTRO != "leap/4"* ]] && [[ $DISTRO != "tumbleweed" ]]; then echo "Support: dhcpcd" >> $cache/partial-$arch-packages/opensuse.conf fi # Leap and tumbleweed doesn't seem to have iproute2 utils installed if [[ $DISTRO == "leap/4"* ]] || [[ $DISTRO == "tumbleweed" ]]; then echo "Support: net-tools iproute2" >> $cache/partial-$arch-packages/opensuse.conf fi if [ "$arch" = "i686" ]; then mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/ for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i586/*" ; do ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/ done mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686 for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/update/i586/*" ; do ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686/ done fi # openSUSE 13.2 has no noarch directory in update [ -d $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch ] || mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch repos=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/noarch") if [[ $DISTRO != "tumbleweed" ]]; then # tumbleweed has no update repo repos+=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch") fi CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:$BUILD_DIR" $BUILD_DIR/init_buildsystem --clean --configdir $BUILD_DIR/configs --cachedir $cache/partial-$arch-cache ${repos[*]} || return 1 chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$oss_repo_url" repo-oss || return 1 if [[ $DISTRO != "tumbleweed" ]]; then chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$update_repo_url" update || return 1 fi # really clean the image rm -fr $cache/partial-$arch/{.build,.guessed_dist,.srcfiles*,installed-pkg} rm -fr $cache/partial-$arch/dev # make sure we have a minimal /dev mkdir -p "$cache/partial-$arch/dev" mknod -m 666 $cache/partial-$arch/dev/null c 1 3 mknod -m 666 $cache/partial-$arch/dev/zero c 1 5 # create mtab symlink rm -f $cache/partial-$arch/etc/mtab ln -sf /proc/self/mounts $cache/partial-$arch/etc/mtab # ensure /var/run and /run are symlinked rm -fr $cache/partial-$arch/var/run ln -s -f ../run $cache/partial-$arch/var/run if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi rm -fr "$cache/partial-$arch-packages" mv "$1/partial-$arch" "$1/rootfs-$arch" echo "Download complete." return 0 } copy_opensuse() { cache=$1 arch=$2 rootfs=$3 # make a local copy of the mini opensuse echo "Copying rootfs to $rootfs ..." mkdir -p $rootfs rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 return 0 } install_opensuse() { # Allow the cache base to be set by environment variable cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/opensuse/$DISTRO}" rootfs=$1 mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs-$arch ... " if [ ! -e "$cache/rootfs-$arch" ]; then download_opensuse $cache $arch if [ $? -ne 0 ]; then echo "Failed to download 'opensuse base'" return 1 fi fi echo "Copy $cache/rootfs-$arch to $rootfs ... " copy_opensuse $cache $arch $rootfs if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse return $? } # Generate a random hardware (MAC) address composed of FE followed by # 5 random bytes... create_hwaddr() { openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' } copy_configuration() { path=$1 rootfs=$2 name=$3 grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo " lxc.rootfs = $rootfs_path " >> $path/config # The following code is to create static MAC addresses for each # interface in the container. This code will work for multiple # interfaces in the default config. It will also strip any # hwaddr stanzas out of the default config since we can not share # MAC addresses between containers. # # This code is largely mimiced from the Fedora Template. mv $path/config $path/config.def while read LINE do # This should catch variable expansions from the default config... if expr "${LINE}" : '.*\$' > /dev/null 2>&1 then LINE=$(eval "echo \"${LINE}\"") fi # There is a tab and a space in the regex bracket below! # Seems that \s doesn't work in brackets. KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') if [[ "${KEY}" != "lxc.network.hwaddr" ]] then echo "${LINE}" >> $path/config if [[ "${KEY}" == "lxc.network.link" ]] then echo "lxc.network.hwaddr = $(create_hwaddr)" >> $path/config fi fi done < $path/config.def rm -f $path/config.def if [ -e "@LXCTEMPLATECONFIG@/opensuse.common.conf" ]; then echo " # Include common configuration lxc.include = @LXCTEMPLATECONFIG@/opensuse.common.conf " >> $path/config fi # Append things which require expansion here... cat <> $path/config lxc.arch = $arch lxc.utsname = $name lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed # When using LXC with apparmor, uncomment the next line to run unconfined: lxc.aa_profile = unconfined # example simple networking setup, uncomment to enable #lxc.network.type = $lxc_network_type #lxc.network.flags = up #lxc.network.link = $lxc_network_link #lxc.network.name = eth0 # Additional example for veth network type # static MAC address, #lxc.network.hwaddr = 00:16:3e:77:52:20 # persistent veth device name on host side # Note: This may potentially collide with other containers of same name! #lxc.network.veth.pair = v-$name-e0 EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/opensuse}" if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse } usage() { cat < -r|--release nn.n --clean Please give the release as 13.1, 13.2 etc. If no release is given, openSUSE Leap 42.2 is installed. EOF return 0 } # Make arch a global. This may become configurable? arch=$(uname -m) options=$(getopt -o hp:n:r:c -l help,rootfs:,path:,name:,release:,clean -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -r|--release) DISTRO=$2; shift 2;; -c|--clean) clean=1; shift 1;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi type zypper > /dev/null if [ $? -ne 0 ]; then echo "'zypper' command is missing" exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi if grep -q Harlequin /etc/os-release || grep -q Tumbleweed /etc/os-release ; then BVER=`rpm -q --qf '%{version}\n' build` if [ $? -ne 0 -o "$BVER" -lt "20141120" ]; then echo "Building openSUSE containers with your version of the build package is broken. Please install the update to version 20141120 or newer." exit 1 fi fi if [ -z "$DISTRO" ]; then echo "" echo "No release selected, using openSUSE Leap 42.2" DISTRO="leap/42.2" else echo "" case "$DISTRO" in 13.1) echo "Selected openSUSE 13.1" ;; 13.2) echo "Selected openSUSE 13.2" ;; 42.1|leap/42.1|leap) echo "Selected openSUSE Leap 42.1" DISTRO="leap/42.1" ;; 42.2|leap/42.2|422) echo "Selected openSUSE Leap 42.2" DISTRO="leap/42.2" ;; 42.3|leap/42.3|423) echo "Selected openSUSE Leap 42.3" DISTRO="leap/42.3" ;; tumbleweed|factory) echo "Selected openSUSE Leap Tumbleweed" DISTRO="tumbleweed" ;; *) echo "You have chosen an invalid release, quitting..." exit 1 ;; esac fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi install_opensuse $rootfs if [ $? -ne 0 ]; then echo "failed to install opensuse" exit 1 fi configure_opensuse $rootfs $name if [ $? -ne 0 ]; then echo "failed to configure opensuse for a container" exit 1 fi copy_configuration $path $rootfs $name if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi lxc-2.0.11/templates/Makefile.am0000644061062106075000000000052713435013473013353 00000000000000templatesdir=@LXCTEMPLATEDIR@ templates_SCRIPTS = \ lxc-alpine \ lxc-altlinux \ lxc-archlinux \ lxc-busybox \ lxc-centos \ lxc-cirros \ lxc-debian \ lxc-download \ lxc-fedora \ lxc-gentoo \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ lxc-plamo \ lxc-slackware \ lxc-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud \ lxc-sparclinux lxc-2.0.11/templates/lxc-archlinux.in0000644061062106075000000002561513435013473014435 00000000000000#!/bin/bash # # template script for generating Arch Linux container for LXC # # # lxc: linux Container library # Authors: # Alexander Vladimirov # John Lane # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin # defaults arch=$(uname -m) default_path="@LXCPATH@" default_locale="en_US.UTF-8" pacman_config="/etc/pacman.conf" common_config="@LXCTEMPLATECONFIG@/common.conf" shared_config="@LXCTEMPLATECONFIG@/archlinux.common.conf" # by default, install 'base' except the kernel pkg_blacklist="linux" base_packages=() for pkg in $(pacman -Sqg base); do [ "${pkg_blacklist#*$pkg}" = "$pkg_blacklist" ] && base_packages+=($pkg) done declare -a additional_packages # split comma-separated string into an array # ${1} - string to split # ${2} - separator (default is ",") # ${result} - result value on success split_string() { local ifs=${IFS} IFS="${2:-,}" read -a result < <(echo "${1}") IFS=${ifs} return 0 } [ -f /etc/arch-release ] && is_arch=true # Arch-specific preconfiguration for container configure_arch() { # on ArchLinux, read defaults from host systemd configuration if [ "${is_arch}" ]; then cp -p /etc/locale.conf /etc/locale.gen "${rootfs_path}/etc/" else echo "LANG=${default_locale}" > "${rootfs_path}/etc/locale.conf" if [ -e "${rootfs_path}/etc/locale.gen" ]; then sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" if [ ! "${default_locale}" = "en_US.UTF-8" ]; then echo "${default_locale} ${default_locale##*.}" >> \ "${rootfs_path}/etc/locale.gen" fi fi fi # hostname and nameservers echo "${name}" > "${rootfs_path}/etc/hostname" # network configuration cat > "${rootfs_path}/etc/systemd/network/eth0.network" << EOF [Match] Name=eth0 [Network] DHCP=ipv4 EOF # chroot and configure system arch-chroot "${rootfs_path}" /bin/bash -s << EOF mkdir /run/lock locale-gen # set default boot target ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target # override getty@.service for container ttys sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ -e 's/After=dev-%i.device/After=/' \ < /lib/systemd/system/getty\@.service \ > /etc/systemd/system/getty\@.service # fix systemd-sysctl service sed -e 's/^ConditionPathIsReadWrite=\/proc\/sys\/$/ConditionPathIsReadWrite=\/proc\/sys\/net\//' \ -e 's/^ExecStart=\/usr\/lib\/systemd\/systemd-sysctl$/ExecStart=\/usr\/lib\/systemd\/systemd-sysctl --prefix net/' \ -i /usr/lib/systemd/system/systemd-sysctl.service # initialize pacman keyring pacman-key --init pacman-key --populate archlinux # enable networkd systemctl enable systemd-networkd systemctl enable systemd-resolved ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf EOF # enable getty on active ttys local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.devttydir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") local devtty="" # bind getty instances to /dev//tty* if lxc.devttydir is set [ -n "${devttydir}" ] && devtty="${devttydir}-" if [ ${nttys:-0} -gt 1 ]; then ( cd "${rootfs_path}/etc/systemd/system/getty.target.wants" for i in $(seq 1 $nttys); do ln -sf "../getty@.service" "getty@${devtty}tty${i}.service"; done ) fi # update securetty to allow console login if devttydir is set if [ -n "${devttydir}" ]; then for i in $(seq 1 ${nttys:-1}); do echo "${devttydir}/tty${i}" >> "${rootfs_path}/etc/securetty" done fi [ -n "${devttydir}" ] && echo "${devttydir}/console" >> "${rootfs_path}/etc/securetty" # Arch default configuration allows only tty1-6 for login [ ${nttys:-0} -gt 6 ] && echo \ "You may want to modify container's /etc/securetty \ file to allow root logins on tty7 and higher" return 0 } # write container configuration files copy_configuration() { mkdir -p "${config_path}" local config="${config_path}/config" echo "lxc.utsname = ${name}" >> "${config}" grep -q "^lxc.arch" "${config}" 2>/dev/null \ || echo "lxc.arch = ${arch}" >> "${config}" grep -q "^lxc.rootfs" "${config}" 2>/dev/null \ || echo "lxc.rootfs = ${rootfs_path}" >> "${config}" [ -e "${shared_config}" ] \ && echo "lxc.include = ${shared_config}" >> "${config}" if [ $? -ne 0 ]; then echo "Failed to configure container" return 1 fi return 0 } # install packages within container chroot install_arch() { [ "${arch}" != "$(uname -m)" ] && different_arch=true if [ "${different_arch}" = "true" ]; then container_pacman_config=$(mktemp) container_mirrorlist=$(mktemp) sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ "${pacman_config}" > "${container_pacman_config}" sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ /etc/pacman.d/mirrorlist > "${container_mirrorlist}" pacman_config="${container_pacman_config}" fi if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ ${base_packages[@]}; then echo "Failed to install container packages" return 1 fi if [ "${different_arch}" = "true" ]; then sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ "${rootfs_path}"/etc/pacman.conf cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist rm "${container_pacman_config}" "${container_mirrorlist}" fi [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" return 0 } usage() { cat < [-p|--path=] [-a|--arch=] [-r|--root_password=] [-P|--packages=] [-e|--enable_units=unit1,unit2...] [-d|--disable_units=unit1,unit2...] [-c|--config=] [-h|--help] Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: -p,--path path to where the container rootfs will be created (${default_path}) --rootfs path for actual container rootfs, (${default_path}/rootfs) -P,--packages preinstall additional packages, comma-separated list -e,--enable_units enable systemd services, comma-separated list -d,--disable_units disable systemd services, comma-separated list -c,--config use specified pacman config when installing container packages -a,--arch use specified architecture instead of host's architecture -r,--root_password set container root password -h,--help print this help EOF return 0 } options=$(getopt -o hp:P:e:d:n:c:a:r: -l help,rootfs:,path:,packages:,enable_units:,disable_units:,name:,config:,arch:,root_password: -- "${@}") if [ ${?} -ne 0 ]; then usage $(basename ${0}) exit 1 fi eval set -- "${options}" while true do case "${1}" in -h|--help) usage ${0} && exit 0;; -p|--path) path=${2}; shift 2;; -n|--name) name=${2}; shift 2;; --rootfs) rootfs_path=${2}; shift 2;; -P|--packages) additional_packages=${2}; shift 2;; -e|--enable_units) enable_units=${2}; shift 2;; -d|--disable_units) disable_units=${2}; shift 2;; -c|--config) pacman_config=${2}; shift 2;; -a|--arch) arch=${2}; shift 2;; -r|--root_password) root_passwd=${2}; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ -z "${name}" ]; then echo "missing required 'name' parameter" exit 1 fi type pacman >/dev/null 2>&1 if [ ${?} -ne 0 ]; then echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" exit 1 fi if [ -z "${path}" ]; then path="${default_path}/${name}" fi if [ "${EUID}" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -z "$rootfs_path" ]; then rootfs_path="${path}/rootfs" fi config_path="${path}" revert() { echo "Interrupted, cleaning up" lxc-destroy -n "${name}" rm -rf "${path}/${name}" rm -rf "${default_path}/${name}" exit 1 } trap revert SIGHUP SIGINT SIGTERM copy_configuration if [ ${?} -ne 0 ]; then echo "failed to write configuration file" rm -rf "${config_path}" exit 1 fi if [ ${#additional_packages[@]} -gt 0 ]; then split_string ${additional_packages} base_packages+=(${result[@]}) fi mkdir -p "${rootfs_path}" install_arch if [ ${?} -ne 0 ]; then echo "failed to install Arch Linux" rm -rf "${config_path}" "${path}" exit 1 fi configure_arch if [ ${?} -ne 0 ]; then echo "failed to configure Arch Linux for a container" rm -rf "${config_path}" "${path}" exit 1 fi if [ ${#enable_units[@]} -gt 0 ]; then split_string ${enable_units} for unit in ${result[@]}; do [ "${unit##*.}" = "service" ] || unit="${unit}.service" ln -s "/usr/lib/systemd/system/${unit}" \ "${rootfs_path}/etc/systemd/system/multi-user.target.wants/" done fi if [ ${#disable_units[@]} -gt 0 ]; then split_string ${disable_units} for unit in ${result[@]}; do [ "${unit##*.}" = "service" ] || unit="${unit}.service" ln -s /dev/null "${rootfs_path}/etc/systemd/system/${unit}" done fi if [ -n "${root_passwd}" ]; then echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd fi cat << EOF Arch Linux container ${name} is successfully created! The configuration is stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for information about configuring Arch Linux. EOF lxc-2.0.11/templates/lxc-gentoo.in0000644061062106075000000006741213435013473013734 00000000000000#!/bin/bash # # LXC template for gentoo # # Author: Guillaume Zitta # # Widely inspired from lxc-gentoo script at https://github.com/globalcitizen/lxc-gentoo # # this version is reworked with : # - out of the lxc-create compat # - vanilla gentoo config # - ready to use cache # # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin # Ensure strict root's umask doesen't render the VM unusable umask 022 LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" ################################################################################ # Various helper functions ################################################################################ # param: $1: the name of the lock # param: $2: the timeout for the lock # The rest contain the command to execute and its parameters execute_exclusively() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ local lock_name="$1" local timeout="$2" shift 2 { printf "Attempting to obtain an exclusive lock (timeout: %s sec) named \"%s\"...\n" "${timeout}" "$lock_name" flock -x -w "${timeout}" 50 if [[ $? -ne 0 ]]; then printf " => unable to obtain lock, aborting.\n" return 2 else printf " => done.\n" fi printf " => Executing \"%s\"\n" "$*" "$@" retval=$? } 50> "@LOCALSTATEDIR@/lock/subsys/lxc-gentoo-${lock_name}" return $retval } # a die function is always a good idea die() { printf "\n[the last exit code leading to this death was: %s ]\n" "$?" local retval="$1" shift 1 printf "$@" exit "$retval" } # gentoo arch/variant detection set_default_arch() { printf "### set_default_arch: default arch/variant autodetect...\n" arch=$(uname -m) if [[ $arch =~ i.86 ]]; then arch="x86" variant="x86" elif [[ $arch == "x86_64" ]]; then arch="amd64" variant="amd64" elif [[ $arch =~ arm.* ]]; then arch="arm" variant="armv7a" else #who knows, it may work... printf " => warn: unexpected arch:${arch} let me knows if it works :)\n" variant="${arch}" fi printf " => Got: arch=%s variant=%s\n" "${arch}" "${variant}" } store_user_message() { user_message="${user_message}=> $@\n" } ################################################################################ # CACHE Preparation ################################################################################ # during setup cachedir is $cacheroot/partial-$arch-$variant # at the end, it will be $cacheroot/rootfs-$arch-$variant cache_setup(){ partialfs="${cacheroot}/partial-${arch}-${variant}" #if cache exists and flush not needed, return [[ -d "${cachefs}" && -z "${flush_cache}" ]] && return 0 printf "###### cache_setup(): doing cache preparation\n" local retval=1 #clean from failed previous run rm -rf "${partialfs}" mkdir -p "${partialfs}" #let's go cache_precheck && \ cache_stage3 && \ cache_portage && \ cache_inittab && \ cache_net && \ cache_dev && \ cache_openrc && \ cache_locale && \ rm -rf "${cachefs}" && \ mv "${partialfs}" "${cachefs}" && \ printf "###### cache_setup: Cache should be ready\n" return $? } cache_precheck() { printf "### cache_precheck(): doing some pre-start checks ...\n" # never hurts to have a fail-safe. [[ -n "${cacheroot//\/}" ]] \ || die 8 "\$cacheroot (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${cacheroot}" } #get latest stage3 tarball cache_stage3() { printf "### cache_stage3(): stage3 cache deployment...\n" if [ -z "${tarball}" ]; then #variables init local stage3_baseurl="${mirror}/releases/${arch}/autobuilds" # get latest-stage3....txt file for subpath local stage3_pointer="${stage3_baseurl}/latest-stage3-${variant}.txt" printf "Determining path to latest Gentoo %s (%s) stage3 archive...\n" "${arch}" "${variant}" printf " => downloading and processing %s\n" "${stage3_pointer}" local stage3_latest_tarball=$(wget -q -O - "${stage3_pointer}" | tail -n1 | cut -d' ' -f1) \ || die 6 "Error: unable to fetch\n" printf " => Got: %s\n" "${stage3_latest_tarball}" printf "Downloading/untarring the actual stage3 tarball...\n" compressor="j" if echo ${stage3_latest_tarball} | grep ".xz$"; then compressor="J" fi wget -O - "${stage3_baseurl}/${stage3_latest_tarball}" \ | tar -x${compressor}pf - --numeric-owner -C "${partialfs}" \ || die 6 "Error: unable to fetch or untar\n" printf " => extracted to: %s\n" "${partialfs}" else printf "Extracting the stage3 tarball...\n" tar -xpf "${tarball}" --numeric-owner -C "${partialfs}" \ || die 6 "unable to untar ${tarball} to ${partialfs}" fi #check if it chroots printf "chroot test..." chroot ${partialfs} /bin/true || die 1 "Error: chroot %s /bin/true, failed" "${partialfs}" printf " OK\n" printf " => stage3 cache extracted in : %s\n" "${partialfs}" return 0 } cache_portage() { printf "### cache_portage: caching portage tree tarball...\n" [[ -z "${flush_cache}" && -f "${portage_cache}" ]] && return 0 rm -f ${portage_cache} printf "Downloading Gentoo portage (software build database) snapshot...\n" execute_exclusively portage 60 wget -O "${portage_cache}" "${mirror}/snapshots/portage-latest.tar.bz2" \ || die 6 "Error: unable to fetch\n" printf " => done.\n" } # custom inittab cache_inittab() { printf "### cache_inittab: tuning inittab...\n" INITTAB="${partialfs}/etc/inittab" [[ -w "$INITTAB" ]] || die 1 "Error: $INITTAB is not writeable" # create console echo "# Lxc main console" >> "$INITTAB" echo "1:12345:respawn:/sbin/agetty -a root --noclear 115200 console linux" >> "$INITTAB" # finally we add a pf line to enable clean shutdown on SIGPWR (issue 60) echo "# clean container shutdown on SIGPWR" >> "$INITTAB" echo "pf:12345:powerwait:/sbin/halt" >> "$INITTAB" # we also blank out /etc/issue here in order to prevent delays spawning login # caused by attempts to determine domainname on disconnected containers sed -i 's/[\][Oo]//g' "${partialfs}/etc/issue" } cache_net() { printf "### cache_net: doing some useful net tuning...\n" # useful for chroot # /etc/resolv.conf grep -i 'search ' /etc/resolv.conf > "${partialfs}/etc/resolv.conf" grep -i 'nameserver ' /etc/resolv.conf >> "${partialfs}/etc/resolv.conf" # fix boot-time interface config wipe under aggressive cap drop # (openrc 0.9.8.4 ~sep 2012 - https://bugs.gentoo.org/show_bug.cgi?id=436266) # initial warkaround was: sed -i -e 's/^#rc_nostop=""/rc_nostop="net.eth0 net.lo"/' "${partialfs}/etc/rc.conf" # but this one does not depends on interfaces names echo 'rc_keyword="-stop"' >> "${partialfs}/etc/conf.d/net" } cache_dev() { printf "### cache_dev(): /dev tuning...\n" #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 mkdir "${partialfs}/dev/pts" mkdir "${partialfs}/dev/shm" mkdir "${partialfs}/dev/mqueue" mkdir -m 755 "${partialfs}/dev/net" mknod -m 666 "${partialfs}/dev/net/tun" c 10 200 return 0 } # fix openrc system cache_openrc() { printf "### cache_openrc(): doing openrc tuning\n" #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 chroot "${partialfs}" sed s/-lxc//g -i "/etc/init.d/devfs" return 0 } cache_locale() { printf "### cache_locale(): initiating minimale locale en_US.UTF-8 \n" echo "en_US.UTF-8 UTF-8" >> "${partialfs}/etc/locale.gen" chroot "${partialfs}" locale-gen return 0 } ################################################################################ # CONTAINER Preparation ################################################################################ container_setup() { printf "##### container_setup(): starting container setup\n" #in most cases lxc-create should have provided a copy of default lxc.conf #let's tag where template starts, or just create the files echo '### lxc-gentoo template stuff starts here' >> "$path/config" #Determine rootfs #If backingstore was specified, lxc.rootfs should be present or --rootfs did the rootfs var creation if [ -z "${rootfs}" ]; then rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` if [ -z "${rootfs}" ]; then #OK it's default rootfs="${path}/rootfs" fi fi store_user_message "rootfs of container is : ${rootfs}" store_user_message "config of container is : ${path}/config" container_precheck && \ container_rootfs && \ container_consoles && \ container_tz && \ container_portage && \ container_net && \ container_hostname && \ container_auth && \ container_sshd && \ container_conf if [ $? -ne 0 ]; then die 1 "container_setup(): one step didn't complete, sorry\n" fi printf "###### container_setup(): container should be ready to start!\n" printf "\n\n" printf "You could now use you container with: lxc-start -n %s\n" "${name}" printf "little things you should know about your container:\n" printf "${user_message}" return 0 } container_precheck() { printf "### container_precheck(): doing some pre-start checks ...\n" # never hurts to have a fail-safe. [[ -n "${name//\/}" ]] \ || die 8 "\$name (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${name}" [[ -n "${rootfs//\/}" ]] \ || die 8 "\$rootfs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${rootfs}" [[ -n "${cachefs//\/}" ]] \ || die 8 "\$cachefs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${cachefs}" # check if the rootfs already exists [[ -d "${rootfs}/etc" ]] && die 18 "Error: \$rootfs (%s) already exists!" "${rootfs}" # check cache [[ ! -d "${cachefs}/etc" ]] && die 1 "Error: \$cachefs (%s) not found!" "${cachefs}" return 0 } container_rootfs() { printf "#### container_rootfs(): copying rootfs %s from cache %s ...\n" "${rootfs}" "${cachefs}" tar -c -f - --numeric-owner -C "${cachefs}" . \ | tar -x -p -f - --numeric-owner -C "${rootfs}" \ || die 1 "Error: cache copy to rootfs failed" printf "chroot test..." chroot "${rootfs}" /bin/true || die 1 "Error: 'chroot %s /bin/true' failed" printf " OK\n" printf " => done\n" return 0 } container_consoles() { printf "#### container_consoles(): setting container consoles ...\n" # disable unwanted ttys if [[ ${tty} < 6 ]]; then local mindis=$(( ${tty} + 1 )) sed -i "s/^c[${mindis}-6]/#&/" "${rootfs}/etc/inittab" fi printf " => main console + ${tty} ttys\n" if [[ -z "${autologin}" ]]; then sed 's/agetty -a root/agetty/' -i "${rootfs}/etc/inittab" elif [[ "${user}" != "root" ]]; then sed "s/agetty -a root/agetty -a ${user}/" -i "${rootfs}/etc/inittab" printf " => Autologin on main console for %s enabled\n" "${user}" [[ -z "${forced_password}" ]] && unset password store_user_message "${user} has autologin on main console" else printf " => Autologin on main console for root enabled\n" [[ -z "${forced_password}" ]] && unset password store_user_message "${user} has autologin on main console" fi printf " => done\n" } container_tz() { printf "#### container_tz(): setting container timezone ...\n" #let's try to copy it from host if [ -L "/etc/localtime" ]; then #host has a symlink #let see if we can reproduct symlink target=$(readlink /etc/localtime) if [[ "$target" != "" ]]; then if [ -f "${rootfs}/${target}" ]; then #same target exists in container chroot "${rootfs}" ln -sf "${target}" "/etc/localtime" printf " => host symlink reproducted in container : %s\n" "${target}" store_user_message "timezone copyed from host" return 0 fi fi fi if [ -e /etc/localtime ]; then # duplicate host timezone cat /etc/localtime > "${rootfs}/etc/localtime" printf " => host localtime copyed to container\n" store_user_message "timezone was staticly copyed from host" else # otherwise set up UTC chroot "${rootfs}" ln -sf /usr/share/zoneinfo/UTC /etc/localtime printf " => fallback: fixed to UTC\n" store_user_message "timezone was fixed to UTC" fi } container_portage() { printf "#### container_portage(): setting container portage... \n" #default entry for conf portage_mount="#container set with private portage tree, no mount here" printf "Warnings are normal here, don't worry\n" #container repos detection if chroot ${rootfs} portageq get_repo_path / gentoo > /dev/null ; then portage_container="$(chroot ${rootfs} portageq get_repo_path / gentoo)" else die 1 "Failed to figure out container portage tree location with portageq get_repo_path / gentoo\n" fi if [[ -n "${private_portage}" ]]; then container_private_portage return 0 fi if [ -z "${portage_dir}" ]; then #gentoo host detection printf "trying to guess portage_dir from host...\n" portage_dir="$(portageq get_repo_path / gentoo 2>/dev/null)" if [ ! -d "${portage_dir}/profiles" ]; then printf " => host portage detection failed (not gentoo host), fallback to private portage tree\n" container_private_portage return 0 fi else if [ ! -d "${portage_dir}/profiles" ]; then die 1 "specified portage_dir (%s) does not contains profiles, is it a portage tree ?\n" "${portage_dir}" fi fi printf "trying to guess portage distfiles dir from host ...\n" portage_distfiles_dir="$(portageq distdir 2>/dev/null)" if [ ! -d "${portage_distfiles_dir}" ]; then portage_distfiles_dir="${portage_dir}/distfiles" fi # if we are here, we have shared portage_dir #ensure dir exists chroot "${rootfs}" mkdir ${portage_container} portage_mount="#container set with shared portage lxc.mount.entry=${portage_dir} ${portage_container/\//} none ro,bind 0 0 lxc.mount.entry=${portage_distfiles_dir} ${portage_container/\//}/distfiles none rw,bind 0 0 #If you use eix, you should uncomment this #lxc.mount.entry=/var/cache/eix var/cache/eix none ro,bind 0 0" store_user_message "container has a shared portage from host's ${portage_dir} to ${portage_container/\//}" #Let's propose binary packages cat <<- EOF >> "${rootfs}/etc/portage/make.conf" # enable this to store built binary packages #FEATURES="\$FEATURES buildpkg" # enable this to use built binary packages #EMERGE_DEFAULT_OPTS="\${EMERGE_DEFAULT_OPTS} --usepkg" # enable and *tune* this kind of entry to slot binaries, specialy if you use multiples archs and variants #PKGDIR="\${PKGDIR}/amd64 #or PKGDIR="\${PKGDIR}/hardened" EOF printf " => portage stuff done, see /etc/portage/make.conf for additional tricks\n" } container_private_portage() { #called from container_portage() do not call directly from container_setup printf "# untaring private portage to %s from %s ... \n" "${rootfs}/${portage_container}" "${portage_cache}" mkdir -p "${rootfs}/${portage_container}" execute_exclusively portage 60 \ tar -xp --strip-components 1 -C "${rootfs}/${portage_container}" \ -f "${portage_cache}" --numeric-owner \ || die 2 "Error: unable to extract the portage tree.\n" store_user_message "container has its own portage tree at ${portage_container}" printf "=> done\n" } #helper func for container_genconf_net() nic_write() { #display with gentoo's confd.net format echo "config_${nic_name}=\"${nic_conf}\"" #add to managed list [[ "${nic_conf}" == "dhcp" ]] && nic_managed="${nic_managed} ${nic_name}" [[ "${nic_conf}" == "null" ]] && nic_unmanaged="${nic_unmanaged} ${nic_name}" [[ -z "${nic_hwaddr}" && ${nic_type} == "veth" ]] && nic_wo_hwaddr="${nic_wo_hwaddr} ${nic_name}" nic_writed=1 } #Analyse lxc.conf and print conf.d/net content container_conf_net() { local file=${1} [[ -z "${nic_last}" ]] && nic_last=-1 [[ -z "${nic_named}" ]] && nic_named=0 OLDIFS=$IFS IFS=" " #let's do some drity bash things to parse lxc network conf for line in $( sed -r "s/[ ]*=[ ]*/_real_ugly_sep_42_/" "${file}" ); do key=$(echo "${line}" | sed 's/_real_ugly_sep_42_.*$//') value=$(echo "${line}" | sed 's/^.*_real_ugly_sep_42_//') #new nic ! if [[ "${key}" == "lxc.network.type" ]]; then #we don't know what to do with it. [[ "${value}" == "empty" ]] && continue #write conf from previous loops [[ "${nic_writed}" == "0" ]] && nic_write #init defaults let nic_last=nic_last+1 nic_writed=0 #if 1 named between 2 not named: last is eth1 #=> Number is ID munis number of named NIC before nic_name="eth$(( ${nic_last} - ${nic_named} ))" nic_conf="dhcp" nic_type="${value}" fi if [[ "${key}" == "lxc.network.hwaddr" ]]; then nic_hwaddr=1 fi if [[ "${key}" =~ ^lxc.network.ipv(4|6) ]]; then #tell openrc to not manage this NIC as LXC set there address nic_conf="null" fi if [[ "${key}" =~ ^lxc.network.name ]]; then nic_name="${value}" let nic_named=nic_named+1 fi if [[ "${key}" == "lxc.include" ]]; then #recursive into include container_conf_net "${value}" fi done #write conf from previous loops [[ "${nic_writed}" == "0" ]] && nic_write IFS=$OLDIFS } container_net() { printf "container_net(): setting container network conf... \n" #Analyse network configuration in config container_conf_net "$path/config" >> "${rootfs}/etc/conf.d/net" # found how much nic finally have nic_count=$(( ${nic_last} + 1 )) # unless openrc manage a nic, we now have to force openrc to automatic # provision of the 'net' dep. If we do not, network dependent services # will fail to load if [[ -z "${nic_managed}" ]]; then #tell openrc that lxc already did the work echo 'rc_provide="net"' >> "${rootfs}/etc/rc.conf" fi #No NIC ? if [[ ${nic_count} == 0 ]]; then #If no Nic, no need to continue bridge=$(brctl show | awk 'NR==2 {print $1}') if [[ "${bridge}" != "" ]]; then store_user_message "No network interface for this container It's a pitty, you have bridge, ${bridge}. If it is for Lxc, use it next time by adding this to your default.conf : lxc.network.type = veth lxc.network.link = ${bridge} lxc.network.flags = up lxc.network.hwaddr = fe:xx:xx:xx:xx:xx" return 0 else store_user_message "No network interface for this container" return 0 fi fi #For each openrc managed nic, activate sys_nic_index=1 for nic in ${nic_managed} do chroot "${rootfs}" ln -s net.lo "/etc/init.d/net.${nic}" chroot "${rootfs}" rc-update add net.${nic} default #fake sysfs for openrc, in case settings does not provide it mkdir -p "${rootfs}/sys/class/net/${nic}" echo ${sys_nic_index} > "${rootfs}/sys/class/net/${nic}/ifindex" echo up > "${rootfs}/sys/class/net/${nic}/operstate" let sys_nic_index=sys_nic_index+1 done #Warn about dynamic hwaddr if [[ -n "${nic_wo_hwaddr}" ]]; then store_user_message "Warning, these veth NIC don't have fixed hwaddr : ${nic_wo_hwaddr} see http://lists.linuxcontainers.org/pipermail/lxc-devel/2013-December/006736.html and man lxc.conf" fi printf " => network conf done.\n" } # custom hostname container_hostname() { printf "#### container_hostname(): setting hostname... \n" printf "hostname=\"%s\"\n" "${name}" > "${rootfs}/etc/conf.d/hostname" printf " => done.\n" } container_auth() { printf "#### container_auth(): setting authentification... \n" if [[ "${user}" != "root" ]]; then printf " non root user requested, creating... \n" chroot "${rootfs}" useradd --create-home -s /bin/bash "${user}" || die 1 "failed to create user ${user}" printf " => user %s created\n" "${user}" fi store_user_message "Connection user is ${user}" #Home of user auth_home=$(chroot "${rootfs}" getent passwd "${user}" | cut -d : -f 6) if [[ -r "${auth_key}" ]]; then printf " deploying auth_key %s for user %s ...\n" "${auth_key}" "${user}" mkdir -p "${rootfs}/${auth_home}/.ssh" cat "${auth_key}" >> "${rootfs}/${auth_home}/.ssh/authorized_keys" chroot "${rootfs}" chown "${user}:" "${auth_home}/.ssh/authorized_keys" printf " => inserted public key in %s/.ssh/authorized_keys\n" "${auth_home}" [[ -z "${forced_password}" ]] && unset password store_user_message "${user} has the ssh key you gave us" fi if [[ -n "${password}" ]]; then printf " setting password for %s ...\n" "${user}" echo "${user}:${password}" | chroot "${rootfs}" chpasswd || die 1 "failed to change password" printf " => done. if you didn't specify , default is 'toor'\n" if [[ -n "${forced_password}" ]]; then store_user_message "${user} has the password you give for him" fi fi printf " => done.\n" } container_sshd() { printf "#### container_sshd(): enabling sshd... \n" chroot "${rootfs}" rc-update add sshd || die 1 "failed to enable sshd\n" printf " => done.\n" } ################################################################################ # lxc configuration files ################################################################################ container_conf() { printf "container_configuration(): making lxc configuration file... \n" #at this point if there conf_file="${path}/config" # if there is exactly one veth network entry, make sure it has an # associated hwaddr. nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' ${conf_file} | wc -l` if [ $nics -eq 1 ]; then grep -q "^lxc.network.hwaddr" ${conf_file} || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" ${conf_file} fi if grep -q "^lxc.rootfs" "${conf_file}" ; then #lxc-create already provided one conf_rootfs_line="" else conf_rootfs_line="lxc.rootfs = $(readlink -f "${rootfs}")" fi if [[ "${arch}" == "x86" || "${arch}" == "amd64" ]]; then local conf_arch_line="lxc.arch = ${arch}" else local conf_arch_line="# lxc.arch = ${arch}" fi cat <<- EOF >> "${conf_file}" # sets container architecture # If desired architecture != amd64 or x86, then we leave it unset as # LXC does not oficially support anything other than x86 or amd64. ${conf_arch_line} # set the hostname lxc.utsname = ${name} lxc.tty = ${tty} ${conf_rootfs_line} ${portage_mount} ${conf_sysfs} ${conf_mounts} lxc.include = ${LXC_TEMPLATE_CONFIG}/gentoo.${settings}.conf EOF printf " => done.\n" } usage() { cat <] [-v|--variant ] [-P|--private-portage] [--portage-dir ] [-t|--tarball ] [-F|--flush-cache] [-c|--cache-only] [-u|--user ] [-w|--password ] [--autologin] [-S|--auth-key ] [-s|--settings ] [-m|--mirror ] [--tty ] arch: the container architecture (e.g. amd64): defaults to host arch (currently: '${arch}') If you choose one that needs emulation tested: amd64, x86 You could try any other gentoo arch, why not... variant: gentoo's Architecture variant as of dec 2013 : (currently: '${variant}') for amd64 arch: amd64 (default), amd64-hardened+nomultilib, amd64-hardened, amd64-nomultilib, x32 for x86 arch: i686 (default), i486, i686-hardened for arm arch: armv7a (default), armv7a_hardfp, armv6j, armv6j_hardfp, armv5tel, armv4tl private-portage: by default, /usr/portage is mount-binded with host one if exists (currently: '${private_portage}') this force container to have his own copy portage-dir: portage dir used for shared portage by default the host on if any (currently: '${portage_dir}') tarball: force usage of local stage3 archive (currently: '${arch}') If empty, latest will be downloaded flush-cache: do like there is no previous cache cache-only: just ensure cache is present if cache exists and "flush-cache" not specified, does nothing user: user used in auth oriented options (currently: '${user}') password: password for user (currently: '${password}') if default, usage of auth-key will disable password setting autologin: enable autologin for user (currently: '${autologin}') This unset default password setting auth-key: SSH Public key file to inject into container for user (currently: '${auth_key}') This unset default password setting settings: choose common configuration (currently: '${settings}') see ${LXC_TEMPLATE_CONFIG}/gentoo.*.conf Available settings: $(ls -1 ${LXC_TEMPLATE_CONFIG}/gentoo.*.conf | xargs basename -a -s .conf | sed 's/^gentoo.//') mirror: gentoo mirror for download (currently: '${mirror}') tty: number of tty (6 max) (currently: '${tty}') EOF exit 0 } #some overridable defaults set_default_arch mirror="http://distfiles.gentoo.org" user="root" tty=1 settings="common" options=$(getopt -o hp:n:a:FcPv:t:S:u:w:s:m: -l help,rootfs:,path:,name:,arch:,flush-cache,cache-only,private-portage,variant:,portage-dir:,tarball:,auth-key:,user:,autologin,password:,settings:,mirror:,tty: -- "$@") eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; --rootfs) rootfs=$2; shift 2;; -p|--path) path=$2; shift 2;; -n|--name) name=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -F|--flush-cache) flush_cache=1; shift 1;; -c|--cache-only) cache_only=1; shift 1;; -P|--private-portage) private_portage=1; shift 1;; -v|--variant) variant=$2; shift 2;; --portage-dir) portage_dir=$2; shift 2;; -t|--tarball) tarball=$2; shift 2;; -S|--auth-key) auth_key=$2; shift 2;; -u|--user) user=$2; shift 2;; -w|--password) forced_password=1; password=$2; shift 2;; -s|--settings) settings=$2; shift 2;; -m|--mirror) mirror=$2; shift 2;; --container-cache) containercache=$2; shift 2;; --tty) [[ $2 -lt 6 ]] && tty=$2; shift 2;; --autologin) autologin=1; shift 1;; --) shift 1; break ;; *) break ;; esac done # Allow the cache path to be set by environment variable cacheroot="${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/gentoo" portage_cache="${cacheroot}/portage.tbz" cachefs="${cacheroot}/rootfs-${arch}-${variant}" alias wget="wget --timeout=8 --read-timeout=15 -c -t10 -nd" do_all() { cache_setup if [ -z "${cache_only}" ]; then container_setup fi } execute_exclusively "cache-${arch}-${variant}" 60 do_all lxc-2.0.11/templates/lxc-slackware.in0000644061062106075000000004525413435013473014415 00000000000000#!/bin/bash # # lxc: linux Container library # Authors: # Daniel Lezcano # Template for slackware by Matteo Bernardini # some parts are taken from the debian one (used as model) # 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 # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Add some directories to PATH in case we create containers with sudo export PATH=/sbin:/usr/sbin:$PATH cache=${cache:-/var/cache/lxc/slackware} # Use the primary Slackware site by default, but please consider changing # this to a closer mirror site. MIRROR=${MIRROR:-http://ftp.slackware.com/pub/slackware} if [ -z "$arch" ]; then case "$( uname -m )" in i?86) arch=i486 ;; arm*) arch=arm ;; *) arch=$( uname -m ) ;; esac fi LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" configure_slackware() { rootfs=$1 hostname=$2 echo "Configuring..." ; echo # The next part contains excerpts taken from SeTconfig (written by # Patrick Volkerding) from the slackware setup disk. # But before pasting them just set a variable to use them as they are T_PX=$rootfs ( cd $T_PX ; chmod 755 ./ ) ( cd $T_PX ; chmod 755 ./var ) if [ -d $T_PX/usr/src/linux ]; then chmod 755 $T_PX/usr/src/linux fi if [ ! -d $T_PX/proc ]; then mkdir $T_PX/proc chown root.root $T_PX/proc fi if [ ! -d $T_PX/sys ]; then mkdir $T_PX/sys chown root.root $T_PX/sys fi chmod 1777 $T_PX/tmp if [ ! -d $T_PX/var/spool/mail ]; then mkdir -p $T_PX/var/spool/mail chmod 755 $T_PX/var/spool chown root.mail $T_PX/var/spool/mail chmod 1777 $T_PX/var/spool/mail fi echo "#!/bin/sh" > $T_PX/etc/rc.d/rc.keymap echo "# Load the keyboard map. More maps are in /usr/share/kbd/keymaps." \ >> $T_PX/etc/rc.d/rc.keymap echo "if [ -x /usr/bin/loadkeys ]; then" >> $T_PX/etc/rc.d/rc.keymap echo " /usr/bin/loadkeys us" >> $T_PX/etc/rc.d/rc.keymap echo "fi" >> $T_PX/etc/rc.d/rc.keymap chmod 755 $T_PX/etc/rc.d/rc.keymap # Network configuration is left to the user, that have to edit # /etc/rc.d/rc.inet1.conf and /etc/resolv.conf of the container # just set the hostname cat < $rootfs/etc/HOSTNAME $hostname.example.net EOF cp $rootfs/etc/HOSTNAME $rootfs/etc/hostname # make needed devices, from Chris Willing's MAKEDEV.sh # http://www.vislab.uq.edu.au/howto/lxc/MAKEDEV.sh DEV=$rootfs/dev mkdir -p ${DEV} mknod -m 666 ${DEV}/null c 1 3 mknod -m 666 ${DEV}/zero c 1 5 mknod -m 666 ${DEV}/random c 1 8 mknod -m 666 ${DEV}/urandom c 1 9 mkdir -m 755 ${DEV}/pts mkdir -m 1777 ${DEV}/shm mknod -m 666 ${DEV}/tty c 5 0 mknod -m 600 ${DEV}/console c 5 1 mknod -m 666 ${DEV}/tty0 c 4 0 mknod -m 666 ${DEV}/tty1 c 4 1 mknod -m 666 ${DEV}/tty2 c 4 2 mknod -m 666 ${DEV}/tty3 c 4 3 mknod -m 666 ${DEV}/tty4 c 4 4 mknod -m 666 ${DEV}/tty5 c 4 5 mknod -m 666 ${DEV}/full c 1 7 mknod -m 600 ${DEV}/initctl p mknod -m 660 ${DEV}/loop0 b 7 0 mknod -m 660 ${DEV}/loop1 b 7 1 ln -s pts/ptmx ${DEV}/ptmx ln -s /proc/self/fd ${DEV}/fd echo "Adding an etc/fstab" cat >$rootfs/etc/fstab <$rootfs/tmp/rcs.patch <<'EOF' --- ./etc/rc.orig/rc.6 2012-08-15 01:03:12.000000000 +0200 +++ ./etc/rc.d/rc.6 2013-02-17 10:26:30.888839354 +0100 @@ -9,6 +9,12 @@ # Author: Miquel van Smoorenburg # Modified by: Patrick J. Volkerding, # +# minor tweaks for an lxc container +# by Matteo Bernardini , +# based also on Chris Willing's modifications +# http://www.vislab.uq.edu.au/howto/lxc/rc.6 +# a check for a container variable is made to jump sections +container="lxc" # Set the path. PATH=/sbin:/etc:/bin:/usr/bin @@ -37,6 +43,9 @@ ;; esac +# lxc container check +if [ ! $container = "lxc" ]; then + # Save the system time to the hardware clock using hwclock --systohc. if [ -x /sbin/hwclock ]; then # Check for a broken motherboard RTC clock (where ioports for rtc are @@ -53,6 +62,8 @@ fi fi +fi # end container check + # Run any local shutdown scripts: if [ -x /etc/rc.d/rc.local_shutdown ]; then /etc/rc.d/rc.local_shutdown stop @@ -148,6 +159,9 @@ sleep 2 fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Shut down PCMCIA devices: if [ -x /etc/rc.d/rc.pcmcia ]; then . /etc/rc.d/rc.pcmcia stop @@ -155,11 +169,16 @@ /bin/sleep 5 fi +fi # end container check + # Turn off process accounting: if [ -x /sbin/accton -a -r /var/log/pacct ]; then /sbin/accton off fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Terminate acpid before syslog: if [ -x /etc/rc.d/rc.acpid -a -r /var/run/acpid.pid ]; then # quit . /etc/rc.d/rc.acpid stop @@ -170,6 +189,8 @@ sh /etc/rc.d/rc.udev force-stop fi +fi # end container check + # Kill all remaining processes. if [ ! "$1" = "fast" ]; then echo "Sending all processes the SIGTERM signal." @@ -179,6 +200,9 @@ /sbin/killall5 -9 fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Try to turn off quota. if /bin/grep -q quota /etc/fstab ; then if [ -x /sbin/quotaoff ]; then @@ -187,6 +211,8 @@ fi fi +fi # end container check + # Carry a random seed between reboots. echo "Saving random seed from /dev/urandom in /etc/random-seed." # Use the pool size from /proc, or 512 bytes: @@ -205,6 +231,9 @@ rm -f /var/lock/subsys/* fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Turn off swap: echo "Turning off swap." /sbin/swapoff -a @@ -216,6 +245,8 @@ echo "Remounting root filesystem read-only." /bin/mount -v -n -o remount,ro / +fi # end container check + # This never hurts: /bin/sync @@ -240,12 +271,17 @@ done fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Deactivate LVM volume groups: if [ -r /etc/lvmtab -o -d /etc/lvm/backup ]; then echo "Deactivating LVM volume groups:" /sbin/vgchange -an --ignorelockingfailure fi +fi # end container check + # This never hurts again (especially since root-on-LVM always fails # to deactivate the / logical volume... but at least it was # remounted as read-only first) @@ -258,6 +294,9 @@ # This is to ensure all processes have completed on SMP machines: wait +# lxc container check +if [ ! $container = "lxc" ]; then + if [ -x /sbin/genpowerd ]; then # See if this is a powerfail situation: if /bin/egrep -q "FAIL|SCRAM" /etc/upsstatus 2> /dev/null ; then @@ -274,6 +313,13 @@ fi fi +else + +# confirm successful shutdown of the container +echo ; echo "* container stopped. *" ; echo + +fi # end container check + # Now halt (poweroff with APM or ACPI enabled kernels) or reboot. if [ "$command" = "reboot" ]; then echo "Rebooting." --- ./etc/rc.orig/rc.S 2012-09-13 21:38:34.000000000 +0200 +++ ./etc/rc.d/rc.S 2013-02-17 09:39:41.579799641 +0100 @@ -4,9 +4,18 @@ # # Mostly written by: Patrick J. Volkerding, # +# minor tweaks for an lxc container +# by Matteo Bernardini , +# based also on Chris Willing's modifications +# http://www.vislab.uq.edu.au/howto/lxc/rc.S +# a check for a container variable is made to jump sections +container="lxc" PATH=/sbin:/usr/sbin:/bin:/usr/bin +# lxc container check +if [ ! $container = "lxc" ]; then + # Try to mount /proc: /sbin/mount -v proc /proc -n -t proc 2> /dev/null @@ -254,10 +263,27 @@ read junk; fi # Done checking root filesystem +else + # We really don't want to start udev in the container + if [ -f /etc/rc.d/rc.udev ]; then + chmod -x /etc/rc.d/rc.udev + fi + # Alsa won't work + if [ -f /etc/rc.d/rc.alsa ]; then + chmod -x /etc/rc.d/rc.alsa + fi + # This too + if [ -f /etc/rc.d/rc.loop ]; then + chmod -x /etc/rc.d/rc.loop + fi +fi # end container check # Any /etc/mtab that exists here is old, so we start with a new one: /bin/rm -f /etc/mtab{,~,.tmp} && /bin/touch /etc/mtab +# lxc container check +if [ ! $container = "lxc" ]; then + # Add entry for / to /etc/mtab: /sbin/mount -f -w / @@ -337,6 +363,8 @@ # mounted read-write. /sbin/swapon -a 2> /dev/null +fi # end container check + # Clean up some temporary files: rm -f /var/run/* /var/run/*/* /var/run/*/*/* /etc/nologin \ /etc/dhcpc/*.pid /etc/forcefsck /etc/fastboot \ @@ -364,7 +392,7 @@ # if the first line of that file begins with the word 'Linux'. # You are free to modify the rest of the file as you see fit. if [ -x /bin/sed ]; then - /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr)\./}" /etc/motd + /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr) lxc container\./}" /etc/motd fi # If there are SystemV init scripts for this runlevel, run them. @@ -372,6 +400,9 @@ . /etc/rc.d/rc.sysvinit fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Run serial port setup script: # CAREFUL! This can make some systems hang if the rc.serial script isn't # set up correctly. If this happens, you may have to edit the file from a @@ -380,6 +411,8 @@ sh /etc/rc.d/rc.serial start fi +fi # end container check + # Carry an entropy pool between reboots to improve randomness. if [ -f /etc/random-seed ]; then echo "Using /etc/random-seed to initialize /dev/urandom." --- ./etc/rc.orig/rc.M 2012-09-25 19:47:07.000000000 +0200 +++ ./etc/rc.d/rc.M 2013-02-17 09:39:41.579799641 +0100 @@ -10,6 +10,10 @@ # Author: Fred N. van Kempen, # Heavily modified by Patrick Volkerding # +# minor tweaks for an lxc container +# by Matteo Bernardini : +# a check for a container variable is made to jump sections +container="lxc" # Tell the viewers what's going to happen. echo "Going multiuser..." @@ -20,6 +24,9 @@ /sbin/ldconfig & fi +# lxc container check +if [ ! $container = "lxc" ]; then + # Screen blanks after 15 minutes idle time, and powers down in one hour # if the kernel supports APM or ACPI power management: /bin/setterm -blank 15 -powersave powerdown -powerdown 60 @@ -33,6 +40,8 @@ /bin/hostname darkstar fi +fi # end container check + # Set the permissions on /var/log/dmesg according to whether the kernel # permits non-root users to access kernel dmesg information: if [ -r /proc/sys/kernel/dmesg_restrict ]; then @@ -135,6 +144,9 @@ chmod 755 / 2> /dev/null chmod 1777 /tmp /var/tmp +# lxc container check +if [ ! $container = "lxc" ]; then + # Start APM or ACPI daemon. # If APM is enabled in the kernel, start apmd: if [ -e /proc/apm ]; then @@ -146,6 +158,8 @@ . /etc/rc.d/rc.acpid start fi +fi # end container check + # Update any existing icon cache files: if find /usr/share/icons 2> /dev/null | grep -q icon-theme.cache ; then for theme_dir in /usr/share/icons/* ; do --- ./etc/rc.orig/rc.inet1 2012-08-05 19:13:27.000000000 +0200 +++ ./etc/rc.d/rc.inet1 2013-02-17 09:39:41.579799641 +0100 @@ -3,6 +3,11 @@ # This script is used to bring up the various network interfaces. # # @(#)/etc/rc.d/rc.inet1 10.2 Sun Jul 24 12:45:56 PDT 2005 (pjv) +# +# minor tweaks for an lxc container +# by Matteo Bernardini : +# a check for a container variable is made to jump sections +container="lxc" ############################ # READ NETWORK CONFIG FILE # @@ -105,6 +110,10 @@ [ "${IFNAME[$i]}" = "${1}" ] && break i=$(($i+1)) done + + # lxc container check + if [ ! $container = "lxc" ]; then + # If the interface is a bridge, then create it first: [ -n "${BRNICS[$i]}" ] && br_open $i # If the interface isn't in the kernel yet (but there's an alias for it in @@ -115,6 +124,9 @@ /sbin/modprobe ${1} fi fi + + fi # end container check + if grep `echo ${1}: | cut -f 1 -d :`: /proc/net/dev 1> /dev/null ; then # interface exists if ! /sbin/ifconfig | grep -w "${1}" 1>/dev/null || \ ! /sbin/ifconfig ${1} | grep -w inet 1> /dev/null ; then # interface not up or not configured EOF ( cd $rootfs ; patch -p1 < tmp/rcs.patch ; rm tmp/rcs.patch ) # restart rc.inet1 to have routing for the loop device echo "/etc/rc.d/rc.inet1 restart" >> $rootfs/etc/rc.d/rc.local # reduce the number of local consoles: two should be enough sed -i '/^c3\|^c4\|^c5\|^c6/s/^/# /' $rootfs/etc/inittab # better not use this in a container sed -i 's/.*genpowerfail.*//' $rootfs/etc/inittab # add a message to rc.local that confirms successful container startup echo "echo ; echo \"* container $name started. *\" ; echo" >> $rootfs/etc/rc.d/rc.local # borrow the time configuration from the local machine cp -a /etc/localtime $rootfs/etc/localtime return 0 } copy_slackware() { rootfs=$1 # make a local copy of the installed filesystem echo -n "Copying rootfs to $rootfs..." mkdir -p $rootfs cp -a $cache/rootfs-$release-$arch/* $rootfs/ || exit 1 # fix fstab with the actual path sed -i "s|$cache/rootfs-$release-$arch|$rootfs|" $rootfs/etc/fstab return 0 } install_slackware() { rootfs=$1 mkdir -p /var/lock/subsys/ ( flock -n -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi if [ "$arch" == "x86_64" ]; then PKGMAIN=slackware64 elif [ "$arch" == "arm" ]; then PKGMAIN=slackwarearm else PKGMAIN=slackware fi export CONF=$cache/slackpkg-conf export ROOT=$cache/rootfs-$release-$arch mkdir -p $cache/cache-$release-$arch $cache/rootfs-$release-$arch \ $cache/slackpkg-$release-$arch $CONF/templates echo "$MIRROR/$PKGMAIN-$release/" > $CONF/mirrors touch $CONF/blacklist cat < $CONF/slackpkg.conf # v2.8 ARCH=$arch TEMP=$cache/cache-$release-$arch WORKDIR=$cache/slackpkg-$release-$arch DELALL=off CHECKMD5=on CHECKGPG=on CHECKSIZE=off PRIORITY=( patches %PKGMAIN extra pasture testing ) POSTINST=on ONLY_NEW_DOTNEW=off ONOFF=on DOWNLOAD_ALL=on DIALOG=off BATCH=on DEFAULT_ANSWER=y USE_INCLUDES=on SPINNING=off EOF # thanks to Vincent Batts for this list of packages # (that I modified a little :P) # http://connie.slackware.com/~vbatts/minimal/ cat < $CONF/templates/minimal-lxc.template aaa_base aaa_elflibs aaa_terminfo bash bin bzip2 coreutils dhcpcd dialog diffutils e2fsprogs elvis etc findutils gawk glibc-solibs gnupg grep gzip hostname iputils libunistring logrotate mpfr net-tools network-scripts ncurses openssh openssl-solibs pkgtools procps-ng sed shadow sharutils slackpkg sysklogd sysvinit sysvinit-functions sysvinit-scripts tar udev util-linux wget which xz EOF TEMPLATE=${TEMPLATE:-minimal-lxc} if [ ! "$TEMPLATE" = "minimal-lxc" ]; then if [ -f /etc/slackpkg/templates/$TEMPLATE.template ]; then cat /etc/slackpkg/templates/$TEMPLATE.template \ > $CONF/templates/$TEMPLATE.template else TEMPLATE="minimal-lxc" fi fi # clean previous installs rm -fR $ROOT/* slackpkg -default_answer=n update slackpkg install-template $TEMPLATE # add a slackpkg default mirror echo "$MIRROR/$PKGMAIN-$release/" >> $ROOT/etc/slackpkg/mirrors # blacklist the devs package (we have to use our premade devices). # do the same with the kernel packages (we use the host's one), # but leave available headers and sources echo "devs" >> $ROOT/etc/slackpkg/blacklist sed -i \ -e "s|^#kernel-|kernel-|" \ -e "s|^kernel-headers|#kernel-headers|" \ -e "s|^kernel-source|#kernel-source|" \ $ROOT/etc/slackpkg/blacklist # force klog to use the system call interface to the kernel message # buffers - needed for unprivileged containers sed -i 's|3\ \-x|3 -x -s|' $ROOT/etc/rc.d/rc.syslog || true return 0 ) 9>/var/lock/subsys/lxc return $? } copy_configuration() { path=$1 rootfs=$2 name=$3 cat <> $path/config lxc.utsname = $name lxc.arch = $arch lxc.mount = $rootfs/etc/fstab lxc.include = ${LXC_TEMPLATE_CONFIG}/slackware.common.conf EOF if [ $? -ne 0 ]; then echo "Failed to add configuration." return 1 fi return 0 } clean() { if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -n -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>/var/lock/subsys/lxc } usage() { cat < --clean EOF return 0 } options=$(getopt -o hp:n:a:r:c -l help,rootfs:,path:,name:,arch:,release:,clean -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -r|--release) release=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi type installpkg if [ $? -ne 0 ]; then echo "'installpkg' command is missing." exit 1 fi type slackpkg if [ $? -ne 0 ]; then echo "'slackpkg' command is missing." exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required." exit 1 fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'." exit 1 fi # If no release version was specified, use current release=${release:-current} if [ -z "$name" ]; then # no name given? set a default one name=slackwarecontainer fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi echo set -e install_slackware $rootfs if [ $? -ne 0 ]; then echo "Failed to install slackware." exit 1 fi echo configure_slackware $cache/rootfs-$release-$arch $name if [ $? -ne 0 ]; then echo "Failed to configure slackware for a container." exit 1 fi echo rootfs=$path/rootfs copy_slackware $rootfs if [ $? -ne 0 ]; then echo "Failed to copy rootfs." exit 1 fi echo copy_configuration $path $rootfs $name if [ $? -ne 0 ]; then echo "Failed to write configuration file." exit 1 fi if [ ! -z $clean ]; then clean || exit 1 exit 0 fi lxc-2.0.11/templates/lxc-ubuntu.in0000644061062106075000000006324013435013473013756 00000000000000#!/bin/bash # # template script for generating ubuntu container for LXC # # This script consolidates and extends the existing lxc ubuntu scripts # # Copyright © 2011 Serge Hallyn # Copyright © 2010 Wilhelm Meier # Author: Wilhelm Meier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin set -e LOCALSTATEDIR="@LOCALSTATEDIR@" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" # Allows the lxc-cache directory to be set by environment variable LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"} if [ -r /etc/default/lxc ]; then . /etc/default/lxc fi # Check if given path is in a btrfs partition is_btrfs() { [ -e $1 -a $(stat -f -c '%T' $1) = "btrfs" ] } # Check if given path is the root of a btrfs subvolume is_btrfs_subvolume() { [ -d $1 -a $(stat -f -c '%T' $1) = "btrfs" -a $(stat -c '%i' $1) -eq 256 ] } try_mksubvolume() { path=$1 [ -d $path ] && return 0 mkdir -p $(dirname $path) if which btrfs >/dev/null 2>&1 && is_btrfs $(dirname $path); then btrfs subvolume create $path else mkdir -p $path fi } try_rmsubvolume() { path=$1 [ -d $path ] || return 0 if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume $path; then btrfs subvolume delete $path else rm -rf $path fi } configure_ubuntu() { rootfs=$1 hostname=$2 release=$3 user=$4 password=$5 # configure the network using the dhcp if chroot $rootfs which netplan >/dev/null 2>&1; then cat < $rootfs/etc/netplan/10-lxc.yaml network: ethernets: eth0: {dhcp4: true} version: 2 EOF else cat < $rootfs/etc/network/interfaces # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). # The loopback network interface auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp EOF fi # set the hostname cat < $rootfs/etc/hostname $hostname EOF # set minimal hosts cat < $rootfs/etc/hosts 127.0.0.1 localhost 127.0.1.1 $hostname # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF if [ ! -f $rootfs/etc/init/container-detect.conf ]; then # suppress log level output for udev sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf # remove jobs for consoles 5 and 6 since we only create 4 consoles in # this template rm -f $rootfs/etc/init/tty{5,6}.conf fi if [ -z "$bindhome" ]; then chroot $rootfs useradd --create-home -s /bin/bash $user echo "$user:$password" | chroot $rootfs chpasswd fi # make sure we have the current locale defined in the container if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then chroot $rootfs locale-gen en_US.UTF-8 || true chroot $rootfs update-locale LANG=en_US.UTF-8 || true else chroot $rootfs locale-gen $LANG || true chroot $rootfs update-locale LANG=$LANG || true fi # generate new SSH keys if [ -x $rootfs/var/lib/dpkg/info/openssh-server.postinst ]; then cat > $rootfs/usr/sbin/policy-rc.d << EOF #!/bin/sh exit 101 EOF chmod +x $rootfs/usr/sbin/policy-rc.d if [ -f "$rootfs/etc/init/ssh.conf" ]; then mv "$rootfs/etc/init/ssh.conf" "$rootfs/etc/init/ssh.conf.disabled" fi rm -f $rootfs/etc/ssh/ssh_host_*key* DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then mv "$rootfs/etc/init/ssh.conf.disabled" "$rootfs/etc/init/ssh.conf" fi rm -f $rootfs/usr/sbin/policy-rc.d fi return 0 } # finish setting up the user in the container by injecting ssh key and # adding sudo group membership. # passed-in user is either 'ubuntu' or the user to bind in from host. finalize_user() { user=$1 sudo_version=$(chroot $rootfs dpkg-query -W -f='${Version}' sudo) if chroot $rootfs dpkg --compare-versions $sudo_version gt "1.8.3p1-1"; then groups="sudo" else groups="sudo admin" fi for group in $groups; do chroot $rootfs groupadd --system $group >/dev/null 2>&1 || true chroot $rootfs adduser ${user} $group >/dev/null 2>&1 || true done if [ -n "$auth_key" -a -f "$auth_key" ]; then u_path="/home/${user}/.ssh" root_u_path="$rootfs/$u_path" mkdir -p $root_u_path cp $auth_key "$root_u_path/authorized_keys" chroot $rootfs chown -R ${user}: "$u_path" echo "Inserted SSH public key from $auth_key into /home/${user}/.ssh/authorized_keys" fi return 0 } # A function to try and autodetect squid-deb-proxy servers on the local network # if either the squid-deb-proxy-client package is installed on the host or # a parent container set the 50squid-deb-proxy-client file. squid_deb_proxy_autodetect() { local apt_discover=/usr/share/squid-deb-proxy-client/apt-avahi-discover local proxy_file=/etc/apt/apt.conf.d/50squid-deb-proxy-client squid_proxy_line= # That's a global :/ # Maybe the host is aware of a squid-deb-proxy? if [ -f $apt_discover ]; then echo -n "Discovering squid-deb-proxy..." squid_proxy_line=$($apt_discover) if [ -n "$squid_proxy_line" ]; then echo "found squid-deb-proxy: $squid_proxy_line" else echo "no squid-deb-proxy found" fi fi # Are we in a nested container, and the parent already knows of a proxy? if [ -f $proxy_file ]; then # Extract the squid URL from the file (whatever is between "") squid_proxy_line=`cat $proxy_file | sed "s/.*\"\(.*\)\".*/\1/"` fi } # # Choose proxies for container # http_proxy will be used by debootstrap on the host. # APT_PROXY will be used to set /etc/apt/apt.conf.d/70proxy in the container. # choose_container_proxy() { local rootfs=$1 local arch=$2 if [ -z "$HTTP_PROXY" ]; then HTTP_PROXY="none" fi case "$HTTP_PROXY" in none) squid_deb_proxy_autodetect if [ -n "$squid_proxy_line" ]; then APT_PROXY=$squid_proxy_line export http_proxy=$squid_proxy_line else APT_PROXY= fi ;; apt) RES=`apt-config shell APT_PROXY Acquire::http::Proxy` eval $RES [ -z "$APT_PROXY" ] || export http_proxy=$APT_PROXY ;; *) APT_PROXY=$HTTP_PROXY export http_proxy=$HTTP_PROXY ;; esac } write_sourceslist() { # $1 => path to the partial cache or the rootfs # $2 => architecture we want to add # $3 => whether to use the multi-arch syntax or not if [ -n "$APT_PROXY" ]; then mkdir -p $1/etc/apt/apt.conf.d cat > $1/etc/apt/apt.conf.d/70proxy << EOF Acquire::http::Proxy "$APT_PROXY" ; EOF fi case $2 in amd64|i386) MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} ;; *) MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} ;; esac if [ -n "$3" ]; then cat >> "$1/etc/apt/sources.list" << EOF deb [arch=$2] $MIRROR ${release} main restricted universe multiverse deb [arch=$2] $MIRROR ${release}-updates main restricted universe multiverse deb [arch=$2] $SECURITY_MIRROR ${release}-security main restricted universe multiverse EOF else cat >> "$1/etc/apt/sources.list" << EOF deb $MIRROR ${release} main restricted universe multiverse deb $MIRROR ${release}-updates main restricted universe multiverse deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse EOF fi } install_packages() { local rootfs="$1" shift local packages="$*" if [ -z $update ] then chroot $rootfs apt-get update update=true fi if [ -n "${packages}" ] then chroot $rootfs apt-get install --force-yes -y --no-install-recommends ${packages} fi } cleanup() { try_rmsubvolume $cache/partial-$arch try_rmsubvolume $cache/rootfs-$arch } suggest_flush() { echo "Container upgrade failed. The container cache may be out of date," echo "in which case flushing the cache (see -F in the help output) may help." } download_ubuntu() { cache=$1 arch=$2 release=$3 case $2 in amd64|i386) MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} ;; *) MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} ;; esac packages_template=${packages_template:-"apt-transport-https,ssh,vim"} debootstrap_parameters= # Try to guess a list of langpacks to install langpacks="language-pack-en" if which dpkg >/dev/null 2>&1; then langpacks=`(echo $langpacks && dpkg -l | grep -E "^ii language-pack-[a-z]* " | cut -d ' ' -f3) | sort -u` fi packages_template="${packages_template},$(echo $langpacks | sed 's/ /,/g')" if [ -n "$variant" ]; then debootstrap_parameters="$debootstrap_parameters --variant=$variant" fi if [ "$variant" = 'minbase' ]; then packages_template="${packages_template},sudo" # Newer releases use netplan, EOL releases not supported case $release in trusty|xenial|zesty) packages_template="${packages_template},ifupdown,isc-dhcp-client" ;; esac fi echo "Installing packages in template: ${packages_template}" trap cleanup EXIT SIGHUP SIGINT SIGTERM # check the mini ubuntu was not already downloaded try_mksubvolume "$cache/partial-$arch" if [ $? -ne 0 ]; then echo "Failed to create '$cache/partial-$arch' directory" return 1 fi choose_container_proxy $cache/partial-$arch/ $arch # download a mini ubuntu into a cache echo "Downloading ubuntu $release minimal ..." if [ -n "$(which qemu-debootstrap)" ]; then qemu-debootstrap --verbose $debootstrap_parameters --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR else debootstrap --verbose $debootstrap_parameters --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR fi if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi # Serge isn't sure whether we should avoid doing this when # $release == `distro-info -d` echo "Installing updates" > $cache/partial-$arch/etc/apt/sources.list write_sourceslist $cache/partial-$arch/ $arch chroot "$1/partial-${arch}" apt-get update if [ $? -ne 0 ]; then echo "Failed to update the apt cache" return 1 fi cat > "$1/partial-${arch}"/usr/sbin/policy-rc.d << EOF #!/bin/sh exit 101 EOF chmod +x "$1/partial-${arch}"/usr/sbin/policy-rc.d ( cat << EOF mount -t proc proc "${1}/partial-${arch}/proc" chroot "${1}/partial-${arch}" apt-get dist-upgrade -y EOF ) | lxc-unshare -s MOUNT -- sh -eu || (suggest_flush; false) rm -f "$1/partial-${arch}"/usr/sbin/policy-rc.d chroot "$1/partial-${arch}" apt-get clean mv "$1/partial-$arch" "$1/rootfs-$arch" trap EXIT trap SIGINT trap SIGTERM trap SIGHUP echo "Download complete" return 0 } copy_ubuntu() { cache=$1 arch=$2 rootfs=$3 # make a local copy of the miniubuntu echo "Copying rootfs to $rootfs ..." try_mksubvolume $rootfs if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume $cache/rootfs-$arch && is_btrfs_subvolume $rootfs; then realrootfs=$(dirname $config)/rootfs [ "$rootfs" = "$realrootfs" ] || umount $rootfs || return 1 btrfs subvolume delete $realrootfs || return 1 btrfs subvolume snapshot $cache/rootfs-$arch $realrootfs || return 1 [ "$rootfs" = "$realrootfs" ] || mount --bind $realrootfs $rootfs || return 1 else rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 fi return 0 } install_ubuntu() { rootfs=$1 release=$2 flushcache=$3 cache="$4/$release" mkdir -p $LOCALSTATEDIR/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi if [ $flushcache -eq 1 ]; then echo "Flushing cache..." try_rmsubvolume $cache/partial-$arch try_rmsubvolume $cache/rootfs-$arch fi echo "Checking cache download in $cache/rootfs-$arch ... " if [ ! -e "$cache/rootfs-$arch" ]; then download_ubuntu $cache $arch $release if [ $? -ne 0 ]; then echo "Failed to download 'ubuntu $release base'" return 1 fi fi echo "Copy $cache/rootfs-$arch to $rootfs ... " copy_ubuntu $cache $arch $rootfs if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>$LOCALSTATEDIR/lock/subsys/lxc-ubuntu$release return $? } copy_configuration() { path=$1 rootfs=$2 name=$3 arch=$4 release=$5 if [ $arch = "i386" ]; then arch="i686" fi # if there is exactly one veth network entry, make sure it has an # associated hwaddr. nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` if [ $nics -eq 1 ]; then grep -q "^lxc.network.hwaddr" $path/config || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config fi # Generate the configuration file ## Relocate all the network config entries sed -i -e "/lxc.network/{w ${path}/config-network" -e "d}" $path/config ## Relocate any other config entries sed -i -e "/lxc./{w ${path}/config-auto" -e "d}" $path/config ## Add all the includes echo "" >> $path/config echo "# Common configuration" >> $path/config if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" >> $path/config fi if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" >> $path/config fi ## Add the container-specific config echo "" >> $path/config echo "# Container specific configuration" >> $path/config [ -e "$path/config-auto" ] && cat $path/config-auto >> $path/config && rm $path/config-auto grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.arch = $arch EOF ## Re-add the previously removed network config echo "" >> $path/config echo "# Network configuration" >> $path/config cat $path/config-network >> $path/config rm $path/config-network if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } post_process() { rootfs=$1 release=$2 packages=$3 # Disable service startup cat > $rootfs/usr/sbin/policy-rc.d << EOF #!/bin/sh exit 101 EOF chmod +x $rootfs/usr/sbin/policy-rc.d # If the container isn't running a native architecture, setup multiarch if [ -x "$(ls -1 ${rootfs}/usr/bin/qemu-*-static 2>/dev/null)" ]; then dpkg_version=$(chroot $rootfs dpkg-query -W -f='${Version}' dpkg) if chroot $rootfs dpkg --compare-versions $dpkg_version ge "1.16.2"; then chroot $rootfs dpkg --add-architecture ${hostarch} else mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch fi # Save existing value of MIRROR and SECURITY_MIRROR DEFAULT_MIRROR=$MIRROR DEFAULT_SECURITY_MIRROR=$SECURITY_MIRROR # Write a new sources.list containing both native and multiarch entries > ${rootfs}/etc/apt/sources.list write_sourceslist $rootfs $arch "native" MIRROR=$DEFAULT_MIRROR SECURITY_MIRROR=$DEFAULT_SECURITY_MIRROR write_sourceslist $rootfs $hostarch "multiarch" # Finally update the lists and install upstart using the host architecture HOST_PACKAGES="upstart:${hostarch} mountall:${hostarch} isc-dhcp-client:${hostarch}" chroot $rootfs apt-get update if chroot $rootfs dpkg -l iproute2 | grep -q ^ii; then HOST_PACKAGES="$HOST_PACKAGES iproute2:${hostarch}" else HOST_PACKAGES="$HOST_PACKAGES iproute:${hostarch}" fi install_packages $rootfs $HOST_PACKAGES fi # Install Packages in container if [ -n "$packages" ] then local packages="`echo $packages | sed 's/,/ /g'`" echo "Installing packages: ${packages}" install_packages $rootfs $packages fi # Set initial timezone as on host if [ -f /etc/timezone ]; then cat /etc/timezone > $rootfs/etc/timezone chroot $rootfs dpkg-reconfigure -f noninteractive tzdata elif [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock echo $ZONE > $rootfs/etc/timezone chroot $rootfs dpkg-reconfigure -f noninteractive tzdata else echo "Timezone in container is not configured. Adjust it manually." fi # rmdir /dev/shm for containers that have /run/shm # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did # get bind mounted to the host's /run/shm. So try to rmdir # it, and in case that fails move it out of the way. # NOTE: This can only be removed once 12.04 goes out of support if [ ! -L $rootfs/dev/shm ] && [ -e $rootfs/dev/shm ]; then rmdir $rootfs/dev/shm 2>/dev/null || mv $rootfs/dev/shm $rootfs/dev/shm.bak ln -s /run/shm $rootfs/dev/shm fi # Re-enable service startup rm $rootfs/usr/sbin/policy-rc.d } do_bindhome() { rootfs=$1 user=$2 # copy /etc/passwd, /etc/shadow, and /etc/group entries into container pwd=`getent passwd $user` || { echo "Failed to copy password entry for $user"; false; } echo $pwd >> $rootfs/etc/passwd # make sure user's shell exists in the container shell=`echo $pwd | cut -d: -f 7` if [ ! -x $rootfs/$shell ]; then echo "shell $shell for user $user was not found in the container." pkg=`dpkg -S $(readlink -m $shell) | cut -d ':' -f1` echo "Installing $pkg" install_packages $rootfs $pkg fi shad=`getent shadow $user` echo "$shad" >> $rootfs/etc/shadow # bind-mount the user's path into the container's /home h=`getent passwd $user | cut -d: -f 6` mkdir -p $rootfs/$h # use relative path in container h2=${h#/} while [ ${h2:0:1} = "/" ]; do h2=${h2#/} done echo "lxc.mount.entry = $h $h2 none bind 0 0" >> $path/config # Make sure the group exists in container grp=`echo $pwd | cut -d: -f 4` # group number for $user grpe=`getent group $grp` || return 0 # if host doesn't define grp, ignore in container chroot $rootfs getent group "$grpe" || echo "$grpe" >> $rootfs/etc/group } usage() { cat <] [-d|--debug] [-F | --flush-cache] [-r|--release ] [-v|--variant] [ -S | --auth-key ] [--rootfs ] [--packages ] [-u|--user ] [--password ] [--mirror ] [--security-mirror ] release: the ubuntu release (e.g. xenial): defaults to host release on ubuntu, otherwise uses latest LTS variant: debootstrap variant to use (see debootstrap(8)) bindhome: bind 's home into the container The ubuntu user will not be created, and will have sudo access. arch: the container architecture (e.g. amd64): defaults to host arch auth-key: SSH Public key file to inject into container packages: list of packages to add comma separated mirror,security-mirror: mirror for download and /etc/apt/sources.list EOF return 0 } options=$(getopt -o a:b:hp:r:v:n:FS:du: -l arch:,bindhome:,help,path:,release:,variant:,name:,flush-cache,auth-key:,debug,rootfs:,packages:,user:,password:,mirror:,security-mirror: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" release=xenial # Default to the last Ubuntu LTS release for non-Ubuntu systems if [ -f /etc/lsb-release ]; then . /etc/lsb-release if [ "$DISTRIB_ID" = "Ubuntu" ]; then release=$DISTRIB_CODENAME fi fi bindhome= # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/dpkg --print-architecture` elif which udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/udpkg --print-architecture` else arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then arch="amd64" elif [ "$arch" = "armv7l" ]; then arch="armhf" elif [ "$arch" = "aarch64" ]; then arch="arm64" elif [ "$arch" = "ppc64le" ]; then arch="ppc64el" fi fi debug=0 hostarch=$arch flushcache=0 packages="" user="ubuntu" password="ubuntu" while true do case "$1" in -h|--help) usage $0 && exit 0;; --rootfs) rootfs=$2; shift 2;; -p|--path) path=$2; shift 2;; -n|--name) name=$2; shift 2;; -u|--user) user=$2; shift 2;; --password) password=$2; shift 2;; -F|--flush-cache) flushcache=1; shift 1;; -r|--release) release=$2; shift 2;; -v|--variant) variant=$2; shift 2;; --packages) packages=$2; shift 2;; -b|--bindhome) bindhome=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -S|--auth-key) auth_key=$2; shift 2;; -d|--debug) debug=1; shift 1;; --mirror) MIRROR=$2; shift 2;; --security-mirror) SECURITY_MIRROR=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ $debug -eq 1 ]; then set -x fi if [ -n "$bindhome" ]; then pwd=`getent passwd $bindhome` if [ $? -ne 0 ]; then echo "Error: no password entry found for $bindhome" exit 1 fi fi if [ "$arch" = "i686" ]; then arch=i386 fi if [ $hostarch = "i386" -a $arch = "amd64" ]; then echo "can't create $arch container on $hostarch" exit 1 fi if [ $hostarch = "armhf" -o $hostarch = "armel" -o $hostarch = "arm64" ] && \ [ $arch != "armhf" -a $arch != "armel" -a $arch != "arm64" ]; then echo "can't create $arch container on $hostarch" exit 1 fi if [ $arch = "arm64" ] && [ $hostarch != "arm64" ]; then echo "can't create $arch container on $hostarch" exit 1 fi if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then echo "can't create $arch container on $hostarch" exit 1 fi which debootstrap >/dev/null 2>&1 || { echo "'debootstrap' command is missing" >&2; false; } if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi # detect rootfs config="$path/config" # if $rootfs exists here, it was passed in with --rootfs if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi install_ubuntu $rootfs $release $flushcache $LXC_CACHE_PATH if [ $? -ne 0 ]; then echo "failed to install ubuntu $release" exit 1 fi configure_ubuntu $rootfs $name $release $user $password if [ $? -ne 0 ]; then echo "failed to configure ubuntu $release for a container" exit 1 fi copy_configuration $path $rootfs $name $arch $release if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi post_process $rootfs $release $trim_container $packages if [ -n "$bindhome" ]; then do_bindhome $rootfs $bindhome finalize_user $bindhome else finalize_user $user fi echo "" echo "##" if [ -n "$bindhome" ]; then echo "# Log in as user $bindhome" else echo "# The default user is '$user' with password '$password'!" echo "# Use the 'sudo' command to run tasks as root in the container." fi echo "##" echo "" lxc-2.0.11/templates/lxc-openmandriva.in0000644061062106075000000003307713435013473015124 00000000000000#!/bin/bash # # template script for generating openmandriva container for LXC # # # lxc: linux Container library # Authors: # Alexander Khryukin # Vokhmin Alexey V # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin #Configurations #distro=cooker hostarch=$(uname -m) # Allow the cache base to be set by environment variable cache_base="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/openmandriva/$arch}" default_path=@LXCPATH@ default_profile=default lxc_network_type=veth lxc_network_link=br0 # is this openmandriva? [ -f /etc/mandriva-release ] && is_openmandriva=true configure_openmandriva() { mkdir -p ${rootfs_path}/etc/sysconfig/network-scripts/ # configure the network using the dhcp cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 ONBOOT=yes BOOTPROTO=dhcp NM_CONTROLLED=no HOSTNAME=${utsname} EOF # set the hostname cat < ${rootfs_path}/etc/sysconfig/network NETWORKING=yes HOSTNAME=${utsname} EOF echo "${utsname}" > ${rootfs_path}/etc/hostname # set minimal hosts cat < $rootfs_path/etc/hosts 127.0.0.1 localhost.localdomain localhost $utsname ::1 localhost6.localdomain6 localhost6 EOF } populate_dev() { echo -n "Create devices in /dev/" dev_path="${rootfs_path}/dev" rm -rf $dev_path mkdir -p $dev_path mknod -m 666 ${dev_path}/null c 1 3 mknod -m 666 ${dev_path}/zero c 1 5 mknod -m 666 ${dev_path}/random c 1 8 mknod -m 666 ${dev_path}/urandom c 1 9 mkdir -m 755 ${dev_path}/pts mkdir -m 1777 ${dev_path}/shm mknod -m 666 ${dev_path}/tty c 5 0 mknod -m 666 ${dev_path}/tty0 c 4 0 mknod -m 666 ${dev_path}/tty1 c 4 1 mknod -m 666 ${dev_path}/tty2 c 4 2 mknod -m 666 ${dev_path}/tty3 c 4 3 mknod -m 666 ${dev_path}/tty4 c 4 4 mknod -m 600 ${dev_path}/console c 5 1 mknod -m 666 ${dev_path}/full c 1 7 mknod -m 600 ${dev_path}/initctl p mknod -m 666 ${dev_path}/ptmx c 5 2 mkdir -m 755 ${dev_path}/net mknod -m 666 ${dev_path}/net/tun c 10 200 } set_guest_root_password() { [ -z "$root_password" ] && return # pass is empty, abort echo " - setting guest root password.." echo "root passwd is: $root_password" echo "root:$root_password" | chroot "$rootfs_path" chpasswd echo "done." } create_chroot_openmandriva() { # check the mini openmandriva was not already downloaded INSTALL_ROOT=$cache/cache mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then echo "Failed to create '$INSTALL_ROOT' directory" return 1 fi # package list to install PKG_LIST="basesystem-minimal locales locales-en initscripts urpmi cronie dhcp-client kbd" # download a mini openmandriva into a cache echo "Downloading openmandriva minimal ..." URPMI="/usr/sbin/urpmi.addmedia --urpmi-root $INSTALL_ROOT main http://abf.rosalinux.ru/downloads/$release/repository/$arch/main/release" echo $URPMI URPMI_BASE="/usr/sbin/urpmi --no-suggests --no-verify-rpm --ignorearch --root $INSTALL_ROOT --urpmi-root $INSTALL_ROOT --auto $PKG_LIST" $URPMI $URPMI_BASE # We're splitting the old loop into two loops plus a directory retrival. # First loop... Try and retrive a mirror list with retries and a slight # delay between attempts... if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi mv "$INSTALL_ROOT" "$cache/rootfs" echo "Download complete." return 0 } copy_openmandriva() { echo -n "Copying rootfs to $rootfs_path ..." mkdir -p $rootfs_path rsync -SHaAX $cache/rootfs/ $rootfs_path/ return 0 } update_openmandriva() { echo "automated update in progress..." urpmi --root $cache/rootfs --urpmi-root $cache/rootfs --auto --auto-update --ignorearch } configure_openmandriva_systemd() { chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/proc-sys-fs-binfmt_misc.automount chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd.service chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd-control.socket chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd-kernel.socket # remove numlock service # KDGKBLED: Inappropriate ioctl for device rm -f ${rootfs_path}/etc/systemd/system/getty@.service.d/enable-numlock.conf unlink ${rootfs_path}/etc/systemd/system/default.target chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target sed -i 's!ConditionPathExists=/dev/tty0!ConditionPathExists=|/dev/tty0\nConditionVirtualization=|lxc!' \ ${rootfs_path}/lib/systemd/system/getty\@.service } install_openmandriva() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs ... " if [ ! -e "$cache/rootfs" ]; then echo $cache/rootfs create_chroot_openmandriva if [ $? -ne 0 ]; then echo "Failed to download 'openmandriva basesystem-minimal'" return 1 fi else echo "Cache found. Updating..." update_openmandriva if [ $? -ne 0 ]; then echo "Failed to update 'openmandriva base', continuing with last known good cache" else echo "Update finished" fi fi echo "Copy $cache/rootfs to $rootfs_path ... " copy_openmandriva if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-openmandriva return $? } copy_configuration() { mkdir -p $config_path grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config cat <> $config_path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined #networking lxc.network.type = $lxc_network_type lxc.network.flags = up lxc.network.link = $lxc_network_link lxc.network.name = eth0 lxc.network.mtu = 1500 EOF if [ ! -z ${ipv4} ]; then cat <> $config_path/config lxc.network.ipv4 = $ipv4 EOF fi if [ ! -z ${gw} ]; then cat <> $config_path/config lxc.network.ipv4.gateway = $gw EOF fi if [ ! -z ${ipv6} ]; then cat <> $config_path/config lxc.network.ipv6 = $ipv6 EOF fi if [ ! -z ${gw6} ]; then cat <> $config_path/config lxc.network.ipv6.gateway = $gw6 EOF fi cat <> $config_path/config #cgroups lxc.cgroup.devices.deny = a # /dev/null and zero lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 4:0 rwm lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 10:135 rwm EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache for OpenMandriva-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-openmandriva } usage() { cat < [-p|--path=] [-c|--clean] [-R|--release=] [-4|--ipv4=] [-6|--ipv6=] [-g|--gw=] [-d|--dns=] [-P|--profile=] [--rootfs=] [-A|--arch=] [-h|--help] Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case -c,--clean clean the cache -R,--release openmandriva2013.0/cooker/rosa2012.1 release for the new container. if the host is OpenMandriva, then it will default to the host's release. -4,--ipv4 specify the ipv4 address to assign to the virtualized interface, eg. 192.168.1.123/24 -6,--ipv6 specify the ipv6 address to assign to the virtualized interface, eg. 2003:db8:1:0:214:1234:fe0b:3596/64 -g,--gw specify the default gw, eg. 192.168.1.1 -G,--gw6 specify the default gw, eg. 2003:db8:1:0:214:1234:fe0b:3596 -d,--dns specify the DNS server, eg. 192.168.1.2 -P,--profile Profile name is the file name in /etc/lxc/profiles contained packages name for install to cache. -A,--arch Define what arch the container will be [i586,x86_64,armv7l,armv7hl] ---rootfs rootfs path -h,--help print this help EOF return 0 } options=$(getopt -o hp:n:P:cR:4:6:g:d:A -l help,rootfs:,path:,name:,profile:,clean:,release:,ipv4:,ipv6:,gw:,dns:,arch: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" release=${release:-"cooker"} if [ -f /etc/lsb-release ]; then . /etc/lsb-release if [ "$DISTRIB_ID" = "OpenMandrivaLinux" ]; then release=openmandriva2013.0 elif [ "$DISTRIB_ID" = "RosaDesktop.Fresh" ]; then release=rosa2012.1 else echo "This is not an OpenMandriva or ROSA release" exit 1 fi fi while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs_path=$2; shift 2;; -n|--name) name=$2; shift 2;; -P|--profile) profile=$2; shift 2;; -c|--clean) clean=1; shift 1;; -R|--release) release=$2; shift 2;; -A|--arch) arch=$2; shift 2;; -4|--ipv4) ipv4=$2; shift 2;; -6|--ipv6) ipv6=$2; shift 2;; -g|--gw) gw=$2; shift 2;; -d|--dns) dns=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done arch=${arch:-$hostarch} if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi if [ -z "${utsname}" ]; then utsname=${name} fi type urpmi >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "'urpmi' command is missing" exit 1 fi if [ -z "$path" ]; then path=$default_path fi if [ -z "$profile" ]; then profile=$default_profile fi if [ $hostarch = "i586" -a $arch = "x86_64" ]; then echo "can't create x86_64 container on i586" exit 1 fi if [ -z "$ipv4" -a -z "$ipv6" ]; then BOOTPROTO="dhcp" else BOOTPROTO="static" fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi # check for 'lxc.rootfs' passed in through default config by lxc-create if [ -z "$rootfs_path" ]; then if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then rootfs_path=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $path/config) else rootfs_path=$path/$name/rootfs fi fi config_path=$default_path/$name cache=$cache_base/$release/$arch/$profile if [ ! -f $config_path/config ]; then echo "A container with that name exists, chose a different name" exit 1 fi install_openmandriva if [ $? -ne 0 ]; then echo "failed to install openmandriva" exit 1 fi configure_openmandriva if [ $? -ne 0 ]; then echo "failed to configure openmandriva for a container" exit 1 fi # If the systemd configuration directory exists - set it up for what we need. if [ -d ${rootfs_path}/etc/systemd/system ] then configure_openmandriva_systemd fi populate_dev if [ $? -ne 0 ]; then echo "failed to populated /dev/ devices" exit 1 fi set_guest_root_password if [ $? -ne 0 ]; then echo "failed to configure password for chroot" exit 1 fi copy_configuration if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi echo "container rootfs and config created" lxc-2.0.11/templates/Makefile.in0000644061062106075000000005112013435013507013355 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = templates ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/libtool.m4 \ $(top_srcdir)/config/ltoptions.m4 \ $(top_srcdir)/config/ltsugar.m4 \ $(top_srcdir)/config/ltversion.m4 \ $(top_srcdir)/config/lt~obsolete.m4 \ $(top_srcdir)/config/tls.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = lxc-alpine lxc-altlinux lxc-archlinux lxc-busybox \ lxc-centos lxc-cirros lxc-debian lxc-download lxc-fedora \ lxc-gentoo lxc-openmandriva lxc-opensuse lxc-oracle lxc-plamo \ lxc-slackware lxc-sshd lxc-ubuntu lxc-ubuntu-cloud \ lxc-sparclinux CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(templatesdir)" SCRIPTS = $(templates_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/lxc-alpine.in \ $(srcdir)/lxc-altlinux.in $(srcdir)/lxc-archlinux.in \ $(srcdir)/lxc-busybox.in $(srcdir)/lxc-centos.in \ $(srcdir)/lxc-cirros.in $(srcdir)/lxc-debian.in \ $(srcdir)/lxc-download.in $(srcdir)/lxc-fedora.in \ $(srcdir)/lxc-gentoo.in $(srcdir)/lxc-openmandriva.in \ $(srcdir)/lxc-opensuse.in $(srcdir)/lxc-oracle.in \ $(srcdir)/lxc-plamo.in $(srcdir)/lxc-slackware.in \ $(srcdir)/lxc-sparclinux.in $(srcdir)/lxc-sshd.in \ $(srcdir)/lxc-ubuntu-cloud.in $(srcdir)/lxc-ubuntu.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CAP_LIBS = @CAP_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CGMANAGER_CFLAGS = @CGMANAGER_CFLAGS@ CGMANAGER_LIBS = @CGMANAGER_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIR = @DATADIR@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFAULT_CGROUP_PATTERN = @DEFAULT_CGROUP_PATTERN@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOCDIR = @DOCDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ HAVE_DOXYGEN = @HAVE_DOXYGEN@ INCLUDEDIR = @INCLUDEDIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBDIR = @LUA_LIBDIR@ LUA_LIBS = @LUA_LIBS@ LUA_SHAREDIR = @LUA_SHAREDIR@ LUA_VERSION = @LUA_VERSION@ LXCBINHOOKDIR = @LXCBINHOOKDIR@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_ABI = @LXC_ABI@ LXC_ABI_MAJOR = @LXC_ABI_MAJOR@ LXC_ABI_MICRO = @LXC_ABI_MICRO@ LXC_ABI_MINOR = @LXC_ABI_MINOR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ LXC_DEVEL = @LXC_DEVEL@ LXC_DISTRO_SYSCONF = @LXC_DISTRO_SYSCONF@ LXC_GENERATE_DATE = @LXC_GENERATE_DATE@ LXC_GLOBAL_CONF = @LXC_GLOBAL_CONF@ LXC_USERNIC_CONF = @LXC_USERNIC_CONF@ LXC_USERNIC_DB = @LXC_USERNIC_DB@ LXC_VERSION = @LXC_VERSION@ LXC_VERSION_BASE = @LXC_VERSION_BASE@ LXC_VERSION_BETA = @LXC_VERSION_BETA@ LXC_VERSION_MAJOR = @LXC_VERSION_MAJOR@ LXC_VERSION_MICRO = @LXC_VERSION_MICRO@ LXC_VERSION_MINOR = @LXC_VERSION_MINOR@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NIH_CFLAGS = @NIH_CFLAGS@ NIH_DBUS_CFLAGS = @NIH_DBUS_CFLAGS@ NIH_DBUS_LIBS = @NIH_DBUS_LIBS@ NIH_LIBS = @NIH_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PREFIX = @PREFIX@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHONDEV_CFLAGS = @PYTHONDEV_CFLAGS@ PYTHONDEV_LIBS = @PYTHONDEV_LIBS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ RUNTIME_PATH = @RUNTIME_PATH@ SBINDIR = @SBINDIR@ SECCOMP_CFLAGS = @SECCOMP_CFLAGS@ SECCOMP_LIBS = @SECCOMP_LIBS@ SED = @SED@ SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSCONFDIR = @SYSCONFDIR@ SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bashcompdir = @bashcompdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ db2xman = @db2xman@ docdir = @docdir@ docdtd = @docdtd@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ templatesdir = @LXCTEMPLATEDIR@ templates_SCRIPTS = \ lxc-alpine \ lxc-altlinux \ lxc-archlinux \ lxc-busybox \ lxc-centos \ lxc-cirros \ lxc-debian \ lxc-download \ lxc-fedora \ lxc-gentoo \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ lxc-plamo \ lxc-slackware \ lxc-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud \ lxc-sparclinux all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu templates/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu templates/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): lxc-alpine: $(top_builddir)/config.status $(srcdir)/lxc-alpine.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-altlinux: $(top_builddir)/config.status $(srcdir)/lxc-altlinux.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-archlinux: $(top_builddir)/config.status $(srcdir)/lxc-archlinux.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-busybox: $(top_builddir)/config.status $(srcdir)/lxc-busybox.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-centos: $(top_builddir)/config.status $(srcdir)/lxc-centos.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-cirros: $(top_builddir)/config.status $(srcdir)/lxc-cirros.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-debian: $(top_builddir)/config.status $(srcdir)/lxc-debian.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-download: $(top_builddir)/config.status $(srcdir)/lxc-download.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-fedora: $(top_builddir)/config.status $(srcdir)/lxc-fedora.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-gentoo: $(top_builddir)/config.status $(srcdir)/lxc-gentoo.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-openmandriva: $(top_builddir)/config.status $(srcdir)/lxc-openmandriva.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-opensuse: $(top_builddir)/config.status $(srcdir)/lxc-opensuse.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-oracle: $(top_builddir)/config.status $(srcdir)/lxc-oracle.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-plamo: $(top_builddir)/config.status $(srcdir)/lxc-plamo.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-slackware: $(top_builddir)/config.status $(srcdir)/lxc-slackware.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-sshd: $(top_builddir)/config.status $(srcdir)/lxc-sshd.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-ubuntu: $(top_builddir)/config.status $(srcdir)/lxc-ubuntu.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-ubuntu-cloud: $(top_builddir)/config.status $(srcdir)/lxc-ubuntu-cloud.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-sparclinux: $(top_builddir)/config.status $(srcdir)/lxc-sparclinux.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-templatesSCRIPTS: $(templates_SCRIPTS) @$(NORMAL_INSTALL) @list='$(templates_SCRIPTS)'; test -n "$(templatesdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(templatesdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(templatesdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(templatesdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(templatesdir)$$dir" || exit $$?; \ } \ ; done uninstall-templatesSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(templates_SCRIPTS)'; test -n "$(templatesdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(templatesdir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(SCRIPTS) installdirs: for dir in "$(DESTDIR)$(templatesdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-templatesSCRIPTS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-templatesSCRIPTS .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip install-templatesSCRIPTS installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-templatesSCRIPTS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: lxc-2.0.11/templates/lxc-centos.in0000644061062106075000000007356213435013473013737 00000000000000#!/bin/bash # # template script for generating CentOS container for LXC # # lxc: linux Container library # Authors: # Daniel Lezcano # Ramez Hanna # Fajar A. Nugraha # Michael H. Warfield # 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 #Configurations default_path=@LXCPATH@ # Some combinations of the tuning knobs below do not exactly make sense. # but that's ok. # # If the "root_password" is non-blank, use it, else set a default. # This can be passed to the script as an environment variable and is # set by a shell conditional assignment. Looks weird but it is what it is. # # If the root password contains a ding ($) then try to expand it. # That will pick up things like ${name} and ${RANDOM}. # If the root password contains more than 3 consecutive X's, pass it as # a template to mktemp and take the result. # # If root_display_password = yes, display the temporary root password at exit. # If root_store_password = yes, store it in the configuration directory # If root_prompt_password = yes, invoke "passwd" to force the user to change # the root password after the container is created. # If root_expire_password = yes, you will be prompted to change the root # password at the first login. # # These are conditional assignments... The can be overridden from the # preexisting environment variables... # # Make sure this is in single quotes to defer expansion to later! # :{root_password='Root-${name}-${RANDOM}'} : ${root_password='Root-${name}-XXXXXX'} # Now, it doesn't make much sense to display, store, and force change # together. But, we gotta test, right??? : ${root_display_password='no'} : ${root_store_password='yes'} # Prompting for something interactive has potential for mayhem # with users running under the API... Don't default to "yes" : ${root_prompt_password='no'} # Expire root password? Default to yes, but can be overridden from # the environment variable : ${root_expire_password='yes'} # These are only going into comments in the resulting config... lxc_network_type=veth lxc_network_link=lxcbr0 # is this CentOS? # Alow for weird remixes like the Raspberry Pi # # Use the Mitre standard CPE identifier for the release ID if possible... # This may be in /etc/os-release or /etc/system-release-cpe. We # should be able to use EITHER. Give preference to /etc/os-release for now. # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin if [ -e /etc/os-release ] then # This is a shell friendly configuration file. We can just source it. # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME . /etc/os-release echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" fi if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] then CPE_NAME=$(head -n1 /etc/system-release-cpe) CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') if [ "${CPE_URI}" != "cpe:/o" ] then CPE_NAME= else # Probably a better way to do this but sill remain posix # compatible but this works, shrug... # Must be nice and not introduce convenient bashisms here. # # According to the official registration at Mitre and NIST, # this should have been something like this for CentOS: # cpe:/o:centos:centos:6 # or this: # cpe:/o:centos:centos:6.5 # ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') # The "enterprise_linux" is a bone toss back to RHEL. # Since CentOS and RHEL are so tightly coupled, we'll # take the RHEL version if we're running on it and do the # equivalent version for CentOS. if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ] then # Instead we got this: cpe:/o:centos:linux:6 ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)') fi VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" fi fi if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ] then centos_host_ver=${VERSION_ID} is_centos=true elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -o "${ID}" = "rhel" -a "${VERSION_ID}" != "" ] then # RHEL 7+ /etc/os-release ID = 'rhel', which doesn't enter this elif without the added OR statement redhat_host_ver=${VERSION_ID} is_redhat=true elif [ -e /etc/centos-release ] then # Only if all other methods fail, try to parse the redhat-release file. centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release ) if [ "$centos_host_ver" != "" ] then is_centos=true fi fi force_mknod() { # delete a device node if exists, and create a new one rm -f $2 && mknod -m $1 $2 $3 $4 $5 } configure_centos() { # disable selinux in CentOS mkdir -p $rootfs_path/selinux echo 0 > $rootfs_path/selinux/enforce # Also kill it in the /etc/selinux/config file if it's there... if [ -f $rootfs_path/etc/selinux/config ] then sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config fi # Nice catch from Dwight Engen in the Oracle template. # Wantonly plagerized here with much appreciation. if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled fi # This is a known problem and documented in RedHat bugzilla as relating # to a problem with auditing enabled. This prevents an error in # the container "Cannot make/remove an entry for the specified session" sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd if [ -f ${rootfs_path}/etc/pam.d/crond ] then sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond fi # In addition to disabling pam_loginuid in the above config files # we'll also disable it by linking it to pam_permit to catch any # we missed or any that get installed after the container is built. # # Catch either or both 32 and 64 bit archs. if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] then ( cd ${rootfs_path}/lib/security/ mv pam_loginuid.so pam_loginuid.so.disabled ln -s pam_permit.so pam_loginuid.so ) fi if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] then ( cd ${rootfs_path}/lib64/security/ mv pam_loginuid.so pam_loginuid.so.disabled ln -s pam_permit.so pam_loginuid.so ) fi # Set default localtime to the host localtime if not set... if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] then # if /etc/localtime is a symlink, this should preserve it. cp -a /etc/localtime ${rootfs_path}/etc/localtime fi # Deal with some dain bramage in the /etc/init.d/halt script. # Trim it and make it our own and link it in before the default # halt script so we can intercept it. This also preventions package # updates from interferring with our interferring with it. # # There's generally not much in the halt script that useful but what's # in there from resetting the hardware clock down is generally very bad. # So we just eliminate the whole bottom half of that script in making # ourselves a copy. That way a major update to the init scripts won't # trash what we've set up. if [ -f ${rootfs_path}/etc/init.d/halt ] then sed -e '/hwclock/,$d' \ < ${rootfs_path}/etc/init.d/halt \ > ${rootfs_path}/etc/init.d/lxc-halt echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt chmod 755 ${rootfs_path}/etc/init.d/lxc-halt # Link them into the rc directories... ( cd ${rootfs_path}/etc/rc.d/rc0.d ln -s ../init.d/lxc-halt S00lxc-halt cd ${rootfs_path}/etc/rc.d/rc6.d ln -s ../init.d/lxc-halt S00lxc-reboot ) fi # configure the network using the dhcp cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes HOSTNAME=${utsname} NM_CONTROLLED=no TYPE=Ethernet MTU=${MTU} DHCP_HOSTNAME=\`hostname\` EOF # set the hostname cat < ${rootfs_path}/etc/sysconfig/network NETWORKING=yes HOSTNAME=${utsname} EOF # set minimal hosts cat < $rootfs_path/etc/hosts 127.0.0.1 localhost $name EOF # set minimal fstab cat < $rootfs_path/etc/fstab /dev/root / rootfs defaults 0 0 EOF # create lxc compatibility init script if [ "$release" = "6" ]; then cat < $rootfs_path/etc/init/lxc-sysinit.conf start on startup env container pre-start script if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then stop; fi rm -f /var/lock/subsys/* rm -f /var/run/*.pid [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab mkdir -p /dev/shm mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm initctl start tty TTY=console telinit 3 exit 0 end script EOF elif [ "$release" = "5" ]; then cat < $rootfs_path/etc/rc.d/lxc.sysinit #! /bin/bash rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/* rm -rf {/,/var}/tmp/* echo "/dev/root / rootfs defaults 0 0" > /etc/mtab exit 0 EOF chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab # prevent mingetty from calling vhangup(2) since it fails with userns. # Same issue as oracle template: prevent mingetty from calling vhangup(2) # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab fi dev_path="${rootfs_path}/dev" rm -rf $dev_path mkdir -p $dev_path mknod -m 666 ${dev_path}/null c 1 3 mknod -m 666 ${dev_path}/zero c 1 5 mknod -m 666 ${dev_path}/random c 1 8 mknod -m 666 ${dev_path}/urandom c 1 9 mkdir -m 755 ${dev_path}/pts mkdir -m 1777 ${dev_path}/shm mknod -m 666 ${dev_path}/tty c 5 0 mknod -m 666 ${dev_path}/tty0 c 4 0 mknod -m 666 ${dev_path}/tty1 c 4 1 mknod -m 666 ${dev_path}/tty2 c 4 2 mknod -m 666 ${dev_path}/tty3 c 4 3 mknod -m 666 ${dev_path}/tty4 c 4 4 mknod -m 600 ${dev_path}/console c 5 1 mknod -m 666 ${dev_path}/full c 1 7 mknod -m 600 ${dev_path}/initctl p mknod -m 666 ${dev_path}/ptmx c 5 2 # setup console and tty[1-4] for login. note that /dev/console and # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. # lxc will maintain these links and bind mount ptys over /dev/lxc/* # since lxc.devttydir is specified in the config. # allow root login on console, tty[1-4], and pts/0 for libvirt echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty echo "lxc/console" >>${rootfs_path}/etc/securetty echo "lxc/tty1" >>${rootfs_path}/etc/securetty echo "lxc/tty2" >>${rootfs_path}/etc/securetty echo "lxc/tty3" >>${rootfs_path}/etc/securetty echo "lxc/tty4" >>${rootfs_path}/etc/securetty echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty echo "pts/0" >>${rootfs_path}/etc/securetty # prevent mingetty from calling vhangup(2) since it fails with userns. # Same issue as oracle template: prevent mingetty from calling vhangup(2) # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. test -f $rootfs_path/etc/init/tty.conf && sed -i 's|mingetty|mingetty --nohangup|' $rootfs_path/etc/init/tty.conf if [ ${root_display_password} = "yes" ] then echo "Setting root password to '$root_password'" fi if [ ${root_store_password} = "yes" ] then touch ${config_path}/tmp_root_pass chmod 600 ${config_path}/tmp_root_pass echo ${root_password} > ${config_path}/tmp_root_pass echo "Storing root password in '${config_path}/tmp_root_pass'" fi echo "root:$root_password" | chroot $rootfs_path chpasswd if [ ${root_expire_password} = "yes" ] then # Also set this password as expired to force the user to change it! chroot $rootfs_path passwd -e root fi # This will need to be enhanced for CentOS 7 when systemd # comes into play... /\/\|=mhw=|\/\/ return 0 } configure_centos_init() { test -f ${rootfs_path}/etc/rc.sysinit && sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit test -f ${rootfs_path}/etc/rc.d/rc.sysinit && sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit if [ "$release" = "6" ]; then chroot ${rootfs_path} chkconfig udev-post off fi chroot ${rootfs_path} chkconfig network on if [ "$release" = "7" ]; then # don't wait for the timeout chroot ${rootfs_path} chkconfig systemd-remount-fs off fi if [ -d ${rootfs_path}/etc/init ] then # This is to make upstart honor SIGPWR cat <${rootfs_path}/etc/init/power-status-changed.conf # power-status-changed - shutdown on SIGPWR # start on power-status-changed exec /sbin/shutdown -h now "SIGPWR received" EOF fi } download_centos() { # check the mini CentOS was not already downloaded INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then echo "Failed to create '$INSTALL_ROOT' directory" return 1 fi # download a mini CentOS into a cache echo "Downloading CentOS minimal ..." YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck" if yum -h | grep -q 'releasever=RELEASEVER'; then YUM="$YUM0 --releasever=$release" else YUM="$YUM0" fi PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie" # use temporary repository definition # always prefer the repo given by the user # if no repo given, use mirrorlist.centos.org for i386 and x86_64 # and http://mirror.centos.org/altarch/ otherwise REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo mkdir -p $(dirname $REPO_FILE) if [ -n "$repo" ]; then cat < $REPO_FILE [base] name=local repository baseurl="$repo" EOF elif [ ${basearch} = 'i386' ] || [ ${basearch} = 'x86_64' ]; then cat < $REPO_FILE [base] name=CentOS-$release - Base mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os [updates] name=CentOS-$release - Updates mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates EOF else cat < $REPO_FILE [base] name=CentOS-$release - Base baseurl=http://mirror.centos.org/altarch/7/os/$basearch/ [updates] name=CentOS-$release - Updates baseurl=http://mirror.centos.org/altarch/7/updates/$basearch/ EOF fi # create minimal device nodes, needed for "yum install" and "yum update" process mkdir -p $INSTALL_ROOT/dev force_mknod 666 $INSTALL_ROOT/dev/null c 1 3 force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9 # create /run directory, just in case it is missing (e.g. RHEL7) mkdir -p $INSTALL_ROOT/run $YUM install $PKG_LIST # create symlink for /var/run -> ../run if [ "$release" = "7" ]; then mv $INSTALL_ROOT/var/run/* $INSTALL_ROOT/run/ rmdir $INSTALL_ROOT/var/run ln -sf ../run $INSTALL_ROOT/var/run fi if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi # use same nameservers as hosts, needed for "yum update later" cp /etc/resolv.conf $INSTALL_ROOT/etc/ # check whether rpmdb is under $HOME if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then echo "Fixing rpmdb location ..." mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/ rm -rf $INSTALL_ROOT/$HOME/.rpmdb chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null fi # check whether rpmdb version is correct chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null ret=$? # if "rpm -q" doesn't work due to rpmdb version difference, # then we need to redo the process using the newly-installed yum if [ $ret -gt 0 ]; then echo "Reinstalling packages ..." mv $REPO_FILE $REPO_FILE.tmp mkdir $INSTALL_ROOT/etc/yum.repos.disabled mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/ mv $REPO_FILE.tmp $REPO_FILE mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/ mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3 mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/ chroot $INSTALL_ROOT $YUM0 install $PKG_LIST if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp rm -rf $INSTALL_ROOT mv $INSTALL_ROOT.tmp $INSTALL_ROOT fi rm -f $REPO_FILE rm -rf $INSTALL_ROOT/var/cache/yum/* mv "$INSTALL_ROOT" "$cache/rootfs" echo "Download complete." return 0 } copy_centos() { # make a local copy of the mini CentOS echo -n "Copying rootfs to $rootfs_path ..." #cp -a $cache/rootfs-$arch $rootfs_path || return 1 # i prefer rsync (no reason really) mkdir -p $rootfs_path rsync -SHaAX $cache/rootfs/ $rootfs_path/ echo return 0 } update_centos() { YUM="chroot $cache/rootfs yum -y --nogpgcheck" $YUM update if [ $? -ne 0 ]; then return 1 fi $YUM clean packages } install_centos() { mkdir -p /var/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs ... " if [ ! -e "$cache/rootfs" ]; then download_centos if [ $? -ne 0 ]; then echo "Failed to download 'CentOS base'" return 1 fi else echo "Cache found. Updating..." update_centos if [ $? -ne 0 ]; then echo "Failed to update 'CentOS base', continuing with last known good cache" else echo "Update finished" fi fi echo "Copy $cache/rootfs to $rootfs_path ... " copy_centos if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>/var/lock/subsys/lxc-centos return $? } create_hwaddr() { openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' } copy_configuration() { mkdir -p $config_path grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo " lxc.rootfs = $rootfs_path " >> $config_path/config # The following code is to create static MAC addresses for each # interface in the container. This code will work for multiple # interfaces in the default config. mv $config_path/config $config_path/config.def while read LINE do # This should catch variable expansions from the default config... if expr "${LINE}" : '.*\$' > /dev/null 2>&1 then LINE=$(eval "echo \"${LINE}\"") fi # There is a tab and a space in the regex bracket below! # Seems that \s doesn't work in brackets. KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') if [[ "${KEY}" != "lxc.network.hwaddr" ]] then echo ${LINE} >> $config_path/config if [[ "${KEY}" == "lxc.network.link" ]] then echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config fi fi done < $config_path/config.def rm -f $config_path/config.def if [ -e "@LXCTEMPLATECONFIG@/centos.common.conf" ]; then echo " # Include common configuration lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf " >> $config_path/config fi # Append things which require expansion here... cat <> $config_path/config lxc.arch = $arch lxc.utsname = $utsname # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined # example simple networking setup, uncomment to enable #lxc.network.type = $lxc_network_type #lxc.network.flags = up #lxc.network.link = $lxc_network_link #lxc.network.name = eth0 # Additional example for veth network type # static MAC address, #lxc.network.hwaddr = 00:16:3e:77:52:20 # persistent veth device name on host side # Note: This may potentially collide with other containers of same name! #lxc.network.veth.pair = v-$name-e0 EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache for CentOS-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-centos } usage() { cat < [-p|--path=] [-c|--clean] [-R|--release=] [-a|--arch=] [-h|--help] Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc/name. -c,--clean clean the cache -R,--release CentOS release for the new container. If the host is CentOS, then it will default to the host's release. --fqdn fully qualified domain name (FQDN) for DNS and system naming --repo repository to use (url) -a,--arch Define what arch the container will be [i686,x86_64] -h,--help print this help EOF return 0 } options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi arch=$(uname -m) eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs_path=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=1; shift 1;; -R|--release) release=$2; shift 2;; --repo) repo="$2"; shift 2;; -a|--arch) newarch=$2; shift 2;; --fqdn) utsname=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi basearch=${arch} # Map a few architectures to their generic CentOS repository archs. # The two ARM archs are a bit of a guesstimate for the v5 and v6 # archs. V6 should have hardware floating point (Rasberry Pi). # The "arm" arch is safer (no hardware floating point). So # there may be cases where we "get it wrong" for some v6 other # than RPi. case "$arch" in i686) basearch=i386 ;; armv3l|armv4l|armv5l) basearch=arm ;; armv6l|armv7l|armv8l) basearch=armhfp ;; *) ;; esac # Somebody wants to specify an arch. This is very limited case. # i386/i586/i686 on i386/x86_64 # - or - # x86_64 on x86_64 if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] then case "${newarch}" in i386|i586|i686) if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] then # Make the arch a generic x86 32 bit... arch=${newarch} basearch=i386 else basearch=bad fi ;; *) basearch=bad ;; esac if [ "${basearch}" = "bad" ] then echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!" exit 1 fi fi # Allow the cache base to be set by environment variable cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/centos/$basearch # Let's do something better for the initial root password. # It's not perfect but it will defeat common scanning brute force # attacks in the case where ssh is exposed. It will also be set to # expired, forcing the user to change it at first login. if [ "${root_password}" = "" ] then root_password=Root-${name}-${RANDOM} else # If it's got a ding in it, try and expand it! if [ $(expr "${root_password}" : '.*$.') != 0 ] then root_password=$(eval echo "${root_password}") fi # If it has more than 3 consecutive X's in it, feed it # through mktemp as a template. if [ $(expr "${root_password}" : '.*XXXX') != 0 ] then root_password=$(mktemp -u ${root_password}) fi fi if [ -z "${utsname}" ]; then utsname=${name} fi # This follows a standard "resolver" convention that an FQDN must have # at least two dots or it is considered a local relative host name. # If it doesn't, append the dns domain name of the host system. # # This changes one significant behavior when running # "lxc_create -n Container_Name" without using the # --fqdn option. # # Old behavior: # utsname and hostname = Container_Name # New behavior: # utsname and hostname = Container_Name.Domain_Name if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then utsname=${utsname}.$(dnsdomainname) fi fi type yum >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "'yum' command is missing" exit 1 fi if [ -z "$path" ]; then path=$default_path/$name fi if [ -z "$release" ]; then if [ "$is_centos" -a "$centos_host_ver" ]; then release=$centos_host_ver elif [ "$is_redhat" -a "$redhat_host_ver" ]; then # This is needed to clean out bullshit like 6workstation and 6server. release=$(expr $redhat_host_ver : '\([0-9.]*\)') else echo "This is not a CentOS or Redhat host and release is missing, defaulting to 6 use -R|--release to specify release" release=6 fi fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -z "$rootfs_path" ]; then rootfs_path=$path/rootfs # check for 'lxc.rootfs' passed in through default config by lxc-create if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then rootfs_path=$(sed -e '/^lxc.rootfs\s*=/!d' -e 's/\s*#.*//' \ -e 's/^lxc.rootfs\s*=\s*//' -e q $path/config) fi fi config_path=$path cache=$cache_base/$release revert() { echo "Interrupted, so cleaning up" lxc-destroy -n $name # maybe was interrupted before copy config rm -rf $path echo "exiting..." exit 1 } trap revert SIGHUP SIGINT SIGTERM copy_configuration if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi install_centos if [ $? -ne 0 ]; then echo "failed to install CentOS" exit 1 fi configure_centos if [ $? -ne 0 ]; then echo "failed to configure CentOS for a container" exit 1 fi configure_centos_init if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi echo " Container rootfs and config have been created. Edit the config file to check/enable networking setup. " if [ ${root_display_password} = "yes" ] then echo "The temporary password for root is: '$root_password' You may want to note that password down before starting the container. " fi if [ ${root_store_password} = "yes" ] then echo "The temporary root password is stored in: '${config_path}/tmp_root_pass' " fi if [ ${root_prompt_password} = "yes" ] then echo "Invoking the passwd command in the container to set the root password. chroot ${rootfs_path} passwd " chroot ${rootfs_path} passwd else if [ ${root_expire_password} = "yes" ] then if ( mountpoint -q -- "${rootfs_path}" ) then echo "To reset the root password, you can do: lxc-start -n ${name} lxc-attach -n ${name} -- passwd lxc-stop -n ${name} " else echo " The root password is set up as "expired" and will require it to be changed at first login, which you should do as soon as possible. If you lose the root password or wish to change it without starting the container, you can change it from the host by running the following command (which will also reset the expired flag): chroot ${rootfs_path} passwd " fi fi fi lxc-2.0.11/templates/lxc-alpine.in0000644061062106075000000003232613435013473013705 00000000000000#!/bin/sh # vim: set ts=4: # Exit on error and treat unset variables as an error. set -eu # # LXC template for Alpine Linux 3+ # # Note: Do not replace tabs with spaces, it would break heredocs! # Authors: # Jakub Jirutka # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #=========================== Constants ============================# # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' readonly LXC_CACHE_DIR="${LXC_CACHE_PATH:-"$LOCAL_STATE_DIR/cache/lxc"}/alpine" # SHA256 checksums of GPG keys for APK. readonly APK_KEYS_SHA256="\ 9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub 2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9 alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub 1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub 12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub 73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041 alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub 9a4cd858d9710963848e6d5f555325dc199d1c952b01cf6e64da2c15deedbd97 alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub 780b3ed41786772cbc7b68136546fa3f897f28a23b30c72dde6225319c44cfff alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub" readonly APK_KEYS_URI='http://alpinelinux.org/keys' readonly DEFAULT_MIRROR_URL='http://dl-cdn.alpinelinux.org/alpine' : ${APK_KEYS_DIR:=/etc/apk/keys} if ! ls "$APK_KEYS_DIR"/alpine* >/dev/null 2>&1; then APK_KEYS_DIR="$LXC_CACHE_DIR/bootstrap/keys" fi readonly APK_KEYS_DIR : ${APK:=$(command -v apk || true)} if [ ! -x "$APK" ]; then APK="$LXC_CACHE_DIR/bootstrap/sbin/apk.static" fi readonly APK #======================== Helper Functions ========================# usage() { cat <<-EOF Template specific options can be passed to lxc-create after a '--' like this: lxc-create --name=NAME [lxc-create-options] -- [template-options] [PKG...] PKG Additional APK package(s) to install into the container. Template options: -a ARCH, --arch=ARCH The container architecture (e.g. x86, x86_64); defaults to the host arch. -d, --debug Run this script in a debug mode (set -x and wget w/o -q). -F, --flush-cache Remove cached files before build. -m URL --mirror=URL The Alpine mirror to use; defaults to $DEFAULT_MIRROR_URL. -r VER, --release=VER The Alpine release branch to install; default is the latest stable. Environment variables: APK The apk-tools binary to use when building rootfs. If not set or not executable and apk is not on PATH, then the script will download the latest apk-tools-static. APK_KEYS_DIR Path to directory with GPG keys for APK. If not set and /etc/apk/keys does not contain alpine keys, then the script will download the keys from ${APK_KEYS_URI}. LXC_CACHE_PATH Path to the cache directory where to store bootstrap files and APK packages. EOF } die() { local retval=$1; shift printf 'ERROR: %s\n' "$@" 1>&2 exit $retval } einfo() { printf "\n==> $1\n" } fetch() { if [ "$DEBUG" = 'yes' ]; then wget -T 10 -O - $@ else wget -T 10 -O - -q $@ fi } latest_release_branch() { local arch="$1" local branch=$(fetch "$MIRROR_URL/latest-stable/releases/$arch/latest-releases.yaml" \ | sed -En 's/^[ \t]*branch: (.*)$/\1/p' \ | head -n 1) [ -n "$branch" ] && echo "$branch" } parse_arch() { case "$1" in x86 | i[3-6]86) echo 'x86';; x86_64 | amd64) echo 'x86_64';; aarch64 | arm64) echo 'aarch64';; armv7) echo 'armv7';; arm*) echo 'armhf';; ppc64le) echo 'ppc64le';; *) return 1;; esac } run_exclusively() { local lock_name="$1" local timeout=$2 shift 2 mkdir -p "$LOCAL_STATE_DIR/lock/subsys" local retval { echo -n "Obtaining an exclusive lock..." if ! flock -x 9; then echo ' failed.' return 1 fi echo ' done' "$@"; retval=$? } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-alpine-$lock_name" return $retval } #============================ Bootstrap ===========================# bootstrap() { if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$LXC_CACHE_DIR/bootstrap" ]; then einfo 'Cleaning cached bootstrap files' rm -Rf "$LXC_CACHE_DIR/bootstrap" fi einfo 'Fetching and/or verifying APK keys' fetch_apk_keys "$APK_KEYS_DIR" if [ ! -x "$APK" ]; then einfo 'Fetching apk-tools static binary' local host_arch=$(parse_arch $(uname -m)) fetch_apk_static "$LXC_CACHE_DIR/bootstrap" "$host_arch" fi } fetch_apk_keys() { local dest="$1" local line keyname mkdir -p "$dest" cd "$dest" echo "$APK_KEYS_SHA256" | while read -r line; do keyname="${line##* }" if [ ! -s "$keyname" ]; then fetch "$APK_KEYS_URI/$keyname" > "$keyname" fi echo "$line" | sha256sum -c - done || exit 2 cd - >/dev/null } fetch_apk_static() { local dest="$1" local arch="$2" local pkg_name='apk-tools-static' mkdir -p "$dest" local pkg_ver=$(fetch "$MIRROR_URL/latest-stable/main/$arch/APKINDEX.tar.gz" \ | tar -xzO APKINDEX \ | sed -n "/P:${pkg_name}/,/^$/ s/V:\(.*\)$/\1/p") [ -n "$pkg_ver" ] || die 2 "Cannot find a version of $pkg_name in APKINDEX" fetch "$MIRROR_URL/latest-stable/main/$arch/${pkg_name}-${pkg_ver}.apk" \ | tar -xz -C "$dest" sbin/ # --extract --gzip --directory [ -s "$dest/sbin/apk.static" ] || die 2 'apk.static not found' local keyname=$(echo "$dest"/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//') openssl dgst -sha1 \ -verify "$APK_KEYS_DIR/$keyname" \ -signature "$dest/sbin/apk.static.SIGN.RSA.$keyname" \ "$dest/sbin/apk.static" \ || die 2 'Signature verification for apk.static failed' # Note: apk doesn't return 0 for --version local out="$("$dest"/sbin/apk.static --version)" echo "$out" [ "${out%% *}" = 'apk-tools' ] || die 3 'apk.static --version failed' } #============================ Install ============================# install() { local dest="$1" local arch="$2" local branch="$3" local extra_packages="$4" local apk_cache="$LXC_CACHE_DIR/apk/$arch" local repo_url="$MIRROR_URL/$branch/main" if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$apk_cache" ]; then einfo "Cleaning cached APK packages for $arch" rm -Rf "$apk_cache" fi mkdir -p "$apk_cache" einfo "Installing Alpine Linux in $dest" cd "$dest" mkdir -p etc/apk ln -s "$apk_cache" etc/apk/cache echo "$repo_url" > etc/apk/repositories install_packages "$arch" "alpine-base $extra_packages" make_dev_nodes setup_inittab setup_hosts setup_network setup_services chroot . /bin/true \ || die 3 'Failed to execute /bin/true in chroot, the builded rootfs is broken!' rm etc/apk/cache cd - >/dev/null } install_packages() { local arch="$1" local packages="$2" $APK --arch="$arch" --root=. --keys-dir="$APK_KEYS_DIR" \ --update-cache --initdb add $packages } make_dev_nodes() { mkdir -p -m 755 dev/pts mkdir -p -m 1777 dev/shm mknod -m 666 dev/zero c 1 5 mknod -m 666 dev/full c 1 7 mknod -m 666 dev/random c 1 8 mknod -m 666 dev/urandom c 1 9 local i; for i in $(seq 0 4); do mknod -m 620 dev/tty$i c 4 $i chown 0:5 dev/tty$i # root:tty done mknod -m 666 dev/tty c 5 0 chown 0:5 dev/tty # root:tty mknod -m 620 dev/console c 5 1 mknod -m 666 dev/ptmx c 5 2 chown 0:5 dev/ptmx # root:tty } setup_inittab() { # Remove unwanted ttys. sed -i '/^tty[5-9]\:\:.*$/d' etc/inittab cat <<-EOF >> etc/inittab # Main LXC console console ::respawn:/sbin/getty 38400 console EOF } setup_hosts() { # This runscript injects localhost entries with the current hostname # into /etc/hosts. cat <<'EOF' > etc/init.d/hosts #!/sbin/openrc-run start() { local start_tag='# begin generated' local end_tag='# end generated' local content=$( cat <<-EOF $start_tag by /etc/init.d/hosts 127.0.0.1 $(hostname).local $(hostname) localhost ::1 $(hostname).local $(hostname) localhost $end_tag EOF ) if grep -q "^${start_tag}" /etc/hosts; then # escape \n, busybox sed doesn't like them content=${content//$'\n'/\\$'\n'} sed -ni "/^${start_tag}/ { a\\${content} # read and discard next line and repeat until $end_tag or EOF :a; n; /^${end_tag}/!ba; n }; p" /etc/hosts else printf "$content" >> /etc/hosts fi } EOF chmod +x etc/init.d/hosts # Wipe it, will be generated by the above runscript. echo -n > etc/hosts } setup_network() { # Note: loopback is automatically started by LXC. cat <<-EOF > etc/network/interfaces auto eth0 iface eth0 inet dhcp hostname \$(hostname) EOF } setup_services() { local svc_name # Specify the LXC subsystem. sed -i 's/^#*rc_sys=.*/rc_sys="lxc"/' etc/rc.conf # boot runlevel for svc_name in bootmisc hosts syslog; do ln -s /etc/init.d/$svc_name etc/runlevels/boot/$svc_name done # default runlevel for svc_name in networking cron crond; do # issue 1164: alpine renamed cron to crond # Use the one that exists. if [ -e etc/init.d/$svc_name ]; then ln -s /etc/init.d/$svc_name etc/runlevels/default/$svc_name fi done } #=========================== Configure ===========================# configure_container() { local config="$1" local hostname="$2" local arch="$3" cat <<-EOF >> "$config" # Specify container architecture. lxc.arch = $arch # Set hostname. lxc.utsname = $hostname # If something doesn't work, try to comment this out. # Dropping sys_admin disables container root from doing a lot of things # that could be bad like re-mounting lxc fstab entries rw for example, # but also disables some useful things like being able to nfs mount, and # things that are already namespaced with ns_capable() kernel checks, like # hostname(1). lxc.cap.drop = sys_admin # Comment this out if you have to debug processes by tracing. lxc.cap.drop = sys_ptrace # Include common configuration. lxc.include = $LXC_TEMPLATE_CONFIG/alpine.common.conf EOF } #============================= Main ==============================# if [ "$(id -u)" != "0" ]; then die 1 "This script must be run as 'root'" fi # Parse command options. options=$(getopt -o a:dFm:n:p:r:h -l arch:,debug,flush-cache,mirror:,name:,\ path:,release:,rootfs:,help,mapped-uid:,mapped-gid: -- "$@") eval set -- "$options" # Clean variables and set defaults. arch="$(uname -m)" debug='no' flush_cache='no' mirror_url= name= path= release= rootfs= # Process command options. while [ $# -gt 0 ]; do case $1 in -a | --arch) arch=$2; shift 2 ;; -d | --debug) debug='yes'; shift 1 ;; -F | --flush-cache) flush_cache='yes'; shift 1 ;; -m | --mirror) mirror_url=$2; shift 2 ;; -n | --name) name=$2; shift 2 ;; -p | --path) path=$2; shift 2 ;; -r | --release) release=$2; shift 2 ;; --rootfs) rootfs=$2; shift 2 ;; -h | --help) usage; exit 0 ;; --) shift; break ;; --mapped-[ug]id) die 1 "This template can't be used for unprivileged containers." \ 'You may want to try the "download" template instead.' ;; *) echo "Unknown option: $1" 1>&2 usage; exit 1 ;; esac done extra_packages="$@" [ "$debug" = 'yes' ] && set -x # Set global variables. readonly DEBUG="$debug" readonly FLUSH_CACHE="$flush_cache" readonly MIRROR_URL="${mirror_url:-$DEFAULT_MIRROR_URL}" # Validate options. [ -n "$name" ] || die 1 'Missing required option --name' [ -n "$path" ] || die 1 'Missing required option --path' if [ -z "$rootfs" ] && [ -f "$path/config" ]; then rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" fi if [ -z "$rootfs" ]; then rootfs="$path/rootfs" fi arch=$(parse_arch "$arch") \ || die 1 "Unsupported architecture: $arch" if [ -z "$release" ]; then release=$(latest_release_branch "$arch") \ || die 2 'Failed to resolve Alpine last release branch' fi # Here we go! run_exclusively 'bootstrap' 10 bootstrap run_exclusively "$arch" 30 install "$rootfs" "$arch" "$release" "$extra_packages" configure_container "$path/config" "$name" "$arch" einfo "Container's rootfs and config have been created" cat <<-EOF Edit the config file $path/config to check/enable networking setup. The installed system is preconfigured for a loopback and single network interface configured via DHCP. To start the container, run "lxc-start -n $name". The root password is not set; to enter the container run "lxc-attach -n $name". EOF lxc-2.0.11/templates/lxc-altlinux.in0000644061062106075000000003271713435013473014301 00000000000000#!/bin/bash # # template script for generating altlinux container for LXC # # # lxc: linux Container library # Authors: # Alexey Shabalin # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin #Configurations arch=$(uname -m) cache_base=@LOCALSTATEDIR@/cache/lxc/altlinux/$arch default_path=@LXCPATH@ default_profile=default profile_dir=/etc/lxc/profiles lxc_network_type=veth lxc_network_link=virbr0 # is this altlinux? [ -f /etc/altlinux-release ] && is_altlinux=true configure_altlinux() { # disable selinux in altlinux mkdir -p $rootfs_path/selinux echo 0 > $rootfs_path/selinux/enforce mkdir -p ${rootfs_path}/etc/net/ifaces/eth0 cat < ${rootfs_path}/etc/net/ifaces/eth0/options BOOTPROTO=${BOOTPROTO} ONBOOT=yes NM_CONTROLLED=yes TYPE=eth EOF if [ ${BOOTPROTO} != "dhcp" ]; then # ip address cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv4address ${ipv4} EOF cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv4route ${gw} EOF cat < ${rootfs_path}/etc/net/ifaces/eth0/resolv.conf nameserver ${dns} EOF cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv6address ${ipv6} EOF cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv6route ${gw6} EOF fi # set the hostname cat < ${rootfs_path}/etc/sysconfig/network NETWORKING=yes CONFMETHOD=etcnet HOSTNAME=${UTSNAME} RESOLV_MODS=yes EOF # set minimal hosts cat < $rootfs_path/etc/hosts 127.0.0.1 localhost.localdomain localhost $name EOF # Allow to login at virsh console. loginuid.so doen't work in the absence of auditd. # sed -i 's/^.*loginuid.so.*$/\#&/' ${rootfs_path}/etc/pam.d/common-login # Allow root to login at virsh console echo "pts/0" >> ${rootfs_path}/etc/securetty echo "console" >> ${rootfs_path}/etc/securetty # Enable services for service in network syslogd random NetworkManager do chroot ${rootfs_path} chkconfig $service --list &>/dev/null && chroot ${rootfs_path} chkconfig $service on || true # For systemd chroot ${rootfs_path} systemctl -q enable $service &>/dev/null|| true done # Disable services for service in rawdevices fbsetfont do chroot ${rootfs_path} chkconfig $service --list &>/dev/null && chroot ${rootfs_path} chkconfig $service off || true chroot ${rootfs_path} systemctl -q disable $service &>/dev/null || true done subst 's/^\([3-9]\+:[0-9]\+:respawn:\/sbin\/mingetty.*\)/#\1/' ${rootfs_path}/etc/inittab echo "c1:2345:respawn:/sbin/mingetty --noclear console" >> ${rootfs_path}/etc/inittab [ -f "${rootfs_path}/etc/syslog.conf" ] && \ subst 's,\/dev\/tty12,/var/log/syslog/console,' ${rootfs_path}/etc/syslog.conf dev_path="${rootfs_path}/dev" rm -rf ${dev_path} mkdir -p ${dev_path} mknod -m 666 ${dev_path}/null c 1 3 mknod -m 666 ${dev_path}/zero c 1 5 mknod -m 644 ${dev_path}/random c 1 8 mknod -m 644 ${dev_path}/urandom c 1 9 mkdir -m 755 ${dev_path}/pts mkdir -m 1777 ${dev_path}/shm mknod -m 666 ${dev_path}/tty c 5 0 chown root:tty ${dev_path}/tty mknod -m 600 ${dev_path}/tty0 c 4 0 mknod -m 600 ${dev_path}/tty1 c 4 1 mknod -m 600 ${dev_path}/tty2 c 4 2 mknod -m 600 ${dev_path}/tty3 c 4 3 mknod -m 600 ${dev_path}/tty4 c 4 4 mknod -m 600 ${dev_path}/console c 5 1 mknod -m 666 ${dev_path}/full c 1 7 mknod -m 600 ${dev_path}/initctl p mknod -m 666 ${dev_path}/ptmx c 5 2 chown root:tty ${dev_path}/ptmx ln -s /proc/self/fd ${dev_path}/fd ln -s /proc/kcore ${dev_path}/core mkdir -m 755 ${dev_path}/mapper mknod -m 600 ${dev_path}/mapper/control c 10 236 mkdir -m 755 ${dev_path}/net mknod -m 666 ${dev_path}/net/tun c 10 200 if [ -n "${root_password}" ]; then echo "setting root passwd to $root_password" echo "root:$root_password" | chroot $rootfs_path chpasswd fi return 0 } download_altlinux() { # check the mini altlinux was not already downloaded INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then echo "Failed to create '$INSTALL_ROOT' directory" return 1 fi # download a mini altlinux into a cache echo "Downloading altlinux minimal ..." APT_GET="apt-get -o RPM::RootDir=$INSTALL_ROOT -y" PKG_LIST="$(grep -hs '^[^#]' "$profile_dir/$profile")" # if no configuration file $profile -- fall back to default list of packages [ -z "$PKG_LIST" ] && PKG_LIST="interactivesystem apt apt-conf etcnet-full openssh-server systemd-sysvinit systemd-units systemd NetworkManager-daemon" mkdir -p $INSTALL_ROOT/var/lib/rpm rpm --root $INSTALL_ROOT --initdb # some scripts want to have /dev/null at least dev_path="$INSTALL_ROOT/dev" if [ ! -c "${dev_path}/null" ]; then mkdir -p "${dev_path}" mknod -m 666 "${dev_path}/null" c 1 3 fi $APT_GET install $PKG_LIST if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi mv "$INSTALL_ROOT" "$cache/rootfs" echo "Download complete." return 0 } copy_altlinux() { # make a local copy of the minialtlinux echo -n "Copying rootfs to $rootfs_path ..." #cp -a $cache/rootfs-$arch $rootfs_path || return 1 # i prefer rsync (no reason really) mkdir -p $rootfs_path rsync -SHaAX $cache/rootfs/ $rootfs_path/ return 0 } update_altlinux() { chroot $cache/rootfs apt-get update chroot $cache/rootfs apt-get -y dist-upgrade } install_altlinux() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs ... " if [ ! -e "$cache/rootfs" ]; then download_altlinux if [ $? -ne 0 ]; then echo "Failed to download 'altlinux base'" return 1 fi else echo "Cache found. Updating..." update_altlinux if [ $? -ne 0 ]; then echo "Failed to update 'altlinux base', continuing with last known good cache" else echo "Update finished" fi fi echo "Copy $cache/rootfs to $rootfs_path ... " copy_altlinux if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux return $? } copy_configuration() { mkdir -p $config_path grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config cat <> $config_path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined #networking #lxc.network.type = $lxc_network_type #lxc.network.flags = up #lxc.network.link = $lxc_network_link #lxc.network.name = veth0 #lxc.network.mtu = 1500 EOF if [ ! -z ${ipv4} ]; then cat <> $config_path/config lxc.network.ipv4 = $ipv4 EOF fi if [ ! -z ${gw} ]; then cat <> $config_path/config lxc.network.ipv4.gateway = $gw EOF fi if [ ! -z ${ipv6} ]; then cat <> $config_path/config lxc.network.ipv6 = $ipv6 EOF fi if [ ! -z ${gw6} ]; then cat <> $config_path/config lxc.network.ipv6.gateway = $gw6 EOF fi cat <> $config_path/config #cgroups lxc.cgroup.devices.deny = a # /dev/null and zero lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 4:0 rwm lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 10:135 rwm lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache for ALTLinux-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux } usage() { cat < [-p|--path=] [-c|--clean] [-R|--release=] [-4|--ipv4=] [-6|--ipv6=] [-g|--gw=] [-d|--dns=] [-P|--profile=] [--rootfs=] [-A|--arch=] [-h|--help] Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case -c,--clean clean the cache -R,--release ALTLinux release for the new container. if the host is ALTLinux, then it will defaultto the host's release. -4,--ipv4 specify the ipv4 address to assign to the virtualized interface, eg. 192.168.1.123/24 -6,--ipv6 specify the ipv6 address to assign to the virtualized interface, eg. 2003:db8:1:0:214:1234:fe0b:3596/64 -g,--gw specify the default gw, eg. 192.168.1.1 -G,--gw6 specify the default gw, eg. 2003:db8:1:0:214:1234:fe0b:3596 -d,--dns specify the DNS server, eg. 192.168.1.2 -P,--profile Profile name is the file name in /etc/lxc/profiles contained packages name for install to cache. -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] ---rootfs rootfs path -h,--help print this help EOF return 0 } options=$(getopt -o hp:n:P:cR:4:6:g:d: -l help,rootfs:,path:,name:,profile:,clean,release:,ipv4:,ipv6:,gw:,dns: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs_path=$2; shift 2;; -n|--name) name=$2; shift 2;; -P|--profile) profile=$2; shift 2;; -c|--clean) clean=1; shift 1;; -R|--release) release=$2; shift 2;; -4|--ipv4) ipv4=$2; shift 2;; -6|--ipv6) ipv6=$2; shift 2;; -g|--gw) gw=$2; shift 2;; -d|--dns) dns=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi type apt-get >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "'apt-get' command is missing" exit 1 fi if [ -z "$path" ]; then path=$default_path fi if [ -z "$profile" ]; then profile=$default_profile fi if [ -z "$release" ]; then if [ "$is_altlinux" ]; then release=$(cat /etc/altlinux-release |awk '/^ALT/ {print $3}') else echo "This is not a ALTLinux host and release missing, use -R|--release to specify release" exit 1 fi fi if [ -z "$ipv4" -a -z "$ipv6" ]; then BOOTPROTO="dhcp" else BOOTPROTO="static" fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi # check for 'lxc.rootfs' passed in through default config by lxc-create if [ -z "$rootfs_path" ]; then if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then rootfs_path=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $path/config) else rootfs_path=$path/rootfs fi fi config_path=$default_path/$name cache=$cache_base/$release/$profile install_altlinux if [ $? -ne 0 ]; then echo "failed to install altlinux" exit 1 fi configure_altlinux if [ $? -ne 0 ]; then echo "failed to configure altlinux for a container" exit 1 fi copy_configuration if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi echo "container rootfs and config created" echo "network configured as $lxc_network_type in the $lxc_network_link" lxc-2.0.11/templates/lxc-download.in0000644061062106075000000004574213435013473014252 00000000000000#!/bin/sh # Client script for LXC container images. # # Copyright © 2014 Stéphane Graber # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA set -eu LOCALSTATEDIR="@LOCALSTATEDIR@" LXC_HOOK_DIR="@LXCHOOKDIR@" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" # Defaults DOWNLOAD_ARCH= DOWNLOAD_BUILD= DOWNLOAD_COMPAT_LEVEL=3 DOWNLOAD_DIST= DOWNLOAD_FLUSH_CACHE="false" DOWNLOAD_FORCE_CACHE="false" DOWNLOAD_INTERACTIVE="false" DOWNLOAD_KEYID="0xE7FB0CAEC8173D669066514CBAEFF88C22F6E216" DOWNLOAD_LIST_IMAGES="false" DOWNLOAD_MODE="system" DOWNLOAD_READY_GPG="false" DOWNLOAD_RELEASE= DOWNLOAD_SERVER="images.linuxcontainers.org" DOWNLOAD_SHOW_GPG_WARNING="true" DOWNLOAD_SHOW_HTTP_WARNING="true" DOWNLOAD_TARGET="system" DOWNLOAD_URL= DOWNLOAD_USE_CACHE="false" DOWNLOAD_VALIDATE="true" DOWNLOAD_VARIANT="default" DOWNLOAD_TEMP= LXC_MAPPED_GID= LXC_MAPPED_UID= LXC_NAME= LXC_PATH= LXC_ROOTFS= if [ -z "${DOWNLOAD_KEYSERVER:-}" ]; then DOWNLOAD_KEYSERVER="hkp://pool.sks-keyservers.net" # Deal with GPG over http proxy if [ -n "${http_proxy:-}" ]; then DOWNLOAD_KEYSERVER="hkp://p80.pool.sks-keyservers.net:80" fi fi # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin # Some useful functions cleanup() { if [ -d "${DOWNLOAD_TEMP}" ]; then rm -Rf "${DOWNLOAD_TEMP}" fi } wget_wrapper() { for i in $(seq 3); do if wget "$@"; then return 0 fi done return 1 } download_file() { if ! wget_wrapper -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then if ! wget_wrapper -T 30 -q "http://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then if [ "$3" = "noexit" ]; then return 1 else echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2 exit 1 fi elif [ "${DOWNLOAD_SHOW_HTTP_WARNING}" = "true" ]; then DOWNLOAD_SHOW_HTTP_WARNING="false" echo "WARNING: Failed to download the file over HTTPs." 1>&2 echo " The file was instead download over HTTP. " 1>&2 echo "A server replay attack may be possible!" 1>&2 fi fi } download_sig() { if ! download_file "$1" "$2" noexit; then if [ "${DOWNLOAD_VALIDATE}" = "true" ]; then if [ "$3" = "normal" ]; then echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2 exit 1 else return 1 fi else return 0 fi fi } gpg_setup() { if [ "${DOWNLOAD_VALIDATE}" = "false" ]; then return fi if [ "${DOWNLOAD_READY_GPG}" = "true" ]; then return fi echo "Setting up the GPG keyring" mkdir -p "${DOWNLOAD_TEMP}/gpg" chmod 700 "${DOWNLOAD_TEMP}/gpg" export GNUPGHOME="${DOWNLOAD_TEMP}/gpg" success= for i in $(seq 3); do if gpg --keyserver "${DOWNLOAD_KEYSERVER}" \ --recv-keys "${DOWNLOAD_KEYID}" >/dev/null 2>&1; then success=1 break fi break done if [ -z "${success}" ]; then echo "ERROR: Unable to fetch GPG key from keyserver." exit 1 fi DOWNLOAD_READY_GPG="true" } gpg_validate() { if [ "${DOWNLOAD_VALIDATE}" = "false" ]; then if [ "${DOWNLOAD_SHOW_GPG_WARNING}" = "true" ]; then echo "WARNING: Running without gpg validation!" 1>&2 fi DOWNLOAD_SHOW_GPG_WARNING="false" return 0 fi if ! gpg --verify "$1" >/dev/null 2>&1; then echo "ERROR: Invalid signature for $1" 1>&2 exit 1 fi } in_userns() { [ -e /proc/self/uid_map ] || { echo no; return; } while read -r line; do fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')" if [ "${fields}" = "0 0 4294967295" ]; then echo no; return; fi if echo "${fields}" | grep -q " 0 1$"; then echo userns-root; return; fi done < /proc/self/uid_map [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && \ { echo userns-root; return; } echo yes } relevant_file() { FILE_PATH="${LXC_CACHE_PATH}/$1" if [ -e "${FILE_PATH}-${DOWNLOAD_MODE}" ]; then FILE_PATH="${FILE_PATH}-${DOWNLOAD_MODE}" fi if [ -e "${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}" ]; then FILE_PATH="${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}" fi echo "${FILE_PATH}" } usage() { cat < ]: The name of the distribution [ -r | --release ]: Release name/version [ -a | --arch ]: Architecture of the container Optional arguments: [ --variant ]: Variant of the image (default: "default") [ --server ]: Image server (default: "images.linuxcontainers.org") [ --keyid ]: GPG keyid (default: 0x...) [ --keyserver ]: GPG keyserver to use. Environment variable: DOWNLOAD_KEYSERVER [ --no-validate ]: Disable GPG validation (not recommended) [ --flush-cache ]: Flush the local copy (if present) [ --force-cache ]: Force the use of the local copy even if expired LXC internal arguments (do not pass manually!): [ --name ]: The container name [ --path ]: The path to the container [ --rootfs ]: The path to the container's rootfs [ --mapped-uid ]: A uid map (user namespaces) [ --mapped-gid ]: A gid map (user namespaces) Environment Variables: DOWNLOAD_KEYSERVER : The URL of the key server to use, instead of the default. Can be further overridden by using optional argument --keyserver EOF return 0 } if ! options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ server:,keyid:,keyserver:,no-validate,flush-cache,force-cache,name:,path:,\ rootfs:,mapped-uid:,mapped-gid: -- "$@"); then usage exit 1 fi eval set -- "$options" while :; do case "$1" in -h|--help) usage && exit 1;; -l|--list) DOWNLOAD_LIST_IMAGES="true"; shift 1;; -d|--dist) DOWNLOAD_DIST="$2"; shift 2;; -r|--release) DOWNLOAD_RELEASE="$2"; shift 2;; -a|--arch) DOWNLOAD_ARCH="$2"; shift 2;; --variant) DOWNLOAD_VARIANT="$2"; shift 2;; --server) DOWNLOAD_SERVER="$2"; shift 2;; --keyid) DOWNLOAD_KEYID="$2"; shift 2;; --keyserver) DOWNLOAD_KEYSERVER="$2"; shift 2;; --no-validate) DOWNLOAD_VALIDATE="false"; shift 1;; --flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;; --force-cache) DOWNLOAD_FORCE_CACHE="true"; shift 1;; --name) LXC_NAME="$2"; shift 2;; --path) LXC_PATH="$2"; shift 2;; --rootfs) LXC_ROOTFS="$2"; shift 2;; --mapped-uid) LXC_MAPPED_UID="$2"; shift 2;; --mapped-gid) LXC_MAPPED_GID="$2"; shift 2;; *) break;; esac done # Check for required binaries for bin in tar xz wget; do if ! command -V "${bin}" >/dev/null 2>&1; then echo "ERROR: Missing required tool: ${bin}" 1>&2 exit 1 fi done # Check for GPG if [ "${DOWNLOAD_VALIDATE}" = "true" ]; then if ! command -V gpg >/dev/null 2>&1; then echo "ERROR: Missing recommended tool: gpg" 1>&2 echo "You can workaround this by using --no-validate." 1>&2 exit 1 fi fi # Check that we have all variables we need if [ -z "${LXC_NAME}" ] || [ -z "${LXC_PATH}" ] || [ -z "${LXC_ROOTFS}" ]; then if [ "${DOWNLOAD_LIST_IMAGES}" != "true" ]; then echo "ERROR: Not running through LXC." 1>&2 exit 1 fi fi USERNS="$(in_userns)" if [ "${USERNS}" != "no" ]; then if [ "${USERNS}" = "yes" ]; then if [ -z "${LXC_MAPPED_UID}" ] || [ "${LXC_MAPPED_UID}" = "-1" ]; then echo "ERROR: In a user namespace without a map." 1>&2 exit 1 fi DOWNLOAD_MODE="user" DOWNLOAD_TARGET="user" else DOWNLOAD_MODE="user" DOWNLOAD_TARGET="system" fi fi if [ -z "${DOWNLOAD_DIST}" ] || [ -z "${DOWNLOAD_RELEASE}" ] || \ [ -z "${DOWNLOAD_ARCH}" ]; then DOWNLOAD_INTERACTIVE="true" fi # Trap all exit signals trap cleanup EXIT HUP INT TERM # /tmp may be mounted in tmpfs or noexec if mountpoint -q /tmp; then DOWNLOAD_TEMP="${LXC_PATH}" fi if ! command -V mktemp >/dev/null 2>&1; then DOWNLOAD_TEMP="${DOWNLOAD_TEMP}/tmp/lxc-download.$$" else DOWNLOAD_TEMP="${DOWNLOAD_TEMP}$(mktemp -d)" fi # Simply list images if [ "${DOWNLOAD_LIST_IMAGES}" = "true" ] || \ [ "${DOWNLOAD_INTERACTIVE}" = "true" ]; then # Initialize GPG gpg_setup # Grab the index DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}" echo "Downloading the image index" if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" \ "${DOWNLOAD_TEMP}/index" noexit || ! download_sig "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc" \ "${DOWNLOAD_TEMP}/index.asc" noexit; then download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal download_sig "${DOWNLOAD_INDEX_PATH}.asc" \ "${DOWNLOAD_TEMP}/index.asc" normal fi gpg_validate "${DOWNLOAD_TEMP}/index.asc" # Parse it echo "" echo "---" printf "DIST\tRELEASE\tARCH\tVARIANT\tBUILD\n" echo "---" while IFS=';' read -r f1 f2 f3 f4 f5 f6; do [ -n "${DOWNLOAD_DIST}" ] && [ "$f1" != "${DOWNLOAD_DIST}" ] && continue [ -n "${DOWNLOAD_RELEASE}" ] && [ "$f2" != "${DOWNLOAD_RELEASE}" ] && continue [ -n "${DOWNLOAD_ARCH}" ] && [ "$f3" != "${DOWNLOAD_ARCH}" ] && continue [ -n "${DOWNLOAD_VARIANT}" ] && [ "$f4" != "${DOWNLOAD_VARIANT}" ] && continue [ -z "${f5}" ] || [ -z "${f6}" ] && continue printf "%s\t%s\t%s\t%s\t%s\n" "${f1}" "${f2}" "${f3}" "${f4}" "${f5}" unset f1 f2 f3 f4 f5 f6 done < "${DOWNLOAD_TEMP}/index" echo "---" if [ "${DOWNLOAD_LIST_IMAGES}" = "true" ]; then exit 1 fi # Interactive mode echo "" if [ -z "${DOWNLOAD_DIST}" ]; then echo "Distribution: " read -r DOWNLOAD_DIST fi if [ -z "${DOWNLOAD_RELEASE}" ]; then echo "Release: " read -r DOWNLOAD_RELEASE fi if [ -z "${DOWNLOAD_ARCH}" ]; then echo "Architecture: " read -r DOWNLOAD_ARCH fi echo "" fi # Setup the cache if [ "${DOWNLOAD_TARGET}" = "system" ]; then LXC_CACHE_BASE="${LOCALSTATEDIR}/cache/lxc/" else LXC_CACHE_BASE="${HOME}/.cache/lxc/" fi # Allow the setting of the LXC_CACHE_PATH with the usage of environment variables. LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${LXC_CACHE_BASE}"}" LXC_CACHE_PATH="${LXC_CACHE_PATH}/download/${DOWNLOAD_DIST}" LXC_CACHE_PATH="${LXC_CACHE_PATH}/${DOWNLOAD_RELEASE}/${DOWNLOAD_ARCH}/" LXC_CACHE_PATH="${LXC_CACHE_PATH}/${DOWNLOAD_VARIANT}" if [ -d "${LXC_CACHE_PATH}" ]; then if [ "${DOWNLOAD_FLUSH_CACHE}" = "true" ]; then echo "Flushing the cache..." rm -Rf "${LXC_CACHE_PATH}" elif [ "${DOWNLOAD_FORCE_CACHE}" = "true" ]; then DOWNLOAD_USE_CACHE="true" else DOWNLOAD_USE_CACHE="true" if [ -e "$(relevant_file expiry)" ]; then if [ "$(cat "$(relevant_file expiry)")" -lt "$(date +%s)" ]; then echo "The cached copy has expired, re-downloading..." DOWNLOAD_USE_CACHE="false" fi fi fi fi # Download what's needed if [ "${DOWNLOAD_USE_CACHE}" = "false" ]; then # Initialize GPG gpg_setup # Grab the index DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}" echo "Downloading the image index" if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" \ "${DOWNLOAD_TEMP}/index" noexit || ! download_sig "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc" \ "${DOWNLOAD_TEMP}/index.asc" noexit; then download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal download_sig "${DOWNLOAD_INDEX_PATH}.asc" \ "${DOWNLOAD_TEMP}/index.asc" normal fi gpg_validate "${DOWNLOAD_TEMP}/index.asc" # Parse it while IFS=';' read -r f1 f2 f3 f4 f5 f6; do if [ "${f1}" != "${DOWNLOAD_DIST}" ] || \ [ "${f2}" != "${DOWNLOAD_RELEASE}" ] || \ [ "${f3}" != "${DOWNLOAD_ARCH}" ] || \ [ "${f4}" != "${DOWNLOAD_VARIANT}" ] || \ [ -z "${f6}" ]; then continue fi DOWNLOAD_BUILD="${f5}" DOWNLOAD_URL="${f6}" unset f1 f2 f3 f4 f5 f6 break done < "${DOWNLOAD_TEMP}/index" if [ -z "${DOWNLOAD_URL}" ]; then echo "ERROR: Couldn't find a matching image." 1>&1 exit 1 fi if [ -d "${LXC_CACHE_PATH}" ] && [ -f "${LXC_CACHE_PATH}/build_id" ] && \ [ "$(cat "${LXC_CACHE_PATH}/build_id")" = "${DOWNLOAD_BUILD}" ]; then echo "The cache is already up to date." echo "Using image from local cache" else # Download the actual files echo "Downloading the rootfs" download_file "${DOWNLOAD_URL}/rootfs.tar.xz" \ "${DOWNLOAD_TEMP}/rootfs.tar.xz" normal download_sig "${DOWNLOAD_URL}/rootfs.tar.xz.asc" \ "${DOWNLOAD_TEMP}/rootfs.tar.xz.asc" normal gpg_validate "${DOWNLOAD_TEMP}/rootfs.tar.xz.asc" echo "Downloading the metadata" download_file "${DOWNLOAD_URL}/meta.tar.xz" \ "${DOWNLOAD_TEMP}/meta.tar.xz" normal download_sig "$DOWNLOAD_URL/meta.tar.xz.asc" \ "${DOWNLOAD_TEMP}/meta.tar.xz.asc" normal gpg_validate "${DOWNLOAD_TEMP}/meta.tar.xz.asc" if [ -d "${LXC_CACHE_PATH}" ]; then rm -Rf "${LXC_CACHE_PATH}" fi mkdir -p "${LXC_CACHE_PATH}" mv "${DOWNLOAD_TEMP}/rootfs.tar.xz" "${LXC_CACHE_PATH}" if ! tar Jxf "${DOWNLOAD_TEMP}/meta.tar.xz" -C "${LXC_CACHE_PATH}"; then echo "ERROR: Invalid rootfs tarball." 2>&1 exit 1 fi echo "${DOWNLOAD_BUILD}" > "${LXC_CACHE_PATH}/build_id" if [ -n "${LXC_MAPPED_UID}" ] && [ "${LXC_MAPPED_UID}" != "-1" ]; then # As the script is run in strict mode (set -eu), all commands # exiting with non 0 would make the script stop. # || true or || : (more portable) prevents that. chown -R "${LXC_MAPPED_UID}" "${LXC_CACHE_BASE}" >/dev/null 2>&1 || : fi if [ -n "${LXC_MAPPED_GID}" ] && [ "${LXC_MAPPED_GID}" != "-1" ]; then chgrp -R "${LXC_MAPPED_GID}" "${LXC_CACHE_BASE}" >/dev/null 2>&1 || : fi echo "The image cache is now ready" fi else echo "Using image from local cache" fi # Unpack the rootfs echo "Unpacking the rootfs" EXCLUDES="" excludelist=$(relevant_file excludes) if [ -f "${excludelist}" ]; then while read -r line; do EXCLUDES="${EXCLUDES} --exclude=${line}" done < "${excludelist}" fi # Do not surround ${EXCLUDES} by quotes. This does not work. The solution could # use array but this is not POSIX compliant. The only POSIX compliant solution # is to use a function wrapper, but the latter can't be used here as the args # are dynamic. We thus need to ignore the warning brought by shellcheck. # shellcheck disable=SC2086 tar --anchored ${EXCLUDES} --numeric-owner -xpJf \ "${LXC_CACHE_PATH}/rootfs.tar.xz" -C "${LXC_ROOTFS}" mkdir -p "${LXC_ROOTFS}/dev/pts/" # Setup the configuration configfile="$(relevant_file config)" fstab="$(relevant_file fstab)" if [ ! -e "${configfile}" ]; then echo "ERROR: meta tarball is missing the configuration file" 1>&2 exit 1 fi ## Extract all the network config entries sed -i -e "/lxc.network/{w ${LXC_PATH}/config-network" -e "d}" \ "${LXC_PATH}/config" ## Extract any other config entry sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config" ## Append the defaults echo "" >> "${LXC_PATH}/config" echo "# Distribution configuration" >> "${LXC_PATH}/config" cat "$configfile" >> "${LXC_PATH}/config" ## Add the container-specific config echo "" >> "${LXC_PATH}/config" echo "# Container specific configuration" >> "${LXC_PATH}/config" if [ -e "${LXC_PATH}/config-auto" ]; then cat "${LXC_PATH}/config-auto" >> "${LXC_PATH}/config" rm "${LXC_PATH}/config-auto" fi if [ -e "${fstab}" ]; then echo "lxc.mount = ${LXC_PATH}/fstab" >> ${LXC_PATH}/config fi echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config ## Re-add the previously removed network config if [ -e "${LXC_PATH}/config-network" ]; then echo "" >> "${LXC_PATH}/config" echo "# Network configuration" >> "${LXC_PATH}/config" cat "${LXC_PATH}/config-network" >> "${LXC_PATH}/config" rm "${LXC_PATH}/config-network" fi TEMPLATE_FILES="${LXC_PATH}/config" # Setup the fstab if [ -e "${fstab}" ]; then cp "${fstab}" "${LXC_PATH}/fstab" TEMPLATE_FILES="${TEMPLATE_FILES};${LXC_PATH}/fstab" fi # Look for extra templates if [ -e "$(relevant_file templates)" ]; then while read -r line; do fullpath="${LXC_ROOTFS}/${line}" [ ! -e "${fullpath}" ] && continue TEMPLATE_FILES="${TEMPLATE_FILES};${fullpath}" done < "$(relevant_file templates)" fi # Replace variables in all templates OLD_IFS=${IFS} IFS=";" for file in ${TEMPLATE_FILES}; do [ ! -f "${file}" ] && continue sed -i "s#LXC_NAME#${LXC_NAME}#g" "${file}" sed -i "s#LXC_PATH#${LXC_PATH}#g" "${file}" sed -i "s#LXC_ROOTFS#${LXC_ROOTFS}#g" "${file}" sed -i "s#LXC_TEMPLATE_CONFIG#${LXC_TEMPLATE_CONFIG}#g" "${file}" sed -i "s#LXC_HOOK_DIR#${LXC_HOOK_DIR}#g" "${file}" done IFS=${OLD_IFS} # prevent mingetty from calling vhangup(2) since it fails with userns on CentOS / Oracle if [ -f "${LXC_ROOTFS}/etc/init/tty.conf" ]; then sed -i 's|mingetty|mingetty --nohangup|' "${LXC_ROOTFS}/etc/init/tty.conf" fi if [ -n "${LXC_MAPPED_UID}" ] && [ "${LXC_MAPPED_UID}" != "-1" ]; then chown "${LXC_MAPPED_UID}" "${LXC_PATH}/config" "${LXC_PATH}/fstab" >/dev/null 2>&1 || : fi if [ -n "${LXC_MAPPED_GID}" ] && [ "${LXC_MAPPED_GID}" != "-1" ]; then chgrp "${LXC_MAPPED_GID}" "${LXC_PATH}/config" "${LXC_PATH}/fstab" >/dev/null 2>&1 || : fi if [ -e "$(relevant_file create-message)" ]; then echo "" echo "---" cat "$(relevant_file create-message)" fi exit 0 lxc-2.0.11/templates/lxc-plamo.in0000644061062106075000000002674313435013473013553 00000000000000#!/bin/bash -eu # # template script for generating Plamo Linux container for LXC # # # lxc: linux Container library # Authors: # KATOH Yasufumi # TAMUKI Shoichi # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # ref. https://github.com/Ponce/lxc-slackware/blob/master/lxc-slackware # lxc-ubuntu script # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin [ -r /etc/default/lxc ] && . /etc/default/lxc DLSCHEME=${DLSCHEME:-"http"} MIRRORSRV=${MIRRORSRV:-"repository.plamolinux.org"} MIRRORPATH=${MIRRORPATH:-"/pub/linux/Plamo"} CATEGORIES=${CATEGORIES-"00_base 01_minimum"} EXTRACTGRS=${EXTRACTGRS-""} IGNOREPKGS=${IGNOREPKGS-"grub kernel lilo linux_firmware microcode_ctl cpufreqd cpufrequtils gpm ntp kmod"} ADDONPKGS=${ADDONPKGS-"`echo contrib/Hamradio/{morse,qrq}`"} download_plamo() { # check the mini plamo was not already downloaded if ! mkdir -p $ptcache ; then echo "Failed to create '$ptcache' directory." return 1 fi # download a mini plamo into a cache echo "Downloading Plamo-$release minimal..." cd $ptcache case $DLSCHEME in http) depth=2 ;; ftp) depth=3 ;; esac rej=${IGNOREPKGS%% *} ; [ -n "$rej" ] && rej="$rej-*" if [ `echo $IGNOREPKGS | wc -w` -gt 1 ] ; then for p in ${IGNOREPKGS#* } ; do rej="$rej,$p-*" ; done fi for i in $CATEGORIES ; do wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz -R "$rej" \ -I $MIRRORPATH/Plamo-$release/$arch/plamo/$i \ -X $MIRRORPATH/Plamo-$release/$arch/plamo/$i/old \ $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/plamo/$i if [ $? -ne 0 ] ; then echo "Failed to download the rootfs, aborting." return 1 fi done for i in $EXTRACTGRS ; do wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz -R "$rej" \ -I $MIRRORPATH/Plamo-$release/$arch/contrib/$i \ -X $MIRRORPATH/Plamo-$release/$arch/contrib/$i/old \ $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/contrib/$i if [ $? -ne 0 ] ; then echo "Failed to download the rootfs, aborting." return 1 fi done for p in $ADDONPKGS ; do wget -nv -e robots=off -r -l $depth -nd -A "`basename $p`-*" \ -I $MIRRORPATH/Plamo-$release/$arch/`dirname $p` \ -X $MIRRORPATH/Plamo-$release/$arch/`dirname $p`/old \ $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/`dirname $p` if [ $? -ne 0 ] ; then echo "Failed to download the rootfs, aborting." return 1 fi done mv $ptcache $dlcache echo "Download complete." return 0 } copy_plamo() { # make a local copy of the mini plamo echo "Copying $rtcache to $rootfs..." mkdir -p $rootfs find $rtcache -mindepth 1 -maxdepth 1 -exec cp -a {} $rootfs \; || return 1 return 0 } install_plamo() { mkdir -p @LOCALSTATEDIR@/lock/subsys ( if ! flock -n 9 ; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $dlcache..." if [ ! -d $dlcache ] ; then if ! download_plamo ; then echo "Failed to download plamo $release base packages." return 1 fi fi # install "installpkg" command temporarily with static linked tar # command into the lxc cache directory to keep the original uid/ # gid of files/directories. echo "Installing 'installpkg' command into $dlcache/sbin..." ( cd $dlcache ; tar xpJf hdsetup-*.txz ; rm -rf tmp usr var ) sed -i "/ldconfig/!s@/sbin@$dlcache&@g" $dlcache/sbin/installpkg* PATH=$dlcache/sbin:$PATH echo "Installing packages to $rtcache..." if [ ! -d $rtcache ] ; then mkdir -p $rtcache for p in `ls -cr $dlcache/*.t?z` ; do installpkg -root $rtcache -priority ADD $p done fi echo "Copy $rtcache to $rootfs..." if ! copy_plamo ; then echo "Failed to copy rootfs." return 1 fi return 0 ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo } configure_plamo() { # suppress log level output for udev sed -i 's/="err"/=0/' $rootfs/etc/udev/udev.conf # /etc/fstab cat <<- "EOF" > $rootfs/etc/fstab none /proc proc defaults 0 0 none /sys sysfs defaults 0 0 none /dev tmpfs defaults 0 0 none /tmp tmpfs defaults 0 0 none /dev/pts devpts gid=5,mode=620 0 0 none /proc/bus/usb usbfs noauto 0 0 none /var/lib/nfs/rpc_pipefs rpc_pipefs defaults 0 0 EOF # /etc/inittab cat <<- "EOF" | patch $rootfs/etc/inittab 32,33c32,33 < # What to do when power fails (shutdown to single user). < pf::powerfail:/sbin/shutdown -f +5 "THE POWER IS FAILING" --- > # What to do when power fails (shutdown). > pf::powerfail:/sbin/shutdown -h +0 "THE POWER IS FAILING" 47a48 > 1:1235:respawn:/sbin/agetty 38400 console 52,53d52 < c5:1235:respawn:/sbin/agetty 38400 tty5 linux < c6:12345:respawn:/sbin/agetty 38400 tty6 linux EOF # set the hostname echo "$name" > $rootfs/etc/HOSTNAME # set minimal hosts echo "127.0.0.1 localhost $name" > $rootfs/etc/hosts # configure the network using the dhcp echo "DHCP" > $rootfs/var/run/inet1-scheme # localtime (JST) ln -s ../usr/share/zoneinfo/Asia/Tokyo $rootfs/etc/localtime # disable pam_loginuid.so in /etc/pam.d/login (for libvirt's lxc driver) sed -i '/pam_loginuid/s/^/#/' $rootfs/etc/pam.d/login # glibc configure mv $rootfs/etc/ld.so.conf{.new,} chroot $rootfs ldconfig # delete unnecessary process from rc.S ed - $rootfs/etc/rc.d/rc.S <<- "EOF" /^mount -w -n -t proc/;/^mkdir \/dev\/shm/-1d /^mknod \/dev\/null/;/^# Clean \/etc\/mtab/-2d /^# copy the rules/;/^# Set the hostname/-1d /^# Check the integrity/;/^# Clean up temporary/-1d w EOF # delete unnecessary process from rc.M ed - $rootfs/etc/rc.d/rc.M <<- "EOF" /^# Screen blanks/;/^# Initialize ip6tables/-1d /^# Initialize sysctl/;/^echo "Starting services/-1d /^sync/;/^# All done/-1d w EOF # delete unnecessary process from rc.6 ed - $rootfs/etc/rc.d/rc.6 <<- "EOF" /^# Save system time/;/^# Unmount any remote filesystems/-1d /^# Turn off swap/;/^# See if this is a powerfail situation/-1d w EOF # /etc/rc.d/rc.inet1.tradnet head -n-93 $rootfs/sbin/netconfig.tradnet > /tmp/netconfig.rconly cat <<- EOF >> /tmp/netconfig.rconly PCMCIA=n RC=$rootfs/etc/rc.d/rc.inet1.tradnet IFCONFIG=sbin/ifconfig ROUTE=sbin/route INET1SCHEME=var/run/inet1-scheme IPADDR=127.0.0.1 NETWORK=127.0.0.0 DHCPCD=usr/sbin/dhclient LOOPBACK=y make_config_file EOF rm -f $rootfs/etc/rc.d/rc.inet1.tradnet sh /tmp/netconfig.rconly rm -f /tmp/netconfig.rconly sed -i '/cmdline/s/if/& false \&\&/' $rootfs/etc/rc.d/rc.inet1.tradnet # /etc/rc.d/rc.inet2 sed -i '/rpc.mountd/s/^/#/' $rootfs/etc/rc.d/rc.inet2 sed -i '/modprobe/s/^/#/' $rootfs/etc/rc.d/rc.inet2 # configure to start only the minimum of service chmod 644 $rootfs/etc/rc.d/init.d/saslauthd chmod 644 $rootfs/etc/rc.d/init.d/open-iscsi rm -f $rootfs/etc/rc.d/init.d/postfix rm -f $rootfs/var/log/initpkg/shadow return 0 } copy_configuration() { ret=0 cat <<- EOF >> $path/config || let ret++ lxc.utsname = $name lxc.arch = $arch EOF if [ -f "@LXCTEMPLATECONFIG@/plamo.common.conf" ] ; then cat <<- "EOF" >> $path/config || let ret++ lxc.include = @LXCTEMPLATECONFIG@/plamo.common.conf EOF fi if [ $ret -ne 0 ] ; then echo "Failed to add configuration." return 1 fi return 0 } post_process() { # nothing do in Plamo Linux true } do_bindhome() { # bind-mount the user's path into the container's /home h=`getent passwd $bindhome | cut -d: -f6` mkdir -p $rootfs/$h echo "lxc.mount.entry = $h $rootfs/$h none bind 0 0" >> $path/config # copy /etc/passwd, /etc/shadow, and /etc/group entries into container if ! pwd=`getent passwd $bindhome` ; then echo "Warning: failed to copy password entry for $bindhome." else echo $pwd >> $rootfs/etc/passwd fi echo `getent shadow $bindhome` >> $rootfs/etc/shadow } cleanup() { [ -d $dlcache -a -d $rtcache ] || return 0 # lock, so we won't purge while someone is creating a repository ( if ! flock -n 9 ; then echo "Cache repository is busy." return 1 fi echo "Purging the download cache..." rm -rf --one-file-system $dlcache $rtcache || return 1 echo "Done." return 0 ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo } usage() { cat <<- EOF $prog [-h|--help] -p|--path= -n|--name= --rootfs= [-c|--clean] [-r|--release=] [-a|--arch=] [-b|--bindhome=] release: $release arch: x86 or x86_64: defaults to host arch bindhome: bind 's home into the container EOF } prog=`basename $0` path="" ; name="" ; rootfs="" clean=0 release=${release:-6.x} arch=`uname -m | sed 's/i.86/x86/'` ; hostarch=$arch bindhome="" sopts=hp:n:cr:a:b: lopts=help,path:,name:,rootfs:,clean,release:,arch:,bindhome: if ! options=`getopt -o $sopts -l $lopts -- "$@"` ; then usage exit 1 fi eval set -- "$options" while true ; do case "$1" in -h|--help) usage && exit 0 ;; -p|--path) path=$2 ; shift 2 ;; -n|--name) name=$2 ; shift 2 ;; --rootfs) rootfs=$2 ; shift 2 ;; -c|--clean) clean=1 ; shift 1 ;; -r|--release) release=$2 ; shift 2 ;; -a|--arch) arch=$2 ; shift 2 ;; -b|--bindhome) bindhome=$2 ; shift 2 ;; --) shift 1 ; break ;; *) break ;; esac done if [ $clean -eq 1 -a -z "$path" ] ; then cleanup || exit 1 exit 0 fi if [ $hostarch == "x86" -a $arch == "x86_64" ] ; then echo "Can't create x86_64 container on x86." exit 1 fi if [ -z "$path" ] ; then echo "'path' parameter is required." exit 1 fi if [ -z "$name" ] ; then echo "'name' parameter is required." exit 1 fi if [ `id -u` -ne 0 ] ; then echo "This script should be run as 'root'." exit 1 fi cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc}" ptcache=$cache/partial-${prog##*-}-$release-$arch dlcache=$cache/cache-${prog##*-}-$release-$arch rtcache=$cache/rootfs-${prog##*-}-$release-$arch if [ -z "$rootfs" ] ; then if grep -q "^lxc.rootfs" $path/config ; then rootfs=`awk -F= '/^lxc.rootfs =/{ print $2 }' $path/config` else rootfs=$path/rootfs fi fi if ! install_plamo ; then echo "Failed to install plamo $release." exit 1 fi if ! configure_plamo ; then echo "Failed to configure plamo $release for a container." exit 1 fi if ! copy_configuration ; then echo "Failed to write configuration file." exit 1 fi post_process if [ -n "$bindhome" ] ; then do_bindhome fi if [ $clean -eq 1 ] ; then cleanup || exit 1 exit 0 fi lxc-2.0.11/templates/lxc-fedora.in0000644061062106075000000014110213435013473013666 00000000000000#!/bin/bash # # template script for generating fedora container for LXC # # # lxc: linux Container library # Authors: # Daniel Lezcano # Ramez Hanna # Michael H. Warfield # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #Configurations default_path=@LXCPATH@ # Some combinations of the tuning knobs below do not exactly make sense. # but that's ok. # # If the "root_password" is non-blank, use it, else set a default. # This can be passed to the script as an environment variable and is # set by a shell conditional assignment. Looks weird but it is what it is. # # If the root password contains a ding ($) then try to expand it. # That will pick up things like ${name} and ${RANDOM}. # If the root password contains more than 3 consecutive X's, pass it as # a template to mktemp and take the result. # # If root_display_password = yes, display the temporary root password at exit. # If root_store_password = yes, store it in the configuration directory # If root_prompt_password = yes, invoke "passwd" to force the user to change # the root password after the container is created. # If root_expire_password = yes, you will be prompted to change the root # password at the first login. # # These are conditional assignments... The can be overridden from the # preexisting environment variables... # # Make sure this is in single quotes to defer expansion to later! # :{root_password='Root-${name}-${RANDOM}'} : ${root_password='Root-${name}-XXXXXX'} # Now, it doesn't make much sense to display, store, and force change # together. But, we gotta test, right??? : ${root_display_password='no'} : ${root_store_password='yes'} # Prompting for something interactive has potential for mayhem # with users running under the API... Don't default to "yes" : ${root_prompt_password='no'} # Expire root password? Default to yes, but can be overridden from # the environment variable : ${root_expire_password='yes'} # These are only going into comments in the resulting config... lxc_network_type=veth lxc_network_link=lxcbr0 # is this fedora? # Alow for weird remixes like the Raspberry Pi # # Use the Mitre standard CPE identifier for the release ID if possible... # This may be in /etc/os-release or /etc/system-release-cpe. We # should be able to use EITHER. Give preference to /etc/os-release for now. # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin if [ -e /etc/os-release ] then # This is a shell friendly configuration file. We can just source it. # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME . /etc/os-release echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" fi if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] then CPE_NAME=$(head -n1 /etc/system-release-cpe) CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') if [ "${CPE_URI}" != "cpe:/o" ] then CPE_NAME= else echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" # Probably a better way to do this but sill remain posix # compatible but this works, shrug... # Must be nice and not introduce convenient bashisms here. ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') fi fi if [ "${CPE_NAME}" != "" -a "${ID}" = "fedora" -a "${VERSION_ID}" != "" ] then fedora_host_ver=${VERSION_ID} is_fedora=true elif [ -e /etc/redhat-release ] then # Only if all other methods fail, try to parse the redhat-release file. fedora_host_ver=$( sed -e '/^Fedora /!d' -e 's/Fedora.*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release ) if [ "$fedora_host_ver" != "" ] then is_fedora=true fi fi configure_fedora() { # disable selinux in fedora mkdir -p $rootfs_path/selinux echo 0 > $rootfs_path/selinux/enforce # Also kill it in the /etc/selinux/config file if it's there... if [[ -f $rootfs_path/etc/selinux/config ]] then sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config fi # Nice catch from Dwight Engen in the Oracle template. # Wantonly plagerized here with much appreciation. if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled fi # This is a known problem and documented in RedHat bugzilla as relating # to a problem with auditing enabled. This prevents an error in # the container "Cannot make/remove an entry for the specified session" sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd if [ -f ${rootfs_path}/etc/pam.d/crond ] then sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond fi # In addition to disabling pam_loginuid in the above config files # we'll also disable it by linking it to pam_permit to catch any # we missed or any that get installed after the container is built. # # Catch either or both 32 and 64 bit archs. if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] then ( cd ${rootfs_path}/lib/security/ mv pam_loginuid.so pam_loginuid.so.disabled ln -s pam_permit.so pam_loginuid.so ) fi if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] then ( cd ${rootfs_path}/lib64/security/ mv pam_loginuid.so pam_loginuid.so.disabled ln -s pam_permit.so pam_loginuid.so ) fi # Set default localtime to the host localtime if not set... if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] then # if /etc/localtime is a symlink, this should preserve it. cp -a /etc/localtime ${rootfs_path}/etc/localtime fi # Deal with some dain bramage in the /etc/init.d/halt script. # Trim it and make it our own and link it in before the default # halt script so we can intercept it. This also preventions package # updates from interferring with our interferring with it. # # There's generally not much in the halt script that useful but what's # in there from resetting the hardware clock down is generally very bad. # So we just eliminate the whole bottom half of that script in making # ourselves a copy. That way a major update to the init scripts won't # trash what we've set up. # # This is mostly for legacy distros since any modern systemd Fedora # release will not have this script so we won't try to intercept it. if [ -f ${rootfs_path}/etc/init.d/halt ] then sed -e '/hwclock/,$d' \ < ${rootfs_path}/etc/init.d/halt \ > ${rootfs_path}/etc/init.d/lxc-halt echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt chmod 755 ${rootfs_path}/etc/init.d/lxc-halt # Link them into the rc directories... ( cd ${rootfs_path}/etc/rc.d/rc0.d ln -s ../init.d/lxc-halt S00lxc-halt cd ${rootfs_path}/etc/rc.d/rc6.d ln -s ../init.d/lxc-halt S00lxc-reboot ) fi # configure the network using the dhcp cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes HOSTNAME=${utsname} DHCP_HOSTNAME=\`hostname\` NM_CONTROLLED=no TYPE=Ethernet MTU=${MTU} EOF # set the hostname cat < ${rootfs_path}/etc/sysconfig/network NETWORKING=yes HOSTNAME=${utsname} EOF # set hostname on systemd Fedora systems if [ $release -gt 14 ]; then echo "${utsname}" > ${rootfs_path}/etc/hostname fi # set minimal hosts cat < $rootfs_path/etc/hosts 127.0.0.1 localhost.localdomain localhost $utsname ::1 localhost6.localdomain6 localhost6 EOF # These mknod's really don't make any sense with modern releases of # Fedora with systemd, devtmpfs, and autodev enabled. They are left # here for legacy reasons and older releases with upstart and sysv init. dev_path="${rootfs_path}/dev" rm -rf $dev_path mkdir -p $dev_path mknod -m 666 ${dev_path}/null c 1 3 mknod -m 666 ${dev_path}/zero c 1 5 mknod -m 666 ${dev_path}/random c 1 8 mknod -m 666 ${dev_path}/urandom c 1 9 mkdir -m 755 ${dev_path}/pts mkdir -m 1777 ${dev_path}/shm mknod -m 666 ${dev_path}/tty c 5 0 mknod -m 666 ${dev_path}/tty0 c 4 0 mknod -m 666 ${dev_path}/tty1 c 4 1 mknod -m 666 ${dev_path}/tty2 c 4 2 mknod -m 666 ${dev_path}/tty3 c 4 3 mknod -m 666 ${dev_path}/tty4 c 4 4 mknod -m 600 ${dev_path}/console c 5 1 mknod -m 666 ${dev_path}/full c 1 7 mknod -m 600 ${dev_path}/initctl p mknod -m 666 ${dev_path}/ptmx c 5 2 # setup console and tty[1-4] for login. note that /dev/console and # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. # lxc will maintain these links and bind mount ptys over /dev/lxc/* # since lxc.devttydir is specified in the config. # allow root login on console, tty[1-4], and pts/0 for libvirt echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty echo "lxc/console" >>${rootfs_path}/etc/securetty echo "lxc/tty1" >>${rootfs_path}/etc/securetty echo "lxc/tty2" >>${rootfs_path}/etc/securetty echo "lxc/tty3" >>${rootfs_path}/etc/securetty echo "lxc/tty4" >>${rootfs_path}/etc/securetty echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty echo "pts/0" >>${rootfs_path}/etc/securetty if [ ${root_display_password} = "yes" ] then echo "Setting root password to '$root_password'" fi if [ ${root_store_password} = "yes" ] then touch ${config_path}/tmp_root_pass chmod 600 ${config_path}/tmp_root_pass echo ${root_password} > ${config_path}/tmp_root_pass echo "Storing root password in '${config_path}/tmp_root_pass'" fi echo "root:$root_password" | chroot $rootfs_path chpasswd if [ ${root_expire_password} = "yes" ] then # Also set this password as expired to force the user to change it! chroot $rootfs_path passwd -e root fi # specifying this in the initial packages doesn't always work. # Even though it should have... echo "installing fedora-release package" mount -o bind /dev ${rootfs_path}/dev mount -t proc proc ${rootfs_path}/proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf ${rootfs_path}/etc/ # Rebuild the rpm database based on the target rpm version... rm -f ${rootfs_path}/var/lib/rpm/__db* chroot ${rootfs_path} rpm --rebuilddb chroot ${rootfs_path} yum -y install fedora-release if [[ ! -e ${rootfs_path}/sbin/NetworkManager ]] then # NetworkManager has not been installed. Use the # legacy chkconfig command to enable the network startup # scripts in the container. chroot ${rootfs_path} chkconfig network on fi umount ${rootfs_path}/proc umount ${rootfs_path}/dev # silence some needless startup errors touch ${rootfs_path}/etc/fstab # give us a console on /dev/console sed -i 's/ACTIVE_CONSOLES=.*$/ACTIVE_CONSOLES="\/dev\/console \/dev\/tty[1-4]"/' \ ${rootfs_path}/etc/sysconfig/init return 0 } configure_fedora_init() { sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit # don't mount devpts, for pete's sake sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.sysinit sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit chroot ${rootfs_path} chkconfig udev-post off chroot ${rootfs_path} chkconfig network on if [ -d ${rootfs_path}/etc/init ] then # This is to make upstart honor SIGPWR. Should do no harm # on systemd systems and some systems may have both. cat <${rootfs_path}/etc/init/power-status-changed.conf # power-status-changed - shutdown on SIGPWR # start on power-status-changed exec /sbin/shutdown -h now "SIGPWR received" EOF fi } configure_fedora_systemd() { rm -f ${rootfs_path}/etc/systemd/system/default.target touch ${rootfs_path}/etc/fstab chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target # Make systemd honor SIGPWR chroot ${rootfs_path} ln -s /usr/lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target # if desired, prevent systemd from over-mounting /tmp with tmpfs if [ $masktmp -eq 1 ]; then chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/tmp.mount fi #dependency on a device unit fails it specially that we disabled udev # sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service # # Actually, the After=dev-%i.device line does not appear in the # Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left # over from an earlier version and it's not doing any harm. We do need # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are # started on the ttys in the container. Lets do it in an override copy of # the service so it can still pass rpm verifies and not be automatically # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ -e 's/After=dev-%i.device/After=/' \ < ${rootfs_path}/lib/systemd/system/getty\@.service \ > ${rootfs_path}/etc/systemd/system/getty\@.service # Setup getty service on the 4 ttys we are going to allow in the # default config. Number should match lxc.tty ( cd ${rootfs_path}/etc/systemd/system/getty.target.wants for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) } ### BEGIN Bootstrap Environment Code... Michael H. Warfield /\/\|=mhw=|\/\/ # Ok... Heads up. If you're reading these comments, you're either a # template owner or someone wondering how the hell I did this (or, worse, # someone in the future trying to maintain it). This code is slightly # "evil coding bastard" code with one significant hack / dirty trick # that you would probably miss just reading the code below. I'll mark # it out with comments. # # Because of what this code does, it deserves a lot of comments so people # can understand WHY I did it this way... # # Ultimate Objective - Build a Fedora container on a host system which does # not have a (complete compatible) version of rpm and/or yum. That basically # means damn near any distro other than Fedora and Ubuntu (which has rpm and # yum available). Only requirements for this function are rsync and # squashfs available to the kernel. If you don't have those, why are you # even attempting to build containers? # # Challenge for this function - Bootstrap a Fedora install bootstrap # run time environment which has all the pieces to run rpm and yum and # from which we can build targets containers even where the host system # has no support for rpm, yum, or fedora. # # Steps: # Stage 0 - Download a Fedora LiveOS squashfs core (netinst core). # Stage 1 - Extract filesystem from Stage 0 and update to full rpm & yum # Stage 2 - Use Stage 1 to build a rootfs with python, rpm, and yum. # # Stage 2 becomes our bootstrap file system which can be cached # and then used to build other arbitrary vesions of Fedora of a # given architecture. Note that this only has to run once for # Fedora on a given architecture since rpm and yum can build other # versions. We'll arbitrarily pick Fedora 20 to build this. This # will need to change as time goes on. # Programmers Note... A future fall back may be to download the netinst # iso image instead of the LiveOS squasfs image and work from that. # That may be more general but will introduce another substep # (mounting the iso) to the stage0 setup. # This system is designed to be as autonomous as possible so all whitelists # and controls are self-contained. # Initial testing - Whitelist nobody. Build for everybody... # Initial deployment - Whitelist Fedora. # Long term - Whitelist Fedora, Debian, Ubuntu, CentOs, Scientific, and NST. # List of distros which do not (should not) need a bootstrap (but we will test # for rpm and yum none the less... OS SHOULD be taken from CPE values but # Debian / Ubuntu doesn't support CPE yet. # BOOTSTRAP_WHITE_LIST="" BOOTSTRAP_WHITE_LIST="fedora" # BOOTSTRAP_WHITE_LIST="fedora debian ubuntu centos scientific sl nst" BOOTSTRAP=0 BOOTSTRAP_DIR= BOOTSTRAP_CHROOT= fedora_get_bootstrap() { echo "Bootstrap Environment testing..." WHITE_LISTED=1 # We need rpm. No rpm - not possible to white list... if ! which rpm > /dev/null 2>&1 then WHITE_LISTED=0 fi # We need yum No yum - not possible to white list... if ! which yum > /dev/null 2>&1 then WHITE_LISTED=0 fi if [[ ${WHITE_LISTED} != 0 ]] then for OS in ${BOOTSTRAP_WHITE_LIST} do if [[ ${ID} = ${OS} ]] then echo " OS ${ID} is whitelisted. Installation Bootstrap Environment not required. " return 0; fi done fi echo " Fedora Installation Bootstrap Build..." if ! which rsync > /dev/null 2>&1 then echo " Unable to locate rsync. Cravely bailing out before even attempting to build an Installation Bootstrap Please install rsync and then rerun this process. " return 255 fi [[ -d ${cache_base} ]] || mkdir -p ${cache_base} cd ${cache_base} # We know we don't have a cache directory of this version or we # would have never reached this code to begin with. But we may # have another Fedora cache directory from which we could run... # We'll give a preference for close matches preferring higher over # lower - which makes for really ugly code... # Is this a "bashism" that will need cleaning up???? BOOTSTRAP_LIST="$(( $release + 1 ))/rootfs $(( $release - 1 ))/rootfs \ $(( $release + 2 ))/rootfs $(( $release - 2 ))/rootfs \ $(( $release + 3 ))/rootfs $(( $release - 3 ))/rootfs \ bootstrap" for bootstrap in ${BOOTSTRAP_LIST} do if [[ -d ${bootstrap} ]] then echo " Existing Bootstrap found. Testing..." mount -o bind /dev ${bootstrap}/dev mount -t proc proc ${bootstrap}/proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf ${bootstrap}/etc/ rm -f ${bootstrap}/var/lib/rpm/__db* chroot ${bootstrap} rpm --rebuilddb chroot ${bootstrap} yum -y update RC=$? umount ${bootstrap}/proc umount ${bootstrap}/dev if [[ 0 == ${RC} ]] then BOOTSTRAP=1 BOOTSTRAP_DIR="${cache_base}/${bootstrap}" BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " BOOTSTRAP_INSTALL_ROOT=/run/install echo " Functional Installation Bootstrap exists and appears to be completed. Will use existing Bootstrap: ${BOOTSTRAP_DIR} " return 0 fi echo " Installation Bootstrap in ${BOOTSTRAP_DIR} exists but appears to be non-functional. Skipping... It should be removed. " fi done TMP_BOOTSTRAP_DIR=$( mktemp -d --tmpdir=${cache_base} bootstrap_XXXXXX ) cd ${TMP_BOOTSTRAP_DIR} mkdir squashfs stage0 stage1 bootstrap ### Stage 0 setup. # Download the LiveOS squashfs image # mount image to "squashfs" # mount contained LiveOS to stage0 # We're going to use the archives.fedoraproject.org mirror for the initial stages... # 1 - It's generally up to date and complete # 2 - It's has high bandwidth access # 3 - It supports rsync and wildcarding (and we need both) # 4 - Not all the mirrors carry the LiveOS images if [[ ! -f ../LiveOS/squashfs.img ]] then echo " Downloading stage 0 LiveOS squashfs file system from archives.fedoraproject.org... Have a beer or a cup of coffee. This will take a bit (~300MB). " sleep 3 # let him read it... # Right now, we are using Fedora 20 for the inial bootstrap. # We could make this the "current" Fedora rev (F > 15). rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/LiveOS . if [[ 0 == $? ]] then echo "Download of squashfs image complete." mv LiveOS .. else echo " Download of squashfs image failed. " return 255 fi else echo "Using cached stage 0 LiveOS squashfs file system." fi mount -o loop ../LiveOS/squashfs.img squashfs if [[ $? != 0 ]] then echo " Mount of LiveOS squashfs image failed! You mush have squashfs support available to mount image. Unable to continue. Correct and retry process later! LiveOS image not removed. Process may be rerun without penalty of downloading LiveOS again. If LiveOS is corrupt, remove ${cache_base}/LiveOS before rerunning to redownload. " return 255 fi mount -o loop squashfs/LiveOS/rootfs.img stage0 if [[ $? != 0 ]] then echo " Mount of LiveOS stage0 rootfs image failed! LiveOS download may be corrupt. Remove ${cache_base}/LiveOS to force a new download or troubleshoot cached image and then rerun process. " return 255 fi ### Stage 1 setup. # Copy stage0 (which is ro) to stage1 area (rw) for modification. # Unmount stage0 mounts - we're done with stage 0 at this point. # Download our rpm and yum rpm packages. # Force install of rpm and yum into stage1 image (dirty hack!) echo "Stage 0 complete, building Stage 1 image... This will take a couple of minutes. Patience..." echo "Creating Stage 1 r/w copy of r/o Stage 0 squashfs image from LiveOS." rsync -aAHS stage0/. stage1/ umount stage0 umount squashfs cd stage1 # Setup stage1 image with pieces to run installs... mount -o bind /dev dev mount -t proc proc proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf etc/ mkdir run/install echo "Updating Stage 1 image with full rpm and yum packages" # Retrieve our 2 rpm packages we need to force down the throat # of this LiveOS image we're camped out on. This is the beginning # of the butt ugly hack. Look close or you may missing it... rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/r/rpm-[0-9]* \ ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/y/yum-[0-9]* . # And here it is... # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! chroot . rpm -ivh --nodeps rpm-* yum-* # Did you catch it? # The LiveOS image contains rpm (but not rpmdb) and yum (but not # yummain.py - What the hell good does yum do with no # yummain.py?!?! - Sigh...). It contains all the supporting # pieces but the rpm database has not be initialized and it # doesn't know all the dependences (seem to) have been met. # So we do a "--nodeps" rpm install in the chrooted environment # to force the installation of the full rpm and yum packages. # # For the purists - Yes, I know the rpm database is wildly out # of whack now. That's why this is a butt ugly hack / dirty trick. # But, this is just the stage1 image that we are going to discard as # soon as the stage2 image is built, so we don't care. All we care # is that the stage2 image ends up with all the pieces it need to # run yum and rpm and that the stage2 rpm database is coherent. # # NOW we can really go to work! ### Stage 2 setup. # Download our Fedora Release rpm packages. # Install fedora-release into bootstrap to initialize fs and databases. # Install rpm, and yum into bootstrap image using yum echo "Stage 1 creation complete. Building stage 2 Installation Bootstrap" mount -o bind ../bootstrap run/install rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/f/fedora-release-20* . # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! chroot . rpm --root /run/install --nodeps -ivh fedora-release-* # yum will take $basearch from host, so force the arch we want sed -i "s|\$basearch|$basearch|" ./run/install/etc/yum.repos.d/* chroot . yum -y --nogpgcheck --installroot /run/install install python rpm yum umount run/install umount proc umount dev # That's it! We should now have a viable installation BOOTSTRAP in # bootstrap We'll do a yum update in that to verify and then # move it to the cache location before cleaning up. cd ../bootstrap mount -o bind /dev dev mount -t proc proc proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf etc/ # yum will take $basearch from host, so force the arch we want sed -i "s|\$basearch|$basearch|" ./etc/yum.repos.d/* chroot . yum -y update RC=$? umount proc umount dev cd .. if [[ ${RC} != 0 ]] then echo " Build of Installation Bootstrap failed. Temp directory not removed so it can be investigated. " return 255 fi # We know have a working run time environment in rootfs... mv bootstrap .. cd .. rm -rf ${TMP_BOOTSTRAP_DIR} echo " Build of Installation Bootstrap complete! We now return you to your normally scheduled template creation. " BOOTSTRAP=1 BOOTSTRAP_DIR="${cache_base}/bootstrap" BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " BOOTSTRAP_INSTALL_ROOT=/run/install return 0 } fedora_bootstrap_mounts() { if [[ ${BOOTSTRAP} -ne 1 ]] then return 0 fi BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " echo "Mounting Bootstrap mount points" [[ -d ${BOOTSTRAP_DIR}/run/install ]] || mkdir -p ${BOOTSTRAP_DIR}/run/install mount -o bind ${INSTALL_ROOT} ${BOOTSTRAP_DIR}/run/install mount -o bind /dev ${BOOTSTRAP_DIR}/dev mount -t proc proc ${BOOTSTRAP_DIR}/proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf ${BOOTSTRAP_DIR}/etc/ } fedora_bootstrap_umounts() { if [[ ${BOOTSTRAP} -ne 1 ]] then return 0 fi umount ${BOOTSTRAP_DIR}/proc umount ${BOOTSTRAP_DIR}/dev umount ${BOOTSTRAP_DIR}/run/install } # This is the code to create the initial roofs for Fedora. It may # require a run time environment by calling the routines above... download_fedora() { # check the mini fedora was not already downloaded INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then echo "Failed to create '$INSTALL_ROOT' directory" return 1 fi # download a mini fedora into a cache echo "Downloading fedora minimal ..." # These will get changed if it's decided that we need a # boostrap environment (can not build natively). These # are the defaults for the non-boostrap (native) mode. BOOTSTRAP_INSTALL_ROOT=${INSTALL_ROOT} BOOTSTRAP_CHROOT= BOOTSTRAP_DIR= PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils fedora-release" MIRRORLIST_URL="http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$release&arch=$basearch" if [[ ${release} -lt 17 ]] then # The reflects the move of db_dump and db_load from db4_utils to # libdb_utils in Fedora 17 and above and it's inclusion as a dep... # Prior to Fedora 11, we need to explicitly include it! PKG_LIST="${PKG_LIST} db4-utils" fi if [[ ${release} -ge 21 ]] then # Since Fedora 21, a separate fedora-repos package is needed. # Before, the information was conained in fedora-release. PKG_LIST="${PKG_LIST} fedora-repos" fi DOWNLOAD_OK=no # We're splitting the old loop into two loops plus a directory retrival. # First loop... Try and retrive a mirror list with retries and a slight # delay between attempts... for trynumber in 1 2 3 4; do [ $trynumber != 1 ] && echo "Trying again..." # This code is mildly "brittle" in that it assumes a certain # page format and parsing HTML. I've done worse. :-P MIRROR_URLS=$(curl -s -S -f "$MIRRORLIST_URL" | sed -e '/^http:/!d' -e '2,6!d') if [ $? -eq 0 ] && [ -n "$MIRROR_URLS" ] then break fi echo "Failed to get a mirror on try $trynumber" sleep 3 done # This will fall through if we didn't get any URLS above for MIRROR_URL in ${MIRROR_URLS} do if [ "$release" -gt "16" ]; then RELEASE_URL="$MIRROR_URL/Packages/f" else RELEASE_URL="$MIRROR_URL/Packages/" fi echo "Fetching release rpm name from $RELEASE_URL..." # This code is mildly "brittle" in that it assumes a certain directory # page format and parsing HTML. I've done worse. :-P RELEASE_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-release-${release}-/!d" -e 's/.*.*//' ) if [ $? -ne 0 -o "${RELEASE_RPM}" = "" ]; then echo "Failed to identify fedora release rpm." continue fi echo "Fetching fedora release rpm from ${RELEASE_URL}/${RELEASE_RPM}......" curl -L -f "${RELEASE_URL}/${RELEASE_RPM}" > ${INSTALL_ROOT}/${RELEASE_RPM} if [ $? -ne 0 ]; then echo "Failed to download fedora release rpm ${RELEASE_RPM}." continue fi # F21 and newer need fedora-repos in addition to fedora-release. if [ "$release" -ge "21" ]; then echo "Fetching repos rpm name from $RELEASE_URL..." REPOS_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-repos-${release}-/!d" -e 's/.*.*//' ) if [ $? -ne 0 -o "${REPOS_RPM}" = "" ]; then echo "Failed to identify fedora repos rpm." continue fi echo "Fetching fedora repos rpm from ${RELEASE_URL}/${REPOS_RPM}..." curl -L -f "${RELEASE_URL}/${REPOS_RPM}" > ${INSTALL_ROOT}/${REPOS_RPM} if [ $? -ne 0 ]; then echo "Failed to download fedora repos rpm ${RELEASE_RPM}." continue fi fi DOWNLOAD_OK=yes break done if [ $DOWNLOAD_OK != yes ]; then echo "Aborting" return 1 fi mkdir -p ${INSTALL_ROOT}/var/lib/rpm if ! fedora_get_bootstrap then echo "Fedora Bootstrap setup failed" return 1 fi fedora_bootstrap_mounts ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --initdb # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --nodeps -ivh ${BOOTSTRAP_INSTALL_ROOT}/${RELEASE_RPM} # F21 and newer need fedora-repos in addition to fedora-release... # Note that fedora-release and fedora-system have a mutual dependency. # So installing the reops package after the release package we can # spare one --nodeps. if [ "$release" -ge "21" ]; then ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} -ivh ${BOOTSTRAP_INSTALL_ROOT}/${REPOS_RPM} fi # yum will take $basearch from host, so force the arch we want sed -i "s|\$basearch|$basearch|" ${BOOTSTRAP_DIR}/${BOOTSTRAP_INSTALL_ROOT}/etc/yum.repos.d/* ${BOOTSTRAP_CHROOT}yum --installroot ${BOOTSTRAP_INSTALL_ROOT} -y --nogpgcheck install ${PKG_LIST} RC=$? if [[ ${BOOTSTRAP} -eq 1 ]] then # Here we have a bit of a sticky problem. We MIGHT have just installed # this template cache using versions of yum and rpm in the bootstrap # chroot that use a different database version than the target version. # That can be a very big problem. Solution is to rebuild the rpmdatabase # with the target database now that we are done building the cache. In the # vast majority of cases, this is a do-not-care with no harm done if we # didn't do it. But it catches several corner cases with older unsupported # releases and it really doesn't cost us a lot of time for a one shot # install that will never be done again for this rev. # # Thanks and appreciation to Dwight Engen and the Oracle template for the # database rewrite hint! echo "Fixing up rpm databases" # Change to our target install directory (if we're not already # there) just to simplify some of the logic to follow... cd ${INSTALL_ROOT} rm -f var/lib/rpm/__db* # Programmers Note (warning): # # Pay careful attention to the following commands! It # crosses TWO chroot boundaries linked by a bind mount! # In the bootstrap case, that's the bind mount of ${INSTALL_ROOT} # to the ${BOOTSTRAP_CHROOT}/run/install directory! This is # a deliberate hack across that bind mount to do a database # translation between two environments, neither of which may # be the host environment! It's ugly and hard to follow but, # if you don't understand it, don't mess with it! The pipe # is in host space between the two chrooted environments! # This is also why we cd'ed into the INSTALL_ROOT directory # in advance of this loop, so everything is relative to the # current working directory and congruent with the same working # space in both chrooted environments. The output into the new # db is also done in INSTALL_ROOT space but works in either host # space or INSTALL_ROOT space for the mv, so we don't care. It's # just not obvious what's happening in the db_dump and db_load # commands... # for db in var/lib/rpm/* ; do ${BOOTSTRAP_CHROOT} db_dump ${BOOTSTRAP_INSTALL_ROOT}/$db | chroot . db_load $db.new mv $db.new $db done # finish up by rebuilding the database... # This should be redundant but we do it for completeness and # any corner cases I may have missed... mount -t proc proc proc mount -o bind /dev dev chroot . rpm --rebuilddb umount dev umount proc fi fedora_bootstrap_umounts if [ ${RC} -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi mv "$INSTALL_ROOT" "$cache/rootfs" echo "Download complete." return 0 } copy_fedora() { # make a local copy of the minifedora echo -n "Copying rootfs to $rootfs_path ..." #cp -a $cache/rootfs-$basearch $rootfs_path || return 1 # i prefer rsync (no reason really) mkdir -p $rootfs_path rsync -SHaAX $cache/rootfs/ $rootfs_path/ echo return 0 } update_fedora() { mount -o bind /dev ${cache}/rootfs/dev mount -t proc proc ${cache}/rootfs/proc # Always make sure /etc/resolv.conf is up to date in the target! cp /etc/resolv.conf ${cache}/rootfs/etc/ chroot ${cache}/rootfs yum -y update umount ${cache}/rootfs/proc umount ${cache}/rootfs/dev } install_fedora() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs ... " if [ ! -e "$cache/rootfs" ]; then download_fedora if [ $? -ne 0 ]; then echo "Failed to download 'fedora base'" return 1 fi else echo "Cache found. Updating..." update_fedora if [ $? -ne 0 ]; then echo "Failed to update 'fedora base', continuing with last known good cache" else echo "Update finished" fi fi echo "Copy $cache/rootfs to $rootfs_path ... " copy_fedora if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora return $? } # Generate a random hardware (MAC) address composed of FE followed by # 5 random bytes... create_hwaddr() { openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' } copy_configuration() { mkdir -p $config_path grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo " lxc.rootfs = $rootfs_path " >> $config_path/config # The following code is to create static MAC addresses for each # interface in the container. This code will work for multiple # interfaces in the default config. It will also strip any # hwaddr stanzas out of the default config since we can not share # MAC addresses between containers. mv $config_path/config $config_path/config.def while read LINE do # This should catch variable expansions from the default config... if expr "${LINE}" : '.*\$' > /dev/null 2>&1 then LINE=$(eval "echo \"${LINE}\"") fi # There is a tab and a space in the regex bracket below! # Seems that \s doesn't work in brackets. KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') if [[ "${KEY}" != "lxc.network.hwaddr" ]] then echo "${LINE}" >> $config_path/config if [[ "${KEY}" == "lxc.network.link" ]] then echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config fi fi done < $config_path/config.def rm -f $config_path/config.def if [ -e "@LXCTEMPLATECONFIG@/fedora.common.conf" ]; then echo " # Include common configuration lxc.include = @LXCTEMPLATECONFIG@/fedora.common.conf " >> $config_path/config fi # Append things which require expansion here... cat <> $config_path/config lxc.arch = $arch lxc.utsname = $utsname # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined # example simple networking setup, uncomment to enable #lxc.network.type = $lxc_network_type #lxc.network.flags = up #lxc.network.link = $lxc_network_link #lxc.network.name = eth0 # Additional example for veth network type # static MAC address, #lxc.network.hwaddr = 00:16:3e:77:52:20 # persistent veth device name on host side # Note: This may potentially collide with other containers of same name! #lxc.network.veth.pair = v-$name-e0 EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { if [ ! -e $cache ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache for Fedora-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora } usage() { cat < [-p|--path=] [-c|--clean] [-R|--release=] [--fqdn=] [-a|--arch=] [--mask-tmp] [-h|--help] Mandatory args: -n,--name container name, used to as an identifier for that container Optional args: -p,--path path to where the container will be created, defaults to @LXCPATH@. --rootfs path for actual rootfs. -c,--clean clean the cache -R,--release Fedora release for the new container. Defaults to host's release if the host is Fedora. --fqdn fully qualified domain name (FQDN) for DNS and system naming -a,--arch Define what arch the container will be [i686,x86_64] --mask-tmp Prevent systemd from over-mounting /tmp with tmpfs. -h,--help print this help EOF return 0 } options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,arch:,fqdn:,mask-tmp -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi arch=$(uname -m) masktmp=0 eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs_path=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=1; shift 1;; -R|--release) release=$2; shift 2;; -a|--arch) newarch=$2; shift 2;; --fqdn) utsname=$2; shift 2;; --mask-tmp) masktmp=1; shift 1;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi basearch=${arch} # Map a few architectures to their generic Fedora repository archs. # The two ARM archs are a bit of a guesstimate for the v5 and v6 # archs. V6 should have hardware floating point (Rasberry Pi). # The "arm" arch is safer (no hardware floating point). So # there may be cases where we "get it wrong" for some v6 other # than RPi. case "$arch" in i686) basearch=i386 ;; armv3l|armv4l|armv5l) basearch=arm ;; armv6l|armv7l|armv8l) basearch=armhfp ;; *) ;; esac mirrorurl="archives.fedoraproject.org::fedora-archive" case "$basearch" in ppc64|s390x) mirrorurl="archives.fedoraproject.org::fedora-secondary" ;; *) ;; esac # Somebody wants to specify an arch. This is very limited case. # i386/i586/i686 on i386/x86_64 # - or - # x86_64 on x86_64 if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] then case "${newarch}" in i386|i586|i686) if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] then # Make the arch a generic x86 32 bit... arch=${newarch} basearch=i386 else basearch=bad fi ;; *) basearch=bad ;; esac if [ "${basearch}" = "bad" ] then echo "You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" exit 1 fi fi # Allow the cache base to be set by environment variable cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/fedora/$basearch # Let's do something better for the initial root password. # It's not perfect but it will defeat common scanning brute force # attacks in the case where ssh is exposed. It will also be set to # expired, forcing the user to change it at first login. if [ "${root_password}" = "" ] then root_password=Root-${name}-${RANDOM} else # If it's got a ding in it, try and expand it! if [ $(expr "${root_password}" : '.*$.') != 0 ] then root_password=$(eval echo "${root_password}") fi # If it has more than 3 consecutive X's in it, feed it # through mktemp as a template. if [ $(expr "${root_password}" : '.*XXXX') != 0 ] then root_password=$(mktemp -u ${root_password}) fi fi if [ -z "${utsname}" ]; then utsname=${name} fi # This follows a standard "resolver" convention that an FQDN must have # at least two dots or it is considered a local relative host name. # If it doesn't, append the dns domain name of the host system. # # This changes one significant behavior when running # "lxc_create -n Container_Name" without using the # --fqdn option. # # Old behavior: # utsname and hostname = Container_Name # New behavior: # utsname and hostname = Container_Name.Domain_Name if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then utsname=${utsname}.$(dnsdomainname) fi fi needed_pkgs="" type curl >/dev/null 2>&1 if [ $? -ne 0 ]; then needed_pkgs="curl $needed_pkgs" fi type openssl >/dev/null 2>&1 if [ $? -ne 0 ]; then needed_pkgs="openssl $needed_pkgs" fi if [ -n "$needed_pkgs" ]; then echo "Missing commands: $needed_pkgs" echo "Please install these using \"sudo yum install $needed_pkgs\"" exit 1 fi if [ -z "$path" ]; then path=$default_path/$name fi if [ -z "$release" ]; then if [ "$is_fedora" -a "$fedora_host_ver" ]; then release=$fedora_host_ver else echo "This is not a fedora host and release missing, defaulting to 22 use -R|--release to specify release" release=22 fi fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -z "$rootfs_path" ]; then rootfs_path=$path/rootfs # check for 'lxc.rootfs' passed in through default config by lxc-create if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then rootfs_path=$(sed -e '/^lxc.rootfs\s*=/!d' -e 's/\s*#.*//' \ -e 's/^lxc.rootfs\s*=\s*//' -e q $path/config) fi fi config_path=$path cache=$cache_base/$release revert() { echo "Interrupted, so cleaning up" lxc-destroy -n $name # maybe was interrupted before copy config rm -rf $path echo "exiting..." exit 1 } trap revert SIGHUP SIGINT SIGTERM copy_configuration if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi install_fedora if [ $? -ne 0 ]; then echo "failed to install fedora" exit 1 fi configure_fedora if [ $? -ne 0 ]; then echo "failed to configure fedora for a container" exit 1 fi # If the systemd configuration directory exists - set it up for what we need. if [ -d ${rootfs_path}/etc/systemd/system ] then configure_fedora_systemd fi # This configuration (rc.sysinit) is not inconsistent with the systemd stuff # above and may actually coexist on some upgraded systems. Let's just make # sure that, if it exists, we update this file, even if it's not used... if [ -f ${rootfs_path}/etc/rc.sysinit ] then configure_fedora_init fi if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi echo " Container rootfs and config have been created. Edit the config file to check/enable networking setup. " if [[ -d ${cache_base}/bootstrap ]] then echo "You have successfully built a Fedora container and cache. This cache may be used to create future containers of various revisions. The directory ${cache_base}/bootstrap contains a bootstrap which may no longer needed and can be removed. " fi if [[ -e ${cache_base}/LiveOS ]] then echo "A LiveOS directory exists at ${cache_base}/LiveOS. This is only used in the creation of the bootstrap run-time-environment and may be removed. " fi if [ ${root_display_password} = "yes" ] then echo "The temporary password for root is: '$root_password' You may want to note that password down before starting the container. " fi if [ ${root_store_password} = "yes" ] then echo "The temporary root password is stored in: '${config_path}/tmp_root_pass' " fi if [ ${root_prompt_password} = "yes" ] then echo "Invoking the passwd command in the container to set the root password. chroot ${rootfs_path} passwd " chroot ${rootfs_path} passwd else if [ ${root_expire_password} = "yes" ] then if ( mountpoint -q -- "${rootfs_path}" ) then echo "To reset the root password, you can do: lxc-start -n ${name} lxc-attach -n ${name} -- passwd lxc-stop -n ${name} " else echo " The root password is set up as "expired" and will require it to be changed at first login, which you should do as soon as possible. If you lose the root password or wish to change it without starting the container, you can change it from the host by running the following command (which will also reset the expired flag): chroot ${rootfs_path} passwd " fi fi fi lxc-2.0.11/templates/lxc-oracle.in0000644061062106075000000012121413435013473013675 00000000000000#!/bin/sh # # Template script for generating Oracle Enterprise Linux container for LXC # based on lxc-fedora, lxc-ubuntu # # Copyright © 2011 Wim Coekaerts # Copyright © 2012 Dwight Engen # # Modified for Oracle Linux 5 # Wim Coekaerts # # Modified for Oracle Linux 6,7 combined OL4,5,6 into one template script # Dwight Engen # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin die() { echo "failed: $1" exit 1 } is_btrfs_subvolume() { if which btrfs >/dev/null 2>&1 && \ btrfs subvolume list "$1" >/dev/null 2>&1; then return 0 fi return 1 } can_chcon() { if which chcon >/dev/null 2>&1; then selinuxenabled >/dev/null 2>&1 return $? fi return 1 } # fix up the container_rootfs container_rootfs_patch() { echo "Patching container rootfs $container_rootfs for Oracle Linux $container_release_major.$container_release_minor" # copy ourself into the container to be used to --patch the rootfs when # yum update on certain packages is done. we do this here instead of in # container_rootfs_configure() in case the patching done in this function # is updated in the future, we can inject the updated version of ourself # into older containers. if [ $container_rootfs != "/" ]; then cp -f `readlink -f $0` $container_rootfs/usr/bin/lxc-patch if [ $container_release_major -lt "6" ]; then mkdir -p $container_rootfs/usr/lib/yum-plugins cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/lib/yum-plugins fi if [ $container_release_major -ge "6" ]; then mkdir -p $container_rootfs/usr/share/yum-plugins cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/share/yum-plugins fi mkdir -p $container_rootfs/etc/yum/pluginconf.d cat < $container_rootfs/etc/yum/pluginconf.d/lxc-patch.conf [main] enabled=1 packages=dbus,initscripts,iptables,openssh-server,setup,selinux-policy,readahead,udev,util-linux,util-linux-ng EOF fi if [ $container_release_major = "4" ]; then # yum plugin type of TYPE_INTERFACE works in all releases but gives a # deprecation warning on major > 4, so we default to TYPE_INTERACTIVE # and fix it up here sed -i 's|TYPE_INTERACTIVE|TYPE_INTERFACE|' $container_rootfs/usr/lib/yum-plugins/lxc-patch.py if [ -f $container_rootfs/etc/yum.repos.d/ULN-Base.repo ]; then mv $container_rootfs/etc/yum.repos.d/ULN-Base.repo \ $container_rootfs/etc/yum.repos.d/ULN-Base.repo.lxc-disabled fi echo "plugins = 1" >>$container_rootfs/etc/yum.conf fi # "disable" selinux in the guest. The policy in the container isn't # likely to match the hosts (unless host == guest exactly) and the # kernel can only be enforcing one policy. # # The OL 5 init honors /etc/selinux/config, but note that # this doesnt actually disable it if it's enabled in the host, since # libselinux::is_selinux_enabled() in the guest will check # /proc/filesystems and see selinuxfs, thus reporting that it is on # (ie. check the output of sestatus in the guest). We also replace # /usr/sbin/selinuxenabled with a symlink to /bin/false so that init # scripts (ie. mcstransd) that call that think selinux is disabled. mkdir -p $container_rootfs/selinux echo 0 > $container_rootfs/selinux/enforce if [ -e $container_rootfs/etc/selinux/config ]; then sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config else mkdir -p $container_rootfs/etc/selinux echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config fi sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/sshd sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/sshd # setting /proc/$$/loginuid doesn't work under user namespace, which # prevents logins from working sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/sshd sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/login if [ -f $container_rootfs/usr/sbin/selinuxenabled ]; then mv $container_rootfs/usr/sbin/selinuxenabled $container_rootfs/usr/sbin/selinuxenabled.lxcorig ln -s /bin/false $container_rootfs/usr/sbin/selinuxenabled fi # ensure /dev/ptmx refers to the newinstance devpts of the container, or # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512) rm -f $container_rootfs/dev/ptmx ln -s pts/ptmx $container_rootfs/dev/ptmx # OL7 has systemd, no rc.sysinit if [ $container_release_major = "7" ]; then # with newer systemd (OL7.2), getty service include container-getty.service # let that be the one who manage the getty service instead if [ ! -f $container_rootfs/usr/lib/systemd/system/container-getty@.service ]; then # from mhw in the fedora template: We do need to disable the # "ConditionalPathExists=/dev/tty0" line or no gettys are started on # the ttys in the container. Lets do it in an override copy of the # service so it can still pass rpm verifies and not be automatically # updated by a new systemd version. sed -e 's/^ConditionPathExists=/#LXC ConditionPathExists=/' \ < $container_rootfs/usr/lib/systemd/system/getty\@.service \ > $container_rootfs/etc/systemd/system/getty\@.service # Setup getty service on the 4 ttys we are going to allow in the # default config. Number should match lxc.tty ( cd $container_rootfs/etc/systemd/system/getty.target.wants for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) # We only want to spawn a getty on /dev/console in lxc, libvirt-lxc # symlinks /dev/console to /dev/tty1 sed -i '/Before=getty.target/a ConditionVirtualization=lxc' $container_rootfs/usr/lib/systemd/system/console-getty.service fi # disable some systemd services, set default boot, sigpwr target rm -f $container_rootfs/usr/lib/systemd/system/sysinit.target.wants/kmod-static-nodes.service chroot $container_rootfs systemctl -q disable graphical.target chroot $container_rootfs systemctl -q enable multi-user.target # systemd in userns won't be able to set /proc/self/oom_score_adj which # prevents the dbus service from starting sed -i 's|^OOMScoreAdjust|#LXC OOMScoreAdjust|' $container_rootfs/usr/lib/systemd/system/dbus.service return fi # silence error in checking for selinux sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.sysinit sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.d/rc.sysinit # on ol4 pam_limits prevents logins when using user namespaces if [ $container_release_major = "4" ]; then sed -i 's|session[ \t]*required[ \t]*/lib/security/\$ISA/pam_limits.so|#session required /lib/security/$ISA/pam_limits.so|' $container_rootfs/etc/pam.d/system-auth fi # avoid error in ol5 attempting to copy non-existent resolv.conf if [ $container_release_major = "5" ]; then sed -i 's|resolv.conf.predhclient|resolv.conf.predhclient 2>/dev/null|' $container_rootfs/sbin/dhclient-script fi # disable interactive ovmd asking questions if [ -f $container_rootfs/etc/sysconfig/ovmd ]; then sed -i 's|INITIAL_CONFIG=yes|INITIAL_CONFIG=no|' $container_rootfs/etc/sysconfig/ovmd fi # disable disabling of ipv4 forwarding and defrag on shutdown since # we mount /proc/sys ro if [ $container_release_major = "5" ]; then sed -i 's|-f /proc/sys/net/ipv4/ip_forward|-w /proc/sys/net/ipv4/ip_forward|' $container_rootfs/etc/rc.d/init.d/network sed -i 's|-f /proc/sys/net/ipv4/ip_always_defrag|-w /proc/sys/net/ipv4/ip_always_defrag|' $container_rootfs/etc/rc.d/init.d/network fi # disable ipv6 on ol6 rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global # remove module stuff for iptables it just shows errors that are not # relevant in a container if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config fi # disable readahead in the container if [ $container_release_major = "6" -a -e $container_rootfs/etc/sysconfig/readahead ]; then rm -f $container_rootfs/etc/init/readahead-collector.conf rm -f $container_rootfs/etc/init/readahead-disable-services.conf sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead fi if [ $container_release_major = "4" ]; then # enable fastboot always sed -i 's|\[ -f /fastboot \]|/bin/true|' $container_rootfs/etc/rc.sysinit sed -i 's|\[ -f /fastboot \]|/bin/true|' $container_rootfs/etc/rc.d/rc.sysinit # dont attempt to set kernel parameters sed -i 's|action $"Configuring kernel parameters|# LXC action $"Configuring kernel parameters|' $container_rootfs/etc/rc.sysinit sed -i 's|action $"Configuring kernel parameters|# LXC action $"Configuring kernel parameters|' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/network 2>/dev/null sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/NetworkManager 2>/dev/null fi # no need to attempt to mount / sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.sysinit sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.d/rc.sysinit # disable udev in the container if [ $container_release_major = "4" ]; then sed -i 's|\[ -x /sbin/start_udev \]|# LXC no udev|' $container_rootfs/etc/rc.sysinit sed -i 's|\[ -x /sbin/start_udev \]|# LXC no udev|' $container_rootfs/etc/rc.d/rc.sysinit else sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.sysinit sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.d/rc.sysinit fi # disable nash raidautorun in the container since no /dev/md* if [ $container_release_major = "4" -o $container_release_major = "5" ]; then sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.sysinit sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.d/rc.sysinit fi # prevent rc.sysinit from attempting to loadkeys if [ \( $container_release_major = "4" -o $container_release_major = "5" \) -a -e $container_rootfs/etc/sysconfig/keyboard ]; then rm $container_rootfs/etc/sysconfig/keyboard fi # dont use the hwclock, it messes up the host's time if [ $container_release_major = "4" ]; then sed -i 's|runcmd $"Syncing hardware clock|# LXC no hwclock runcmd $"Syncing hardware clock|' $container_rootfs/etc/rc.d/init.d/halt else sed -i 's|\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/init.d/halt fi sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.sysinit sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.sysinit sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.d/rc.sysinit # dont start lvm if [ $container_release_major -lt "6" -a -f $container_rootfs/sbin/lvm.static ]; then mv $container_rootfs/sbin/lvm.static $container_rootfs/sbin/lvm.static.lxc-disabled fi if [ $container_release_major = "6" ]; then touch $container_rootfs/.nolvm fi # fix assumptions that plymouth is available sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit rm -f $container_rootfs/etc/init/plymouth-shutdown.conf rm -f $container_rootfs/etc/init/quit-plymouth.conf rm -f $container_rootfs/etc/init/splash-manager.conf # dont try to unmount /dev/lxc devices sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt # don't try to unmount swap sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt # sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC, so make sure to # mount /dev/shm (normally done by dracut initrd) as tmpfs if [ $container_release_major = "4" -o $container_release_major = "5" ]; then grep -q "mount -t tmpfs tmpfs /dev/shm" $container_rootfs/etc/rc.sysinit if [ $? -eq 1 ]; then echo "mkdir -p /dev/shm && mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.sysinit echo "mkdir -p /dev/shm && mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.d/rc.sysinit fi fi if [ $container_release_major = "6" ]; then sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit fi # there might be other services that are useless but the below set is a good start # some of these might not exist in the image, so we silence chkconfig complaining # about the service file not being found for service in \ acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd \ ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ lm_sensors lvm2-monitor mdmonitor microcode_ctl \ ntpd pcmcia postfix sendmail udev-post xfs ; do chroot $container_rootfs chkconfig 2>/dev/null $service off done for service in rsyslog ; do chroot $container_rootfs chkconfig 2>/dev/null $service on done } container_rootfs_configure() { container_rootfs_patch echo "Configuring container for Oracle Linux $container_release_major.$container_release_minor" # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest # will report its name and be resolv'able by the hosts dnsmasq cat < $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes HOSTNAME=$name DHCP_HOSTNAME=\`hostname\` NM_CONTROLLED=no TYPE=Ethernet EOF # set the hostname if [ $container_release_major -ge "7" ]; then # systemd honors /etc/hostname echo "$name" >$container_rootfs/etc/hostname fi cat < $container_rootfs/etc/sysconfig/network NETWORKING=yes NETWORKING_IPV6=no HOSTNAME=$name EOF # set minimal hosts echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts # this file has to exist for libvirt/Virtual machine monitor to boot the container touch $container_rootfs/etc/mtab # setup console and tty[1-4] for login. note that /dev/console and # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. # lxc will maintain these links and bind mount ptys over /dev/lxc/* # since lxc.devttydir is specified in the config. # allow root login on console, tty[1-4], and pts/0 for libvirt echo "# LXC (Linux Containers)" >>$container_rootfs/etc/securetty echo "lxc/console" >>$container_rootfs/etc/securetty for i in 1 2 3 4; do echo "lxc/tty$i" >>$container_rootfs/etc/securetty done echo "# For libvirt/Virtual Machine Monitor" >>$container_rootfs/etc/securetty for i in 0 1 2 3 4; do echo "pts/$i" >>$container_rootfs/etc/securetty done # prevent mingetty from calling vhangup(2) since it fails with userns if [ -f $container_rootfs/etc/init/tty.conf ]; then sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf fi # create maygetty which only spawns a getty on the console when running # under lxc, not libvirt-lxc which symlinks /dev/console to the same pty # as /dev/tty1 cat <$container_rootfs/sbin/maygetty #!/bin/sh if [ "\$container" = "lxc" ]; then exec /sbin/mingetty \$@ fi exec sleep infinity EOF chmod 755 $container_rootfs/sbin/maygetty # start a getty on /dev/console, /dev/tty[1-4] if [ $container_release_major = "4" -o $container_release_major = "5" ]; then sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/inittab sed -i '/1:2345:respawn/i cns:2345:respawn:/sbin/maygetty --nohangup --noclear console' $container_rootfs/etc/inittab sed -i '/5:2345:respawn/d' $container_rootfs/etc/inittab sed -i '/6:2345:respawn/d' $container_rootfs/etc/inittab fi if [ $container_release_major = "6" ]; then cat < $container_rootfs/etc/init/console.conf # console - getty # # This service maintains a getty on the console from the point the system is # started until it is shut down again. start on stopped rc RUNLEVEL=[2345] stop on runlevel [!2345] env container respawn exec /sbin/maygetty --nohangup --noclear /dev/console EOF fi # lxc-shutdown sends SIGPWR to init, OL4 and OL5 have SysVInit, just # make it do shutdown now instead of delaying 2 minutes. OL6 uses # upstart, so we create an upstart job to handle SIGPWR to shut down # cleanly. We use "init 0" instead of shutdown -h now to avoid SELinux # permission denied when upstart's shutdown tries to connect to the # /com/ubuntu/upstart socket. if [ $container_release_major = "4" -o $container_release_major = "5" ]; then sed -i 's|pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; |pf::powerfail:/sbin/shutdown -f -h now "|' $container_rootfs/etc/inittab elif [ $container_release_major = "6" ]; then cat < $container_rootfs/etc/init/power-status-changed.conf # power-status-changed - used to cleanly shut down the container # # This task is run whenever init receives SIGPWR # Used to shut down the machine. start on power-status-changed exec init 0 EOF fi # start with a clean /var/log/messages rm -f $container_rootfs/var/log/messages # set initial timezone as on host if [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock if [ $container_release_major = "5" -o $container_release_major = "6" ]; then echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock chroot $container_rootfs tzdata-update else ZONE="${ZONE// /_}" chroot $container_rootfs ln -sf ../usr/share/zoneinfo/$ZONE /etc/localtime fi else ZONE=`readlink /etc/localtime | sed -s "s/\.\.\/usr\/share\/zoneinfo\///g"` if [ "$ZONE" ]; then if [ $container_release_major = "5" -o $container_release_major = "6" ]; then echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock chroot $container_rootfs tzdata-update else # if /etc/localtime is a symlink, this should preserve it. cp -a /etc/localtime $container_rootfs/etc/localtime fi else echo "Timezone in container is not configured. Adjust it manually." fi fi # add oracle user chroot $container_rootfs useradd -m -s /bin/bash oracle printf "Added container user:\033[1moracle\033[0m\n" printf "Added container user:\033[1mroot\033[0m\n" } # create the container's lxc config file container_config_create() { echo "Create configuration file $cfg_dir/config" mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" echo "# Common configuration" >> $cfg_dir/config if [ -e "@LXCTEMPLATECONFIG@/oracle.common.conf" ]; then echo "lxc.include = @LXCTEMPLATECONFIG@/oracle.common.conf" >> $cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" # Container configuration for Oracle Linux $container_release_major.$container_release_minor lxc.arch = $arch lxc.utsname = $name EOF grep -q "^lxc.rootfs" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs = $container_rootfs" >> $cfg_dir/config if [ $container_release_major != "4" ]; then echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config fi # systemd services like logind and journald need these if [ $container_release_major != "7" ]; then echo "lxc.cap.drop = setfcap setpcap" >>$cfg_dir/config fi echo "# Networking" >>$cfg_dir/config # see if the default network settings were already specified lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_type" ]; then echo "lxc.network.type = veth" >>$cfg_dir/config lxc_network_type=veth fi lxc_network_link=`grep '^lxc.network.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_link" ]; then echo "lxc.network.link = lxcbr0" >>$cfg_dir/config lxc_network_link=lxcbr0 fi lxc_network_hwaddr=`grep '^lxc.network.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_hwaddr" ]; then # generate a hwaddr for the container # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n1 | awk '{print $2}' | cut -c1-6 | \ sed 's/\(..\)/\1:/g; s/.$//'`" echo "lxc.network.hwaddr = $hwaddr" >>$cfg_dir/config fi lxc_network_flags=`grep '^lxc.network.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_flags" ]; then echo "lxc.network.flags = up" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" lxc.network.name = eth0 lxc.network.mtu = 1500 EOF } container_rootfs_clone() { if is_btrfs_subvolume $template_rootfs; then # lxc-create already made $container_rootfs a btrfs subvolume, but # in this case we want to snapshot the original subvolume so we we # have to delete the one that lxc-create made btrfs subvolume delete $container_rootfs btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" else echo "Copying rootfs ..." cp -axT $template_rootfs $container_rootfs || die "copy template" fi } container_rootfs_repo_create() { echo "# LXC generated .repo file" >$1 echo "[$2]" >>$1 echo "name=Oracle Linux $container_release_major.$container_release_minor ($basearch)" >>$1 echo "baseurl=$3/" >>$1 echo "enabled=1" >>$1 echo "skip_if_unavailable=1" >>$1 if [ "$4" != "" ]; then echo "gpgkey=$yum_url/RPM-GPG-KEY-oracle-ol$container_release_major" >>$1 echo "gpgcheck=1" >>$1 else echo "gpgcheck=0" >>$1 fi } container_rootfs_dev_create() { # create required devices. note that /dev/console will be created by lxc # or libvirt itself to be a symlink to the right pty. # take care to not nuke /dev in case $container_rootfs isn't set dev_path="$container_rootfs/dev" if [ $container_rootfs != "/" -a -d $dev_path ]; then rm -rf $dev_path fi mkdir -p $dev_path if can_chcon; then # ensure symlinks created in /dev have the right context chcon -t device_t $dev_path fi mknod -m 666 $dev_path/null c 1 3 mknod -m 666 $dev_path/zero c 1 5 mknod -m 666 $dev_path/random c 1 8 mknod -m 666 $dev_path/urandom c 1 9 mkdir -m 755 $dev_path/pts mkdir -m 1777 $dev_path/shm mknod -m 666 $dev_path/tty c 5 0 mknod -m 666 $dev_path/tty1 c 4 1 mknod -m 666 $dev_path/tty2 c 4 2 mknod -m 666 $dev_path/tty3 c 4 3 mknod -m 666 $dev_path/tty4 c 4 4 mknod -m 666 $dev_path/full c 1 7 mknod -m 600 $dev_path/initctl p # set selinux labels same as host if can_chcon; then for node in null zero random urandom pts shm \ tty tty0 tty1 tty2 tty3 tty4 full ; do chcon --reference /dev/$node $dev_path/$node 2>/dev/null done fi } container_rootfs_create() { if can_chcon; then chcon --reference / $container_rootfs 2>/dev/null fi cmds="rpm wget yum" if [ $container_release_major -lt "6" ]; then if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then db_dump_cmd="db5.1_dump" fi if [ $host_distribution = "OracleServer" -o \ $host_distribution = "Fedora" ]; then db_dump_cmd="db_dump" fi cmds="$cmds $db_dump_cmd file" fi for cmd in $cmds; do which $cmd >/dev/null 2>&1 if [ $? -ne 0 ]; then die "The $cmd command is required, please install it" fi done mkdir -p @LOCALSTATEDIR@/lock/subsys ( flock -x 9 if [ $? -ne 0 ]; then die "The template is busy." fi echo "Yum installing release $container_release_major.$container_release_minor for $basearch" if [ -n "$repourl" ]; then yum_url=$repourl else yum_url=http://public-yum.oracle.com fi if [ $container_release_major = "4" -o $container_release_major = "5" ]; then latest_L="el" latest_U="EL" else latest_L="ol" latest_U="OL" fi if [ -n "$baseurl" ]; then # create .repo pointing at baseurl repo="lxc-install" mkdir -p $container_rootfs/etc/yum.repos.d container_rootfs_repo_create \ $container_rootfs/etc/yum.repos.d/lxc-install.repo $repo $baseurl else # get public-yum repo file if [ $container_release_major = "4" ]; then repofile=public-yum-el4.repo elif [ $container_release_major = "5" ]; then repofile=public-yum-el5.repo elif [ $container_release_major = "6" ]; then repofile=public-yum-ol6.repo elif [ $container_release_major = "7" ]; then repofile=public-yum-ol7.repo else die "Unsupported release $container_release_major" fi mkdir -p $container_rootfs/etc/yum.repos.d wget -q $yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile if [ $? -ne 0 ]; then die "Unable to download repo file $yum_url/$repofile, release unavailable" fi # yum will take $basearch from host, so force the arch we want sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile # replace url if they specified one if [ -n "$repourl" ]; then sed -i "s|baseurl=http://public-yum.oracle.com/repo|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile sed -i "s|gpgkey=http://public-yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile fi # disable all repos, then enable the repo for the version we are installing. if [ $container_release_minor = "latest" ]; then repo=$latest_L""$container_release_major"_"$container_release_minor elif [ $container_release_major = "7" ]; then repo="ol"$container_release_major"_u"$container_release_minor"_base" elif [ $container_release_major = "6" ]; then if [ $container_release_minor = "0" ]; then repo="ol"$container_release_major"_ga_base" else repo="ol"$container_release_major"_u"$container_release_minor"_base" fi elif [ $container_release_major = "5" ]; then if [ $container_release_minor = "0" ]; then repo="el"$container_release_major"_ga_base" elif [ $container_release_minor -lt "6" ]; then repo="el"$container_release_major"_u"$container_release_minor"_base" else repo="ol"$container_release_major"_u"$container_release_minor"_base" fi elif [ $container_release_major = "4" -a $container_release_minor -gt "5" ]; then repo="el"$container_release_major"_u"$container_release_minor"_base" else die "Unsupported release $container_release_major.$container_release_minor" fi sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile fi container_rootfs_dev_create # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt echo "" >$container_rootfs/etc/fstab # create rpm db, download and yum install minimal packages mkdir -p $container_rootfs/var/lib/rpm rpm --root $container_rootfs --initdb yum_args="--installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils oraclelinux-release" if [ $container_release_major -lt "6" ]; then min_pkgs="$min_pkgs db4-utils" fi # we unshare the mount namespace because yum installing the ol4 # packages causes $rootfs/proc to be mounted on lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs $user_pkgs if [ $? -ne 0 ]; then die "Failed to download and install the rootfs, aborting." fi # rsyslog and pam depend on coreutils for some common commands in # their POSTIN scriptlets, but coreutils wasn't installed yet. now # that coreutils is installed, reinstall the packages so their POSTIN # runs right. similarly, libutempter depends on libselinux.so.1 when # it runs /usr/sbin/groupadd, so reinstall it too redo_pkgs="" if [ $container_release_major = "5" ]; then if [ $container_release_minor = "latest" ]; then redo_pkgs="pam rsyslog libutempter" elif [ $container_release_minor -lt 2 ]; then redo_pkgs="pam" elif [ $container_release_minor -lt 6 ]; then redo_pkgs="pam rsyslog" elif [ $container_release_minor -gt 5 ]; then redo_pkgs="pam rsyslog libutempter" fi fi # shadow utils fails on ol4 and ol6.1 if [ $container_release_major = "4" -o \ $container_release_major = "6" -a $container_release_minor = "1" ]; then redo_pkgs="shadow-utils" fi if [ x"$redo_pkgs" != x ]; then rpm --root $container_rootfs --nodeps -e $redo_pkgs lxc-unshare -s MOUNT yum -- $yum_args install $redo_pkgs if [ $? -ne 0 ]; then die "Unable to reinstall packages" fi fi # if installing from a baseurl, create a .repo that the container # can use to update to _latest from http://public-yum.oracle.com if [ -n "$baseurl" ]; then container_rootfs_repo_create \ "$container_rootfs/etc/yum.repos.d/public-yum-"$latestL""$container_release_major".repo" \ $latest_L""$container_release_major"_latest" \ $yum_url"/repo/OracleLinux/"$latest_U""$container_release_major"/latest/$basearch" gpg fi # these distributions put the rpm database in a place the guest is # not expecting it, so move it if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then mv $container_rootfs/$HOME/.rpmdb/* $container_rootfs/var/lib/rpm fi # if the native rpm created the db with Hash version 9, we need to # downgrade it to Hash version 8 for use with OL5.x db_version=`file $container_rootfs/var/lib/rpm/Packages | \ grep -o 'version [0-9]*' |awk '{print $2}'` if [ $container_release_major -lt "6" -a $db_version != "8" ]; then echo "Fixing (downgrading) rpm database from version $db_version" rm -f $container_rootfs/var/lib/rpm/__db* for db in $container_rootfs/var/lib/rpm/* ; do $db_dump_cmd $db |chroot $container_rootfs db_load /var/lib/rpm/`basename $db`.new mv $db.new $db done fi # the host rpm may not be the same as the guest, rebuild the db with # the guest rpm version echo "Rebuilding rpm database" rm -f $container_rootfs/var/lib/rpm/__db* chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-oracle-$name if [ $? -ne 0 ]; then exit 1 fi } container_release_get() { if [ -f $1/etc/oracle-release ]; then container_release_version=`cat $1/etc/oracle-release |awk '/^Oracle/ {print $5}'` container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` elif grep -q "Enterprise Linux AS" $1/etc/redhat-release; then container_release_major=`cat $1/etc/redhat-release |awk '{print $7}'` container_release_minor=`cat $1/etc/redhat-release |awk '{print $10}' |tr -d ")"` container_release_version="$container_release_major.$container_release_minor" elif grep -q "Enterprise Linux Server" $1/etc/redhat-release; then container_release_version=`cat $1/etc/redhat-release |awk '{print $7}'` container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` else echo "Unable to determine container release version" exit 1 fi } usage() { cat < architecture (ie. i386, x86_64) -R|--release= release to download for the new container --rootfs= rootfs path -r|--rpms= additional rpms to install into container -u|--url= replace yum repo url (ie. Oracle public-yum mirror) --baseurl= use package repository (ie. file:///mnt) arch and release must also be specified -t|--templatefs= copy/clone rootfs at path instead of downloading -P|--patch= only patch the rootfs at path for use as a container -h|--help Release is of the format "major.minor", for example "5.8", "6.3", or "6.latest" This template supports Oracle Linux releases 4.6 - 7.0 EOF return 0 } options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch:,baseurl: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) cfg_dir=$2; shift 2;; --rootfs) container_rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -R|--release) container_release_version=$2; shift 2;; -r|--rpms) user_pkgs=$2; shift 2;; -u|--url) repourl=$2; shift 2;; -t|--templatefs) template_rootfs=$2; shift 2;; --patch) patch_rootfs=$2; shift 2;; --baseurl) baseurl=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done # make sure mandatory args are given and valid if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -n "$baseurl" ]; then if [ "$arch" = "" -o "$container_release_version" = "" ]; then echo "The --arch and --release must be specified when using --baseurl" usage exit 1 fi fi if [ "$arch" = "" ]; then arch=$(uname -m) fi if [ -n "$patch_rootfs" ]; then container_rootfs="$patch_rootfs" container_release_get $container_rootfs container_rootfs_patch exit 0 fi if [ -z $name ]; then echo "Container name must be given" usage exit 1 fi if [ -z $cfg_dir ]; then echo "Configuration directory must be given, check lxc-create" usage exit 1 fi basearch=$arch if [ "$arch" = "i686" ]; then basearch="i386" fi if [ "$arch" != "i386" -a "$arch" != "x86_64" ]; then echo "Bad architecture given, check lxc-create" usage exit 1 fi if which lsb_release >/dev/null 2>&1; then host_distribution=`lsb_release --id |awk '{print $3}'` host_release_version=`lsb_release --release |awk '{print $2}'` host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` else if [ -f /etc/fedora-release ]; then host_distribution="Fedora" host_release_version=`cat /etc/fedora-release |awk '{print $3}'` host_release_major=$host_release_version host_release_minor=0 elif [ -f /etc/oracle-release ]; then host_distribution="OracleServer" host_release_version=`cat /etc/oracle-release |awk '{print $5}'` host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` else echo "Unable to determine host distribution, ensure lsb_release is installed" exit 1 fi fi echo "Host is $host_distribution $host_release_version" if [ -z "$container_rootfs" ]; then container_rootfs="$cfg_dir/rootfs" fi if [ -n "$template_rootfs" ]; then container_release_get $template_rootfs else if [ -z "$container_release_version" ]; then if [ $host_distribution = "OracleServer" ]; then container_release_version=$host_release_version else echo "No release specified with -R, defaulting to 6.5" container_release_version="6.5" fi fi container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` fi container_config_create if [ -n "$template_rootfs" ]; then container_rootfs_clone else container_rootfs_create fi container_release_get $container_rootfs container_rootfs_configure echo "Container : $container_rootfs" echo "Config : $cfg_dir/config" echo "Network : eth0 ($lxc_network_type) on $lxc_network_link" lxc-2.0.11/templates/lxc-sparclinux.in0000644061062106075000000006407413435013473014632 00000000000000#!/bin/sh # # Template script for generating Linux for SPARC for LXC # based on lxc-fedora, lxc-ubuntu # # Copyright © 2011 Wim Coekaerts # Copyright © 2012 Dwight Engen # Copyright � 2015 Wim Coekaerts # # Modified for Oracle Linux 5 # Wim Coekaerts # # Modified for Oracle Linux 6,7 combined OL4,5,6 into one template script # Dwight Engen # # Modified for Linux for SPARC 1.0 # Wim Coekaerts # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin die() { echo "failed: $1" exit 1 } is_btrfs_subvolume() { if which btrfs >/dev/null 2>&1 && \ btrfs subvolume list "$1" >/dev/null 2>&1; then return 0 fi return 1 } can_chcon() { if which chcon >/dev/null 2>&1; then selinuxenabled >/dev/null 2>&1 return $? fi return 1 } # fix up the container_rootfs container_rootfs_patch() { echo "Patching container rootfs $container_rootfs for Linux for SPARC $container_release_major.$container_release_minor" # copy ourself into the container to be used to --patch the rootfs when # yum update on certain packages is done. we do this here instead of in # container_rootfs_configure() in case the patching done in this function # is updated in the future, we can inject the updated version of ourself # into older containers. if [ $container_rootfs != "/" ]; then cp -f `readlink -f $0` $container_rootfs/usr/bin/lxc-patch mkdir -p $container_rootfs/usr/share/yum-plugins cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/share/yum-plugins mkdir -p $container_rootfs/etc/yum/pluginconf.d cat < $container_rootfs/etc/yum/pluginconf.d/lxc-patch.conf [main] enabled=1 packages=dbus,initscripts,iptables,openssh-server,setup,selinux-policy,readahead,udev,util-linux,util-linux-ng EOF fi # "disable" selinux in the guest. The policy in the container isn't # likely to match the hosts (unless host == guest exactly) and the # kernel can only be enforcing one policy. # mkdir -p $container_rootfs/selinux echo 0 > $container_rootfs/selinux/enforce if [ -e $container_rootfs/etc/selinux/config ]; then sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config else mkdir -p $container_rootfs/etc/selinux echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config fi sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/sshd sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/sshd # setting /proc/$$/loginuid doesn't work under user namespace, which # prevents logins from working sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/sshd sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/login if [ -f $container_rootfs/usr/sbin/selinuxenabled ]; then mv $container_rootfs/usr/sbin/selinuxenabled $container_rootfs/usr/sbin/selinuxenabled.lxcorig ln -s /bin/false $container_rootfs/usr/sbin/selinuxenabled fi # ensure /dev/ptmx refers to the newinstance devpts of the container, or # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512) rm -f $container_rootfs/dev/ptmx ln -s pts/ptmx $container_rootfs/dev/ptmx # silence error in checking for selinux sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.sysinit sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.d/rc.sysinit # disable ipv6 rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global # remove module stuff for iptables it just shows errors that are not # relevant in a container if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config fi # disable readahead in the container if [ $container_release_major = "1" -a -e $container_rootfs/etc/sysconfig/readahead ]; then rm -f $container_rootfs/etc/init/readahead-collector.conf rm -f $container_rootfs/etc/init/readahead-disable-services.conf sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead fi # no need to attempt to mount / sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.sysinit sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.d/rc.sysinit # disable udev in the container sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.sysinit sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/init.d/halt sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.sysinit sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/rc.sysinit sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.sysinit sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.d/rc.sysinit touch $container_rootfs/.nolvm # fix assumptions that plymouth is available sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit rm -f $container_rootfs/etc/init/plymouth-shutdown.conf rm -f $container_rootfs/etc/init/quit-plymouth.conf rm -f $container_rootfs/etc/init/splash-manager.conf # dont try to unmount /dev/lxc devices sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt # don't try to unmount swap sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit # there might be other services that are useless but the below set is a good start # some of these might not exist in the image, so we silence chkconfig complaining # about the service file not being found for service in \ acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd \ ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ lm_sensors lvm2-monitor mdmonitor microcode_ctl \ ntpd pcmcia postfix sendmail udev-post xfs ; do chroot $container_rootfs chkconfig 2>/dev/null $service off done for service in rsyslog ; do chroot $container_rootfs chkconfig 2>/dev/null $service on done } container_rootfs_configure() { container_rootfs_patch echo "Configuring container for Linux for SPARC $container_release_major.$container_release_minor" # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest # will report its name and be resolv'able by the hosts dnsmasq cat < $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes HOSTNAME=$name DHCP_HOSTNAME=\`hostname\` NM_CONTROLLED=no TYPE=Ethernet EOF cat < $container_rootfs/etc/sysconfig/network NETWORKING=yes NETWORKING_IPV6=no HOSTNAME=$name EOF # set minimal hosts echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts # this file has to exist for libvirt/Virtual machine monitor to boot the container touch $container_rootfs/etc/mtab # setup console and tty[1-4] for login. note that /dev/console and # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. # lxc will maintain these links and bind mount ptys over /dev/lxc/* # since lxc.devttydir is specified in the config. # allow root login on console, tty[1-4], and pts/0 for libvirt echo "# LXC (Linux Containers)" >>$container_rootfs/etc/securetty echo "lxc/console" >>$container_rootfs/etc/securetty for i in 1 2 3 4; do echo "lxc/tty$i" >>$container_rootfs/etc/securetty done echo "# For libvirt/Virtual Machine Monitor" >>$container_rootfs/etc/securetty for i in 0 1 2 3 4; do echo "pts/$i" >>$container_rootfs/etc/securetty done # prevent mingetty from calling vhangup(2) since it fails with userns if [ -f $container_rootfs/etc/init/tty.conf ]; then sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf fi # create maygetty which only spawns a getty on the console when running # under lxc, not libvirt-lxc which symlinks /dev/console to the same pty # as /dev/tty1 cat <$container_rootfs/sbin/maygetty #!/bin/sh if [ "\$container" = "lxc" ]; then exec /sbin/mingetty \$@ fi exec sleep infinity EOF chmod 755 $container_rootfs/sbin/maygetty cat < $container_rootfs/etc/init/console.conf # console - getty # # This service maintains a getty on the console from the point the system is # started until it is shut down again. start on stopped rc RUNLEVEL=[2345] stop on runlevel [!2345] env container respawn exec /sbin/maygetty --nohangup --noclear /dev/console EOF cat < $container_rootfs/etc/init/power-status-changed.conf # power-status-changed - used to cleanly shut down the container # # This task is run whenever init receives SIGPWR # Used to shut down the machine. start on power-status-changed exec init 0 EOF # start with a clean /var/log/messages rm -f $container_rootfs/var/log/messages # set initial timezone as on host if [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock chroot $container_rootfs tzdata-update else echo "Timezone in container is not configured. Adjust it manually." fi # add oracle user chroot $container_rootfs useradd -m -s /bin/bash oracle printf "Added container user:\033[1moracle\033[0m\n" printf "Added container user:\033[1mroot\033[0m\n" } # create the container's lxc config file container_config_create() { echo "Create configuration file $cfg_dir/config" mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" echo "# Common configuration" >> $cfg_dir/config if [ -e "@LXCTEMPLATECONFIG@/sparclinux.common.conf" ]; then echo "lxc.include = @LXCTEMPLATECONFIG@/sparclinux.common.conf" >> $cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" # Container configuration for Linux for SPARC $container_release_major.$container_release_minor lxc.arch = $arch lxc.utsname = $name EOF grep -q "^lxc.rootfs" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs = $container_rootfs" >> $cfg_dir/config echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config echo "lxc.cap.drop = setfcap setpcap" >>$cfg_dir/config echo "# Networking" >>$cfg_dir/config # see if the default network settings were already specified lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_type" ]; then echo "lxc.network.type = veth" >>$cfg_dir/config lxc_network_type=veth fi lxc_network_link=`grep '^lxc.network.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_link" ]; then echo "lxc.network.link = lxcbr0" >>$cfg_dir/config lxc_network_link=lxcbr0 fi lxc_network_hwaddr=`grep '^lxc.network.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_hwaddr" ]; then # generate a hwaddr for the container # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n1 | awk '{print $2}' | cut -c1-6 | \ sed 's/\(..\)/\1:/g; s/.$//'`" echo "lxc.network.hwaddr = $hwaddr" >>$cfg_dir/config fi lxc_network_flags=`grep '^lxc.network.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_flags" ]; then echo "lxc.network.flags = up" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" lxc.network.name = eth0 lxc.network.mtu = 1500 EOF } container_rootfs_clone() { if is_btrfs_subvolume $template_rootfs; then # lxc-create already made $container_rootfs a btrfs subvolume, but # in this case we want to snapshot the original subvolume so we we # have to delete the one that lxc-create made btrfs subvolume delete $container_rootfs btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" else echo "Copying rootfs ..." cp -axT $template_rootfs $container_rootfs || die "copy template" fi } container_rootfs_repo_create() { echo "# LXC generated .repo file" >$1 echo "[$2]" >>$1 echo "name=Linux for SPARC $container_release_major.$container_release_minor ($basearch)" >>$1 echo "baseurl=$3/" >>$1 echo "enabled=1" >>$1 echo "skip_if_unavailable=1" >>$1 if [ "$4" != "" ]; then echo "gpgkey=$yum_url/RPM-GPG-KEY-oracle-ol$container_release_major" >>$1 echo "gpgcheck=1" >>$1 else echo "gpgcheck=0" >>$1 fi } container_rootfs_dev_create() { # create required devices. note that /dev/console will be created by lxc # or libvirt itself to be a symlink to the right pty. # take care to not nuke /dev in case $container_rootfs isn't set dev_path="$container_rootfs/dev" if [ $container_rootfs != "/" -a -d $dev_path ]; then rm -rf $dev_path fi mkdir -p $dev_path if can_chcon; then # ensure symlinks created in /dev have the right context chcon -t device_t $dev_path fi mknod -m 666 $dev_path/null c 1 3 mknod -m 666 $dev_path/zero c 1 5 mknod -m 666 $dev_path/random c 1 8 mknod -m 666 $dev_path/urandom c 1 9 mkdir -m 755 $dev_path/pts mkdir -m 1777 $dev_path/shm mknod -m 666 $dev_path/tty c 5 0 mknod -m 666 $dev_path/tty1 c 4 1 mknod -m 666 $dev_path/tty2 c 4 2 mknod -m 666 $dev_path/tty3 c 4 3 mknod -m 666 $dev_path/tty4 c 4 4 mknod -m 666 $dev_path/full c 1 7 mknod -m 600 $dev_path/initctl p # set selinux labels same as host if can_chcon; then for node in null zero random urandom pts shm \ tty tty0 tty1 tty2 tty3 tty4 full ; do chcon --reference /dev/$node $dev_path/$node 2>/dev/null done fi } container_rootfs_create() { if can_chcon; then chcon --reference / $container_rootfs 2>/dev/null fi cmds="rpm wget yum" for cmd in $cmds; do which $cmd >/dev/null 2>&1 if [ $? -ne 0 ]; then die "The $cmd command is required, please install it" fi done mkdir -p @LOCALSTATEDIR@/lock/subsys ( flock -x 9 if [ $? -ne 0 ]; then die "The template is busy." fi echo "Yum installing release $container_release_major.$container_release_minor for $basearch" if [ -n "$repourl" ]; then yum_url=$repourl else yum_url=http://yum.oracle.com fi if [ -n "$baseurl" ]; then # create .repo pointing at baseurl repo="lxc-install" mkdir -p $container_rootfs/etc/yum.repos.d container_rootfs_repo_create \ $container_rootfs/etc/yum.repos.d/lxc-install.repo $repo $baseurl else # get public-yum repo file if [ $container_release_major = "1" ]; then repofile=yum-linux-sparc64.repo else die "Unsupported release $container_release_major" fi mkdir -p $container_rootfs/etc/yum.repos.d wget -q $yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile if [ $? -ne 0 ]; then die "Unable to download repo file $yum_url/$repofile, release unavailable" fi # yum will take $basearch from host, so force the arch we want sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile # replace url if they specified one if [ -n "$repourl" ]; then sed -i "s|baseurl=http://yum.oracle.com/|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile sed -i "s|gpgkey=http://yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile fi # disable all repos, then enable the repo for the version we are installing. if [ $container_release_minor = "latest" ]; then repo="lfs"_$container_release_minor else die "Unsupported release $container_release_major.$container_release_minor" fi sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile fi container_rootfs_dev_create # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt echo "" >$container_rootfs/etc/fstab # create rpm db, download and yum install minimal packages mkdir -p $container_rootfs/var/lib/rpm rpm --root $container_rootfs --initdb yum_args="--installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils sparclinux-release" # we unshare the mount namespace because yum installing the ol4 # packages causes $rootfs/proc to be mounted on lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs $user_pkgs if [ $? -ne 0 ]; then die "Failed to download and install the rootfs, aborting." fi # rsyslog and pam depend on coreutils for some common commands in # their POSTIN scriptlets, but coreutils wasn't installed yet. now # that coreutils is installed, reinstall the packages so their POSTIN # runs right. similarly, libutempter depends on libselinux.so.1 when # it runs /usr/sbin/groupadd, so reinstall it too redo_pkgs="" if [ x"$redo_pkgs" != x ]; then rpm --root $container_rootfs --nodeps -e $redo_pkgs lxc-unshare -s MOUNT yum -- $yum_args install $redo_pkgs if [ $? -ne 0 ]; then die "Unable to reinstall packages" fi fi # these distributions put the rpm database in a place the guest is # not expecting it, so move it if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then mv $container_rootfs/$HOME/.rpmdb/* $container_rootfs/var/lib/rpm fi # if the native rpm created the db with Hash version 9, we need to # downgrade it to Hash version 8 for use with OL5.x db_version=`file $container_rootfs/var/lib/rpm/Packages | \ grep -o 'version [0-9]*' |awk '{print $2}'` # the host rpm may not be the same as the guest, rebuild the db with # the guest rpm version echo "Rebuilding rpm database" rm -f $container_rootfs/var/lib/rpm/__db* chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-sparclinux-$name if [ $? -ne 0 ]; then exit 1 fi } container_release_get() { if [ -f $1/etc/sparclinux-release ]; then container_release_version=`cat $1/etc/sparclinux-release |awk '/^Linux/ {print $5}'` container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` else echo "Unable to determine container release version" exit 1 fi } usage() { cat < architecture (sparc64) -R|--release= release to download for the new container --rootfs= rootfs path -r|--rpms= additional rpms to install into container -u|--url= replace yum repo url (ie. Oracle public-yum mirror) --baseurl= use package repository (ie. file:///mnt) arch and release must also be specified -t|--templatefs= copy/clone rootfs at path instead of downloading -P|--patch= only patch the rootfs at path for use as a container -h|--help Release is of the format "major.minor", for example "1.0" or "1.latest" This template supports Linux for SPARC release 1.0 EOF return 0 } options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch:,baseurl: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) cfg_dir=$2; shift 2;; --rootfs) container_rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -R|--release) container_release_version=$2; shift 2;; -r|--rpms) user_pkgs=$2; shift 2;; -u|--url) repourl=$2; shift 2;; -t|--templatefs) template_rootfs=$2; shift 2;; --patch) patch_rootfs=$2; shift 2;; --baseurl) baseurl=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done # make sure mandatory args are given and valid if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -n "$baseurl" ]; then if [ "$arch" = "" -o "$container_release_version" = "" ]; then echo "The --arch and --release must be specified when using --baseurl" usage exit 1 fi fi if [ "$arch" = "" ]; then arch=$(uname -m) fi if [ -n "$patch_rootfs" ]; then container_rootfs="$patch_rootfs" container_release_get $container_rootfs container_rootfs_patch exit 0 fi if [ -z $name ]; then echo "Container name must be given" usage exit 1 fi if [ -z $cfg_dir ]; then echo "Configuration directory must be given, check lxc-create" usage exit 1 fi basearch=$arch if [ "$arch" != "sparc64" ]; then echo "Bad architecture given, check lxc-create" usage exit 1 fi if [ -f /etc/sparclinux-release ]; then host_distribution="SPARCLinux" host_release_version=`cat /etc/sparclinux-release |awk '{print $5}'` host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` else echo "Unable to determine host distribution" exit 1 fi echo "Host is $host_distribution $host_release_version" if [ -z "$container_rootfs" ]; then container_rootfs="$cfg_dir/rootfs" fi if [ -n "$template_rootfs" ]; then container_release_get $template_rootfs else if [ -z "$container_release_version" ]; then if [ $host_distribution = "SPARCLinux" ]; then container_release_version=$host_release_version else echo "No release specified with -R, defaulting to 1.latest" container_release_version="1.latest" fi fi container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` fi container_config_create if [ -n "$template_rootfs" ]; then container_rootfs_clone else container_rootfs_create fi container_release_get $container_rootfs container_rootfs_configure echo "Container : $container_rootfs" echo "Config : $cfg_dir/config" echo "Network : eth0 ($lxc_network_type) on $lxc_network_link" lxc-2.0.11/templates/lxc-busybox.in0000644061062106075000000002660113435013473014127 00000000000000#!/bin/bash # # lxc: linux Container library # Authors: # Daniel Lezcano # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA LXC_MAPPED_UID= LXC_MAPPED_GID= SSH= # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin am_in_userns() { [ -e /proc/self/uid_map ] || { echo no; return; } [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) [ "$line" = "0 0 4294967295" ] && { echo no; return; } echo yes } in_userns=0 [ $(am_in_userns) = "yes" ] && in_userns=1 copy_binary() { binary_path=`which $1` if [ $? -ne 0 ]; then echo "Unable to find $1 binary on the system" return 1 fi dir_path="${binary_path%/*}" echo /{,usr/}{,s}bin | grep $dir_path >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Binary $1 is located at $binary_path and will not be copied" echo "($dir_path not supported)" return 1 fi cp $binary_path $rootfs/$binary_path if [ $? -ne 0 ]; then echo "Failed to copy $binary_path to rootfs" return 1 fi return 0 } install_busybox() { rootfs=$1 name=$2 res=0 tree="\ $rootfs/selinux \ $rootfs/dev \ $rootfs/home \ $rootfs/root \ $rootfs/etc \ $rootfs/etc/init.d \ $rootfs/bin \ $rootfs/usr/bin \ $rootfs/sbin \ $rootfs/usr/sbin \ $rootfs/proc \ $rootfs/sys \ $rootfs/mnt \ $rootfs/tmp \ $rootfs/var/log \ $rootfs/usr/share/udhcpc \ $rootfs/dev/pts \ $rootfs/dev/shm \ $rootfs/lib \ $rootfs/usr/lib \ $rootfs/lib64 \ $rootfs/usr/lib64" mkdir -p $tree || return 1 chmod 755 $tree || return 1 pushd $rootfs/dev > /dev/null || return 1 # minimal devices needed for busybox if [ $in_userns -eq 1 ]; then for dev in tty console tty0 tty1 ram0 null urandom; do echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config done else mknod -m 666 tty c 5 0 || res=1 mknod -m 666 console c 5 1 || res=1 mknod -m 666 tty0 c 4 0 || res=1 mknod -m 666 tty1 c 4 0 || res=1 mknod -m 666 tty5 c 4 0 || res=1 mknod -m 600 ram0 b 1 0 || res=1 mknod -m 666 null c 1 3 || res=1 mknod -m 666 zero c 1 5 || res=1 mknod -m 666 urandom c 1 9 || res=1 fi popd > /dev/null # root user defined cat <> $rootfs/etc/passwd root:x:0:0:root:/root:/bin/sh EOF cat <> $rootfs/etc/group root:x:0:root EOF # mount everything cat <> $rootfs/etc/init.d/rcS #!/bin/sh /bin/syslogd /bin/mount -a /bin/udhcpc EOF # executable chmod 744 $rootfs/etc/init.d/rcS || return 1 # launch rcS first then make a console available # and propose a shell on the tty, the last one is # not needed cat <> $rootfs/etc/inittab ::sysinit:/etc/init.d/rcS tty1::respawn:/bin/getty -L tty1 115200 vt100 console::askfirst:/bin/sh EOF # writable and readable for other chmod 644 $rootfs/etc/inittab || return 1 cat <> $rootfs/usr/share/udhcpc/default.script #!/bin/sh case "\$1" in deconfig) ip addr flush dev \$interface ;; renew|bound) # flush all the routes if [ -n "\$router" ]; then ip route del default 2> /dev/null fi # check broadcast if [ -n "\$broadcast" ]; then broadcast="broadcast \$broadcast" fi # add a new ip address ip addr add \$ip/\$mask \$broadcast dev \$interface if [ -n "\$router" ]; then ip route add default via \$router dev \$interface fi [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf for i in \$dns ; do echo nameserver \$i >> /etc/resolv.conf done ;; esac exit 0 EOF chmod 744 $rootfs/usr/share/udhcpc/default.script return $res } install_dropbear() { # copy dropbear binary copy_binary dropbear || return 1 # make symlinks to various ssh utilities utils="\ $rootfs/usr/bin/dbclient \ $rootfs/usr/bin/scp \ $rootfs/usr/bin/ssh \ $rootfs/usr/sbin/dropbearkey \ $rootfs/usr/sbin/dropbearconvert \ " echo $utils | xargs -n1 ln -s /usr/sbin/dropbear # add necessary config files mkdir $rootfs/etc/dropbear dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1 dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1 echo "'dropbear' ssh utility installed" return 0 } install_openssh() { # tools to be installed server_utils="sshd" client_utils="\ ssh \ scp \ " client_optional_utils="\ sftp \ ssh-add \ ssh-agent \ ssh-keygen \ ssh-keyscan \ ssh-argv0 \ ssh-copy-id \ " # new folders used by ssh ssh_tree="\ $rootfs/etc/ssh \ $rootfs/var/empty/sshd \ $rootfs/var/lib/empty/sshd \ $rootfs/var/run/sshd \ " # create folder structure mkdir -p $ssh_tree if [ $? -ne 0 ]; then return 1 fi # copy binaries for bin in $server_utils $client_utils; do copy_binary $bin || return 1 done for bin in $client_optional_utils; do tool_path=`which $bin` && copy_binary $bin done # add user and group cat <> $rootfs/etc/passwd sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin EOF cat <> $rootfs/etc/group sshd:x:74: EOF # generate container keys ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1 ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1 # by default setup root password with no password cat < $rootfs/etc/ssh/sshd_config Port 22 Protocol 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key UsePrivilegeSeparation yes KeyRegenerationInterval 3600 ServerKeyBits 768 SyslogFacility AUTH LogLevel INFO LoginGraceTime 120 PermitRootLogin yes StrictModes yes RSAAuthentication yes PubkeyAuthentication yes IgnoreRhosts yes RhostsRSAAuthentication no HostbasedAuthentication no PermitEmptyPasswords yes ChallengeResponseAuthentication no EOF echo "'OpenSSH' utility installed" return 0 } configure_busybox() { rootfs=$1 which busybox >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "busybox executable is not accessible" return 1 fi # copy busybox in the rootfs cp $(which busybox) $rootfs/bin if [ $? -ne 0 ]; then echo "failed to copy busybox in the rootfs" return 1 fi # symlink busybox for the commands it supports # it would be nice to just use "chroot $rootfs busybox --install -s /bin" # but that only works right in a chroot with busybox >= 1.19.0 pushd $rootfs/bin > /dev/null || return 1 ./busybox --help | grep 'Currently defined functions:' -A300 | \ grep -v 'Currently defined functions:' | tr , '\n' | \ xargs -n1 ln -s busybox popd > /dev/null # relink /sbin/init ln $rootfs/bin/busybox $rootfs/sbin/init # /etc/fstab must exist for "mount -a" touch $rootfs/etc/fstab # passwd exec must be setuid chmod +s $rootfs/bin/passwd touch $rootfs/etc/shadow return 0 } copy_configuration() { path=$1 rootfs=$2 name=$3 grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.haltsignal = SIGUSR1 lxc.rebootsignal = SIGTERM lxc.utsname = $name lxc.tty = 1 lxc.pts = 1 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0 EOF libdirs="\ lib \ usr/lib \ lib64 \ usr/lib64" for dir in $libdirs; do if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config fi done echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config } remap_userns() { path=$1 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1 chown -R root $path/rootfs >/dev/null 2>&1 fi if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1 chgrp -R root $path/rootfs >/dev/null 2>&1 fi } usage() { cat < -s|--ssh={dropbear,openssh} EOF return 0 } options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" while true do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; -s|--ssh) SSH=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' $config 2>/dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi install_busybox $rootfs $name if [ $? -ne 0 ]; then echo "failed to install busybox's rootfs" exit 1 fi configure_busybox $rootfs if [ $? -ne 0 ]; then echo "failed to configure busybox template" exit 1 fi copy_configuration $path $rootfs $name if [ $? -ne 0 ]; then echo "failed to write configuration file" exit 1 fi remap_userns $path if [ $? -ne 0 ]; then echo "failed to remap files to user" exit 1 fi if [ -n "$SSH" ]; then case "$SSH" in "dropbear") install_dropbear if [ $? -ne 0 ]; then echo "Unable to install 'dropbear' ssh utility" exit 1 fi ;; "openssh") install_openssh if [ $? -ne 0 ]; then echo "Unable to install 'OpenSSH' utility" exit 1 fi ;; *) echo "$SSH: unrecognized ssh utility" exit 1 esac else which dropbear >/dev/null 2>&1 if [ $? -eq 0 ]; then install_dropbear fi fi lxc-2.0.11/templates/lxc-debian.in0000644061062106075000000004702313435013473013657 00000000000000#!/bin/bash # # lxc: linux Container library # Authors: # Daniel Lezcano # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Detect use under userns (unsupported) for arg in "$@"; do [ "$arg" = "--" ] && break if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then echo "This template can't be used for unprivileged containers." 1>&2 echo "You may want to try the \"download\" template instead." 1>&2 exit 1 fi done # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin export GREP_OPTIONS="" MIRROR=${MIRROR:-http://deb.debian.org/debian} SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.debian.org/} LOCALSTATEDIR="@LOCALSTATEDIR@" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" # Allows the lxc-cache directory to be set by environment variable LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"} configure_debian() { rootfs=$1 hostname=$2 num_tty=$3 # squeeze only has /dev/tty and /dev/tty0 by default, # therefore creating missing device nodes for tty1-4. for tty in $(seq 1 "$num_tty"); do if [ ! -e "$rootfs/dev/tty$tty" ]; then mknod "$rootfs/dev/tty$tty" c 4 "$tty" fi done # configure the inittab cat < $rootfs/etc/inittab id:3:initdefault: si::sysinit:/etc/init.d/rcS l0:0:wait:/etc/init.d/rc 0 l1:1:wait:/etc/init.d/rc 1 l2:2:wait:/etc/init.d/rc 2 l3:3:wait:/etc/init.d/rc 3 l4:4:wait:/etc/init.d/rc 4 l5:5:wait:/etc/init.d/rc 5 l6:6:wait:/etc/init.d/rc 6 # Normally not reached, but fallthrough in case of emergency. z6:6:respawn:/sbin/sulogin 1:2345:respawn:/sbin/getty 38400 console $(for tty in $(seq 1 "$num_tty"); do echo "c${tty}:12345:respawn:/sbin/getty 38400 tty${tty} linux" ; done;) p6::ctrlaltdel:/sbin/init 6 p0::powerfail:/sbin/init 0 EOF # symlink mtab [ -e "$rootfs/etc/mtab" ] && rm "$rootfs/etc/mtab" ln -s /proc/self/mounts "$rootfs/etc/mtab" # disable selinux in debian mkdir -p "$rootfs/selinux" echo 0 > "$rootfs/selinux/enforce" # configure the network using the dhcp cat < $rootfs/etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp EOF # set the hostname cat < $rootfs/etc/hostname $hostname EOF # reconfigure some services # but first reconfigure locales - so we get no noisy perl-warnings if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then cat >> "$rootfs/etc/locale.gen" << EOF en_US.UTF-8 UTF-8 EOF chroot "$rootfs" locale-gen en_US.UTF-8 UTF-8 chroot "$rootfs" update-locale LANG=en_US.UTF-8 else encoding=$(echo "$LANG" | cut -d. -f2) chroot "$rootfs" sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \ -i /etc/locale.gen 2> /dev/null cat >> "$rootfs/etc/locale.gen" << EOF $LANG $encoding EOF chroot "$rootfs" locale-gen "$LANG" "$encoding" chroot "$rootfs" update-locale LANG="$LANG" fi # remove pointless services in a container chroot "$rootfs" /usr/sbin/update-rc.d -f checkroot.sh disable chroot "$rootfs" /usr/sbin/update-rc.d -f umountfs disable chroot "$rootfs" /usr/sbin/update-rc.d -f hwclock.sh disable chroot "$rootfs" /usr/sbin/update-rc.d -f hwclockfirst.sh disable # generate new SSH keys if [ -x "$rootfs/var/lib/dpkg/info/openssh-server.postinst" ]; then cat > "$rootfs/usr/sbin/policy-rc.d" << EOF #!/bin/sh exit 101 EOF chmod +x "$rootfs/usr/sbin/policy-rc.d" if [ -f "$rootfs/etc/init/ssh.conf" ]; then mv "$rootfs/etc/init/ssh.conf" "$rootfs/etc/init/ssh.conf.disabled" fi rm -f "$rootfs/etc/ssh/"ssh_host_*key* DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot "$rootfs" /var/lib/dpkg/info/openssh-server.postinst configure sed -i "s/root@$(hostname)/root@$hostname/g" "$rootfs/etc/ssh/"ssh_host_*.pub if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then mv "$rootfs/etc/init/ssh.conf.disabled" "$rootfs/etc/init/ssh.conf" fi rm -f "$rootfs/usr/sbin/policy-rc.d" fi # set initial timezone as on host if [ -f /etc/timezone ]; then cat /etc/timezone > "$rootfs/etc/timezone" chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata elif [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock echo "$ZONE" > "$rootfs/etc/timezone" chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata else echo "Timezone in container is not configured. Adjust it manually." fi return 0 } write_sourceslist() { local rootfs="$1"; shift local release="$1"; shift local arch="$1"; shift local prefix="deb" if [ -n "${arch}" ]; then prefix="deb [arch=${arch}]" fi if [ "$mainonly" = 1 ]; then non_main='' else non_main=' contrib non-free' fi cat >> "${rootfs}/etc/apt/sources.list" << EOF ${prefix} $MIRROR ${release} main${non_main} EOF if [ "$release" != "unstable" -a "$release" != "sid" ]; then cat >> "${rootfs}/etc/apt/sources.list" << EOF ${prefix} $SECURITY_MIRROR ${release}/updates main${non_main} EOF fi } install_packages() { local rootfs="$1"; shift local packages="$*" chroot "${rootfs}" apt-get update if [ -n "${packages}" ]; then chroot "${rootfs}" apt-get install --force-yes -y --no-install-recommends ${packages} fi } configure_debian_systemd() { path=$1 rootfs=$2 config=$3 num_tty=$4 # just in case systemd is not installed mkdir -p "${rootfs}/lib/systemd/system" mkdir -p "${rootfs}/etc/systemd/system/getty.target.wants" # Fix getty-static-service as debootstrap does not install dbus if [ -e "$rootfs//lib/systemd/system/getty-static.service" ] ; then local tty_services tty_services=$(for i in $(seq 2 "$num_tty"); do echo -n "getty@tty${i}.service "; done; ) sed 's/ getty@tty.*/'" $tty_services "'/g' \ "$rootfs/lib/systemd/system/getty-static.service" | \ sed 's/\(tty2-tty\)[5-9]/\1'"${num_tty}"'/g' > "$rootfs/etc/systemd/system/getty-static.service" fi # This function has been copied and adapted from lxc-fedora rm -f "${rootfs}/etc/systemd/system/default.target" chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/udev.service chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/systemd-udevd.service chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target # Setup getty service on the ttys we are going to allow in the # default config. Number should match lxc.tty ( cd "${rootfs}/etc/systemd/system/getty.target.wants" for i in $(seq 1 "$num_tty") ; do ln -sf ../getty\@.service getty@tty"${i}".service; done ) # Since we use static-getty.target; we need to mask container-getty@.service generated by # container-getty-generator, so we don't get multiple instances of agetty running. # See https://github.com/lxc/lxc/issues/520 and https://github.com/lxc/lxc/issues/484 ( cd "${rootfs}/etc/systemd/system/getty.target.wants" for i in $(seq 0 "$num_tty"); do ln -sf /dev/null container-getty\@"${i}".service; done ) return 0 } cleanup() { rm -rf "$cache/partial-$release-$arch" rm -rf "$cache/rootfs-$release-$arch" } download_debian() { case "$release" in wheezy) init=sysvinit iproute=iproute ;; *) init=init iproute=iproute2 ;; esac packages=\ $init,\ ifupdown,\ locales,\ dialog,\ isc-dhcp-client,\ netbase,\ net-tools,\ $iproute,\ openssh-server cache=$1 arch=$2 release=$3 trap cleanup EXIT SIGHUP SIGINT SIGTERM # Create the cache mkdir -p "$cache" # If debian-archive-keyring isn't installed, fetch GPG keys directly releasekeyring=/usr/share/keyrings/debian-archive-keyring.gpg if [ ! -f $releasekeyring ]; then releasekeyring="$cache/archive-key.gpg" case $release in "wheezy") gpgkeyname="archive-key-7.0" ;; *) gpgkeyname="archive-key-8" ;; esac wget https://ftp-master.debian.org/keys/${gpgkeyname}.asc -O - --quiet \ | gpg --import --no-default-keyring --keyring="${releasekeyring}" fi # check the mini debian was not already downloaded mkdir -p "$cache/partial-$release-$arch" if [ $? -ne 0 ]; then echo "Failed to create '$cache/partial-$release-$arch' directory" return 1 fi # download a mini debian into a cache echo "Downloading debian minimal ..." debootstrap --verbose --variant=minbase --arch="$arch" \ --include="$packages" --keyring="${releasekeyring}" \ "$release" "$cache/partial-$release-$arch" "$MIRROR" if [ $? -ne 0 ]; then echo "Failed to download the rootfs, aborting." return 1 fi mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" echo "Download complete." trap EXIT trap SIGINT trap SIGTERM trap SIGHUP return 0 } copy_debian() { cache=$1 arch=$2 rootfs=$3 release=$4 # make a local copy of the minidebian echo -n "Copying rootfs to $rootfs..." mkdir -p "$rootfs" rsync -SHaAX "$cache/rootfs-$release-$arch"/ "$rootfs"/ || return 1 return 0 } install_debian() { rootfs=$1 release=$2 arch=$3 cache="$4/debian" mkdir -p $LOCALSTATEDIR/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/rootfs-$release-$arch ... " if [ ! -e "$cache/rootfs-$release-$arch" ]; then download_debian "$cache" "$arch" "$release" if [ $? -ne 0 ]; then echo "Failed to download 'debian base'" return 1 fi fi copy_debian "$cache" "$arch" "$rootfs" "$release" if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian return $? } copy_configuration() { path=$1 rootfs=$2 hostname=$3 arch=$4 num_tty=$5 # Generate the configuration file # if there is exactly one veth network entry, make sure it has an # associated hwaddr. nics=$(grep -ce '^lxc\.network\.type[ \t]*=[ \t]*veth' "$path/config") if [ "$nics" -eq 1 ]; then grep -q "^lxc.network.hwaddr" "$path/config" || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" "$path/config" fi ## Add all the includes echo "" >> "$path/config" echo "# Common configuration" >> "$path/config" if [ -e "${LXC_TEMPLATE_CONFIG}/debian.common.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.common.conf" >> "$path/config" fi if [ -e "${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" ]; then echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" >> "$path/config" fi ## Add the container-specific config echo "" >> "$path/config" echo "# Container specific configuration" >> "$path/config" grep -q "^lxc.rootfs" "$path/config" 2> /dev/null || echo "lxc.rootfs = $rootfs" >> "$path/config" cat <> $path/config lxc.tty = $num_tty lxc.utsname = $hostname lxc.arch = $arch EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } post_process() { local rootfs="$1"; shift local release="$1"; shift local arch="$1"; shift local hostarch="$1"; shift local packages="$*" # Disable service startup cat > "${rootfs}/usr/sbin/policy-rc.d" << EOF #!/bin/sh exit 101 EOF chmod +x "${rootfs}/usr/sbin/policy-rc.d" # If the container isn't running a native architecture, setup multiarch if [ "${arch}" != "${hostarch}" ]; then # Test if dpkg supports multiarch if ! chroot "$rootfs" dpkg --print-foreign-architectures 2>&1; then chroot "$rootfs" dpkg --add-architecture "${hostarch}" fi fi # Write a new sources.list containing both native and multiarch entries > "${rootfs}/etc/apt/sources.list" if [ "${arch}" = "${hostarch}" ]; then write_sourceslist "${rootfs}" "${release}" "${arch}" else write_sourceslist "${rootfs}" "${release}" fi # Install Packages in container if [ -n "${packages}" ]; then local pack_list pack_list="${packages//,/ }" echo "Installing packages: ${pack_list}" install_packages "${rootfs}" "${pack_list}" fi # Re-enable service startup rm "${rootfs}/usr/sbin/policy-rc.d" # end } clean() { cache=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc/debian"} if [ ! -e "$cache" ]; then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -x 9 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf "$cache" && echo "Done." || exit 1 exit 0 ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian } usage() { cat < [-c|--clean] [-a|--arch=] [-r|--release=] [--mirror=] [--security-mirror=] [--package=] Options : -h, --help print this help text -p, --path=PATH directory where config and rootfs of this VM will be kept -a, --arch=ARCH The container architecture. Can be one of: i686, x86_64, amd64, armhf, armel, powerpc. Defaults to host arch. -r, --release=RELEASE Debian release. Can be one of: wheezy, jessie, stretch, buster, sid. Defaults to current stable. --mirror=MIRROR Debian mirror to use during installation. Overrides the MIRROR environment variable (see below). --security-mirror=SECURITY_MIRROR Debian mirror to use for security updates. Overrides the SECURITY_MIRROR environment variable (see below). --packages=PACKAGE_NAME1,PACKAGE_NAME2,... List of additional packages to install. Comma separated, without space. -c, --clean only clean up the cache and terminate --enable-non-free include also Debian's contrib and non-free repositories. Environment variables: MIRROR The Debian package mirror to use. See also the --mirror switch above. Defaults to '$MIRROR' SECURITY_MIRROR The Debian package security mirror to use. See also the --security-mirror switch above. Defaults to '$SECURITY_MIRROR' EOF return 0 } options=$(getopt -o hp:n:a:r:c -l arch:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror: -- "$@") if [ $? -ne 0 ]; then usage "$(basename "$0")" exit 1 fi eval set -- "$options" littleendian=$(lscpu | grep '^Byte Order' | grep -q Little && echo yes) arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then arch="amd64" elif [ "$arch" = "armv7l" ]; then arch="armhf" elif [ "$arch" = "aarch64" ]; then arch="arm64" elif [ "$arch" = "ppc" ]; then arch="powerpc" elif [ "$arch" = "ppc64le" ]; then arch="ppc64el" elif [ "$arch" = "mips" -a "$littleendian" = "yes" ]; then arch="mipsel" elif [ "$arch" = "mips64" -a "$littleendian" = "yes" ]; then arch="mips64el" fi hostarch=$arch mainonly=1 while true do case "$1" in -h|--help) usage "$0" && exit 1;; --) shift 1; break ;; -a|--arch) arch=$2; shift 2;; -c|--clean) clean=1; shift 1;; --enable-non-free) mainonly=0; shift 1;; --mirror) MIRROR=$2; shift 2;; -n|--name) name=$2; shift 2;; --packages) packages=$2; shift 2;; -p|--path) path=$2; shift 2;; -r|--release) release=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; --security-mirror) SECURITY_MIRROR=$2; shift 2;; *) break ;; esac done if [ ! -z "$clean" -a -z "$path" ]; then clean || exit 1 exit 0 fi if [ "$arch" = "i686" ]; then arch=i386 fi if [ "$arch" = "x86_64" ]; then arch=amd64 fi if [ $hostarch = "i386" -a $arch = "amd64" ]; then echo "can't create $arch container on $hostarch" exit 1 fi if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ [ $arch != "armhf" -a $arch != "armel" ]; then echo "can't create $arch container on $hostarch" exit 1 fi if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then echo "can't create $arch container on $hostarch" exit 1 fi type debootstrap if [ $? -ne 0 ]; then echo "'debootstrap' command is missing" exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 fi current_release=$(wget "${MIRROR}/dists/stable/Release" -O - 2> /dev/null | head |awk '/^Codename: (.*)$/ { print $2; }') release=${release:-${current_release}} valid_releases=('wheezy' 'jessie' 'stretch' 'buster' 'testing' 'sid' 'unstable') if [[ ! "${valid_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]]; then echo "Invalid release ${release}, valid ones are: ${valid_releases[*]}" exit 1 fi # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then if grep -q '^lxc.rootfs' "$config" 2> /dev/null ; then rootfs=$(awk -F= '/^lxc.rootfs[ \t]+=/{ print $2 }' "$config") else rootfs=$path/rootfs fi fi # determine the number of ttys - default is 4 if grep -q '^lxc.tty' "$config" 2> /dev/null ; then num_tty=$(awk -F= '/^lxc.tty[ \t]+=/{ print $2 }' "$config") else num_tty=4 fi install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH" if [ $? -ne 0 ]; then echo "failed to install debian" exit 1 fi configure_debian "$rootfs" "$name" $num_tty if [ $? -ne 0 ]; then echo "failed to configure debian for a container" exit 1 fi copy_configuration "$path" "$rootfs" "$name" $arch $num_tty if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi configure_debian_systemd "$path" "$rootfs" "$config" $num_tty post_process "${rootfs}" "${release}" "${arch}" "${hostarch}" "${packages}" if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi lxc-2.0.11/NEWS0000644061062106075000000000000013435013473010002 00000000000000lxc-2.0.11/configure.ac0000644061062106075000000007635213435013473011620 00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. m4_define([lxc_devel], 0) m4_define([lxc_version_major], 2) m4_define([lxc_version_minor], 0) m4_define([lxc_version_micro], 11) m4_define([lxc_version_beta], []) m4_define([lxc_abi_major], 1) m4_define([lxc_abi_minor], 2) m4_define([lxc_abi_micro], 0) m4_define([lxc_abi], [lxc_abi_major.lxc_abi_minor.lxc_abi_micro]) m4_define([lxc_version_base], [lxc_version_major.lxc_version_minor.lxc_version_micro]) m4_define([lxc_version], [ifelse(lxc_devel, 1, ifelse(lxc_version_beta, [], [lxc_version_base], [lxc_version_base.lxc_version_beta])-devel, ifelse(lxc_version_beta, [], [lxc_version_base], [lxc_version_base.lxc_version_beta]))]) AC_INIT([lxc], [lxc_version]) # We need pkg-config PKG_PROG_PKG_CONFIG AC_SUBST(LXC_VERSION_BASE, lxc_version_base) AC_SUBST(LXC_VERSION_BETA, lxc_version_beta) AC_SUBST([LXC_VERSION_MAJOR], [lxc_version_major]) AC_SUBST([LXC_VERSION_MINOR], [lxc_version_minor]) AC_SUBST([LXC_VERSION_MICRO], [lxc_version_micro]) AC_SUBST([LXC_VERSION], [lxc_version]) AC_SUBST([LXC_DEVEL], [lxc_devel]) AC_SUBST([LXC_ABI_MAJOR], [lxc_abi_major]) AC_SUBST([LXC_ABI_MINOR], [lxc_abi_minor]) AC_SUBST([LXC_ABI_MICRO], [lxc_abi_micro]) AC_SUBST([LXC_ABI], [lxc_abi]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_AUX_DIR([config]) AC_CONFIG_HEADERS([src/config.h]) AM_INIT_AUTOMAKE([-Wall -Werror -Wno-portability subdir-objects]) AC_CANONICAL_HOST AM_PROG_CC_C_O AC_GNU_SOURCE # libtool LT_INIT AC_SUBST([LIBTOOL_DEPS]) # Detect the distribution. This is used for the default configuration and # for some distro-specific build options. 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, centos, fedora, suse, gentoo, debian, arch, slackware, plamo, paldo, openmandriva, pardus, sparclinux, altlinux.])) if type lsb_release >/dev/null 2>&1 && 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/sparclinux-release,with_distro="sparclinux") AC_CHECK_FILE(/etc/centos-release,with_distro="centos") 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/plamo-version,with_distro="plamo") AC_CHECK_FILE(/etc/frugalware-release,with_distro="frugalware") AC_CHECK_FILE(/etc/mandrakelinux-release, with_distro="openmandriva") AC_CHECK_FILE(/etc/mandriva-release,with_distro="openmandriva") AC_CHECK_FILE(/etc/pardus-release,with_distro="pardus") AC_CHECK_FILE(/etc/altlinux-release,with_distro="altlinux") fi with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]'` if test "z$with_distro" = "zforsparc"; then with_distro="sparclinux" fi if test "z$with_distro" = "z"; then with_distro="unknown" fi case $with_distro in ubuntu|raspbian) distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/default" ;; redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo) distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/sysconfig" ;; *) distroconf=default.conf.unknown distrosysconf="$sysconfdir/default" ;; esac AC_MSG_RESULT([$with_distro]) AM_CONDITIONAL([HAVE_DEBIAN], [test x"$with_distro" = "xdebian" -o x"$with_distro" = "xubuntu" -o x"$with_distro" = "xraspbian"]) AM_CONDITIONAL([DISTRO_UBUNTU], [test "x$with_distro" = "xubuntu"]) AC_CONFIG_LINKS([config/etc/default.conf:config/etc/${distroconf}]) # Check for init system type AC_MSG_CHECKING([for init system type]) AC_ARG_WITH([init-script], [AC_HELP_STRING([--with-init-script@<:@=TYPE@<:@,TYPE,...@:>@@:>@], [Type(s) of init script to install: sysvinit, systemd, upstart, distro @<:@default=distro@:>@])],[],[with_init_script=distro]) case "$with_init_script" in distro) case $with_distro in fedora|altlinux|opensuse*) init_script=systemd ;; redhat|oracle|oracleserver|sparclinux|plamo) init_script=sysvinit ;; centos) init_script=sysvinit,systemd ;; debian|raspbian|ubuntu) init_script=upstart,systemd ;; *) echo -n "Linux distribution init system unknown." init_script= ;; esac ;; *) init_script=$with_init_script ;; esac # Check valid init systems were given, run in subshell so we don't mess up IFS (IFS="," ; for init_sys in $init_script; do case "$init_sys" in none|sysvinit|systemd|upstart) ;; *) exit 1 ;; esac done) || AC_MSG_ERROR([Unknown init system type in $init_script]) AM_CONDITIONAL([INIT_SCRIPT_SYSV], [echo "$init_script" |grep -q "sysvinit"]) AM_CONDITIONAL([INIT_SCRIPT_SYSTEMD], [echo "$init_script" |grep -q "systemd"]) AM_CONDITIONAL([INIT_SCRIPT_UPSTART], [echo "$init_script" |grep -q "upstart"]) AC_MSG_RESULT($init_script) # systemd unit dir AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) if test -z "$with_systemdsystemunitdir"; then with_systemdsystemunitdir=/lib/systemd/system fi if test "x$with_systemdsystemunitdir" != "xno"; then AC_SUBST([SYSTEMD_UNIT_DIR], [$with_systemdsystemunitdir]) fi AC_ARG_ENABLE([werror], [AC_HELP_STRING([--disable-werror], [do not treat warnings as errors])], [], [enable_werror=yes]) # Allow enabling deprecated executables AC_ARG_ENABLE([deprecated], [AC_HELP_STRING([--enable-deprecated], [enable deprecated executables [default=no]])], [], [enable_deprecated=false]) AM_CONDITIONAL([ENABLE_DEPRECATED], [test "x$enable_deprecated" = "xyes"]) # Allow disabling rpath AC_ARG_ENABLE([rpath], [AC_HELP_STRING([--enable-rpath], [set rpath in executables [default=no]])], [], [enable_rpath=no]) AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"]) # Documentation (manpages) AC_ARG_ENABLE([doc], [AC_HELP_STRING([--enable-doc], [make man pages [default=auto]])], [], [enable_doc=auto]) if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then db2xman="" dbparsers="docbook2x-man db2x_docbook2man docbook2man docbook-to-man" AC_MSG_CHECKING(for docbook2x-man) for name in ${dbparsers}; do if "$name" --help >/dev/null 2>&1; then db2xman="$name" break; fi done if test -n "${db2xman}"; then AC_MSG_RESULT([${db2xman}]) enable_doc="yes" else AC_MSG_RESULT([no]) if test "x$enable_doc" = "xyes"; then AC_MSG_ERROR([docbook2x-man is required, but could not be found]) fi enable_doc="no" fi AC_SUBST(db2xman) fi AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$db2xman" != "x"]) AM_CONDITIONAL([USE_DOCBOOK2X], [test "x$db2xman" != "xdocbook2man"]) if test "x$db2xman" = "xdocbook2man"; then docdtd="\"-//Davenport//DTD DocBook V3.0//EN\"" else docdtd="\"-//OASIS//DTD DocBook XML\" \"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd\"" fi AC_SUBST(docdtd) # Documentation (API) AC_ARG_ENABLE([api-docs], [AC_HELP_STRING([--enable-api-docs], [make API documentation [default=auto]])], [], [enable_api_docs=auto]) if test "x$enable_api_docs" = "xyes" -o "x$enable_api_docs" = "xauto"; then AC_CHECK_PROGS([HAVE_DOXYGEN],[doxygen]) AC_SUBST([HAVE_DOXYGEN]) if test "x$HAVE_DOXYGEN" != "x"; then enable_api_docs="yes" else if test "x$enable_api_docs" = "xyes"; then AC_MSG_ERROR([doxygen is required, but could not be found]) fi enable_api_docs="no" fi fi AM_CONDITIONAL([ENABLE_API_DOCS], [test "x$HAVE_DOXYGEN" != "x"]) # Apparmor AC_ARG_ENABLE([apparmor], [AC_HELP_STRING([--enable-apparmor], [enable apparmor support [default=auto]])], [], [enable_apparmor=auto]) if test "$enable_apparmor" = "auto" ; then AC_CHECK_LIB([apparmor],[aa_change_profile],[enable_apparmor=yes], [enable_apparmor=no]) fi AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"]) # GnuTLS AC_ARG_ENABLE([gnutls], [AC_HELP_STRING([--enable-gnutls], [enable GnuTLS support [default=auto]])], [], [enable_gnutls=auto]) if test "$enable_gnutls" = "auto" ; then AC_CHECK_LIB([gnutls], [gnutls_hash_fast], [enable_gnutls=yes], [enable_gnutls=no]) fi AM_CONDITIONAL([ENABLE_GNUTLS], [test "x$enable_gnutls" = "xyes"]) AM_COND_IF([ENABLE_GNUTLS], [AC_CHECK_HEADER([gnutls/gnutls.h],[],[AC_MSG_ERROR([You must install the GnuTLS development package in order to compile lxc])]) AC_CHECK_LIB([gnutls], [gnutls_hash_fast],[],[AC_MSG_ERROR([You must install the GnuTLS development package in order to compile lxc])]) AC_SUBST([GNUTLS_LIBS], [-lgnutls])]) # SELinux AC_ARG_ENABLE([selinux], [AC_HELP_STRING([--enable-selinux], [enable SELinux support [default=auto]])], [], [enable_selinux=auto]) if test "x$enable_selinux" = xauto; then AC_CHECK_LIB([selinux],[setexeccon_raw],[enable_selinux=yes],[enable_selinux=no]) fi AM_CONDITIONAL([ENABLE_SELINUX], [test "x$enable_selinux" = "xyes"]) AM_COND_IF([ENABLE_SELINUX], [AC_CHECK_HEADER([selinux/selinux.h],[],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])]) AC_CHECK_LIB([selinux], [setexeccon_raw],[true],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])]) AC_SUBST([SELINUX_LIBS], [-lselinux])]) # Seccomp syscall filter AC_ARG_ENABLE([seccomp], [AC_HELP_STRING([--enable-seccomp], [enable seccomp support [default=auto]])], [], [enable_seccomp=auto]) if test "x$enable_seccomp" = "xauto" ; 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], [PKG_CHECK_MODULES([SECCOMP],[libseccomp],[],[ 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]) ]) ]) # cgmanager AC_ARG_ENABLE([cgmanager], [AC_HELP_STRING([--enable-cgmanager], [enable cgmanager support [default=auto]])], [], [enable_cgmanager=auto]) if test "x$enable_cgmanager" = "xauto" ; then AC_CHECK_LIB([cgmanager],[cgmanager_create],[enable_cgmanager=yes],[enable_cgmanager=no],[-lnih -lnih-dbus -ldbus-1]) fi AM_CONDITIONAL([ENABLE_CGMANAGER], [test "x$enable_cgmanager" = "xyes"]) AM_COND_IF([ENABLE_CGMANAGER], [PKG_CHECK_MODULES([CGMANAGER], [libcgmanager]) PKG_CHECK_MODULES([NIH], [libnih >= 1.0.2]) PKG_CHECK_MODULES([NIH_DBUS], [libnih-dbus >= 1.0.0]) PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16]) ]) AC_MSG_CHECKING(for get_pid_cgroup_abs_sync) save_LIBS=$LIBS AC_SEARCH_LIBS([cgmanager_get_pid_cgroup_abs_sync], [cgmanager], [have_abs_cgroups=yes], [have_abs_cgroups=no], [-lnih -lnih-dbus -ldbus-1]) LIBS=$save_LIBS if test "x$have_abs_cgroups" = "xyes"; then AC_DEFINE([HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC], 1, [Have cgmanager_get_pid_cgroup_abs_sync]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING(for cgmanager_list_controllers) save_LIBS=$LIBS AC_SEARCH_LIBS([cgmanager_list_controllers_sync], [cgmanager], [have_list_controllers=yes], [have_list_controllers=no], [-lnih -lnih-dbus -ldbus-1]) LIBS=$save_LIBS if test "x$have_list_controllers" = "xyes"; then AC_DEFINE([HAVE_CGMANAGER_LIST_CONTROLLERS], 1, [Have cgmanager_list_controllers]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING(for static libcap) # Check for static libcap, make sure the function checked for differs from the # the one checked below so the cache doesn't give a wrong answer OLD_CFLAGS="$CFLAGS" OLD_CPPFLAGS="$CPPFLAGS" OLD_LDFLAGS="$LDFLAGS" OLD_LIBS="$LIBS" CFLAGS="" CPPFLAGS="" LDFLAGS="-static" LIBS="-lcap" AC_LINK_IFELSE([ AC_LANG_SOURCE( [[int main() { return 0; }]] )],[have_static_libcap=yes],[have_static_libcap=no]) AM_CONDITIONAL([HAVE_STATIC_LIBCAP], [test "x$have_static_libcap" = "xyes"]) if test "x$have_static_libcap" = "xyes"; then AC_DEFINE([HAVE_STATIC_LIBCAP], 1, [Have static libcap]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi CPPFLAGS="$OLD_CPPFLAGS" CFLAGS="$OLD_CFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" # Linux capabilities AC_ARG_ENABLE([capabilities], [AC_HELP_STRING([--enable-capabilities], [enable kernel capabilities support [default=auto]])], [], [enable_capabilities=auto]) if test "x$enable_capabilities" = "xauto"; then AC_CHECK_LIB([cap],[cap_set_proc],[enable_capabilities=yes],[enable_capabilities=no]) fi AM_CONDITIONAL([ENABLE_CAP], [test "x$enable_capabilities" = "xyes"]) AM_COND_IF([ENABLE_CAP], [AC_CHECK_HEADER([sys/capability.h],[],[AC_MSG_ERROR([You must install the libcap development package in order to compile lxc])]) AC_CHECK_LIB(cap,cap_set_proc,[],[AC_MSG_ERROR([You must install the libcap development package in order to compile lxc])]) # Test whether we support getting file capabilities via cap_get_file(). AC_CHECK_LIB(cap,cap_get_file, AC_DEFINE(LIBCAP_SUPPORTS_FILE_CAPABILITIES,1,[Have cap_get_file]),[],[]) AC_SUBST([CAP_LIBS], [-lcap])]) # HAVE_SCMP_FILTER_CTX=1 will tell us we have libseccomp api >= 1.0.0 OLD_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $SECCOMP_CFLAGS" AC_CHECK_TYPES([scmp_filter_ctx], [], [], [[#include ]]) AC_CHECK_DECLS([seccomp_syscall_resolve_name_arch], [], [], [[#include ]]) CFLAGS="$OLD_CFLAGS" # Configuration examples AC_ARG_ENABLE([examples], [AC_HELP_STRING([--enable-examples], [install examples [default=yes]])], [], [enable_examples=yes]) AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"]) # Python3 module and scripts AC_ARG_ENABLE([python], [AC_HELP_STRING([--enable-python], [enable python binding [default=auto]])], [], [enable_python=auto]) if test "x$enable_python" = "xauto"; then PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[enable_python=yes],[enable_python=no]) if test "$CC" = "clang"; then enable_python=no fi fi if test "x$enable_python" = "xyes" && test "$CC" = "clang"; then AC_MSG_ERROR([Python3 is incompatible with the clang compiler]) fi 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])]) PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[],[AC_MSG_ERROR([You must install python3-dev])]) AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])]) # Enable dumping stack traces AC_ARG_ENABLE([mutex-debugging], [AC_HELP_STRING([--enable-mutex-debugging], [Makes mutexes to report error and provide stack trace [default=no]])], [], [enable_mutex_debugging=no]) AM_CONDITIONAL([MUTEX_DEBUGGING], [test "x$enable_mutex_debugging" = "xyes"]) AM_COND_IF([MUTEX_DEBUGGING], AC_DEFINE_UNQUOTED([MUTEX_DEBUGGING], 1, [Enabling mutex debugging])) # Not in older autoconf versions # AS_VAR_COPY(DEST, SOURCE) # ------------------------- # Set the polymorphic shell variable DEST to the contents of the polymorphic # shell variable SOURCE. m4_ifdef([AS_VAR_COPY], [], [AC_DEFUN([AS_VAR_COPY], [AS_LITERAL_IF([$1[]$2], [$1=$$2], [eval $1=\$$2])]) ]) dnl PKG_CHECK_VAR was introduced with pkg-config 0.28 m4_ifdef([PKG_CHECK_VAR], [], [AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])# PKG_CHECK_VAR ]) # Lua module and scripts AC_ARG_ENABLE([lua], [AC_HELP_STRING([--enable-lua], [enable lua binding [default=auto]])], [], [enable_lua=auto]) AC_ARG_WITH([lua-pc], [AS_HELP_STRING( [--with-lua-pc=PKG], [Specify pkg-config package name for lua] )], [], [with_lua_pc=no]) if test "x$enable_lua" = "xyes" -a "x$with_lua_pc" != "xno"; then # exit with error if not found PKG_CHECK_MODULES([LUA], [$with_lua_pc], [LUAPKGCONFIG=$with_lua_pc]) fi if test "x$enable_lua" = "xauto" -a "x$with_lua_pc" != "xno"; then PKG_CHECK_MODULES([LUA], [$with_lua_pc], [LUAPKGCONFIG=$with_lua_pc enable_lua=yes], [enable_lua=no]) fi if test "x$enable_lua" != "xno"; then PKG_CHECK_MODULES([LUA], [lua], [LUAPKGCONFIG=lua], [PKG_CHECK_MODULES([LUA], [lua5.2], [LUAPKGCONFIG=lua5.2], [PKG_CHECK_MODULES([LUA], [lua5.1], [LUAPKGCONFIG=lua5.1], [AS_IF([test "x$enable_lua" = "xyes"], [AC_MSG_ERROR([Lua not found. Please use --with-lua-pc=PKG])], [enable_lua=no])] )] )]) AS_IF([test "x$LUAPKGCONFIG" != "x"], [enable_lua=yes]) fi AM_CONDITIONAL([ENABLE_LUA], [test "x$enable_lua" = "xyes"]) AM_COND_IF([ENABLE_LUA], [AC_MSG_CHECKING([Lua version]) PKG_CHECK_VAR([LUA_VERSION], [$LUAPKGCONFIG], [V],, [PKG_CHECK_VAR([LUA_VERSION], [$LUAPKGCONFIG], [major_version])]) AC_MSG_RESULT([$LUA_VERSION]) AC_SUBST([LUA_LIBDIR], [$libdir/lua/$LUA_VERSION]) AC_SUBST([LUA_SHAREDIR], [$datadir/lua/$LUA_VERSION]) ]) # Optional bash integration AC_ARG_ENABLE([bash], [AC_HELP_STRING([--enable-bash], [build bash integration [default=yes]])], [], [enable_bash=yes]) AM_CONDITIONAL([ENABLE_BASH], [test "x$enable_bash" = "xyes"]) AM_COND_IF([ENABLE_BASH], [AC_MSG_CHECKING([bash completion directory]) PKG_CHECK_VAR(bashcompdir, [bash-completion], [completionsdir], , bashcompdir="${sysconfdir}/bash_completion.d") AC_MSG_RESULT([$bashcompdir]) AC_SUBST(bashcompdir) ]) # Optional test binaries AC_ARG_ENABLE([tests], [AC_HELP_STRING([--enable-tests], [build test/example binaries [default=no]])], [], [enable_tests=no]) AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" = "xyes"]) # Allow overriding the default runtime dir (/run) AC_ARG_WITH([runtime-path], [AC_HELP_STRING( [--with-runtime-path=dir], [runtime directory (default: /run)] )], [], [with_runtime_path=['/run']]) # LXC container path, where the containers are actually stored # This is overridden by an entry in the file called LXCCONF # (i.e. /etc/lxc/lxc.conf) AC_ARG_WITH([config-path], [AC_HELP_STRING( [--with-config-path=dir], [lxc configuration repository path] )], [], [with_config_path=['${localstatedir}/lib/lxc']]) # The path of the global lxc configuration file. AC_ARG_WITH([global-conf], [AC_HELP_STRING( [--with-global-conf=dir], [global lxc configuration file] )], [], [with_global_conf=['${sysconfdir}/lxc/lxc.conf']]) # The path of the userns network configuration file AC_ARG_WITH([usernic-conf], [AC_HELP_STRING( [--with-usernic-conf], [user network interface configuration file] )], [], [with_usernic_conf=['${sysconfdir}/lxc/lxc-usernet']]) # The path of the runtime usernic database AC_ARG_WITH([usernic-db], [AC_HELP_STRING( [--with-usernic-db], [lxc user nic database] )], [], [with_usernic_db=['${with_runtime_path}/lxc/nics']]) # Rootfs path, where the container mount structure is assembled AC_ARG_WITH([rootfs-path], [AC_HELP_STRING( [--with-rootfs-path=dir], [lxc rootfs mount point] )], [], [with_rootfs_path=['${libdir}/lxc/rootfs']]) # cgroup pattern specification AC_ARG_WITH([cgroup-pattern], [AC_HELP_STRING( [--with-cgroup-pattern=pattern], [pattern for container cgroups] )], [], [with_cgroup_pattern=['/lxc/%n']]) # Container log path. By default, use $lxcpath. AC_MSG_CHECKING([Whether to place logfiles in container config path]) AC_ARG_ENABLE([configpath-log], [AC_HELP_STRING([--enable-configpath-log], [use logfiles in config path [default=no]])], [], [enable_configpath_log=no]) AC_MSG_RESULT([$enable_configpath_log]) AM_CONDITIONAL([USE_CONFIGPATH_LOGS], [test "$enable_configpath_log" = "yes"]) if test "$enable_configpath_log" = "yes"; then default_log_path="${with_config_path}" else default_log_path="${localstatedir}/log/lxc" fi AC_ARG_WITH([log-path], [AC_HELP_STRING( [--with-log-path=dir], [per container log path] )], [], [with_log_path=['${default_log_path}']]) # Expand some useful variables AS_AC_EXPAND(PREFIX, "$prefix") AS_AC_EXPAND(LIBDIR, "$libdir") AS_AC_EXPAND(BINDIR, "$bindir") AS_AC_EXPAND(SBINDIR, "$sbindir") AS_AC_EXPAND(LIBEXECDIR, "$libexecdir") AS_AC_EXPAND(INCLUDEDIR, "$includedir") AS_AC_EXPAND(SYSCONFDIR, "$sysconfdir") AS_AC_EXPAND(LXC_DEFAULT_CONFIG, "$sysconfdir/lxc/default.conf") AS_AC_EXPAND(DATADIR, "$datadir") AS_AC_EXPAND(LOCALSTATEDIR, "$localstatedir") AS_AC_EXPAND(DOCDIR, "$docdir") AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date --utc --date=@${SOURCE_DATE_EPOCH:-$(date +%s)} '+%Y-%m-%d')") AS_AC_EXPAND(LXCPATH, "$with_config_path") AS_AC_EXPAND(LXC_GLOBAL_CONF, "$with_global_conf") AS_AC_EXPAND(LXC_USERNIC_CONF, "$with_usernic_conf") AS_AC_EXPAND(LXC_USERNIC_DB, "$with_usernic_db") AS_AC_EXPAND(LXC_DISTRO_SYSCONF, "$distrosysconf") AS_AC_EXPAND(LXCROOTFSMOUNT, "$with_rootfs_path") AS_AC_EXPAND(LXCTEMPLATEDIR, "$datadir/lxc/templates") AS_AC_EXPAND(LXCTEMPLATECONFIG, "$datadir/lxc/config") AS_AC_EXPAND(LXCHOOKDIR, "$datadir/lxc/hooks") AS_AC_EXPAND(LXCBINHOOKDIR, "$libexecdir/lxc/hooks") AS_AC_EXPAND(LXCINITDIR, "$libexecdir") AS_AC_EXPAND(LOGPATH, "$with_log_path") AS_AC_EXPAND(RUNTIME_PATH, "$with_runtime_path") AC_SUBST(DEFAULT_CGROUP_PATTERN, ["$with_cgroup_pattern"]) # We need the install path so criu knows where to reference the hook scripts. AC_DEFINE_UNQUOTED([DATADIR], "$DATADIR", ["Prefix for shared files."]) # Check for some standard kernel headers AC_CHECK_HEADERS([linux/unistd.h linux/netlink.h linux/genetlink.h], [], AC_MSG_ERROR([Please install the Linux kernel headers.]), [#include ]) # Check for alternate C libraries AC_MSG_CHECKING(for bionic libc) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#ifndef __ANDROID__ error: Not bionic! #endif]])], [is_bionic=yes], [is_bionic=no]) if test "x$is_bionic" = "xyes"; then AC_DEFINE([IS_BIONIC], 1, [bionic libc]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi AM_CONDITIONAL([IS_BIONIC], [test "x$is_bionic" = "xyes"]) # Some systems lack PR_CAPBSET_DROP definition => HAVE_DECL_PR_CAPBSET_DROP AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include ]) # Check for some headers AC_CHECK_HEADERS([sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h]) # lookup major()/minor()/makedev() AC_HEADER_MAJOR # Check for some syscalls functions AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat gettid memfd_create]) # Check for strerror_r() support. Defines: # - HAVE_STRERROR_R if available # - HAVE_DECL_STRERROR_R if defined # - STRERROR_R_CHAR_P if it returns char * AC_FUNC_STRERROR_R # Check for some functions AC_CHECK_LIB(pthread, main) AC_CHECK_FUNCS(pthread_atfork) AC_CHECK_FUNCS(statvfs) AC_CHECK_LIB(util, openpty) AC_CHECK_FUNCS([getgrgid_r openpty hasmntopt setmntent endmntent utmpxname]) AC_CHECK_FUNCS([getline], AM_CONDITIONAL(HAVE_GETLINE, true) AC_DEFINE(HAVE_GETLINE,1,[Have getline]), AM_CONDITIONAL(HAVE_GETLINE, false)) AC_CHECK_FUNCS([getsubopt], AM_CONDITIONAL(HAVE_GETSUBOPT, true) AC_DEFINE(HAVE_GETSUBOPT,1,[Have getsubopt]), AM_CONDITIONAL(HAVE_GETSUBOPT, false)) AC_CHECK_FUNCS([fgetln], AM_CONDITIONAL(HAVE_FGETLN, true) AC_DEFINE(HAVE_FGETLN,1,[Have fgetln]), AM_CONDITIONAL(HAVE_FGETLN, false)) AC_CHECK_FUNCS([pthread_setcancelstate], AM_CONDITIONAL(HAVE_PTHREAD_SETCANCELSTATE, true) AC_DEFINE(HAVE_PTHREAD_SETCANCELSTATE,1,[Have pthread_setcancelstate]), AM_CONDITIONAL(HAVE_PTHREAD_SETCANCELSTATE, false)) AC_CHECK_FUNCS([strlcpy], AM_CONDITIONAL(HAVE_STRLCPY, true) AC_DEFINE(HAVE_STRLCPY,1,[Have strlcpy]), AM_CONDITIONAL(HAVE_STRLCPY, false)) AC_CHECK_FUNCS([strlcat], AM_CONDITIONAL(HAVE_STRLCAT, true) AC_DEFINE(HAVE_STRLCAT,1,[Have strlcat]), AM_CONDITIONAL(HAVE_STRLCAT, false)) # Check for some libraries AX_PTHREAD AC_SEARCH_LIBS(clock_gettime, [rt]) # Check for some standard binaries AC_PROG_GCC_TRADITIONAL AC_PROG_SED # See if we support thread-local storage. LXC_CHECK_TLS if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" if test "x$enable_werror" = "xyes"; then CFLAGS="$CFLAGS -Werror" fi fi AC_ARG_ENABLE([thread-safety], [AC_HELP_STRING([--enable-thread-safety], [enforce thread-safety otherwise fail the build [default=yes]])], [], [enable_thread_safety=yes]) AM_CONDITIONAL([ENFORCE_THREAD_SAFETY], [test "x$enable_thread_safety" = "xyes"]) AC_ARG_ENABLE([memfd-rexec], [AC_HELP_STRING([--enable-memfd-rexec], [enforce liblxc as a memfd to protect against certain symlink attacks [default=yes]])], [], [enable_memfd_rexec=yes]) AM_CONDITIONAL([ENFORCE_MEMFD_REXEC], [test "x$enable_memfd_rexec" = "xyes"]) if test "x$enable_memfd_rexec" = "xyes"; then AC_DEFINE([ENFORCE_MEMFD_REXEC], 1, [Rexec liblxc as memfd]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi # Files requiring some variable expansion AC_CONFIG_FILES([ Makefile lxc.pc lxc.spec config/Makefile config/apparmor/Makefile config/selinux/Makefile config/bash/Makefile config/bash/lxc config/init/Makefile config/init/common/Makefile config/init/common/lxc-containers config/init/common/lxc-net config/init/systemd/Makefile config/init/systemd/lxc.service config/init/systemd/lxc@.service config/init/systemd/lxc-net.service config/init/sysvinit/Makefile config/init/sysvinit/lxc-containers config/init/sysvinit/lxc-net config/init/upstart/lxc.conf config/init/upstart/lxc-net.conf config/init/upstart/Makefile config/etc/Makefile config/templates/Makefile config/templates/alpine.common.conf config/templates/alpine.userns.conf config/templates/archlinux.common.conf config/templates/archlinux.userns.conf config/templates/centos.common.conf config/templates/centos.userns.conf config/templates/common.conf config/templates/common.conf.d/Makefile config/templates/debian.common.conf config/templates/debian.userns.conf config/templates/fedora.common.conf config/templates/fedora.userns.conf config/templates/gentoo.common.conf config/templates/gentoo.moresecure.conf config/templates/gentoo.userns.conf config/templates/nesting.conf config/templates/opensuse.common.conf config/templates/opensuse.userns.conf config/templates/oracle.common.conf config/templates/oracle.userns.conf config/templates/plamo.common.conf config/templates/plamo.userns.conf config/templates/slackware.common.conf config/templates/slackware.userns.conf config/templates/ubuntu-cloud.common.conf config/templates/ubuntu-cloud.lucid.conf config/templates/ubuntu-cloud.userns.conf config/templates/ubuntu.common.conf config/templates/ubuntu.lucid.conf config/templates/ubuntu.userns.conf config/templates/openwrt.common.conf config/templates/sparclinux.common.conf config/templates/sparclinux.userns.conf config/templates/userns.conf config/yum/Makefile config/sysconfig/Makefile config/sysconfig/lxc doc/Makefile doc/api/Makefile doc/lxc-attach.sgml doc/lxc-autostart.sgml doc/lxc-cgroup.sgml doc/lxc-checkconfig.sgml doc/lxc-checkpoint.sgml doc/lxc-clone.sgml doc/lxc-config.sgml doc/lxc-console.sgml doc/lxc-copy.sgml doc/lxc-create.sgml doc/lxc-destroy.sgml doc/lxc-device.sgml doc/lxc-execute.sgml doc/lxc-freeze.sgml doc/lxc-info.sgml doc/lxc-ls.sgml doc/lxc-monitor.sgml doc/lxc-snapshot.sgml doc/lxc-start-ephemeral.sgml doc/lxc-start.sgml doc/lxc-stop.sgml doc/lxc-top.sgml doc/lxc-unfreeze.sgml doc/lxc-unshare.sgml doc/lxc-user-nic.sgml doc/lxc-usernsexec.sgml doc/lxc-wait.sgml doc/lxc.conf.sgml doc/lxc.container.conf.sgml doc/lxc.system.conf.sgml doc/lxc-usernet.sgml doc/lxc.sgml doc/common_options.sgml doc/see_also.sgml doc/rootfs/Makefile doc/examples/Makefile doc/examples/lxc-macvlan.conf doc/examples/lxc-vlan.conf doc/examples/lxc-no-netns.conf doc/examples/lxc-empty-netns.conf doc/examples/lxc-phys.conf doc/examples/lxc-veth.conf doc/examples/lxc-complex.conf doc/ja/Makefile doc/ja/lxc-attach.sgml doc/ja/lxc-autostart.sgml doc/ja/lxc-cgroup.sgml doc/ja/lxc-checkconfig.sgml doc/ja/lxc-checkpoint.sgml doc/ja/lxc-clone.sgml doc/ja/lxc-config.sgml doc/ja/lxc-console.sgml doc/ja/lxc-copy.sgml doc/ja/lxc-create.sgml doc/ja/lxc-destroy.sgml doc/ja/lxc-device.sgml doc/ja/lxc-execute.sgml doc/ja/lxc-freeze.sgml doc/ja/lxc-info.sgml doc/ja/lxc-ls.sgml doc/ja/lxc-monitor.sgml doc/ja/lxc-snapshot.sgml doc/ja/lxc-start-ephemeral.sgml doc/ja/lxc-start.sgml doc/ja/lxc-stop.sgml doc/ja/lxc-top.sgml doc/ja/lxc-unfreeze.sgml doc/ja/lxc-unshare.sgml doc/ja/lxc-user-nic.sgml doc/ja/lxc-usernsexec.sgml doc/ja/lxc-wait.sgml doc/ja/lxc.conf.sgml doc/ja/lxc.container.conf.sgml doc/ja/lxc.system.conf.sgml doc/ja/lxc-usernet.sgml doc/ja/lxc.sgml doc/ja/common_options.sgml doc/ja/see_also.sgml doc/ko/Makefile doc/ko/lxc-attach.sgml doc/ko/lxc-autostart.sgml doc/ko/lxc-cgroup.sgml doc/ko/lxc-checkconfig.sgml doc/ko/lxc-checkpoint.sgml doc/ko/lxc-clone.sgml doc/ko/lxc-config.sgml doc/ko/lxc-console.sgml doc/ko/lxc-copy.sgml doc/ko/lxc-create.sgml doc/ko/lxc-destroy.sgml doc/ko/lxc-device.sgml doc/ko/lxc-execute.sgml doc/ko/lxc-freeze.sgml doc/ko/lxc-info.sgml doc/ko/lxc-ls.sgml doc/ko/lxc-monitor.sgml doc/ko/lxc-snapshot.sgml doc/ko/lxc-start-ephemeral.sgml doc/ko/lxc-start.sgml doc/ko/lxc-stop.sgml doc/ko/lxc-top.sgml doc/ko/lxc-unfreeze.sgml doc/ko/lxc-unshare.sgml doc/ko/lxc-user-nic.sgml doc/ko/lxc-usernsexec.sgml doc/ko/lxc-wait.sgml doc/ko/lxc.conf.sgml doc/ko/lxc.container.conf.sgml doc/ko/lxc.system.conf.sgml doc/ko/lxc-usernet.sgml doc/ko/lxc.sgml doc/ko/common_options.sgml doc/ko/see_also.sgml hooks/Makefile templates/Makefile templates/lxc-alpine templates/lxc-altlinux templates/lxc-archlinux templates/lxc-busybox templates/lxc-centos templates/lxc-cirros templates/lxc-debian templates/lxc-download templates/lxc-fedora templates/lxc-gentoo templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle templates/lxc-plamo templates/lxc-slackware templates/lxc-sshd templates/lxc-ubuntu templates/lxc-ubuntu-cloud templates/lxc-sparclinux src/Makefile src/lxc/Makefile src/lxc/lxc.functions src/lxc/tools/lxc-checkconfig src/lxc/tools/lxc-start-ephemeral src/lxc/version.h src/python-lxc/Makefile src/python-lxc/setup.py src/lua-lxc/Makefile src/tests/Makefile src/tests/lxc-test-usernic ]) AC_CONFIG_COMMANDS([default],[[]],[[]]) AC_OUTPUT # Configuration overview cat << EOF ---------------------------- Environment: - compiler: $CC - distribution: $with_distro - init script type(s): $init_script - rpath: $enable_rpath - GnuTLS: $enable_gnutls - Bash integration: $enable_bash Security features: - Apparmor: $enable_apparmor - Linux capabilities: $enable_capabilities - seccomp: $enable_seccomp - SELinux: $enable_selinux - cgmanager: $enable_cgmanager - memfd rexec: $enable_memfd_rexec Bindings: - lua: $enable_lua - python3: $enable_python Documentation: - examples: $enable_examples - API documentation: $enable_api_docs - user documentation: $enable_doc Debugging: - tests: $enable_tests - mutex debugging: $enable_mutex_debugging Paths: - Logs in configpath: $enable_configpath_log Thread-safety: - enforce: $enable_thread_safety EOF if test "x$ac_cv_func_pthread_atfork" = "xno" ; then cat << EOF WARNING: Threading not supported on your platform You are compiling LXC for bionic target which lacks certain threading related functionality used by LXC API (like pthread_atfork). Please note that, because of the missing functionality, multithreaded usage of LXC API cause some problems. EOF fi lxc-2.0.11/src/0000755061062106075000000000000013435013523010160 500000000000000lxc-2.0.11/src/lxc/0000755061062106075000000000000013435013523010746 500000000000000lxc-2.0.11/src/lxc/lxcutmp.c0000644061062106075000000002615413435013473012542 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIMERFD_H #include #else #include #ifndef TFD_NONBLOCK #define TFD_NONBLOCK O_NONBLOCK #endif #ifndef TFD_CLOEXEC #define TFD_CLOEXEC O_CLOEXEC #endif static int timerfd_create (clockid_t __clock_id, int __flags) { return syscall(__NR_timerfd_create, __clock_id, __flags); } static int timerfd_settime (int __ufd, int __flags, const struct itimerspec *__utmr, struct itimerspec *__otmr) { return syscall(__NR_timerfd_settime, __ufd, __flags, __utmr, __otmr); } #endif #include "conf.h" #include "cgroup.h" #include "start.h" #include "mainloop.h" #include "lxc.h" #include "log.h" #ifndef __USE_GNU #define __USE_GNU #endif #ifdef HAVE_UTMPX_H #include #ifndef HAVE_UTMPXNAME #include #endif #else #include #ifndef RUN_LVL #define RUN_LVL 1 #endif static void setutxent(void) { return setutent(); } static struct utmp * getutxent (void) { return (struct utmp *) getutent(); } static void endutxent (void) { #ifdef IS_BIONIC /* bionic isn't exporting endutend */ return; #else return endutent(); #endif } #endif #ifndef HAVE_UTMPXNAME static int utmpxname(const char *file) { int result; result = utmpname(file); #ifdef IS_BIONIC /* Yeah bionic is that weird */ result = result - 1; #endif return result; } #endif #undef __USE_GNU /* This file watches the /var/run/utmp file in the container * (that should probably be configurable) * We use inotify to put a watch on the /var/run directory for * create and modify events. These can trigger a read of the * utmp file looking for runlevel changes. If a runlevel change * to reboot or halt states is detected, we set up an itimer to * regularly check for the container shutdown, and reboot or halt * as appropriate when we get down to 1 task remaining. */ lxc_log_define(lxc_utmp, lxc); struct lxc_utmp { struct lxc_handler *handler; #define CONTAINER_STARTING 0 #define CONTAINER_REBOOTING 1 #define CONTAINER_HALTING 2 #define CONTAINER_RUNNING 4 char container_state; int timer_fd; int prev_runlevel, curr_runlevel; }; typedef void (*lxc_mainloop_timer_t) (void *data); static int utmp_get_runlevel(struct lxc_utmp *utmp_data); static int utmp_get_ntasks(struct lxc_handler *handler); static int utmp_shutdown_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr); static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, lxc_mainloop_callback_t callback, void *data); static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, struct lxc_utmp *utmp_data); static int utmp_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { struct inotify_event *ie; int size, ret, length; struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; /* * we're monitoring a directory. ie->name is not included in * sizeof(struct inotify_event) if we don't read it all at once, * read gives us EINVAL, so we read and cast to struct ie */ char buffer[MAXPATHLEN]; if (ioctl(fd, FIONREAD, &size) < 0) { SYSERROR("cannot determine the size of this notification"); return -1; } if (read(fd, buffer, size) < size) { SYSERROR("failed to read notification"); return -1; } ie = (struct inotify_event *)buffer; if (ie->len <= 0) { if (ie->mask & IN_UNMOUNT) { DEBUG("watched directory removed"); goto out; } SYSERROR("inotify event with no name (mask %d)", ie->mask); return -1; } ret = 0; DEBUG("got inotify event %d for %s", ie->mask, ie->name); length = (4 < ie->len) ? 4 : ie->len; /* only care about utmp */ if (strncmp(ie->name, "utmp", length)) return 0; if (ie->mask & (IN_MODIFY | IN_CREATE)) ret = utmp_get_runlevel(utmp_data); if (ret < 0) goto out; /* container halting, from running or starting state */ if (utmp_data->curr_runlevel == '0' && ((utmp_data->container_state == CONTAINER_RUNNING) || (utmp_data->container_state == CONTAINER_STARTING))) { utmp_data->container_state = CONTAINER_HALTING; if (utmp_data->timer_fd == -1) lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); DEBUG("Container halting"); goto out; } /* container rebooting, from running or starting state */ if (utmp_data->curr_runlevel == '6' && ((utmp_data->container_state == CONTAINER_RUNNING) || (utmp_data->container_state == CONTAINER_STARTING))) { utmp_data->container_state = CONTAINER_REBOOTING; if (utmp_data->timer_fd == -1) lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); DEBUG("Container rebooting"); goto out; } /* normal operation, running, from starting state. */ if (utmp_data->curr_runlevel > '0' && utmp_data->curr_runlevel < '6') { utmp_data->container_state = CONTAINER_RUNNING; if (utmp_data->timer_fd > 0) lxc_utmp_del_timer(descr, utmp_data); DEBUG("Container running"); goto out; } out: return 0; } static int utmp_get_runlevel(struct lxc_utmp *utmp_data) { #if HAVE_UTMPX_H struct utmpx *utmpx; #else struct utmp *utmpx; #endif char path[MAXPATHLEN]; struct lxc_handler *handler = utmp_data->handler; if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp", handler->pid) > MAXPATHLEN) { ERROR("path is too long"); return -1; } if (!access(path, F_OK) && !utmpxname(path)) goto utmp_ok; if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp", handler->pid) > MAXPATHLEN) { ERROR("path is too long"); return -1; } if (utmpxname(path)) { SYSERROR("failed to 'utmpxname'"); return -1; } utmp_ok: setutxent(); while ((utmpx = getutxent())) { if (utmpx->ut_type == RUN_LVL) { utmp_data->prev_runlevel = utmpx->ut_pid / 256; utmp_data->curr_runlevel = utmpx->ut_pid % 256; DEBUG("utmp handler - run level is %c/%c", utmp_data->prev_runlevel, utmp_data->curr_runlevel); } } endutxent(); return 0; } static int utmp_get_ntasks(struct lxc_handler *handler) { int ntasks; ntasks = cgroup_nrtasks(handler); if (ntasks < 0) { ERROR("failed to get the number of tasks"); return -1; } DEBUG("there are %d tasks running", ntasks); return ntasks; } int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_handler *handler) { char path[MAXPATHLEN]; char path2[MAXPATHLEN]; int fd, wd; struct lxc_utmp *utmp_data; /* We set up a watch for the /var/run directory. We're only interested * in utmp at the moment, but want to watch for delete and create * events as well. */ if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run", handler->pid) > MAXPATHLEN) { ERROR("path is too long"); return -1; } if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp", handler->pid) > MAXPATHLEN) { ERROR("path is too long"); return -1; } if (!access(path2, F_OK)) goto run_ok; if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run", handler->pid) > MAXPATHLEN) { ERROR("path is too long"); return -1; } if (access(path, F_OK)) { WARN("'%s' not found", path); return 0; } run_ok: utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp)); if (NULL == utmp_data) { SYSERROR("failed to malloc handler utmp_data"); return -1; } memset(utmp_data, 0, sizeof(struct lxc_utmp)); fd = inotify_init(); if (fd < 0) { SYSERROR("failed to inotify_init"); goto out; } if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { SYSERROR("failed to set inotify fd to close-on-exec"); goto out_close; } wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE); if (wd < 0) { SYSERROR("failed to add watch for '%s'", path); goto out_close; } utmp_data->handler = handler; utmp_data->container_state = CONTAINER_STARTING; utmp_data->timer_fd = -1; utmp_data->prev_runlevel = 'N'; utmp_data->curr_runlevel = 'N'; if (lxc_mainloop_add_handler (descr, fd, utmp_handler, (void *)utmp_data)) { SYSERROR("failed to add mainloop"); goto out_close; } DEBUG("Added '%s' to inotifywatch", path); return 0; out_close: close(fd); out: free(utmp_data); return -1; } static int utmp_shutdown_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ntasks; ssize_t nread; struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; struct lxc_handler *handler = utmp_data->handler; struct lxc_conf *conf = handler->conf; uint64_t expirations; /* read and clear notifications */ nread = read(fd, &expirations, sizeof(expirations)); if (nread < 0) SYSERROR("Failed to read timer notification"); ntasks = utmp_get_ntasks(handler); if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) { INFO("container has shutdown"); /* shutdown timer */ lxc_utmp_del_timer(descr, utmp_data); kill(handler->pid, SIGKILL); } if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) { INFO("container has rebooted"); conf->reboot = 1; /* shutdown timer */ lxc_utmp_del_timer(descr, utmp_data); /* this seems a bit rough. */ kill(handler->pid, SIGKILL); } return 0; } int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, lxc_mainloop_callback_t callback, void *data) { int fd, result; struct itimerspec timeout; struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (fd < 0) { SYSERROR("failed to create timer"); return -1; } DEBUG("Setting up utmp shutdown timer"); /* set a one second timeout. Repeated. */ timeout.it_value.tv_sec = 1; timeout.it_value.tv_nsec = 0; timeout.it_interval.tv_sec = 1; timeout.it_interval.tv_nsec = 0; result = timerfd_settime(fd, 0, &timeout, NULL); if (result < 0) { SYSERROR("timerfd_settime:"); return -1; } if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) { SYSERROR("failed to add utmp timer to mainloop"); close(fd); return -1; } utmp_data->timer_fd = fd; return 0; } int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, struct lxc_utmp *utmp_data) { int result; DEBUG("Clearing utmp shutdown timer"); result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd); if (result < 0) SYSERROR("failed to del utmp timer from mainloop"); /* shutdown timer_fd */ close(utmp_data->timer_fd); utmp_data->timer_fd = -1; if (result < 0) return -1; else return 0; } lxc-2.0.11/src/lxc/mainloop.c0000644061062106075000000000670313435013473012662 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "mainloop.h" struct mainloop_handler { lxc_mainloop_callback_t callback; int fd; void *data; }; #define MAX_EVENTS 10 int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) { int i, nfds, ret; struct mainloop_handler *handler; struct epoll_event events[MAX_EVENTS]; for (;;) { nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms); if (nfds < 0) { if (errno == EINTR) continue; return -1; } for (i = 0; i < nfds; i++) { handler = events[i].data.ptr; /* If the handler returns a positive value, exit the * mainloop. */ ret = handler->callback(handler->fd, events[i].events, handler->data, descr); if (ret == LXC_MAINLOOP_CLOSE) return 0; } if (nfds == 0) return 0; if (lxc_list_empty(&descr->handlers)) return 0; } } int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, void *data) { struct epoll_event ev; struct mainloop_handler *handler; struct lxc_list *item; handler = malloc(sizeof(*handler)); if (!handler) return -1; handler->callback = callback; handler->fd = fd; handler->data = data; ev.events = EPOLLIN; ev.data.ptr = handler; if (epoll_ctl(descr->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) goto out_free_handler; item = malloc(sizeof(*item)); if (!item) goto out_free_handler; item->elem = handler; lxc_list_add(&descr->handlers, item); return 0; out_free_handler: free(handler); return -1; } int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd) { struct mainloop_handler *handler; struct lxc_list *iterator; lxc_list_for_each(iterator, &descr->handlers) { handler = iterator->elem; if (handler->fd == fd) { /* found */ if (epoll_ctl(descr->epfd, EPOLL_CTL_DEL, fd, NULL)) return -1; lxc_list_del(iterator); free(iterator->elem); free(iterator); return 0; } } return -1; } int lxc_mainloop_open(struct lxc_epoll_descr *descr) { /* hint value passed to epoll create */ descr->epfd = epoll_create1(EPOLL_CLOEXEC); if (descr->epfd < 0) return -1; lxc_list_init(&descr->handlers); return 0; } int lxc_mainloop_close(struct lxc_epoll_descr *descr) { struct lxc_list *iterator, *next; iterator = descr->handlers.next; while (iterator != &descr->handlers) { next = iterator->next; lxc_list_del(iterator); free(iterator->elem); free(iterator); iterator = next; } if (descr->epfd >= 0) return close(descr->epfd); return 0; } lxc-2.0.11/src/lxc/lxc_init.c0000644061062106075000000003055013435013473012652 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "initutils.h" #include "log.h" #include "utils.h" #include "parse.h" #include "version.h" /* option keys for long only options */ #define OPT_USAGE 0x1000 #define OPT_VERSION OPT_USAGE - 1 lxc_log_define(lxc_init, lxc); static sig_atomic_t was_interrupted = 0; static void interrupt_handler(int sig) { if (!was_interrupted) was_interrupted = sig; } static struct option long_options[] = { { "name", required_argument, 0, 'n' }, { "help", no_argument, 0, 'h' }, { "usage", no_argument, 0, OPT_USAGE }, { "version", no_argument, 0, OPT_VERSION }, { "quiet", no_argument, 0, 'q' }, { "logfile", required_argument, 0, 'o' }, { "logpriority", required_argument, 0, 'l' }, { "lxcpath", required_argument, 0, 'P' }, { 0, 0, 0, 0 } }; static char short_options[] = "n:hqo:l:P:"; struct arguments { const struct option *options; const char *shortopts; const char *name; char *log_file; char *log_priority; bool quiet; const char *lxcpath; /* remaining arguments */ char *const *argv; int argc; }; static int arguments_parse(struct arguments *my_args, int argc, char *const argv[]); static struct arguments my_args = { .options = long_options, .shortopts = short_options }; static void prevent_forking(void) { FILE *f; size_t len = 0; char *line = NULL; char path[MAXPATHLEN]; f = fopen("/proc/self/cgroup", "r"); if (!f) return; while (getline(&line, &len, f) != -1) { int fd, ret; char *p, *p2; p = strchr(line, ':'); if (!p) continue; p++; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; /* This is a cgroup v2 entry. Skip it. */ if ((p2 - p) == 0) continue; if (strcmp(p, "pids") != 0) continue; p2++; p2 += lxc_char_left_gc(p2, strlen(p2)); p2[lxc_char_right_gc(p2, strlen(p2))] = '\0'; ret = snprintf(path, sizeof(path), "/sys/fs/cgroup/pids/%s/pids.max", p2); if (ret < 0 || (size_t)ret >= sizeof(path)) { ERROR("Failed to create string"); goto on_error; } fd = open(path, O_WRONLY); if (fd < 0) { SYSERROR("Failed to open \"%s\"", path); goto on_error; } ret = write(fd, "1", 1); if (ret != 1) SYSERROR("Failed to write to \"%s\"", path); close(fd); break; } on_error: free(line); fclose(f); } static void kill_children(pid_t pid) { FILE *f; char path[PATH_MAX]; int ret; ret = snprintf(path, sizeof(path), "/proc/%d/task/%d/children", pid, pid); if (ret < 0 || (size_t)ret >= sizeof(path)) { ERROR("failed snprintf"); return; } f = fopen(path, "r"); if (!f) { SYSERROR("couldn't open %s", path); return; } while (!feof(f)) { pid_t pid; if (fscanf(f, "%d ", &pid) != 1) { ERROR("couldn't scan pid"); fclose(f); return; } kill_children(pid); kill(pid, SIGKILL); } fclose(f); } static void remove_self(void) { int ret; ssize_t n; char path[MAXPATHLEN] = {0}; n = readlink("/proc/self/exe", path, sizeof(path)); if (n < 0 || n >= MAXPATHLEN) { SYSERROR("Failed to readlink \"/proc/self/exe\""); return; } path[n] = '\0'; ret = umount2(path, MNT_DETACH); if (ret < 0) { SYSERROR("Failed to unmount \"%s\"", path); return; } ret = unlink(path); if (ret < 0) { SYSERROR("Failed to unlink \"%s\"", path); return; } } int main(int argc, char *argv[]) { int i, ret; pid_t pid, sid; struct sigaction act; struct lxc_log log; sigset_t mask, omask; int have_status = 0, exit_with = 1, shutdown = 0; if (arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); log.prefix = "lxc-init"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath; ret = lxc_log_init(&log); if (ret < 0) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (!my_args.argc) { ERROR("Please specify a command to execute"); exit(EXIT_FAILURE); } /* Mask all the signals so we are safe to install a signal handler and * to fork. */ ret = sigfillset(&mask); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&mask, SIGILL); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&mask, SIGSEGV); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&mask, SIGBUS); if (ret < 0) exit(EXIT_FAILURE); ret = pthread_sigmask(SIG_SETMASK, &mask, &omask); if (ret < 0) exit(EXIT_FAILURE); ret = sigfillset(&act.sa_mask); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&act.sa_mask, SIGILL); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&act.sa_mask, SIGSEGV); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&act.sa_mask, SIGBUS); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&act.sa_mask, SIGSTOP); if (ret < 0) exit(EXIT_FAILURE); ret = sigdelset(&act.sa_mask, SIGKILL); if (ret < 0) exit(EXIT_FAILURE); act.sa_flags = 0; act.sa_handler = interrupt_handler; for (i = 1; i < NSIG; i++) { /* Exclude some signals: ILL, SEGV and BUS are likely to reveal * a bug and we want a core. STOP and KILL cannot be handled * anyway: they're here for documentation. 32 and 33 are not * defined. */ if (i == SIGILL || i == SIGSEGV || i == SIGBUS || i == SIGSTOP || i == SIGKILL || i == 32 || i == 33) continue; ret = sigaction(i, &act, NULL); if (ret < 0) { if (errno == EINVAL) continue; SYSERROR("Failed to change signal action"); exit(EXIT_FAILURE); } } remove_self(); pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (!pid) { /* restore default signal handlers */ for (i = 1; i < NSIG; i++) { sighandler_t sigerr; if (i == SIGILL || i == SIGSEGV || i == SIGBUS || i == SIGSTOP || i == SIGKILL || i == 32 || i == 33) continue; sigerr = signal(i, SIG_DFL); if (sigerr == SIG_ERR) { DEBUG("%s - Failed to reset to default action " "for signal \"%d\": %d", strerror(errno), i, pid); } } ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); if (ret < 0) { SYSERROR("Failed to set signal mask"); exit(EXIT_FAILURE); } sid = setsid(); if (sid < 0) DEBUG("Failed to make child session leader"); if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0) DEBUG("Failed to set controlling terminal"); NOTICE("Exec'ing \"%s\"", my_args.argv[0]); ret = execvp(my_args.argv[0], my_args.argv); ERROR("%s - Failed to exec \"%s\"", strerror(errno), my_args.argv[0]); exit(ret); } INFO("Attempting to set proc title to \"init\""); setproctitle("init"); /* Let's process the signals now. */ ret = sigdelset(&omask, SIGALRM); if (ret < 0) exit(EXIT_FAILURE); ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); if (ret < 0) { SYSERROR("Failed to set signal mask"); exit(EXIT_FAILURE); } /* No need of other inherited fds but stderr. */ close(STDIN_FILENO); close(STDOUT_FILENO); for (;;) { int status; pid_t waited_pid; switch (was_interrupted) { case 0: /* Some applications send SIGHUP in order to get init to reload * its configuration. We don't want to forward this onto the * application itself, because it probably isn't expecting this * signal since it was expecting init to do something with it. * * Instead, let's explicitly ignore it here. The actual * terminal case is handled in the monitor's handler, which * sends this task a SIGTERM in the case of a SIGHUP, which is * what we want. */ case SIGHUP: break; case SIGPWR: case SIGTERM: if (!shutdown) { shutdown = 1; prevent_forking(); if (lxc_raw_getpid() != 1) { kill_children(lxc_raw_getpid()); } else { ret = kill(-1, SIGTERM); if (ret < 0) DEBUG("%s - Failed to send SIGTERM to " "all children", strerror(errno)); } alarm(1); } break; case SIGALRM: prevent_forking(); if (lxc_raw_getpid() != 1) { kill_children(lxc_raw_getpid()); } else { ret = kill(-1, SIGKILL); if (ret < 0) DEBUG("%s - Failed to send SIGTERM to " "all children", strerror(errno)); } break; default: ret = kill(pid, was_interrupted); if (ret < 0) DEBUG("%s - Failed to send signal \"%d\" to " "%d", strerror(errno), was_interrupted, pid); break; } ret = EXIT_SUCCESS; was_interrupted = 0; waited_pid = wait(&status); if (waited_pid < 0) { if (errno == ECHILD) goto out; if (errno == EINTR) continue; ERROR("%s - Failed to wait on child %d", strerror(errno), pid); goto out; } /* Reset timer each time a process exited. */ if (shutdown) alarm(1); /* Keep the exit code of the started application (not wrapped * pid) and continue to wait for the end of the orphan group. */ if (waited_pid == pid && !have_status) { exit_with = lxc_error_set_and_log(waited_pid, status); have_status = 1; } } out: if (ret < 0) exit(EXIT_FAILURE); exit(exit_with); } static void print_usage(const struct option longopts[]) { fprintf(stderr, "Usage: lxc-init [-n|--name=NAME] [-h|--help] [--usage] [--version] \n\ [-q|--quiet] [-o|--logfile=LOGFILE] [-l|--logpriority=LOGPRIORITY] [-P|--lxcpath=LXCPATH]\n"); exit(0); } static void print_version() { printf("%s\n", LXC_VERSION); exit(0); } static void print_help() { fprintf(stderr, "\ Usage: lxc-init --name=NAME -- COMMAND\n\ \n\ lxc-init start a COMMAND as PID 2 inside a container\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -o, --logfile=FILE Output log to FILE instead of stderr\n\ -l, --logpriority=LEVEL Set log priority to LEVEL\n\ -q, --quiet Don't produce any output\n\ -P, --lxcpath=PATH Use specified container path\n\ -?, --help Give this help list\n\ --usage Give a short usage message\n\ --version Print the version number\n\ \n\ Mandatory or optional arguments to long options are also mandatory or optional\n\ for any corresponding short options.\n\ \n\ See the lxc-init man page for further information.\n\n"); } static int arguments_parse(struct arguments *args, int argc, char *const argv[]) { while (true) { int c; int index = 0; c = getopt_long(argc, argv, args->shortopts, args->options, &index); if (c == -1) break; switch (c) { case 'n': args->name = optarg; break; case 'o': args->log_file = optarg; break; case 'l': args->log_priority = optarg; break; case 'q': args->quiet = true; break; case 'P': remove_trailing_slashes(optarg); args->lxcpath = optarg; break; case OPT_USAGE: print_usage(args->options); case OPT_VERSION: print_version(); case '?': print_help(); exit(EXIT_FAILURE); case 'h': print_help(); exit(EXIT_SUCCESS); } } /* * Reclaim the remaining command arguments */ args->argv = &argv[optind]; args->argc = argc - optind; /* If no lxcpath was given, use default */ if (!args->lxcpath) { args->lxcpath = lxc_global_config_value("lxc.lxcpath"); } /* Check the command options */ if (!args->name) { if(!args->quiet) fprintf(stderr, "lxc-init: missing container name, use --name option\n"); return -1; } return 0; } lxc-2.0.11/src/lxc/error.h0000644061062106075000000000174013435013473012176 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ERROR_H #define __LXC_ERROR_H extern int lxc_error_set_and_log(int pid, int status); #endif lxc-2.0.11/src/lxc/conf.h0000644061062106075000000002702613435013473011777 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONF_H #define __LXC_CONF_H #include "config.h" #include #include #include #include #include #include #include "list.h" #include "start.h" /* for lxc_handler */ #if HAVE_SCMP_FILTER_CTX typedef void * scmp_filter_ctx; #endif /* worth moving to configure.ac? */ #define subuidfile "/etc/subuid" #define subgidfile "/etc/subgid" /* * Defines a generic struct to configure the control group. * It is up to the programmer to specify the right subsystem. * @subsystem : the targeted subsystem * @value : the value to set */ struct lxc_cgroup { char *subsystem; char *value; }; enum idtype { ID_TYPE_UID, ID_TYPE_GID }; /* * id_map is an id map entry. Form in confile is: * lxc.id_map = u 0 9800 100 * lxc.id_map = u 1000 9900 100 * lxc.id_map = g 0 9800 100 * lxc.id_map = g 1000 9900 100 * meaning the container can use uids and gids 0-99 and 1000-1099, * with [ug]id 0 mapping to [ug]id 9800 on the host, and [ug]id 1000 to * [ug]id 9900 on the host. */ struct id_map { enum idtype idtype; unsigned long hostid, nsid, range; }; /* * Defines a structure containing a pty information for * virtualizing a tty * @name : the path name of the slave pty side * @master : the file descriptor of the master * @slave : the file descriptor of the slave */ struct lxc_pty_info { char name[MAXPATHLEN]; int master; int slave; int busy; }; /* * Defines the number of tty configured and contains the * instantiated ptys * @nbtty = number of configured ttys */ struct lxc_tty_info { int nbtty; struct lxc_pty_info *pty_info; }; struct lxc_tty_state; /* * Defines the structure to store the console information * @peer : the file descriptor put/get console traffic * @name : the file name of the slave pty */ struct lxc_console { int slave; int master; int peer; struct lxc_pty_info peerpty; struct lxc_epoll_descr *descr; char *path; char *log_path; int log_fd; char name[MAXPATHLEN]; struct termios *tios; struct lxc_tty_state *tty_state; }; /* * Defines a structure to store the rootfs location, the * optionals pivot_root, rootfs mount paths * @path : the rootfs source (directory or device) * @mount : where it is mounted * @options : mount options * @bev_type : optional backing store type */ struct lxc_rootfs { char *path; char *mount; char *options; char *bdev_type; }; /* * Automatic mounts for LXC to perform inside the container */ enum { LXC_AUTO_PROC_RW = 0x001, /* /proc read-write */ LXC_AUTO_PROC_MIXED = 0x002, /* /proc/sys and /proc/sysrq-trigger read-only */ LXC_AUTO_PROC_MASK = 0x003, LXC_AUTO_SYS_RW = 0x004, /* /sys */ LXC_AUTO_SYS_RO = 0x008, /* /sys read-only */ LXC_AUTO_SYS_MIXED = 0x00C, /* /sys read-only and /sys/class/net read-write */ LXC_AUTO_SYS_MASK = 0x00C, LXC_AUTO_CGROUP_RO = 0x010, /* /sys/fs/cgroup (partial mount, read-only) */ LXC_AUTO_CGROUP_RW = 0x020, /* /sys/fs/cgroup (partial mount, read-write) */ LXC_AUTO_CGROUP_MIXED = 0x030, /* /sys/fs/cgroup (partial mount, paths r/o, cgroup r/w) */ LXC_AUTO_CGROUP_FULL_RO = 0x040, /* /sys/fs/cgroup (full mount, read-only) */ LXC_AUTO_CGROUP_FULL_RW = 0x050, /* /sys/fs/cgroup (full mount, read-write) */ LXC_AUTO_CGROUP_FULL_MIXED = 0x060, /* /sys/fs/cgroup (full mount, parent r/o, own r/w) */ /* These are defined in such a way as to retain * binary compatibility with earlier versions of * this code. If the previous mask is applied, * both of these will default back to the _MIXED * variants, which is safe. */ LXC_AUTO_CGROUP_NOSPEC = 0x0B0, /* /sys/fs/cgroup (partial mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FULL_NOSPEC = 0x0E0, /* /sys/fs/cgroup (full mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FORCE = 0x100, /* mount cgroups even when cgroup namespaces are supported */ LXC_AUTO_CGROUP_MASK = 0x1F0, /* all known cgroup options, doe not contain LXC_AUTO_CGROUP_FORCE */ LXC_AUTO_ALL_MASK = 0x1FF, /* all known settings */ }; /* * Defines the global container configuration * @rootfs : root directory to run the container * @pivotdir : pivotdir path, if not set default will be used * @mount : list of mount points * @tty : numbers of tty * @pts : new pts instance * @mount_list : list of mount point (alternative to fstab file) * @network : network configuration * @utsname : container utsname * @fstab : path to a fstab file format * @caps : list of the capabilities to drop * @keepcaps : list of the capabilities to keep * @tty_info : tty data * @console : console data * @ttydir : directory (under /dev) in which to create console and ttys * @lsm_aa_profile : apparmor profile to switch to or NULL * @lsm_se_context : selinux type to switch to or NULL */ enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, LXCHOOK_START, LXCHOOK_STOP, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, LXCHOOK_DESTROY, NUM_LXC_HOOKS }; extern char *lxchook_names[NUM_LXC_HOOKS]; struct lxc_conf { int is_execute; char *fstab; unsigned int tty; unsigned int pts; int reboot; int need_utmp_watch; signed long personality; struct utsname *utsname; struct lxc_list cgroup; struct { struct lxc_list id_map; /* Pointer to the idmap entry for the container's root uid in * the id_map list. Do not free! */ struct id_map *root_nsuid_map; /* Pointer to the idmap entry for the container's root gid in * the id_map list. Do not free! */ struct id_map *root_nsgid_map; }; struct lxc_list network; int auto_mounts; struct lxc_list mount_list; struct lxc_list caps; struct lxc_list keepcaps; struct lxc_tty_info tty_info; char *pty_names; // comma-separated list of lxc.tty pty names struct lxc_console console; struct lxc_rootfs rootfs; char *ttydir; int close_all_fds; struct lxc_list hooks[NUM_LXC_HOOKS]; char *lsm_aa_profile; unsigned int lsm_aa_allow_incomplete; char *lsm_se_context; int tmp_umount_proc; char *seccomp; // filename with the seccomp rules #if HAVE_SCMP_FILTER_CTX scmp_filter_ctx seccomp_ctx; #endif int maincmd_fd; unsigned int autodev; // if 1, mount and fill a /dev at start int haltsignal; // signal used to halt container int rebootsignal; // signal used to reboot container int stopsignal; // signal used to hard stop container unsigned int kmsg; // if 1, create /dev/kmsg symlink char *rcfile; // Copy of the top level rcfile we read // Logfile and logleve can be set in a container config file. // Those function as defaults. The defaults can be overriden // by command line. However we don't want the command line // specified values to be saved on c->save_config(). So we // store the config file specified values here. char *logfile; // the logfile as specifed in config int loglevel; // loglevel as specifed in config (if any) int logfd; int inherit_ns_fd[LXC_NS_MAX]; unsigned int start_auto; unsigned int start_delay; int start_order; struct lxc_list groups; int nbd_idx; /* unshare the mount namespace in the monitor */ unsigned int monitor_unshare; /* set to true when rootfs has been setup */ bool rootfs_setup; /* list of included files */ struct lxc_list includes; /* config entries which are not "lxc.*" are aliens */ struct lxc_list aliens; /* list of environment variables we'll add to the container when * started */ struct lxc_list environment; /* text representation of the config file */ char *unexpanded_config; size_t unexpanded_len, unexpanded_alloced; /* init command */ char *init_cmd; /* if running in a new user namespace, the UID/GID that init and COMMAND * should run under when using lxc-execute */ uid_t init_uid; gid_t init_gid; /* indicator if the container will be destroyed on shutdown */ unsigned int ephemeral; }; extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size); #ifdef HAVE_TLS extern __thread struct lxc_conf *current_config; #else extern struct lxc_conf *current_config; #endif int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, const char *lxcpath, char *argv[]); extern int detect_shared_rootfs(void); /* * 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_map_ids(struct lxc_list *idmap, pid_t pid); 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_config_keepcaps(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_automounts(struct lxc_conf *c); extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); extern int lxc_clear_idmaps(struct lxc_conf *c); extern int lxc_clear_groups(struct lxc_conf *c); extern int lxc_clear_environment(struct lxc_conf *c); extern int lxc_delete_autodev(struct lxc_handler *handler); extern void lxc_clear_includes(struct lxc_conf *conf); extern int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath); /* * Configure the container from inside */ struct cgroup_process_info; extern int lxc_setup(struct lxc_handler *handler); extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype); extern int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype); extern int chown_mapped_root(char *path, struct lxc_conf *conf); extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata); extern void tmp_proc_unmount(struct lxc_conf *lxc_conf); void remount_all_slave(void); extern void suggest_default_idmap(void); extern FILE *make_anonymous_mount_file(struct lxc_list *mount); extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings); extern unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags); extern int run_script(const char *name, const char *section, const char *script, ...); extern int in_caplist(int cap, struct lxc_list *caps); #endif /* __LXC_CONF_H */ lxc-2.0.11/src/lxc/log.c0000644061062106075000000003621713435013473011630 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Cedric Le Goater * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "caps.h" #include "utils.h" #include "lxccontainer.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif /* We're logging in seconds and nanoseconds. Assuming that the underlying * datatype is currently at maximum a 64bit integer, we have a date string that * is of maximum length (2^64 - 1) * 2 = (21 + 21) = 42. */ #define LXC_LOG_TIME_SIZE ((LXC_NUMSTRLEN64)*2) int lxc_log_fd = -1; int lxc_quiet_specified; int lxc_log_use_global_fd; static int lxc_loglevel_specified; static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc"; static char *log_fname = NULL; lxc_log_define(lxc_log, lxc); /*---------------------------------------------------------------------------*/ static int log_append_stderr(const struct lxc_log_appender *appender, struct lxc_log_event *event) { if (event->priority < LXC_LOG_LEVEL_ERROR) return 0; fprintf(stderr, "%s: ", log_prefix); fprintf(stderr, "%s: %s: %d ", event->locinfo->file, event->locinfo->func, event->locinfo->line); vfprintf(stderr, event->fmt, *event->vap); fprintf(stderr, "\n"); return 0; } /*---------------------------------------------------------------------------*/ int lxc_unix_epoch_to_utc(char *buf, size_t bufsize, const struct timespec *time) { int64_t epoch_to_days, z, era, doe, yoe, year, doy, mp, day, month, d_in_s, hours, h_in_s, minutes, seconds; char nanosec[LXC_NUMSTRLEN64]; int ret; /* See https://howardhinnant.github.io/date_algorithms.html for an * explanation of the algorithm used here. */ /* Convert Epoch in seconds to number of days. */ epoch_to_days = time->tv_sec / 86400; /* Shift the Epoch from 1970-01-01 to 0000-03-01. */ z = epoch_to_days + 719468; /* compute the era from the serial date by simply dividing by the number * of days in an era (146097). */ era = (z >= 0 ? z : z - 146096) / 146097; /* The day-of-era (doe) can then be found by subtracting the era number * times the number of days per era, from the serial date. */ doe = (z - era * 146097); /* From the day-of-era (doe), the year-of-era (yoe, range [0, 399]) can * be computed. */ yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; /* Given year-of-era, and era, one can now compute the year. */ year = yoe + era * 400; /* Also the day-of-year, again with the year beginning on Mar. 1, can be * computed from the day-of-era and year-of-era. */ doy = doe - (365 * yoe + yoe / 4 - yoe / 100); /* Given day-of-year, find the month number. */ mp = (5 * doy + 2) / 153; /* From day-of-year and month-of-year we can now easily compute * day-of-month. */ day = doy - (153 * mp + 2) / 5 + 1; /* Transform the month number from the [0, 11] / [Mar, Feb] system to * the civil system: [1, 12] to find the correct month. */ month = mp + (mp < 10 ? 3 : -9); /* The algorithm assumes that a year begins on 1 March, so add 1 before * that. */ if (month < 3) year++; /* Transform days in the epoch to seconds. */ d_in_s = epoch_to_days * 86400; /* To find the current hour simply substract the Epoch_to_days from the * total Epoch and divide by the number of seconds in an hour. */ hours = (time->tv_sec - d_in_s) / 3600; /* Transform hours to seconds. */ h_in_s = hours * 3600; /* Calculate minutes by substracting the seconds for all days in the * epoch and for all hours in the epoch and divide by the number of * minutes in an hour. */ minutes = (time->tv_sec - d_in_s - h_in_s) / 60; /* Calculate the seconds by substracting the seconds for all days in the * epoch, hours in the epoch and minutes in the epoch. */ seconds = (time->tv_sec - d_in_s - h_in_s - (minutes * 60)); /* Make string from nanoseconds. */ ret = snprintf(nanosec, LXC_NUMSTRLEN64, "%"PRId64, (int64_t)time->tv_nsec); if (ret < 0 || ret >= LXC_NUMSTRLEN64) return -1; /* Create final timestamp for the log and shorten nanoseconds to 3 * digit precision. */ ret = snprintf(buf, bufsize, "%" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 ".%.3s", year, month, day, hours, minutes, seconds, nanosec); if (ret < 0 || (size_t)ret >= bufsize) return -1; return 0; } /* This function needs to make extra sure that it is thread-safe. We had some * problems with that before. This especially involves time-conversion * functions. I don't want to find any localtime() or gmtime() functions or * relatives in here. Not even localtime_r() or gmtime_r() or relatives. They * all fiddle with global variables and locking in various libcs. They cause * deadlocks when liblxc is used multi-threaded and no matter how smart you * think you are, you __will__ cause trouble using them. * (As a short example how this can cause trouble: LXD uses forkstart to fork * off a new process that runs the container. At the same time the go runtime * LXD relies on does its own multi-threading thing which we can't controll. The * fork()ing + threading then seems to mess with the locking states in these * time functions causing deadlocks.) * The current solution is to be good old unix people and use the Epoch as our * reference point and simply use the seconds and nanoseconds that have past * since then. This relies on clock_gettime() which is explicitly marked MT-Safe * with no restrictions! This way, anyone who is really strongly invested in * getting the actual time the log entry was created, can just convert it for * themselves. Our logging is mostly done for debugging purposes so don't try * to make it pretty. Pretty might cost you thread-safety. */ static int log_append_logfile(const struct lxc_log_appender *appender, struct lxc_log_event *event) { char buffer[LXC_LOG_BUFFER_SIZE]; char date_time[LXC_LOG_TIME_SIZE]; int n; ssize_t ret; int fd_to_use = -1; #ifndef NO_LXC_CONF if (!lxc_log_use_global_fd && current_config) fd_to_use = current_config->logfd; #endif if (fd_to_use == -1) fd_to_use = lxc_log_fd; if (fd_to_use == -1) return 0; if (lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp) < 0) return 0; n = snprintf(buffer, sizeof(buffer), "%15s %s %-8s %s - %s:%s:%d - ", log_prefix, date_time, lxc_log_priority_to_string(event->priority), event->category, event->locinfo->file, event->locinfo->func, event->locinfo->line); if (n < 0) return n; if ((size_t)n < (sizeof(buffer) - 1)) { ret = vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap); if (ret < 0) return 0; n += ret; } if ((size_t)n >= sizeof(buffer)) n = sizeof(buffer) - 1; buffer[n] = '\n'; again: ret = write(fd_to_use, buffer, n + 1); if (ret < 0 && errno == EINTR) goto again; return ret; } static struct lxc_log_appender log_appender_stderr = { .name = "stderr", .append = log_append_stderr, .next = NULL, }; static struct lxc_log_appender log_appender_logfile = { .name = "logfile", .append = log_append_logfile, .next = NULL, }; static struct lxc_log_category log_root = { .name = "root", .priority = LXC_LOG_LEVEL_ERROR, .appender = NULL, .parent = NULL, }; struct lxc_log_category lxc_log_category_lxc = { .name = "lxc", .priority = LXC_LOG_LEVEL_ERROR, .appender = &log_appender_logfile, .parent = &log_root }; /*---------------------------------------------------------------------------*/ static int build_dir(const char *name) { char *n = strdup(name); // because we'll be modifying it char *p, *e; int ret; if (!n) { ERROR("Out of memory while creating directory '%s'.", name); return -1; } e = &n[strlen(n)]; for (p = n+1; p < e; p++) { if (*p != '/') continue; *p = '\0'; if (access(n, F_OK)) { ret = lxc_unpriv(mkdir(n, 0755)); if (ret && errno != EEXIST) { SYSERROR("failed to create directory '%s'.", n); free(n); return -1; } } *p = '/'; } free(n); return 0; } /*---------------------------------------------------------------------------*/ static int log_open(const char *name) { int fd; int newfd; fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0666)); if (fd == -1) { ERROR("failed to open log file \"%s\" : %s", name, strerror(errno)); return -1; } if (fd > 2) return fd; newfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (newfd == -1) ERROR("failed to dup log fd %d : %s", fd, strerror(errno)); close(fd); return newfd; } /* * Build the path to the log file * @name : the name of the container * @lxcpath : the lxcpath to use as a basename or NULL to use LOGPATH * Returns malloced path on success, or NULL on failure */ static char *build_log_path(const char *name, const char *lxcpath) { char *p; int len, ret, use_dir; if (!name) return NULL; #if USE_CONFIGPATH_LOGS use_dir = 1; #else use_dir = 0; #endif /* * If USE_CONFIGPATH_LOGS is true or lxcpath is given, the resulting * path will be: * '$logpath' + '/' + '$name' + '/' + '$name' + '.log' + '\0' * * If USE_CONFIGPATH_LOGS is false the resulting path will be: * '$logpath' + '/' + '$name' + '.log' + '\0' */ len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */ if (lxcpath) use_dir = 1; else lxcpath = LOGPATH; if (use_dir) len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */ else len += strlen(lxcpath) + 1; p = malloc(len); if (!p) return p; if (use_dir) ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name); else ret = snprintf(p, len, "%s/%s.log", lxcpath, name); if (ret < 0 || ret >= len) { free(p); return NULL; } return p; } extern void lxc_log_close(void) { if (lxc_log_fd == -1) return; close(lxc_log_fd); lxc_log_fd = -1; free(log_fname); log_fname = NULL; } /* * This can be called: * 1. when a program calls lxc_log_init with no logfile parameter (in which * case the default is used). In this case lxc.logfile can override this. * 2. when a program calls lxc_log_init with a logfile parameter. In this * case we don't want lxc.logfile to override this. * 3. When a lxc.logfile entry is found in config file. */ static int __lxc_log_set_file(const char *fname, int create_dirs) { if (lxc_log_fd != -1) { // we are overriding the default. lxc_log_close(); } if (!fname) return -1; if (strlen(fname) == 0) { log_fname = NULL; return 0; } #if USE_CONFIGPATH_LOGS // we don't build_dir for the default if the default is // i.e. /var/lib/lxc/$container/$container.log if (create_dirs) #endif if (build_dir(fname)) { ERROR("failed to create dir for log file \"%s\" : %s", fname, strerror(errno)); return -1; } lxc_log_fd = log_open(fname); if (lxc_log_fd == -1) return -1; log_fname = strdup(fname); return 0; } static int _lxc_log_set_file(const char *name, const char *lxcpath, int create_dirs) { char *logfile; int ret; logfile = build_log_path(name, lxcpath); if (!logfile) { ERROR("could not build log path"); return -1; } ret = __lxc_log_set_file(logfile, create_dirs); free(logfile); return ret; } /* * lxc_log_init: * Called from lxc front-end programs (like lxc-create, lxc-start) to * initalize the log defaults. */ extern int lxc_log_init(struct lxc_log *log) { int lxc_priority = LXC_LOG_LEVEL_ERROR; int ret; if (lxc_log_fd != -1) { WARN("lxc_log_init called with log already initialized"); return 0; } if (log->level) lxc_priority = lxc_log_priority_to_int(log->level); if (!lxc_loglevel_specified) { lxc_log_category_lxc.priority = lxc_priority; lxc_loglevel_specified = 1; } if (!lxc_quiet_specified) { if (!log->quiet) lxc_log_category_lxc.appender->next = &log_appender_stderr; } if (log->prefix) lxc_log_set_prefix(log->prefix); if (log->file) { if (strcmp(log->file, "none") == 0) return 0; ret = __lxc_log_set_file(log->file, 1); lxc_log_use_global_fd = 1; } else { /* if no name was specified, there nothing to do */ if (!log->name) return 0; ret = -1; if (!log->lxcpath) log->lxcpath = LOGPATH; /* try LOGPATH if lxcpath is the default for the privileged containers */ if (!geteuid() && strcmp(LXCPATH, log->lxcpath) == 0) ret = _lxc_log_set_file(log->name, NULL, 0); /* try in lxcpath */ if (ret < 0) ret = _lxc_log_set_file(log->name, log->lxcpath, 1); /* try LOGPATH in case its writable by the caller */ if (ret < 0) ret = _lxc_log_set_file(log->name, NULL, 0); } /* * If !file, that is, if the user did not request this logpath, then * ignore failures and continue logging to console */ if (!log->file && ret != 0) { INFO("Ignoring failure to open default logfile."); ret = 0; } return ret; } /* * 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 *dest, int level) { if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET) { ERROR("invalid log priority %d", level); return -1; } *dest = level; return 0; } extern int lxc_log_get_level(void) { return lxc_log_category_lxc.priority; } extern bool lxc_log_has_valid_level(void) { int log_level = lxc_log_get_level(); if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET) return false; return true; } /* * 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 file if previously unset. */ extern int lxc_log_set_file(int *fd, const char *fname) { if (*fd != -1) { close(*fd); *fd = -1; } if (build_dir(fname)) { ERROR("failed to create dir for log file \"%s\" : %s", fname, strerror(errno)); return -1; } *fd = log_open(fname); if (*fd == -1) return -errno; return 0; } extern const char *lxc_log_get_file(void) { return log_fname; } extern void lxc_log_set_prefix(const char *prefix) { /* We don't care if thte prefix is truncated. */ (void)strlcpy(log_prefix, prefix, sizeof(log_prefix)); } extern const char *lxc_log_get_prefix(void) { return log_prefix; } extern void lxc_log_options_no_override() { lxc_quiet_specified = 1; lxc_loglevel_specified = 1; } lxc-2.0.11/src/lxc/criu.c0000644061062106075000000007357513435013473012021 00000000000000/* * lxc: linux Container library * * Copyright © 2014-2015 Canonical Ltd. * * Authors: * Tycho Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "cgroup.h" #include "conf.h" #include "commands.h" #include "criu.h" #include "log.h" #include "lxc.h" #include "lxclock.h" #include "network.h" #include "storage.h" #include "utils.h" #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #define CRIU_VERSION "2.0" #define CRIU_GITID_VERSION "2.0" #define CRIU_GITID_PATCHLEVEL 0 #define CRIU_IN_FLIGHT_SUPPORT "2.4" #define CRIU_EXTERNAL_NOT_VETH "2.8" lxc_log_define(lxc_criu, lxc); struct criu_opts { /* the thing to hook to stdout and stderr for logging */ int pipefd; /* The type of criu invocation, one of "dump" or "restore" */ char *action; /* the user-provided migrate options relevant to this action */ struct migrate_opts *user; /* The container to dump */ struct lxc_container *c; /* dump: stop the container or not after dumping? */ char tty_id[32]; /* the criu tty id for /dev/console, i.e. "tty[${rdev}:${dev}]" */ /* restore: the file to write the init process' pid into */ struct lxc_handler *handler; int console_fd; /* The path that is bind mounted from /dev/console, if any. We don't * want to use `--ext-mount-map auto`'s result here because the pts * device may have a different path (e.g. if the pty number is * different) on the target host. NULL if lxc.console = "none". */ char *console_name; /* The detected version of criu */ char *criu_version; }; static int load_tty_major_minor(char *directory, char *output, int len) { FILE *f; char path[PATH_MAX]; int ret; ret = snprintf(path, sizeof(path), "%s/tty.info", directory); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many chacters: %d", ret); return -1; } f = fopen(path, "r"); if (!f) { /* This means we're coming from a liblxc which didn't export * the tty info. In this case they had to have lxc.console = * none, so there's no problem restoring. */ if (errno == ENOENT) return 0; SYSERROR("couldn't open %s", path); return -1; } if (!fgets(output, len, f)) { fclose(f); SYSERROR("couldn't read %s", path); return -1; } fclose(f); return 0; } static int cmp_version(const char *v1, const char *v2) { int ret; int oct_v1[3], oct_v2[3]; memset(oct_v1, -1, sizeof(oct_v1)); memset(oct_v2, -1, sizeof(oct_v2)); ret = sscanf(v1, "%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2]); if (ret < 1) return -1; ret = sscanf(v2, "%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2]); if (ret < 1) return -1; /* Major version is greater. */ if (oct_v1[0] > oct_v2[0]) return 1; if (oct_v1[0] < oct_v2[0]) return -1; /* Minor number is greater.*/ if (oct_v1[1] > oct_v2[1]) return 1; if (oct_v1[1] < oct_v2[1]) return -1; /* Patch number is greater. */ if (oct_v1[2] > oct_v2[2]) return 1; /* Patch numbers are equal. */ if (oct_v1[2] == oct_v2[2]) return 0; return -1; } static void exec_criu(struct criu_opts *opts) { char **argv, log[PATH_MAX]; int static_args = 23, argc = 0, i, ret; int netnr = 0; struct lxc_list *it; FILE *mnts; struct mntent mntent; char buf[4096], tty_info[32]; size_t pos; /* If we are currently in a cgroup /foo/bar, and the container is in a * cgroup /lxc/foo, lxcfs will give us an ENOENT if some task in the * container has an open fd that points to one of the cgroup files * (systemd always opens its "root" cgroup). So, let's escape to the * /actual/ root cgroup so that lxcfs thinks criu has enough rights to * see all cgroups. */ if (!cgroup_escape()) { ERROR("failed to escape cgroups"); return; } /* The command line always looks like: * criu $(action) --tcp-established --file-locks --link-remap \ * --manage-cgroups=full --action-script foo.sh -D $(directory) \ * -o $(directory)/$(action).log --ext-mount-map auto * --enable-external-sharing --enable-external-masters * --enable-fs hugetlbfs --enable-fs tracefs --ext-mount-map console:/dev/pts/n * +1 for final NULL */ if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) { /* -t pid --freeze-cgroup /lxc/ct */ static_args += 4; /* --prev-images-dir */ if (opts->user->predump_dir) static_args += 2; /* --page-server --address
--port */ if (opts->user->pageserver_address && opts->user->pageserver_port) static_args += 5; /* --leave-running (only for final dump) */ if (strcmp(opts->action, "dump") == 0 && !opts->user->stop) static_args++; /* --external tty[88,4] */ if (opts->tty_id[0]) static_args += 2; /* --force-irmap */ if (!opts->user->preserves_inodes) static_args++; /* --ghost-limit 1024 */ if (opts->user->ghost_limit) static_args += 2; } else if (strcmp(opts->action, "restore") == 0) { /* --root $(lxc_mount_point) --restore-detached * --restore-sibling * --lsm-profile apparmor:whatever */ static_args += 6; tty_info[0] = 0; if (load_tty_major_minor(opts->user->directory, tty_info, sizeof(tty_info))) return; /* --inherit-fd fd[%d]:tty[%s] */ if (tty_info[0]) static_args += 2; } else { return; } if (cgroup_num_hierarchies() > 0) static_args += 2 * cgroup_num_hierarchies(); if (opts->user->verbose) static_args++; if (opts->user->action_script) static_args += 2; static_args += 2 * lxc_list_len(&opts->c->lxc_conf->mount_list); ret = snprintf(log, PATH_MAX, "%s/%s.log", opts->user->directory, opts->action); if (ret < 0 || ret >= PATH_MAX) { ERROR("logfile name too long"); return; } argv = malloc(static_args * sizeof(*argv)); if (!argv) return; memset(argv, 0, static_args * sizeof(*argv)); #define DECLARE_ARG(arg) \ do { \ if (arg == NULL) { \ ERROR("Got NULL argument for criu"); \ goto err; \ } \ argv[argc++] = strdup(arg); \ if (!argv[argc-1]) \ goto err; \ } while (0) argv[argc++] = on_path("criu", NULL); if (!argv[argc-1]) { ERROR("Couldn't find criu binary"); goto err; } DECLARE_ARG(opts->action); DECLARE_ARG("--tcp-established"); DECLARE_ARG("--file-locks"); DECLARE_ARG("--link-remap"); DECLARE_ARG("--manage-cgroups=full"); DECLARE_ARG("--ext-mount-map"); DECLARE_ARG("auto"); DECLARE_ARG("--enable-external-sharing"); DECLARE_ARG("--enable-external-masters"); DECLARE_ARG("--enable-fs"); DECLARE_ARG("hugetlbfs"); DECLARE_ARG("--enable-fs"); DECLARE_ARG("tracefs"); DECLARE_ARG("-D"); DECLARE_ARG(opts->user->directory); DECLARE_ARG("-o"); DECLARE_ARG(log); for (i = 0; i < cgroup_num_hierarchies(); i++) { char **controllers = NULL, *fullname; char *path, *tmp; if (!cgroup_get_hierarchies(i, &controllers)) { ERROR("failed to get hierarchy %d", i); goto err; } /* if we are in a dump, we have to ask the monitor process what * the right cgroup is. if this is a restore, we can just use * the handler the restore task created. */ if (!strcmp(opts->action, "dump") || !strcmp(opts->action, "pre-dump")) { path = lxc_cmd_get_cgroup_path(opts->c->name, opts->c->config_path, controllers[0]); if (!path) { ERROR("failed to get cgroup path for %s", controllers[0]); goto err; } } else { const char *p; p = cgroup_get_cgroup(opts->handler, controllers[0]); if (!p) { ERROR("failed to get cgroup path for %s", controllers[0]); goto err; } path = strdup(p); if (!path) { ERROR("strdup failed"); goto err; } } tmp = lxc_deslashify(path); if (!tmp) { ERROR("Failed to remove extraneous slashes from \"%s\"", path); free(path); goto err; } free(path); path = tmp; fullname = lxc_string_join(",", (const char **) controllers, false); if (!fullname) { ERROR("failed to join controllers"); free(path); goto err; } ret = sprintf(buf, "%s:%s", fullname, path); free(path); free(fullname); if (ret < 0 || ret >= sizeof(buf)) { ERROR("sprintf of cgroup root arg failed"); goto err; } DECLARE_ARG("--cgroup-root"); DECLARE_ARG(buf); } if (opts->user->verbose) DECLARE_ARG("-vvvvvv"); if (opts->user->action_script) { DECLARE_ARG("--action-script"); DECLARE_ARG(opts->user->action_script); } mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list); if (!mnts) goto err; while (getmntent_r(mnts, &mntent, buf, sizeof(buf))) { char *fmt, *key, *val, *mntdata; char arg[2 * PATH_MAX + 2]; unsigned long flags; if (parse_mntopts(mntent.mnt_opts, &flags, &mntdata) < 0) goto err; free(mntdata); /* only add --ext-mount-map for actual bind mounts */ if (!(flags & MS_BIND)) continue; if (strcmp(opts->action, "dump") == 0) { fmt = "/%s:%s"; key = mntent.mnt_dir; val = mntent.mnt_dir; } else { fmt = "%s:%s"; key = mntent.mnt_dir; val = mntent.mnt_fsname; } ret = snprintf(arg, sizeof(arg), fmt, key, val); if (ret < 0 || ret >= sizeof(arg)) { fclose(mnts); ERROR("snprintf failed"); goto err; } DECLARE_ARG("--ext-mount-map"); DECLARE_ARG(arg); } fclose(mnts); if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) { char pid[32], *freezer_relative; if (sprintf(pid, "%d", opts->c->init_pid(opts->c)) < 0) goto err; DECLARE_ARG("-t"); DECLARE_ARG(pid); freezer_relative = lxc_cmd_get_cgroup_path(opts->c->name, opts->c->config_path, "freezer"); if (!freezer_relative) { ERROR("failed getting freezer path"); goto err; } ret = snprintf(log, sizeof(log), "/sys/fs/cgroup/freezer/%s", freezer_relative); if (ret < 0 || ret >= sizeof(log)) goto err; if (!opts->user->disable_skip_in_flight && strcmp(opts->criu_version, CRIU_IN_FLIGHT_SUPPORT) >= 0) DECLARE_ARG("--skip-in-flight"); DECLARE_ARG("--freeze-cgroup"); DECLARE_ARG(log); if (opts->tty_id[0]) { DECLARE_ARG("--ext-mount-map"); DECLARE_ARG("/dev/console:console"); DECLARE_ARG("--external"); DECLARE_ARG(opts->tty_id); } if (opts->user->predump_dir) { DECLARE_ARG("--prev-images-dir"); DECLARE_ARG(opts->user->predump_dir); } if (opts->user->pageserver_address && opts->user->pageserver_port) { DECLARE_ARG("--page-server"); DECLARE_ARG("--address"); DECLARE_ARG(opts->user->pageserver_address); DECLARE_ARG("--port"); DECLARE_ARG(opts->user->pageserver_port); } if (!opts->user->preserves_inodes) DECLARE_ARG("--force-irmap"); if (opts->user->ghost_limit) { char ghost_limit[32]; ret = sprintf(ghost_limit, "%"PRIu64, opts->user->ghost_limit); if (ret < 0 || ret >= sizeof(ghost_limit)) { ERROR("failed to print ghost limit %"PRIu64, opts->user->ghost_limit); goto err; } DECLARE_ARG("--ghost-limit"); DECLARE_ARG(ghost_limit); } /* only for final dump */ if (strcmp(opts->action, "dump") == 0 && !opts->user->stop) DECLARE_ARG("--leave-running"); } else if (strcmp(opts->action, "restore") == 0) { void *m; int additional; struct lxc_conf *lxc_conf = opts->c->lxc_conf; DECLARE_ARG("--root"); DECLARE_ARG(opts->c->lxc_conf->rootfs.mount); DECLARE_ARG("--restore-detached"); DECLARE_ARG("--restore-sibling"); if (tty_info[0]) { if (opts->console_fd < 0) { ERROR("lxc.console configured on source host but not target"); goto err; } ret = snprintf(buf, sizeof(buf), "fd[%d]:%s", opts->console_fd, tty_info); if (ret < 0 || ret >= sizeof(buf)) goto err; DECLARE_ARG("--inherit-fd"); DECLARE_ARG(buf); } if (opts->console_name) { if (snprintf(buf, sizeof(buf), "console:%s", opts->console_name) < 0) { SYSERROR("sprintf'd too many bytes"); } DECLARE_ARG("--ext-mount-map"); DECLARE_ARG(buf); } if (lxc_conf->lsm_aa_profile || lxc_conf->lsm_se_context) { if (lxc_conf->lsm_aa_profile) ret = snprintf(buf, sizeof(buf), "apparmor:%s", lxc_conf->lsm_aa_profile); else ret = snprintf(buf, sizeof(buf), "selinux:%s", lxc_conf->lsm_se_context); if (ret < 0 || ret >= sizeof(buf)) goto err; DECLARE_ARG("--lsm-profile"); DECLARE_ARG(buf); } additional = lxc_list_len(&opts->c->lxc_conf->network) * 2; m = realloc(argv, (argc + additional + 1) * sizeof(*argv)); if (!m) goto err; argv = m; lxc_list_for_each(it, &opts->c->lxc_conf->network) { size_t retlen; char eth[128], *veth; char *fmt; struct lxc_netdev *n = it->elem; bool external_not_veth; if (cmp_version(opts->criu_version, CRIU_EXTERNAL_NOT_VETH) >= 0) { /* Since criu version 2.8 the usage of --veth-pair * has been deprecated: * git tag --contains f2037e6d3445fc400 * v2.8 */ external_not_veth = true; } else { external_not_veth = false; } if (n->name[0] != '\0') { retlen = strlcpy(eth, n->name, sizeof(eth)); if (retlen >= sizeof(eth)) goto err; } else { ret = snprintf(eth, sizeof(eth), "eth%d", netnr); if (ret < 0 || ret >= sizeof(eth)) goto err; } switch (n->type) { case LXC_NET_VETH: veth = n->priv.veth_attr.pair; if (veth[0] == '\0') veth = n->priv.veth_attr.veth1; if (n->link[0] != '\0') { if (external_not_veth) fmt = "veth[%s]:%s@%s"; else fmt = "%s=%s@%s"; ret = snprintf(buf, sizeof(buf), fmt, eth, veth, n->link); } else { if (external_not_veth) fmt = "veth[%s]:%s"; else fmt = "%s=%s"; ret = snprintf(buf, sizeof(buf), fmt, eth, veth); } if (ret < 0 || ret >= sizeof(buf)) goto err; break; case LXC_NET_MACVLAN: if (n->link[0] == '\0') { ERROR("no host interface for macvlan %s", n->name); goto err; } ret = snprintf(buf, sizeof(buf), "macvlan[%s]:%s", eth, n->link); if (ret < 0 || ret >= sizeof(buf)) goto err; break; case LXC_NET_NONE: case LXC_NET_EMPTY: break; default: /* we have screened for this earlier... */ ERROR("unexpected network type %d", n->type); goto err; } if (external_not_veth) DECLARE_ARG("--external"); else DECLARE_ARG("--veth-pair"); DECLARE_ARG(buf); netnr++; } } argv[argc] = NULL; buf[0] = 0; pos = 0; for (i = 0; argv[i]; i++) { ret = snprintf(buf + pos, sizeof(buf) - pos, "%s ", argv[i]); if (ret < 0 || ret >= sizeof(buf) - pos) goto err; else pos += ret; } INFO("execing: %s", buf); /* before criu inits its log, it sometimes prints things to stdout/err; * let's be sure we capture that. */ if (dup2(opts->pipefd, STDOUT_FILENO) < 0) { SYSERROR("dup2 stdout failed"); goto err; } if (dup2(opts->pipefd, STDERR_FILENO) < 0) { SYSERROR("dup2 stderr failed"); goto err; } close(opts->pipefd); #undef DECLARE_ARG execv(argv[0], argv); err: for (i = 0; argv[i]; i++) free(argv[i]); free(argv); } /* * Check to see if the criu version is recent enough for all the features we * use. This version allows either CRIU_VERSION or (CRIU_GITID_VERSION and * CRIU_GITID_PATCHLEVEL) to work, enabling users building from git to c/r * things potentially before a version is released with a particular feature. * * The intent is that when criu development slows down, we can drop this, but * for now we shouldn't attempt to c/r with versions that we know won't work. * * Note: If version != NULL criu_version() stores the detected criu version in * version. Allocates memory for version which must be freed by caller. */ static bool criu_version_ok(char **version) { int pipes[2]; pid_t pid; if (pipe(pipes) < 0) { SYSERROR("pipe() failed"); return false; } pid = fork(); if (pid < 0) { SYSERROR("fork() failed"); return false; } if (pid == 0) { char *args[] = { "criu", "--version", NULL }; char *path; close(pipes[0]); close(STDERR_FILENO); if (dup2(pipes[1], STDOUT_FILENO) < 0) _exit(EXIT_FAILURE); path = on_path("criu", NULL); if (!path) _exit(EXIT_FAILURE); execv(path, args); _exit(EXIT_FAILURE); } else { FILE *f; char *tmp; int patch; close(pipes[1]); if (wait_for_pid(pid) < 0) { close(pipes[0]); SYSERROR("execing criu failed, is it installed?"); return false; } f = fdopen(pipes[0], "r"); if (!f) { close(pipes[0]); return false; } tmp = malloc(1024); if (!tmp) { fclose(f); return false; } if (fscanf(f, "Version: %1023[^\n]s", tmp) != 1) goto version_error; if (fgetc(f) != '\n') goto version_error; if (strcmp(tmp, CRIU_VERSION) >= 0) goto version_match; if (fscanf(f, "GitID: v%1023[^-]s", tmp) != 1) goto version_error; if (fgetc(f) != '-') goto version_error; if (fscanf(f, "%d", &patch) != 1) goto version_error; if (strcmp(tmp, CRIU_GITID_VERSION) < 0) goto version_error; if (patch < CRIU_GITID_PATCHLEVEL) goto version_error; version_match: fclose(f); if (!version) free(tmp); else *version = tmp; return true; version_error: fclose(f); free(tmp); ERROR("must have criu " CRIU_VERSION " or greater to checkpoint/restore"); return false; } } /* Check and make sure the container has a configuration that we know CRIU can * dump. */ static bool criu_ok(struct lxc_container *c, char **criu_version) { struct lxc_list *it; if (geteuid()) { ERROR("Must be root to checkpoint"); return false; } if (!criu_version_ok(criu_version)) return false; /* We only know how to restore containers with veth networks. */ lxc_list_for_each(it, &c->lxc_conf->network) { struct lxc_netdev *n = it->elem; switch(n->type) { case LXC_NET_VETH: case LXC_NET_NONE: case LXC_NET_EMPTY: case LXC_NET_MACVLAN: break; default: ERROR("Found un-dumpable network: %s (%s)", lxc_net_type_to_str(n->type), n->name); if (criu_version) { free(*criu_version); *criu_version = NULL; } return false; } } return true; } static bool restore_net_info(struct lxc_container *c) { int ret; struct lxc_list *it; bool has_error = true; if (container_mem_lock(c)) return false; lxc_list_for_each(it, &c->lxc_conf->network) { struct lxc_netdev *netdev = it->elem; char template[IFNAMSIZ]; if (netdev->type != LXC_NET_VETH) continue; ret = snprintf(template, sizeof(template), "vethXXXXXX"); if (ret < 0 || ret >= sizeof(template)) goto out_unlock; if (netdev->priv.veth_attr.pair[0] == '\0' && netdev->priv.veth_attr.veth1[0] == '\0') { if (!lxc_mkifname(template)) goto out_unlock; (void)strlcpy(netdev->priv.veth_attr.veth1, template, IFNAMSIZ); } } has_error = false; out_unlock: container_mem_unlock(c); return !has_error; } /* do_restore never returns, the calling process is used as the monitor process. * do_restore calls _exit() if it fails. */ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_opts *opts, char *criu_version) { int fd, ret; pid_t pid; struct lxc_handler *handler; int status = 0; int pipes[2] = {-1, -1}; /* Try to detach from the current controlling tty if it exists. * Othwerise, lxc_init (via lxc_console) will attach the container's * console output to the current tty, which is probably not what any * library user wants, and if they do, they can just manually configure * it :) */ fd = open("/dev/tty", O_RDWR); if (fd >= 0) { if (ioctl(fd, TIOCNOTTY, NULL) < 0) SYSERROR("couldn't detach from tty"); close(fd); } handler = lxc_init_handler(c->name, c->lxc_conf, c->config_path, false); if (!handler) goto out; if (lxc_init(c->name, handler) < 0) goto out; if (!cgroup_init(handler)) { ERROR("failed initing cgroups"); goto out_fini_handler; } if (!cgroup_create(handler)) { ERROR("failed creating groups"); goto out_fini_handler; } if (!restore_net_info(c)) { ERROR("failed restoring network info"); goto out_fini_handler; } ret = resolve_clone_flags(handler); if (ret < 0) { ERROR("%s - Unsupported clone flag specified", strerror(errno)); goto out_fini_handler; } if (pipe2(pipes, O_CLOEXEC) < 0) { SYSERROR("pipe() failed"); goto out_fini_handler; } pid = fork(); if (pid < 0) goto out_fini_handler; if (pid == 0) { struct criu_opts os; struct lxc_rootfs *rootfs; int flags; close(status_pipe); status_pipe = -1; close(pipes[0]); pipes[0] = -1; if (unshare(CLONE_NEWNS)) goto out_fini_handler; /* CRIU needs the lxc root bind mounted so that it is the root of some * mount. */ rootfs = &c->lxc_conf->rootfs; if (rootfs_is_blockdev(c->lxc_conf)) { if (do_rootfs_setup(c->lxc_conf, c->name, c->config_path) < 0) goto out_fini_handler; } else { if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST) goto out_fini_handler; if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { SYSERROR("remount / to private failed"); goto out_fini_handler; } if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) { rmdir(rootfs->mount); goto out_fini_handler; } } os.pipefd = pipes[1]; os.action = "restore"; os.user = opts; os.c = c; os.console_fd = c->lxc_conf->console.slave; os.criu_version = criu_version; os.handler = handler; if (os.console_fd >= 0) { /* Twiddle the FD_CLOEXEC bit. We want to pass this FD to criu * via --inherit-fd, so we don't want it to close. */ flags = fcntl(os.console_fd, F_GETFD); if (flags < 0) { SYSERROR("F_GETFD failed: %d", os.console_fd); goto out_fini_handler; } flags &= ~FD_CLOEXEC; if (fcntl(os.console_fd, F_SETFD, flags) < 0) { SYSERROR("F_SETFD failed"); goto out_fini_handler; } } os.console_name = c->lxc_conf->console.name; /* exec_criu() returning is an error */ exec_criu(&os); umount(rootfs->mount); rmdir(rootfs->mount); goto out_fini_handler; } else { int ret; char title[2048]; close(pipes[1]); pipes[1] = -1; pid_t w = waitpid(pid, &status, 0); if (w == -1) { SYSERROR("waitpid"); goto out_fini_handler; } if (WIFEXITED(status)) { char buf[4096]; if (WEXITSTATUS(status)) { int n; n = lxc_read_nointr(pipes[0], buf, sizeof(buf)); if (n < 0) { SYSERROR("failed reading from criu stderr"); goto out_fini_handler; } if (n == sizeof(buf)) n--; buf[n] = 0; ERROR("criu process exited %d, output:\n%s", WEXITSTATUS(status), buf); goto out_fini_handler; } else { ret = snprintf(buf, sizeof(buf), "/proc/self/task/%lu/children", (unsigned long)syscall(__NR_gettid)); if (ret < 0 || ret >= sizeof(buf)) { ERROR("snprintf'd too many characters: %d", ret); goto out_fini_handler; } FILE *f = fopen(buf, "r"); if (!f) { SYSERROR("couldn't read restore's children file %s", buf); goto out_fini_handler; } ret = fscanf(f, "%d", (int*) &handler->pid); fclose(f); if (ret != 1) { ERROR("reading restore pid failed"); goto out_fini_handler; } if (lxc_set_state(c->name, handler, RUNNING)) { ERROR("error setting running state after restore"); goto out_fini_handler; } } } else { ERROR("CRIU was killed with signal %d", WTERMSIG(status)); goto out_fini_handler; } close(pipes[0]); ret = lxc_write_nointr(status_pipe, &status, sizeof(status)); close(status_pipe); status_pipe = -1; if (sizeof(status) != ret) { SYSERROR("failed to write all of status"); goto out_fini_handler; } /* * See comment in lxcapi_start; we don't care if these * fail because it's just a beauty thing. We just * assign the return here to silence potential. */ ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); if (ret < 0 || (size_t)ret >= sizeof(title)) INFO("Setting truncated process name"); ret = setproctitle(title); if (ret < 0) INFO("Failed to set process name"); ret = lxc_poll(c->name, handler); if (ret) lxc_abort(c->name, handler); lxc_fini(c->name, handler); _exit(ret); } out_fini_handler: if (pipes[0] >= 0) close(pipes[0]); if (pipes[1] >= 0) close(pipes[1]); lxc_fini(c->name, handler); out: if (status_pipe >= 0) { /* ensure getting here was a failure, e.g. if we failed to * parse the child pid or something, even after a successful * restore */ if (!status) status = 1; if (lxc_write_nointr(status_pipe, &status, sizeof(status)) != sizeof(status)) SYSERROR("writing status failed"); close(status_pipe); } _exit(EXIT_FAILURE); } static int save_tty_major_minor(char *directory, struct lxc_container *c, char *tty_id, int len) { FILE *f; char path[PATH_MAX]; int ret; struct stat sb; if (c->lxc_conf->console.path && !strcmp(c->lxc_conf->console.path, "none")) { tty_id[0] = 0; return 0; } ret = snprintf(path, sizeof(path), "/proc/%d/root/dev/console", c->init_pid(c)); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many chacters: %d", ret); return -1; } ret = stat(path, &sb); if (ret < 0) { SYSERROR("stat of %s failed", path); return -1; } ret = snprintf(path, sizeof(path), "%s/tty.info", directory); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } ret = snprintf(tty_id, len, "tty[%llx:%llx]", (long long unsigned) sb.st_rdev, (long long unsigned) sb.st_dev); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } f = fopen(path, "w"); if (!f) { SYSERROR("failed to open %s", path); return -1; } ret = fprintf(f, "%s", tty_id); fclose(f); if (ret < 0) SYSERROR("failed to write to %s", path); return ret; } /* do one of either predump or a regular dump */ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *opts) { int ret; pid_t pid; int criuout[2]; char *criu_version = NULL; if (!criu_ok(c, &criu_version)) return false; ret = pipe(criuout); if (ret < 0) { SYSERROR("pipe() failed"); free(criu_version); return false; } if (mkdir_p(opts->directory, 0700) < 0) goto fail; pid = fork(); if (pid < 0) { SYSERROR("fork failed"); goto fail; } if (pid == 0) { struct criu_opts os; struct lxc_handler h; close(criuout[0]); lxc_zero_handler(&h); h.name = c->name; if (!cgroup_init(&h)) { ERROR("failed to cgroup_init()"); _exit(EXIT_FAILURE); } os.pipefd = criuout[1]; os.action = mode; os.user = opts; os.c = c; os.console_name = c->lxc_conf->console.path; os.criu_version = criu_version; ret = save_tty_major_minor(opts->directory, c, os.tty_id, sizeof(os.tty_id)); if (ret < 0) { free(criu_version); _exit(EXIT_FAILURE); } /* exec_criu() returning is an error */ exec_criu(&os); free(criu_version); _exit(EXIT_FAILURE); } else { int status; ssize_t n; char buf[4096]; bool ret; close(criuout[1]); pid_t w = waitpid(pid, &status, 0); if (w == -1) { SYSERROR("waitpid"); close(criuout[0]); free(criu_version); return false; } n = lxc_read_nointr(criuout[0], buf, sizeof(buf)); close(criuout[0]); if (n < 0) { SYSERROR("read"); n = 0; } if (n == sizeof(buf)) buf[n-1] = 0; else buf[n] = 0; if (WIFEXITED(status)) { if (WEXITSTATUS(status)) { ERROR("dump failed with %d", WEXITSTATUS(status)); ret = false; } else { ret = true; } } else if (WIFSIGNALED(status)) { ERROR("dump signaled with %d", WTERMSIG(status)); ret = false; } else { ERROR("unknown dump exit %d", status); ret = false; } if (!ret) ERROR("criu output: %s", buf); free(criu_version); return ret; } fail: close(criuout[0]); close(criuout[1]); rmdir(opts->directory); free(criu_version); return false; } bool __criu_pre_dump(struct lxc_container *c, struct migrate_opts *opts) { return do_dump(c, "pre-dump", opts); } bool __criu_dump(struct lxc_container *c, struct migrate_opts *opts) { char path[PATH_MAX]; int ret; ret = snprintf(path, sizeof(path), "%s/inventory.img", opts->directory); if (ret < 0 || ret >= sizeof(path)) return false; if (access(path, F_OK) == 0) { ERROR("please use a fresh directory for the dump directory"); return false; } return do_dump(c, "dump", opts); } bool __criu_restore(struct lxc_container *c, struct migrate_opts *opts) { pid_t pid; int status, nread; int pipefd[2]; char *criu_version = NULL; if (geteuid()) { ERROR("Must be root to restore"); return false; } if (pipe(pipefd)) { ERROR("failed to create pipe"); return false; } if (!criu_ok(c, &criu_version)) { close(pipefd[0]); close(pipefd[1]); return false; } pid = fork(); if (pid < 0) { close(pipefd[0]); close(pipefd[1]); free(criu_version); return false; } if (pid == 0) { close(pipefd[0]); // this never returns do_restore(c, pipefd[1], opts, criu_version); } close(pipefd[1]); free(criu_version); nread = lxc_read_nointr(pipefd[0], &status, sizeof(status)); close(pipefd[0]); if (sizeof(status) != nread) { ERROR("reading status from pipe failed"); goto err_wait; } // If the criu process was killed or exited nonzero, wait() for the // handler, since the restore process died. Otherwise, we don't need to // wait, since the child becomes the monitor process. if (!WIFEXITED(status) || WEXITSTATUS(status)) goto err_wait; return true; err_wait: if (wait_for_pid(pid)) ERROR("restore process died"); return false; } lxc-2.0.11/src/lxc/confile.c0000644061062106075000000031413213435013473012461 00000000000000/* * lxc: linux Container library * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "storage/storage_utils.h" #include "parse.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "utils.h" #include "log.h" #include "conf.h" #include "network.h" #include "lxcseccomp.h" #include "storage.h" #if HAVE_SYS_PERSONALITY_H #include #endif lxc_log_define(lxc_confile, lxc); static int set_config_personality(const char *, const char *, struct lxc_conf *, void *); static int get_config_personality(const char *, char *, int, struct lxc_conf *); static int clr_config_personality(const char *, struct lxc_conf *, void *); static int set_config_pts(const char *, const char *, struct lxc_conf *, void *); static int get_config_pts(const char *, char *, int, struct lxc_conf *); static int clr_config_pts(const char *, struct lxc_conf *, void *); static int set_config_tty(const char *, const char *, struct lxc_conf *, void *); static int get_config_tty(const char *, char *, int, struct lxc_conf *); static int clr_config_tty(const char *, struct lxc_conf *, void *); static int set_config_ttydir(const char *, const char *, struct lxc_conf *, void *); static int get_config_ttydir(const char *, char *, int, struct lxc_conf *); static int clr_config_ttydir(const char *, struct lxc_conf *, void *); static int set_config_kmsg(const char *, const char *, struct lxc_conf *, void *); static int get_config_kmsg(const char *, char *, int, struct lxc_conf *); static int clr_config_kmsg(const char *, struct lxc_conf *, void *); static int set_config_lsm_aa_profile(const char *, const char *, struct lxc_conf *, void *); static int get_config_lsm_aa_profile(const char *, char *, int, struct lxc_conf *); static int clr_config_lsm_aa_profile(const char *, struct lxc_conf *, void *); static int set_config_lsm_aa_incomplete(const char *, const char *, struct lxc_conf *, void *); static int get_config_lsm_aa_incomplete(const char *, char *, int, struct lxc_conf *); static int clr_config_lsm_aa_incomplete(const char *, struct lxc_conf *, void *); static int set_config_lsm_se_context(const char *, const char *, struct lxc_conf *, void *); static int get_config_lsm_se_context(const char *, char *, int, struct lxc_conf *); static int clr_config_lsm_se_context(const char *, struct lxc_conf *, void *); static int set_config_cgroup(const char *, const char *, struct lxc_conf *, void *); static int get_config_cgroup(const char *, char *, int, struct lxc_conf *); static int clr_config_cgroup(const char *, struct lxc_conf *, void *); static int set_config_idmaps(const char *, const char *, struct lxc_conf *, void *); static int get_config_idmaps(const char *, char *, int, struct lxc_conf *); static int clr_config_idmaps(const char *, struct lxc_conf *, void *); static int set_config_loglevel(const char *, const char *, struct lxc_conf *, void *); static int get_config_loglevel(const char *, char *, int, struct lxc_conf *); static int clr_config_loglevel(const char *, struct lxc_conf *, void *); static int set_config_logfile(const char *, const char *, struct lxc_conf *, void *); static int get_config_logfile(const char *, char *, int, struct lxc_conf *); static int clr_config_logfile(const char *, struct lxc_conf *, void *); static int set_config_mount(const char *, const char *, struct lxc_conf *, void *); static int get_config_mount(const char *, char *, int, struct lxc_conf *); static int clr_config_mount(const char *, struct lxc_conf *, void *); static int set_config_mount_auto(const char *, const char *, struct lxc_conf *, void *); static int get_config_mount_auto(const char *, char *, int, struct lxc_conf *); static int clr_config_mount_auto(const char *, struct lxc_conf *, void *); static int set_config_fstab(const char *, const char *, struct lxc_conf *, void *); static int get_config_fstab(const char *, char *, int, struct lxc_conf *); static int clr_config_fstab(const char *, struct lxc_conf *, void *); static int set_config_rootfs_mount(const char *, const char *, struct lxc_conf *, void *); static int get_config_rootfs_mount(const char *, char *, int, struct lxc_conf *); static int clr_config_rootfs_mount(const char *, struct lxc_conf *, void *); static int set_config_rootfs_options(const char *, const char *, struct lxc_conf *, void *); static int get_config_rootfs_options(const char *, char *, int, struct lxc_conf *); static int clr_config_rootfs_options(const char *, struct lxc_conf *, void *); static int set_config_rootfs_backend(const char *, const char *, struct lxc_conf *, void *); static int get_config_rootfs_backend(const char *, char *, int, struct lxc_conf *); static int clr_config_rootfs_backend(const char *, struct lxc_conf *, void *); static int set_config_rootfs(const char *, const char *, struct lxc_conf *, void *); static int get_config_rootfs(const char *, char *, int, struct lxc_conf *); static int clr_config_rootfs(const char *, struct lxc_conf *, void *); static int set_config_pivotdir(const char *, const char *, struct lxc_conf *, void *); static int get_config_pivotdir(const char *, char *, int, struct lxc_conf *); static int clr_config_pivotdir(const char *, struct lxc_conf *, void *); static int set_config_utsname(const char *, const char *, struct lxc_conf *, void *); static int get_config_utsname(const char *, char *, int, struct lxc_conf *); static int clr_config_utsname(const char *, struct lxc_conf *, void *); static int set_config_hooks(const char *, const char *, struct lxc_conf *, void *); static int get_config_hooks(const char *, char *, int, struct lxc_conf *); static int clr_config_hooks(const char *, struct lxc_conf *, void *); static int set_config_network_type(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_flags(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_link(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_name(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_veth_pair(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_macvlan_mode(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_hwaddr(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_vlan_id(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_mtu(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_ipv4(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_ipv4_gateway(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_script_up(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_script_down(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_ipv6(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_ipv6_gateway(const char *, const char *, struct lxc_conf *, void *); static int set_config_network_nic(const char *, const char *, struct lxc_conf *, void *); static int get_config_network_item(const char *, char *, int, struct lxc_conf *); static int clr_config_network_item(const char *, struct lxc_conf *, void *); static int set_config_network(const char *, const char *, struct lxc_conf *, void *); static int get_config_network(const char *, char *, int, struct lxc_conf *); static int clr_config_network(const char *, struct lxc_conf *, void *); static int set_config_cap_drop(const char *, const char *, struct lxc_conf *, void *); static int get_config_cap_drop(const char *, char *, int, struct lxc_conf *); static int clr_config_cap_drop(const char *, struct lxc_conf *, void *); static int set_config_cap_keep(const char *, const char *, struct lxc_conf *, void *); static int get_config_cap_keep(const char *, char *, int, struct lxc_conf *); static int clr_config_cap_keep(const char *, struct lxc_conf *, void *); static int set_config_console_logfile(const char *, const char *, struct lxc_conf *, void *); static int get_config_console_logfile(const char *, char *, int, struct lxc_conf *); static int clr_config_console_logfile(const char *, struct lxc_conf *, void *); static int set_config_console(const char *, const char *, struct lxc_conf *, void *); static int get_config_console(const char *, char *, int, struct lxc_conf *); static int clr_config_console(const char *, struct lxc_conf *, void *); static int set_config_seccomp(const char *, const char *, struct lxc_conf *, void *); static int get_config_seccomp(const char *, char *, int, struct lxc_conf *); static int clr_config_seccomp(const char *, struct lxc_conf *, void *); static int set_config_includefiles(const char *, const char *, struct lxc_conf *, void *); static int get_config_includefiles(const char *, char *, int, struct lxc_conf *); static int clr_config_includefiles(const char *, struct lxc_conf *, void *); static int set_config_autodev(const char *, const char *, struct lxc_conf *, void *); static int get_config_autodev(const char *, char *, int, struct lxc_conf *); static int clr_config_autodev(const char *, struct lxc_conf *, void *); static int set_config_haltsignal(const char *, const char *, struct lxc_conf *, void *); static int get_config_haltsignal(const char *, char *, int, struct lxc_conf *); static int clr_config_haltsignal(const char *, struct lxc_conf *, void *); static int set_config_rebootsignal(const char *, const char *, struct lxc_conf *, void *); static int get_config_rebootsignal(const char *, char *, int, struct lxc_conf *); static int clr_config_rebootsignal(const char *, struct lxc_conf *, void *); static int set_config_stopsignal(const char *, const char *, struct lxc_conf *, void *); static int get_config_stopsignal(const char *, char *, int, struct lxc_conf *); static int clr_config_stopsignal(const char *, struct lxc_conf *, void *); static int set_config_start(const char *, const char *, struct lxc_conf *, void *); static int get_config_start(const char *, char *, int, struct lxc_conf *); static int clr_config_start(const char *, struct lxc_conf *, void *); static int set_config_monitor(const char *, const char *, struct lxc_conf *, void *); static int get_config_monitor(const char *, char *, int, struct lxc_conf *); static int clr_config_monitor(const char *, struct lxc_conf *, void *); static int set_config_group(const char *, const char *, struct lxc_conf *, void *); static int get_config_group(const char *, char *, int, struct lxc_conf *); static int clr_config_group(const char *, struct lxc_conf *, void *); static int set_config_environment(const char *, const char *, struct lxc_conf *, void *); static int get_config_environment(const char *, char *, int, struct lxc_conf *); static int clr_config_environment(const char *, struct lxc_conf *, void *); static int set_config_init_cmd(const char *, const char *, struct lxc_conf *, void *); static int get_config_init_cmd(const char *, char *, int, struct lxc_conf *); static int clr_config_init_cmd(const char *, struct lxc_conf *, void *); static int set_config_init_uid(const char *, const char *, struct lxc_conf *, void *); static int get_config_init_uid(const char *, char *, int, struct lxc_conf *); static int clr_config_init_uid(const char *, struct lxc_conf *, void *); static int set_config_init_gid(const char *, const char *, struct lxc_conf *, void *); static int get_config_init_gid(const char *, char *, int, struct lxc_conf *); static int clr_config_init_gid(const char *, struct lxc_conf *, void *); static int set_config_ephemeral(const char *, const char *, struct lxc_conf *, void *); static int get_config_ephemeral(const char *, char *, int, struct lxc_conf *); static int clr_config_ephemeral(const char *, struct lxc_conf *, void *); static struct lxc_config_t config[] = { { "lxc.arch", set_config_personality, get_config_personality, clr_config_personality, }, { "lxc.pts", set_config_pts, get_config_pts, clr_config_pts, }, { "lxc.tty", set_config_tty, get_config_tty, clr_config_tty, }, { "lxc.devttydir", set_config_ttydir, get_config_ttydir, clr_config_ttydir, }, { "lxc.kmsg", set_config_kmsg, get_config_kmsg, clr_config_kmsg, }, { "lxc.aa_profile", set_config_lsm_aa_profile, get_config_lsm_aa_profile, clr_config_lsm_aa_profile, }, { "lxc.aa_allow_incomplete", set_config_lsm_aa_incomplete, get_config_lsm_aa_incomplete, clr_config_lsm_aa_incomplete, }, { "lxc.se_context", set_config_lsm_se_context, get_config_lsm_se_context, clr_config_lsm_se_context, }, { "lxc.cgroup", set_config_cgroup, get_config_cgroup, clr_config_cgroup, }, { "lxc.id_map", set_config_idmaps, get_config_idmaps, clr_config_idmaps, }, { "lxc.loglevel", set_config_loglevel, get_config_loglevel, clr_config_loglevel, }, { "lxc.logfile", set_config_logfile, get_config_logfile, clr_config_logfile, }, { "lxc.mount.entry", set_config_mount, get_config_mount, clr_config_mount, }, { "lxc.mount.auto", set_config_mount_auto, get_config_mount_auto, clr_config_mount_auto, }, { "lxc.mount", set_config_fstab, get_config_fstab, clr_config_fstab, }, { "lxc.rootfs.mount", set_config_rootfs_mount, get_config_rootfs_mount, clr_config_rootfs_mount, }, { "lxc.rootfs.options", set_config_rootfs_options, get_config_rootfs_options, clr_config_rootfs_options, }, { "lxc.rootfs.backend", set_config_rootfs_backend, get_config_rootfs_backend, clr_config_rootfs_backend, }, { "lxc.rootfs", set_config_rootfs, get_config_rootfs, clr_config_rootfs, }, { "lxc.pivotdir", set_config_pivotdir, get_config_pivotdir, clr_config_pivotdir, }, { "lxc.utsname", set_config_utsname, get_config_utsname, clr_config_utsname, }, { "lxc.hook.pre-start", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.pre-mount", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.mount", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.autodev", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.start", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.stop", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.post-stop", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.clone", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.destroy", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.network.type", set_config_network_type, get_config_network_item, clr_config_network_item, }, { "lxc.network.flags", set_config_network_flags, get_config_network_item, clr_config_network_item, }, { "lxc.network.link", set_config_network_link, get_config_network_item, clr_config_network_item, }, { "lxc.network.name", set_config_network_name, get_config_network_item, clr_config_network_item, }, { "lxc.network.macvlan.mode", set_config_network_macvlan_mode, get_config_network_item, clr_config_network_item, }, { "lxc.network.veth.pair", set_config_network_veth_pair, get_config_network_item, clr_config_network_item, }, { "lxc.network.script.up", set_config_network_script_up, get_config_network_item, clr_config_network_item, }, { "lxc.network.script.down", set_config_network_script_down, get_config_network_item, clr_config_network_item, }, { "lxc.network.hwaddr", set_config_network_hwaddr, get_config_network_item, clr_config_network_item, }, { "lxc.network.mtu", set_config_network_mtu, get_config_network_item, clr_config_network_item, }, { "lxc.network.vlan.id", set_config_network_vlan_id, get_config_network_item, clr_config_network_item, }, { "lxc.network.ipv4.gateway", set_config_network_ipv4_gateway, get_config_network_item, clr_config_network_item, }, { "lxc.network.ipv4", set_config_network_ipv4, get_config_network_item, clr_config_network_item, }, { "lxc.network.ipv6.gateway", set_config_network_ipv6_gateway, get_config_network_item, clr_config_network_item, }, { "lxc.network.ipv6", set_config_network_ipv6, get_config_network_item, clr_config_network_item, }, { "lxc.network.", set_config_network_nic, get_config_network_item, clr_config_network_item, }, { "lxc.network", set_config_network, get_config_network, clr_config_network, }, { "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, }, { "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, }, { "lxc.console.logfile", set_config_console_logfile, get_config_console_logfile, clr_config_console_logfile, }, { "lxc.console", set_config_console, get_config_console, clr_config_console, }, { "lxc.seccomp", set_config_seccomp, get_config_seccomp, clr_config_seccomp, }, { "lxc.include", set_config_includefiles, get_config_includefiles, clr_config_includefiles, }, { "lxc.autodev", set_config_autodev, get_config_autodev, clr_config_autodev, }, { "lxc.haltsignal", set_config_haltsignal, get_config_haltsignal, clr_config_haltsignal, }, { "lxc.rebootsignal", set_config_rebootsignal, get_config_rebootsignal, clr_config_rebootsignal, }, { "lxc.stopsignal", set_config_stopsignal, get_config_stopsignal, clr_config_stopsignal, }, { "lxc.start.auto", set_config_start, get_config_start, clr_config_start, }, { "lxc.start.delay", set_config_start, get_config_start, clr_config_start, }, { "lxc.start.order", set_config_start, get_config_start, clr_config_start, }, { "lxc.monitor.unshare", set_config_monitor, get_config_monitor, clr_config_monitor, }, { "lxc.group", set_config_group, get_config_group, clr_config_group, }, { "lxc.environment", set_config_environment, get_config_environment, clr_config_environment, }, { "lxc.init_cmd", set_config_init_cmd, get_config_init_cmd, clr_config_init_cmd, }, { "lxc.init_uid", set_config_init_uid, get_config_init_uid, clr_config_init_uid, }, { "lxc.init_gid", set_config_init_gid, get_config_init_gid, clr_config_init_gid, }, { "lxc.ephemeral", set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, }, }; struct signame { int num; const char *name; }; static const struct signame signames[] = { { SIGHUP, "HUP" }, { SIGINT, "INT" }, { SIGQUIT, "QUIT" }, { SIGILL, "ILL" }, { SIGABRT, "ABRT" }, { SIGFPE, "FPE" }, { SIGKILL, "KILL" }, { SIGSEGV, "SEGV" }, { SIGPIPE, "PIPE" }, { SIGALRM, "ALRM" }, { SIGTERM, "TERM" }, { SIGUSR1, "USR1" }, { SIGUSR2, "USR2" }, { SIGCHLD, "CHLD" }, { SIGCONT, "CONT" }, { SIGSTOP, "STOP" }, { SIGTSTP, "TSTP" }, { SIGTTIN, "TTIN" }, { SIGTTOU, "TTOU" }, #ifdef SIGTRAP { SIGTRAP, "TRAP" }, #endif #ifdef SIGIOT { SIGIOT, "IOT" }, #endif #ifdef SIGEMT { SIGEMT, "EMT" }, #endif #ifdef SIGBUS { SIGBUS, "BUS" }, #endif #ifdef SIGSTKFLT { SIGSTKFLT, "STKFLT" }, #endif #ifdef SIGCLD { SIGCLD, "CLD" }, #endif #ifdef SIGURG { SIGURG, "URG" }, #endif #ifdef SIGXCPU { SIGXCPU, "XCPU" }, #endif #ifdef SIGXFSZ { SIGXFSZ, "XFSZ" }, #endif #ifdef SIGVTALRM { SIGVTALRM, "VTALRM" }, #endif #ifdef SIGPROF { SIGPROF, "PROF" }, #endif #ifdef SIGWINCH { SIGWINCH, "WINCH" }, #endif #ifdef SIGIO { SIGIO, "IO" }, #endif #ifdef SIGPOLL { SIGPOLL, "POLL" }, #endif #ifdef SIGINFO { SIGINFO, "INFO" }, #endif #ifdef SIGLOST { SIGLOST, "LOST" }, #endif #ifdef SIGPWR { SIGPWR, "PWR" }, #endif #ifdef SIGUNUSED { SIGUNUSED, "UNUSED" }, #endif #ifdef SIGSYS { SIGSYS, "SYS" }, #endif }; static const size_t config_size = sizeof(config) / sizeof(struct lxc_config_t); extern struct lxc_config_t *lxc_getconfig(const char *key) { size_t i; for (i = 0; i < config_size; i++) if (!strncmp(config[i].name, key, strlen(config[i].name))) return &config[i]; return NULL; } static int set_config_string_item(char **conf_item, const char *value) { char *new_value; if (lxc_config_value_empty(value)) { free(*conf_item); *conf_item = NULL; return 0; } new_value = strdup(value); if (!new_value) { SYSERROR("failed to duplicate string \"%s\"", value); return -1; } free(*conf_item); *conf_item = new_value; return 0; } static int set_config_string_item_max(char **conf_item, const char *value, size_t max) { if (strlen(value) >= max) { ERROR("%s is too long (>= %lu)", value, (unsigned long)max); return -1; } return set_config_string_item(conf_item, value); } static int set_config_path_item(char **conf_item, const char *value) { return set_config_string_item_max(conf_item, value, PATH_MAX); } static struct lxc_config_t *get_network_config_ops(const char *key, struct lxc_conf *lxc_conf, ssize_t *idx, char **deindexed_key) { int ret; unsigned int tmpidx; size_t numstrlen; char *copy, *idx_start, *idx_end; struct lxc_config_t *config = NULL; /* check that this is a sensible network key */ if (strncmp("lxc.network.", key, 12)) { ERROR("Invalid network configuration key \"%s\"", key); return NULL; } copy = strdup(key); if (!copy) { ERROR("Failed to duplicate string \"%s\"", key); return NULL; } /* lxc.network. */ if (!isdigit(*(key + 12))) { ERROR("Failed to detect digit in string \"%s\"", key + 12); goto on_error; } /* beginning of index string */ idx_start = (copy + 11); *idx_start = '\0'; /* end of index string */ idx_end = strchr((copy + 12), '.'); if (idx_end) *idx_end = '\0'; /* parse current index */ ret = lxc_safe_uint((idx_start + 1), &tmpidx); if (ret < 0) { ERROR("Failed to parse usigned integer from string \"%s\": %s", idx_start + 1, strerror(-ret)); *idx = ret; goto on_error; } /* This, of course is utterly nonsensical on so many levels, but * better safe than sorry. * (Checking for INT_MAX here is intentional.) */ if (tmpidx == INT_MAX) { SYSERROR("Number of configured networks would overflow the " "counter"); goto on_error; } *idx = tmpidx; numstrlen = strlen((idx_start + 1)); /* repair configuration key */ *idx_start = '.'; /* lxc.network.. */ if (idx_end) { *idx_end = '.'; if (strlen(idx_end + 1) == 0) { ERROR("No subkey in network configuration key \"%s\"", key); goto on_error; } memmove(copy + 12, idx_end + 1, strlen(idx_end + 1)); copy[strlen(key) - numstrlen + 1] = '\0'; config = lxc_getconfig(copy); if (!config) { ERROR("Unknown network configuration key \"%s\"", key); goto on_error; } } if (deindexed_key) *deindexed_key = copy; return config; on_error: free(copy); return NULL; } /* Config entry is something like "lxc.network.0.ipv4" the key 'lxc.network.' * was found. So we make sure next comes an integer, find the right callback * (by rewriting the key), and call it. */ static int set_config_network_nic(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; const char *idxstring; struct lxc_config_t *config; struct lxc_netdev *netdev; ssize_t idx = -1; char *deindexed_key = NULL; idxstring = key + 12; if (!isdigit(*idxstring)) return -1; if (lxc_config_value_empty(value)) return clr_config_network_item(key, lxc_conf, data); config = get_network_config_ops(key, lxc_conf, &idx, &deindexed_key); if (!config || idx < 0) return -1; netdev = lxc_get_netdev_by_idx(lxc_conf, (unsigned int)idx); if (!netdev) { free(deindexed_key); return -1; } ret = config->set(deindexed_key, value, lxc_conf, netdev); free(deindexed_key); return ret; } static int set_config_network(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (!lxc_config_value_empty(value)) { ERROR("lxc.network must not have a value"); return -1; } return lxc_clear_config_network(lxc_conf); } static int macvlan_mode(int *valuep, const char *value); static int set_config_network_type(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_list *network = &lxc_conf->network; struct lxc_netdev *netdev; struct lxc_list *list; if (lxc_config_value_empty(value)) return lxc_clear_config_network(lxc_conf); netdev = malloc(sizeof(*netdev)); if (!netdev) { SYSERROR("failed to allocate memory"); return -1; } memset(netdev, 0, sizeof(*netdev)); lxc_list_init(&netdev->ipv4); lxc_list_init(&netdev->ipv6); list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); free(netdev); return -1; } lxc_list_init(list); list->elem = netdev; lxc_list_add_tail(network, list); if (!strcmp(value, "veth")) netdev->type = LXC_NET_VETH; else if (!strcmp(value, "macvlan")) { netdev->type = LXC_NET_MACVLAN; macvlan_mode(&netdev->priv.macvlan_attr.mode, "private"); } else if (!strcmp(value, "vlan")) netdev->type = LXC_NET_VLAN; else if (!strcmp(value, "phys")) netdev->type = LXC_NET_PHYS; else if (!strcmp(value, "empty")) netdev->type = LXC_NET_EMPTY; else if (!strcmp(value, "none")) netdev->type = LXC_NET_NONE; else { ERROR("invalid network type %s", value); return -1; } return 0; } static int config_ip_prefix(struct in_addr *addr) { if (IN_CLASSA(addr->s_addr)) return 32 - IN_CLASSA_NSHIFT; if (IN_CLASSB(addr->s_addr)) return 32 - IN_CLASSB_NSHIFT; if (IN_CLASSC(addr->s_addr)) return 32 - IN_CLASSC_NSHIFT; return 0; } /* * If you have p="lxc.network.0.link", pass it p+12 * to get back '0' (the index of the nic). */ static int get_network_netdev_idx(const char *key) { int ret, idx; if (*key < '0' || *key > '9') return -1; ret = sscanf(key, "%d", &idx); if (ret != 1) return -1; return idx; } /* * If you have p="lxc.network.0", pass this p+12 and it will return * the netdev of the first configured nic. */ static struct lxc_netdev *get_netdev_from_key(const char *key, struct lxc_list *network) { int idx; struct lxc_list *it; int i = 0; struct lxc_netdev *netdev = NULL; idx = get_network_netdev_idx(key); if (idx == -1) return NULL; lxc_list_for_each(it, network) { if (idx == i++) { netdev = it->elem; break; } } return netdev; } extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, char *retv, int inlen) { struct lxc_netdev *netdev; int len; int fulllen = 0; netdev = get_netdev_from_key(key + 12, &c->network); if (!netdev) return -1; if (!retv) inlen = 0; else memset(retv, 0, inlen); strprint(retv, inlen, "type\n"); strprint(retv, inlen, "script.up\n"); strprint(retv, inlen, "script.down\n"); if (netdev->type != LXC_NET_EMPTY) { strprint(retv, inlen, "flags\n"); strprint(retv, inlen, "link\n"); strprint(retv, inlen, "name\n"); strprint(retv, inlen, "hwaddr\n"); strprint(retv, inlen, "mtu\n"); strprint(retv, inlen, "ipv6\n"); strprint(retv, inlen, "ipv6.gateway\n"); strprint(retv, inlen, "ipv4\n"); strprint(retv, inlen, "ipv4.gateway\n"); } switch (netdev->type) { case LXC_NET_VETH: strprint(retv, inlen, "veth.pair\n"); break; case LXC_NET_MACVLAN: strprint(retv, inlen, "macvlan.mode\n"); break; case LXC_NET_VLAN: strprint(retv, inlen, "vlan.id\n"); break; case LXC_NET_PHYS: break; } return fulllen; } static struct lxc_netdev *network_netdev(const char *key, const char *value, struct lxc_list *network) { struct lxc_netdev *netdev = NULL; if (lxc_list_empty(network)) { ERROR("network is not created for '%s' = '%s' option", key, value); return NULL; } if (get_network_netdev_idx(key + 12) == -1) netdev = lxc_list_last_elem(network); else netdev = get_netdev_from_key(key + 12, network); if (!netdev) { ERROR("no network device defined for '%s' = '%s' option", key, value); return NULL; } return netdev; } #ifndef MACVLAN_MODE_PRIVATE #define MACVLAN_MODE_PRIVATE 1 #endif #ifndef MACVLAN_MODE_VEPA #define MACVLAN_MODE_VEPA 2 #endif #ifndef MACVLAN_MODE_BRIDGE #define MACVLAN_MODE_BRIDGE 4 #endif #ifndef MACVLAN_MODE_PASSTHRU #define MACVLAN_MODE_PASSTHRU 8 #endif static int macvlan_mode(int *valuep, const char *value) { struct mc_mode { char *name; int mode; } m[] = { { "private", MACVLAN_MODE_PRIVATE }, { "vepa", MACVLAN_MODE_VEPA }, { "bridge", MACVLAN_MODE_BRIDGE }, { "passthru", MACVLAN_MODE_PASSTHRU }, }; size_t i; for (i = 0; i < sizeof(m) / sizeof(m[0]); i++) { if (strcmp(m[i].name, value)) continue; *valuep = m[i].mode; return 0; } return -1; } static int rand_complete_hwaddr(char *hwaddr) { const char hex[] = "0123456789abcdef"; char *curs = hwaddr; #ifndef HAVE_RAND_R randseed(true); #else unsigned int seed; seed = randseed(false); #endif while (*curs != '\0' && *curs != '\n') { if (*curs == 'x' || *curs == 'X') { if (curs - hwaddr == 1) { /* ensure address is unicast */ #ifdef HAVE_RAND_R *curs = hex[rand_r(&seed) & 0x0E]; } else { *curs = hex[rand_r(&seed) & 0x0F]; #else *curs = hex[rand() & 0x0E]; } else { *curs = hex[rand() & 0x0F]; #endif } } curs++; } return 0; } static int set_config_network_flags(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; if (lxc_config_value_empty(value)) { netdev->flags = 0; return 0; } netdev->flags |= IFF_UP; return 0; } static int set_config_network_link(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; return network_ifname(netdev->link, value); } static int set_config_network_name(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; return network_ifname(netdev->name, value); } static int set_config_network_veth_pair(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; if (netdev->type != LXC_NET_VETH) { ERROR("Invalid veth pair for a non-veth netdev"); return -1; } return network_ifname(netdev->priv.veth_attr.pair, value); } static int set_config_network_macvlan_mode(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; if (netdev->type != LXC_NET_MACVLAN) { ERROR("Invalid macvlan.mode for a non-macvlan netdev"); return -1; } if (lxc_config_value_empty(value)) { netdev->priv.macvlan_attr.mode = 0; return 0; } return macvlan_mode(&netdev->priv.macvlan_attr.mode, value); } static int set_config_network_hwaddr(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; char *new_value; new_value = strdup(value); if (!new_value) { SYSERROR("failed to strdup \"%s\"", value); return -1; } rand_complete_hwaddr(new_value); netdev = network_netdev(key, new_value, &lxc_conf->network); if (!netdev) { free(new_value); return -1; }; if (lxc_config_value_empty(new_value)) { free(new_value); netdev->hwaddr = NULL; return 0; } netdev->hwaddr = new_value; return 0; } static int set_config_network_vlan_id(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; if (netdev->type != LXC_NET_VLAN) { ERROR("Invalid vlan.id for a non-macvlan netdev"); return -1; } if (lxc_config_value_empty(value)) { netdev->priv.vlan_attr.vid = 0; return 0; } if (get_u16(&netdev->priv.vlan_attr.vid, value, 0)) return -1; return 0; } static int set_config_network_mtu(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; return set_config_string_item(&netdev->mtu, value); } static int set_config_network_ipv4(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; struct lxc_inetdev *inetdev; struct lxc_list *list; char *cursor, *slash; char *addr = NULL, *bcast = NULL, *prefix = NULL; if (lxc_config_value_empty(value)) return clr_config_network_item(key, lxc_conf, NULL); netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; inetdev = malloc(sizeof(*inetdev)); if (!inetdev) { SYSERROR("failed to allocate ipv4 address"); return -1; } memset(inetdev, 0, sizeof(*inetdev)); list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); free(inetdev); return -1; } lxc_list_init(list); list->elem = inetdev; addr = strdup(value); if (!addr) { ERROR("no address specified"); free(inetdev); free(list); return -1; } cursor = strstr(addr, " "); if (cursor) { *cursor = '\0'; bcast = cursor + 1; } slash = strstr(addr, "/"); if (slash) { *slash = '\0'; prefix = slash + 1; } if (!inet_pton(AF_INET, addr, &inetdev->addr)) { SYSERROR("invalid ipv4 address: %s", value); free(inetdev); free(addr); free(list); return -1; } if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { SYSERROR("invalid ipv4 broadcast address: %s", value); free(inetdev); free(list); free(addr); return -1; } /* No prefix specified, determine it from the network class. */ if (prefix) { if (lxc_safe_uint(prefix, &inetdev->prefix) < 0) return -1; } else { inetdev->prefix = config_ip_prefix(&inetdev->addr); } /* If no broadcast address, let compute one from the * prefix and address. */ if (!bcast) { inetdev->bcast.s_addr = inetdev->addr.s_addr; inetdev->bcast.s_addr |= htonl(INADDR_BROADCAST >> inetdev->prefix); } lxc_list_add_tail(&netdev->ipv4, list); free(addr); return 0; } static int set_config_network_ipv4_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; free(netdev->ipv4_gateway); if (lxc_config_value_empty(value)) { netdev->ipv4_gateway = NULL; return 0; } if (!strcmp(value, "auto")) { netdev->ipv4_gateway = NULL; netdev->ipv4_gateway_auto = true; } else { struct in_addr *gw; gw = malloc(sizeof(*gw)); if (!gw) { SYSERROR("failed to allocate ipv4 gateway address"); return -1; } if (!inet_pton(AF_INET, value, gw)) { SYSERROR("invalid ipv4 gateway address: %s", value); free(gw); return -1; } netdev->ipv4_gateway = gw; netdev->ipv4_gateway_auto = false; } return 0; } static int set_config_network_ipv6(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; struct lxc_inet6dev *inet6dev; struct lxc_list *list; char *slash, *valdup, *netmask; if (lxc_config_value_empty(value)) return clr_config_network_item(key, lxc_conf, NULL); netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; inet6dev = malloc(sizeof(*inet6dev)); if (!inet6dev) { SYSERROR("failed to allocate ipv6 address"); return -1; } memset(inet6dev, 0, sizeof(*inet6dev)); list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); free(inet6dev); return -1; } lxc_list_init(list); list->elem = inet6dev; valdup = strdup(value); if (!valdup) { ERROR("no address specified"); free(list); free(inet6dev); return -1; } inet6dev->prefix = 64; slash = strstr(valdup, "/"); if (slash) { *slash = '\0'; netmask = slash + 1; if (lxc_safe_uint(netmask, &inet6dev->prefix) < 0) return -1; } if (!inet_pton(AF_INET6, valdup, &inet6dev->addr)) { SYSERROR("invalid ipv6 address: %s", valdup); free(list); free(inet6dev); free(valdup); return -1; } lxc_list_add_tail(&netdev->ipv6, list); free(valdup); return 0; } static int set_config_network_ipv6_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; free(netdev->ipv6_gateway); if (lxc_config_value_empty(value)) { netdev->ipv6_gateway = NULL; return 0; } if (!strcmp(value, "auto")) { netdev->ipv6_gateway = NULL; netdev->ipv6_gateway_auto = true; } else { struct in6_addr *gw; gw = malloc(sizeof(*gw)); if (!gw) { SYSERROR("failed to allocate ipv6 gateway address"); return -1; } if (!inet_pton(AF_INET6, value, gw)) { SYSERROR("invalid ipv6 gateway address: %s", value); free(gw); return -1; } netdev->ipv6_gateway = gw; netdev->ipv6_gateway_auto = false; } return 0; } static int set_config_network_script_up(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; return set_config_string_item(&netdev->upscript, value); } static int set_config_network_script_down(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) return -1; return set_config_string_item(&netdev->downscript, value); } static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) { struct lxc_list *hooklist; hooklist = malloc(sizeof(*hooklist)); if (!hooklist) { free(hook); return -1; } hooklist->elem = hook; lxc_list_add_tail(&lxc_conf->hooks[which], hooklist); return 0; } static int set_config_seccomp(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->seccomp, value); } static int set_config_init_cmd(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->init_cmd, value); } static int set_config_init_uid(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { unsigned int init_uid; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->init_uid = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &init_uid) < 0) return -1; lxc_conf->init_uid = init_uid; return 0; } static int set_config_init_gid(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { unsigned int init_gid; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->init_gid = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &init_gid) < 0) return -1; lxc_conf->init_gid = init_gid; return 0; } static int set_config_hooks(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *copy; if (lxc_config_value_empty(value)) return lxc_clear_hooks(lxc_conf, key); if (strcmp(key + 4, "hook") == 0) { ERROR("lxc.hook cannot take a value"); return -1; } copy = strdup(value); if (!copy) { SYSERROR("failed to dup string '%s'", value); return -1; } if (strcmp(key + 9, "pre-start") == 0) return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); else if (strcmp(key + 9, "pre-mount") == 0) return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy); else if (strcmp(key + 9, "autodev") == 0) return add_hook(lxc_conf, LXCHOOK_AUTODEV, copy); else if (strcmp(key + 9, "mount") == 0) return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); else if (strcmp(key + 9, "start") == 0) return add_hook(lxc_conf, LXCHOOK_START, copy); else if (strcmp(key + 9, "stop") == 0) return add_hook(lxc_conf, LXCHOOK_STOP, copy); else if (strcmp(key + 9, "post-stop") == 0) return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); else if (strcmp(key + 9, "clone") == 0) return add_hook(lxc_conf, LXCHOOK_CLONE, copy); else if (strcmp(key + 9, "destroy") == 0) return add_hook(lxc_conf, LXCHOOK_DESTROY, copy); SYSERROR("Unknown key: %s", key); free(copy); return -1; } static int set_config_personality(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { signed long personality = lxc_config_parse_arch(value); if (personality >= 0) lxc_conf->personality = personality; else WARN("unsupported personality '%s'", value); return 0; } static int set_config_pts(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->pts = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->pts) < 0) return -1; return 0; } /* We only need to check whether the first byte of the key after the lxc.start. * prefix matches our expectations since they fortunately all start with a * different letter. If anything was wrong with the key we would have already * noticed when the callback was called. */ static int set_config_start(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { bool is_empty; is_empty = lxc_config_value_empty(value); if (*(key + 10) == 'a') { /* lxc.start.auto */ /* Set config value to default. */ if (is_empty) { lxc_conf->start_auto = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->start_auto) < 0) return -1; if (lxc_conf->start_auto > 1) return -1; return 0; } else if (*(key + 10) == 'd') { /* lxc.start.delay */ /* Set config value to default. */ if (is_empty) { lxc_conf->start_delay = 0; return 0; } /* Parse new config value. */ return lxc_safe_uint(value, &lxc_conf->start_delay); } else if (*(key + 10) == 'o') { /* lxc.start.order */ /* Set config value to default. */ if (is_empty) { lxc_conf->start_order = 0; return 0; } /* Parse new config value. */ return lxc_safe_int(value, &lxc_conf->start_order); } SYSERROR("Unknown key: %s", key); return -1; } static int set_config_monitor(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->monitor_unshare = 0; return 0; } /* Parse new config value. */ if (strcmp(key + 12, "unshare") == 0) return lxc_safe_uint(value, &lxc_conf->monitor_unshare); SYSERROR("Unknown key: %s", key); return -1; } static int set_config_group(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *groups, *groupptr, *sptr, *token; struct lxc_list *grouplist; int ret = -1; if (lxc_config_value_empty(value)) return lxc_clear_groups(lxc_conf); groups = strdup(value); if (!groups) { SYSERROR("failed to dup '%s'", value); return -1; } /* In case several groups are specified in a single line * split these groups in a single element for the list. */ for (groupptr = groups;; groupptr = NULL) { token = strtok_r(groupptr, " \t", &sptr); if (!token) { ret = 0; break; } grouplist = malloc(sizeof(*grouplist)); if (!grouplist) { SYSERROR("failed to allocate groups list"); break; } grouplist->elem = strdup(token); if (!grouplist->elem) { SYSERROR("failed to dup '%s'", token); free(grouplist); break; } lxc_list_add_tail(&lxc_conf->groups, grouplist); } free(groups); return ret; } static int set_config_environment(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_list *list_item = NULL; if (lxc_config_value_empty(value)) return lxc_clear_environment(lxc_conf); list_item = malloc(sizeof(*list_item)); if (!list_item) goto on_error; list_item->elem = strdup(value); if (!list_item->elem) goto on_error; lxc_list_add_tail(&lxc_conf->environment, list_item); return 0; on_error: free(list_item); return -1; } static int set_config_tty(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->tty = 0; return 0; } /* Parse new config value. */ return lxc_safe_uint(value, &lxc_conf->tty); } static int set_config_ttydir(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item_max(&lxc_conf->ttydir, value, NAME_MAX + 1); } static int set_config_kmsg(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->kmsg = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->kmsg) < 0) return -1; if (lxc_conf->kmsg > 1) return -1; return 0; } static int set_config_lsm_aa_profile(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item(&lxc_conf->lsm_aa_profile, value); } static int set_config_lsm_aa_incomplete(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->lsm_aa_allow_incomplete = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_incomplete) < 0) return -1; if (lxc_conf->lsm_aa_allow_incomplete > 1) { ERROR("Wrong value for lxc.lsm_aa_allow_incomplete. Can only " "be set to 0 or 1"); return -1; } return 0; } static int set_config_lsm_se_context(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item(&lxc_conf->lsm_se_context, value); } static int set_config_logfile(const char *key, const char *value, struct lxc_conf *c, void *data) { int ret; if (lxc_config_value_empty(value)) { free(c->logfile); c->logfile = NULL; return 0; } /* Store these values in the lxc_conf, and then try to set for actual * current logging. */ ret = set_config_path_item(&c->logfile, value); if (ret == 0) ret = lxc_log_set_file(&c->logfd, c->logfile); return ret; } static int set_config_loglevel(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int newlevel; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->loglevel = LXC_LOG_LEVEL_NOTSET; return 0; } /* Parse new config value. */ if (value[0] >= '0' && value[0] <= '9') { if (lxc_safe_int(value, &newlevel) < 0) return -1; } else { newlevel = lxc_log_priority_to_int(value); } /* Store these values in the lxc_conf, and then try to set for actual * current logging. */ lxc_conf->loglevel = newlevel; return lxc_log_set_level(&lxc_conf->loglevel, newlevel); } static int set_config_autodev(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->autodev = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->autodev) < 0) return -1; if (lxc_conf->autodev > 1) { ERROR("Wrong value for lxc.autodev. Can only be set to 0 or 1"); return -1; } return 0; } static int sig_num(const char *sig) { unsigned int signum; if (lxc_safe_uint(sig, &signum) < 0) return -1; return signum; } static int rt_sig_num(const char *signame) { int rtmax = 0, sig_n = 0; if (strncasecmp(signame, "max-", 4) == 0) { rtmax = 1; } signame += 4; if (!isdigit(*signame)) return -1; sig_n = sig_num(signame); sig_n = rtmax ? SIGRTMAX - sig_n : SIGRTMIN + sig_n; if (sig_n > SIGRTMAX || sig_n < SIGRTMIN) return -1; return sig_n; } static int sig_parse(const char *signame) { size_t n; if (isdigit(*signame)) { return sig_num(signame); } else if (strncasecmp(signame, "sig", 3) == 0) { signame += 3; if (strncasecmp(signame, "rt", 2) == 0) return rt_sig_num(signame + 2); for (n = 0; n < sizeof(signames) / sizeof((signames)[0]); n++) { if (strcasecmp(signames[n].name, signame) == 0) return signames[n].num; } } return -1; } static int set_config_haltsignal(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int sig_n; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->haltsignal = 0; return 0; } /* Parse new config value. */ sig_n = sig_parse(value); if (sig_n < 0) return -1; lxc_conf->haltsignal = sig_n; return 0; } static int set_config_rebootsignal(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int sig_n; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->rebootsignal = 0; return 0; } /* Parse new config value. */ sig_n = sig_parse(value); if (sig_n < 0) return -1; lxc_conf->rebootsignal = sig_n; return 0; } static int set_config_stopsignal(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int sig_n; /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->stopsignal = 0; return 0; } /* Parse new config value. */ sig_n = sig_parse(value); if (sig_n < 0) return -1; lxc_conf->stopsignal = sig_n; return 0; } static int set_config_cgroup(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *subkey; char *token = "lxc.cgroup."; struct lxc_list *cglist = NULL; struct lxc_cgroup *cgelem = NULL; if (lxc_config_value_empty(value)) return lxc_clear_cgroups(lxc_conf, key); subkey = strstr(key, token); if (!subkey) return -1; if (!strlen(subkey)) return -1; if (strlen(subkey) == strlen(token)) return -1; subkey += strlen(token); cglist = malloc(sizeof(*cglist)); if (!cglist) goto out; cgelem = malloc(sizeof(*cgelem)); if (!cgelem) goto out; memset(cgelem, 0, sizeof(*cgelem)); cgelem->subsystem = strdup(subkey); cgelem->value = strdup(value); if (!cgelem->subsystem || !cgelem->value) goto out; cglist->elem = cgelem; lxc_list_add_tail(&lxc_conf->cgroup, cglist); return 0; out: free(cglist); if (cgelem) { free(cgelem->subsystem); free(cgelem->value); free(cgelem); } return -1; } static int set_config_idmaps(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { unsigned long hostid, nsid, range; char type; int ret; struct lxc_list *idmaplist = NULL; struct id_map *idmap = NULL; if (lxc_config_value_empty(value)) return lxc_clear_idmaps(lxc_conf); idmaplist = malloc(sizeof(*idmaplist)); if (!idmaplist) goto on_error; idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; memset(idmap, 0, sizeof(*idmap)); ret = parse_idmaps(value, &type, &nsid, &hostid, &range); if (ret < 0) goto on_error; INFO("Read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range); if (type == 'u') idmap->idtype = ID_TYPE_UID; else if (type == 'g') idmap->idtype = ID_TYPE_GID; else goto on_error; idmap->hostid = hostid; idmap->nsid = nsid; idmap->range = range; idmaplist->elem = idmap; lxc_list_add_tail(&lxc_conf->id_map, idmaplist); if (!lxc_conf->root_nsuid_map && idmap->idtype == ID_TYPE_UID) if (idmap->nsid == 0) lxc_conf->root_nsuid_map = idmap; if (!lxc_conf->root_nsgid_map && idmap->idtype == ID_TYPE_GID) if (idmap->nsid == 0) lxc_conf->root_nsgid_map = idmap; idmap = NULL; return 0; on_error: free(idmaplist); free(idmap); return -1; } static int set_config_fstab(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { clr_config_fstab(key, lxc_conf, NULL); return -1; } return set_config_path_item(&lxc_conf->fstab, value); } static int set_config_mount_auto(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *autos, *autoptr, *sptr, *token; int i; int ret = -1; static struct { const char *token; int mask; int flag; } allowed_auto_mounts[] = { { "proc", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, { "proc:mixed", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, { "proc:rw", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW }, { "sys", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, { "sys:ro", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, { "sys:mixed", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, { "sys:rw", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW }, { "cgroup", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC }, { "cgroup:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED }, { "cgroup:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO }, { "cgroup:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW }, { "cgroup:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC | LXC_AUTO_CGROUP_FORCE }, { "cgroup:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED | LXC_AUTO_CGROUP_FORCE }, { "cgroup:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO | LXC_AUTO_CGROUP_FORCE }, { "cgroup:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC }, { "cgroup-full:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED }, { "cgroup-full:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO }, { "cgroup-full:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW }, { "cgroup-full:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW | LXC_AUTO_CGROUP_FORCE }, /* For adding anything that is just a single on/off, but has no * options: keep mask and flag identical and just define the enum * value as an unused bit so far */ { NULL, 0, 0 } }; if (lxc_config_value_empty(value)) { lxc_conf->auto_mounts = 0; return 0; } autos = strdup(value); if (!autos) { SYSERROR("failed to dup '%s'", value); return -1; } for (autoptr = autos;; autoptr = NULL) { token = strtok_r(autoptr, " \t", &sptr); if (!token) { ret = 0; break; } for (i = 0; allowed_auto_mounts[i].token; i++) { if (!strcmp(allowed_auto_mounts[i].token, token)) break; } if (!allowed_auto_mounts[i].token) { ERROR("Invalid filesystem to automount: %s", token); break; } lxc_conf->auto_mounts &= ~allowed_auto_mounts[i].mask; lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag; } free(autos); return ret; } static int set_config_mount(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *mntelem; struct lxc_list *mntlist; if (lxc_config_value_empty(value)) return lxc_clear_mount_entries(lxc_conf); mntlist = malloc(sizeof(*mntlist)); if (!mntlist) return -1; mntelem = strdup(value); if (!mntelem) { free(mntlist); return -1; } mntlist->elem = mntelem; lxc_list_add_tail(&lxc_conf->mount_list, mntlist); return 0; } static int set_config_cap_keep(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *keepcaps, *keepptr, *sptr, *token; struct lxc_list *keeplist; int ret = -1; if (lxc_config_value_empty(value)) return lxc_clear_config_keepcaps(lxc_conf); keepcaps = strdup(value); if (!keepcaps) { SYSERROR("failed to dup '%s'", value); return -1; } /* In case several capability keep is specified in a single line * split these caps in a single element for the list. */ for (keepptr = keepcaps;; keepptr = NULL) { token = strtok_r(keepptr, " \t", &sptr); if (!token) { ret = 0; break; } if (!strcmp(token, "none")) lxc_clear_config_keepcaps(lxc_conf); keeplist = malloc(sizeof(*keeplist)); if (!keeplist) { SYSERROR("failed to allocate keepcap list"); break; } keeplist->elem = strdup(token); if (!keeplist->elem) { SYSERROR("failed to dup '%s'", token); free(keeplist); break; } lxc_list_add_tail(&lxc_conf->keepcaps, keeplist); } free(keepcaps); return ret; } static int set_config_cap_drop(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *dropcaps, *dropptr, *sptr, *token; struct lxc_list *droplist; int ret = -1; if (lxc_config_value_empty(value)) return lxc_clear_config_caps(lxc_conf); dropcaps = strdup(value); if (!dropcaps) { SYSERROR("failed to dup '%s'", value); return -1; } /* In case several capability drop is specified in a single line * split these caps in a single element for the list. */ for (dropptr = dropcaps;; dropptr = NULL) { token = strtok_r(dropptr, " \t", &sptr); if (!token) { ret = 0; break; } droplist = malloc(sizeof(*droplist)); if (!droplist) { SYSERROR("failed to allocate drop list"); break; } droplist->elem = strdup(token); if (!droplist->elem) { SYSERROR("failed to dup '%s'", token); free(droplist); break; } lxc_list_add_tail(&lxc_conf->caps, droplist); } free(dropcaps); return ret; } static int set_config_console(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->console.path, value); } static int set_config_console_logfile(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->console.log_path, value); } /* * If we find a lxc.network.hwaddr in the original config file, we expand it in * the unexpanded_config, so that after a save_config we store the hwaddr for * re-use. * This is only called when reading the config file, not when executing a * lxc.include. * 'x' and 'X' are substituted in-place. */ static void update_hwaddr(const char *line) { char *p; line += lxc_char_left_gc(line, strlen(line)); if (line[0] == '#') return; if (strncmp(line, "lxc.network.hwaddr", 18) != 0) return; /* Let config_network_hwaddr raise the error. */ p = strchr(line, '='); if (!p) return; p++; while (isblank(*p)) p++; if (!*p) return; rand_complete_hwaddr(p); } int append_unexp_config_line(const char *line, struct lxc_conf *conf) { size_t len = conf->unexpanded_len, linelen = strlen(line); update_hwaddr(line); while (conf->unexpanded_alloced <= len + linelen + 2) { char *tmp = realloc(conf->unexpanded_config, conf->unexpanded_alloced + 1024); if (!tmp) return -1; if (!conf->unexpanded_config) *tmp = '\0'; conf->unexpanded_config = tmp; conf->unexpanded_alloced += 1024; } strcat(conf->unexpanded_config, line); conf->unexpanded_len += linelen; if (line[linelen - 1] != '\n') { strcat(conf->unexpanded_config, "\n"); conf->unexpanded_len++; } return 0; } static int do_includedir(const char *dirp, struct lxc_conf *lxc_conf) { struct dirent *direntp; DIR *dir; char path[MAXPATHLEN]; int len; int ret = -1; dir = opendir(dirp); if (!dir) { SYSERROR("failed to open '%s'", dirp); return -1; } while ((direntp = readdir(dir))) { const char *fnam; fnam = direntp->d_name; if (!strcmp(fnam, ".")) continue; if (!strcmp(fnam, "..")) continue; len = strlen(fnam); if (len < 6 || strncmp(fnam + len - 5, ".conf", 5) != 0) continue; len = snprintf(path, MAXPATHLEN, "%s/%s", dirp, fnam); if (len < 0 || len >= MAXPATHLEN) { ERROR("lxc.include filename too long under '%s'", dirp); ret = -1; goto out; } ret = lxc_config_read(path, lxc_conf, true); if (ret < 0) goto out; } ret = 0; out: if (closedir(dir)) WARN("lxc.include dir: failed to close directory"); return ret; } static int set_config_includefiles(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { clr_config_includefiles(key, lxc_conf, NULL); return 0; } /* Parse new config value. */ if (is_dir(value)) return do_includedir(value, lxc_conf); return lxc_config_read(value, lxc_conf, true); } static int set_config_rootfs(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->rootfs.path, value); } static int set_config_rootfs_mount(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->rootfs.mount, value); } static int set_config_rootfs_options(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item(&lxc_conf->rootfs.options, value); } static int set_config_rootfs_backend(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { free(lxc_conf->rootfs.bdev_type); lxc_conf->rootfs.bdev_type = NULL; return 0; } if (!is_valid_storage_type(value)) { ERROR("Bad rootfs.backend: '%s'", value); return -1; } return set_config_string_item(&lxc_conf->rootfs.bdev_type, value); } static int set_config_pivotdir(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { WARN("lxc.pivotdir is ignored. It will soon become an error."); return 0; } static int set_config_utsname(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct utsname *utsname; if (lxc_config_value_empty(value)) { clr_config_utsname(key, lxc_conf, NULL); return 0; } utsname = malloc(sizeof(*utsname)); if (!utsname) { SYSERROR("failed to allocate memory"); return -1; } if (strlen(value) >= sizeof(utsname->nodename)) { ERROR("node name '%s' is too long", value); free(utsname); return -1; } strcpy(utsname->nodename, value); free(lxc_conf->utsname); lxc_conf->utsname = utsname; return 0; } struct parse_line_conf { struct lxc_conf *conf; bool from_include; }; static int parse_line(char *buffer, void *data) { char *dot, *key, *line, *linep, *value; bool empty_line; struct lxc_config_t *config; int ret = 0; char *dup = buffer; struct parse_line_conf *plc = data; /* If there are newlines in the config file we should keep them. */ empty_line = lxc_is_line_empty(dup); if (empty_line) dup = "\n"; /* we have to dup the buffer otherwise, at the re-exec for * reboot we modified the original string on the stack by * replacing '=' by '\0' below */ linep = line = strdup(buffer); if (!line) return -1; if (!plc->from_include) { ret = append_unexp_config_line(line, plc->conf); if (ret < 0) goto on_error; } if (empty_line) goto on_error; line += lxc_char_left_gc(line, strlen(line)); /* ignore comments */ if (line[0] == '#') goto on_error; /* martian option - don't add it to the config itself */ if (strncmp(line, "lxc.", 4)) goto on_error; ret = -1; dot = strchr(line, '='); if (!dot) { ERROR("Invalid configuration line: %s", line); goto on_error; } *dot = '\0'; value = dot + 1; key = line; key[lxc_char_right_gc(key, strlen(key))] = '\0'; value += lxc_char_left_gc(value, strlen(value)); value[lxc_char_right_gc(value, strlen(value))] = '\0'; if (*value == '\'' || *value == '\"') { size_t len = strlen(value); if (len > 1 && value[len - 1] == *value) { value[len - 1] = '\0'; value++; } } config = lxc_getconfig(key); if (!config) { ERROR("Unknown configuration key \"%s\"", key); goto on_error; } ret = config->set(key, value, plc->conf, data); on_error: free(linep); return ret; } static int lxc_config_readline(char *buffer, struct lxc_conf *conf) { struct parse_line_conf c; c.conf = conf; c.from_include = false; return parse_line(buffer, &c); } int lxc_config_read(const char *file, struct lxc_conf *conf, bool from_include) { struct parse_line_conf c; c.conf = conf; c.from_include = from_include; if (access(file, R_OK) == -1) { return -1; } /* Catch only the top level config file name in the structure */ if (!conf->rcfile) conf->rcfile = strdup(file); return lxc_file_for_each_line(file, parse_line, &c); } int lxc_config_define_add(struct lxc_list *defines, char *arg) { struct lxc_list *dent; dent = malloc(sizeof(struct lxc_list)); if (!dent) return -1; dent->elem = arg; lxc_list_add_tail(defines, dent); return 0; } int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) { struct lxc_list *it, *next; int ret = 0; lxc_list_for_each(it, defines) { ret = lxc_config_readline(it->elem, conf); if (ret) break; } lxc_list_for_each_safe(it, defines, next) { lxc_list_del(it); free(it); } return ret; } signed long lxc_config_parse_arch(const char *arch) { #if HAVE_SYS_PERSONALITY_H size_t i; struct per_name { char *name; unsigned long per; } pername[] = { { "arm", PER_LINUX32 }, { "armel", PER_LINUX32 }, { "armhf", PER_LINUX32 }, { "armv7l", PER_LINUX32 }, { "athlon", PER_LINUX32 }, { "i386", PER_LINUX32 }, { "i486", PER_LINUX32 }, { "i586", PER_LINUX32 }, { "i686", PER_LINUX32 }, { "linux32", PER_LINUX32 }, { "mips", PER_LINUX32 }, { "mipsel", PER_LINUX32 }, { "ppc", PER_LINUX32 }, { "powerpc", PER_LINUX32 }, { "x86", PER_LINUX32 }, { "amd64", PER_LINUX }, { "arm64", PER_LINUX }, { "linux64", PER_LINUX }, { "mips64", PER_LINUX }, { "mips64el", PER_LINUX }, { "ppc64", PER_LINUX }, { "ppc64el", PER_LINUX }, { "ppc64le", PER_LINUX }, { "powerpc64", PER_LINUX }, { "s390x", PER_LINUX }, { "x86_64", PER_LINUX }, }; size_t len = sizeof(pername) / sizeof(pername[0]); for (i = 0; i < len; i++) if (!strcmp(pername[i].name, arch)) return pername[i].per; #endif return -1; } int lxc_fill_elevated_privileges(char *flaglist, int *flags) { char *token, *saveptr = NULL; int i, aflag; struct { const char *token; int flag; } all_privs[] = { { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, { "LSM", LXC_ATTACH_LSM_EXEC }, { NULL, 0 } }; if (!flaglist) { /* For the sake of backward compatibility, drop all privileges * if none is specified. */ for (i = 0; all_privs[i].token; i++) { *flags |= all_privs[i].flag; } return 0; } token = strtok_r(flaglist, "|", &saveptr); while (token) { aflag = -1; for (i = 0; all_privs[i].token; i++) { if (!strcmp(all_privs[i].token, token)) aflag = all_privs[i].flag; } if (aflag < 0) return -1; *flags |= aflag; token = strtok_r(NULL, "|", &saveptr); } return 0; } static inline int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) { if (!retv) inlen = 0; else memset(retv, 0, inlen); return snprintf(retv, inlen, "%d", v); } /* Write out a configuration file. */ void write_config(FILE *fout, struct lxc_conf *c) { int ret; size_t len = c->unexpanded_len; if (!len) return; ret = fwrite(c->unexpanded_config, 1, len, fout); if (ret != len) SYSERROR("Error writing configuration file"); } bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, const char *v) { int ret; size_t len; char *tmp; len = strlen(key) + strlen(v) + 4; tmp = alloca(len); if (lxc_config_value_empty(v)) ret = snprintf(tmp, len, "%s =", key); else ret = snprintf(tmp, len, "%s = %s", key, v); if (ret < 0 || ret >= len) return false; /* Save the line verbatim into unexpanded_conf */ if (append_unexp_config_line(tmp, conf)) return false; return true; } void clear_unexp_config_line(struct lxc_conf *conf, const char *key, bool rm_subkeys) { char *lend; char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return; while (*lstart) { lend = strchr(lstart, '\n'); char v; if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) { lstart = lend; continue; } if (!rm_subkeys) { v = lstart[strlen(key)]; if (!isspace(v) && v != '=') { lstart = lend; continue; } } conf->unexpanded_len -= (lend - lstart); if (*lend == '\0') { *lstart = '\0'; return; } memmove(lstart, lend, strlen(lend) + 1); } } bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname, const char *ovldir) { int ret; char *lend, *newdir, *olddir, *p, *q; size_t newdirlen, olddirlen; char *lstart = conf->unexpanded_config; const char *key = "lxc.mount.entry"; olddirlen = strlen(ovldir) + strlen(oldpath) + strlen(oldname) + 2; olddir = alloca(olddirlen + 1); ret = snprintf(olddir, olddirlen + 1, "%s=%s/%s", ovldir, oldpath, oldname); if (ret < 0 || ret >= olddirlen + 1) { ERROR("failed to create string"); return false; } newdirlen = strlen(ovldir) + strlen(newpath) + strlen(newname) + 2; newdir = alloca(newdirlen + 1); ret = snprintf(newdir, newdirlen + 1, "%s=%s/%s", ovldir, newpath, newname); if (ret < 0 || ret >= newdirlen + 1) { ERROR("failed to create string"); return false; } if (!conf->unexpanded_config) return true; while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) goto next; p = strchr(lstart + strlen(key), '='); if (!p) goto next; p++; while (isblank(*p)) p++; if (p >= lend) goto next; /* Whenever an lxc.mount.entry entry is found in a line we check * if the substring " overlay" or the substring " aufs" is * present before doing any further work. We check for " * overlay" and " aufs" since both substrings need to have at * least one space before them in a valid overlay * lxc.mount.entry (/A B overlay). When the space before is * missing it is very likely that these substrings are part of a * path or something else. (Checking q >= lend ensures that we * only count matches in the current line.) */ if ((!(q = strstr(p, " overlay")) || q >= lend) && (!(q = strstr(p, " aufs")) || q >= lend)) goto next; if (!(q = strstr(p, olddir)) || (q >= lend)) goto next; /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { size_t diff = olddirlen - newdirlen; memcpy(q, newdir, newdirlen); if (olddirlen != newdirlen) { memmove(q + newdirlen, q + newdirlen + diff, strlen(q) - newdirlen - diff + 1); lend -= diff; conf->unexpanded_len -= diff; } } else { char *new; size_t diff = newdirlen - olddirlen; size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = q - conf->unexpanded_config; new = realloc(conf->unexpanded_config, newlen + 1); if (!new) { ERROR("Out of memory"); return false; } conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); /* move over the remainder to make room for the newdir */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); conf->unexpanded_config = new; memcpy(new + poffset, newdir, newdirlen); lend += diff; } next: lstart = lend; } return true; } bool clone_update_unexp_hooks(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname) { int ret; char *lend, *newdir, *olddir, *p; char *lstart = conf->unexpanded_config; size_t newdirlen, olddirlen; const char *key = "lxc.hook"; olddirlen = strlen(oldpath) + strlen(oldname) + 1; olddir = alloca(olddirlen + 1); ret = snprintf(olddir, olddirlen + 1, "%s/%s", oldpath, oldname); if (ret < 0 || ret >= olddirlen + 1) { ERROR("failed to create string"); return false; } newdirlen = strlen(newpath) + strlen(newname) + 1; newdir = alloca(newdirlen + 1); ret = snprintf(newdir, newdirlen + 1, "%s/%s", newpath, newname); if (ret < 0 || ret >= newdirlen + 1) { ERROR("failed to create string"); return false; } if (!conf->unexpanded_config) return true; while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) goto next; p = strchr(lstart + strlen(key), '='); if (!p) goto next; p++; while (isblank(*p)) p++; if (p >= lend) goto next; if (strncmp(p, olddir, strlen(olddir)) != 0) goto next; /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { size_t diff = olddirlen - newdirlen; memcpy(p, newdir, newdirlen); if (olddirlen != newdirlen) { memmove(p + newdirlen, p + newdirlen + diff, strlen(p) - newdirlen - diff + 1); lend -= diff; conf->unexpanded_len -= diff; } } else { char *new; size_t diff = newdirlen - olddirlen; size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = p - conf->unexpanded_config; new = realloc(conf->unexpanded_config, newlen + 1); if (!new) { ERROR("failed to allocate memory"); return false; } conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); /* move over the remainder to make room for the newdir */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); conf->unexpanded_config = new; memcpy(new + poffset, newdir, newdirlen); lend += diff; } next: lstart = lend; } return true; } #define DO(cmd) \ { \ if (!(cmd)) { \ ERROR("Error writing to new config"); \ return false; \ } \ } static bool new_hwaddr(char *hwaddr) { int ret; (void)randseed(true); ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255, rand() % 255, rand() % 255); if (ret < 0 || ret >= 18) { SYSERROR("Failed to call snprintf()."); return false; } return true; } /* * This is called only from clone. We wish to update all hwaddrs in the * unexpanded config file. We can't/don't want to update any which come from * lxc.includes (there shouldn't be any). * We can't just walk the c->lxc-conf->network list because that includes netifs * from the include files. So we update the ones which we find in the unexp * config file, then find the original macaddr in the conf->network, and update * that to the same value. */ bool network_new_hwaddrs(struct lxc_conf *conf) { char *lend, *p, *p2; struct lxc_list *it; const char *key = "lxc.network.hwaddr"; char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return true; while (*lstart) { char newhwaddr[18], oldhwaddr[17]; lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) { lstart = lend; continue; } p = strchr(lstart + strlen(key), '='); if (!p) { lstart = lend; continue; } p++; while (isblank(*p)) p++; if (!*p) return true; p2 = p; while (*p2 && !isblank(*p2) && *p2 != '\n') p2++; if ((p2 - p) != 17) { WARN("Bad hwaddr entry"); lstart = lend; continue; } memcpy(oldhwaddr, p, 17); if (!new_hwaddr(newhwaddr)) return false; memcpy(p, newhwaddr, 17); lxc_list_for_each(it, &conf->network) { struct lxc_netdev *n = it->elem; if (n->hwaddr && memcmp(oldhwaddr, n->hwaddr, 17) == 0) memcpy(n->hwaddr, newhwaddr, 17); } lstart = lend; } return true; } static int set_config_ephemeral(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { /* Set config value to default. */ if (lxc_config_value_empty(value)) { lxc_conf->ephemeral = 0; return 0; } /* Parse new config value. */ if (lxc_safe_uint(value, &lxc_conf->ephemeral) < 0) return -1; if (lxc_conf->ephemeral > 1) { ERROR( "Wrong value for lxc.ephemeral. Can only be set to 0 or 1"); return -1; } return 0; } /* Callbacks to get configuration items. */ static int get_config_personality(const char *key, char *retv, int inlen, struct lxc_conf *c) { int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); #if HAVE_SYS_PERSONALITY_H int len = 0; switch (c->personality) { case PER_LINUX32: strprint(retv, inlen, "i686"); break; case PER_LINUX: strprint(retv, inlen, "x86_64"); break; default: break; } #endif return fulllen; } static int get_config_pts(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->pts); } static int get_config_tty(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->tty); } static inline int lxc_get_conf_str(char *retv, int inlen, const char *value) { if (!value) return 0; if (retv && inlen >= strlen(value) + 1) strncpy(retv, value, strlen(value) + 1); return strlen(value); } static int get_config_ttydir(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->ttydir); } static int get_config_kmsg(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->kmsg); } static int get_config_lsm_aa_profile(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->lsm_aa_profile); } static int get_config_lsm_aa_incomplete(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->lsm_aa_allow_incomplete); } static int get_config_lsm_se_context(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->lsm_se_context); } /* * If you ask for a specific cgroup value, i.e. lxc.cgroup.devices.list, * then just the value(s) will be printed. Since there still could be * more than one, it is newline-separated. * (Maybe that's ambigous, since some values, i.e. devices.list, will * already have newlines?) * If you ask for 'lxc.cgroup", then all cgroup entries will be printed, * in 'lxc.cgroup.subsystem.key = value' format. */ static int get_config_cgroup(const char *key, char *retv, int inlen, struct lxc_conf *c) { struct lxc_list *it; int len; int fulllen = 0; bool get_all = false; if (!retv) inlen = 0; else memset(retv, 0, inlen); if (!strcmp(key, "lxc.cgroup")) get_all = true; else if (!strncmp(key, "lxc.cgroup.", 11)) key += 11; else return -1; lxc_list_for_each(it, &c->cgroup) { struct lxc_cgroup *cg = it->elem; if (get_all) { strprint(retv, inlen, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); } else if (!strcmp(cg->subsystem, key)) { strprint(retv, inlen, "%s\n", cg->value); } } return fulllen; } static int get_config_idmaps(const char *key, char *retv, int inlen, struct lxc_conf *c) { struct lxc_list *it; int len, listlen, ret; int fulllen = 0; /* "u 1000 1000000 65536" * * let's render this as * * sizeof(char) * + * sizeof(" ") * + * sizeof(uint64_t) * + * sizeof(" ") * + * sizeof(uint64_t) * + * sizeof(" ") * + * sizeof(uint64_t) * + * \0 */ #define __LXC_IDMAP_STR_BUF (3 * LXC_NUMSTRLEN64 + 3 + 1 + 1) char buf[__LXC_IDMAP_STR_BUF]; if (!retv) inlen = 0; else memset(retv, 0, inlen); listlen = lxc_list_len(&c->id_map); lxc_list_for_each(it, &c->id_map) { struct id_map *map = it->elem; ret = snprintf(buf, __LXC_IDMAP_STR_BUF, "%c %lu %lu %lu", (map->idtype == ID_TYPE_UID) ? 'u' : 'g', map->nsid, map->hostid, map->range); if (ret < 0 || ret >= __LXC_IDMAP_STR_BUF) return -1; strprint(retv, inlen, "%s%s", buf, (listlen-- > 1) ? "\n" : ""); } return fulllen; } static int get_config_loglevel(const char *key, char *retv, int inlen, struct lxc_conf *c) { const char *v; v = lxc_log_priority_to_string(c->loglevel); return lxc_get_conf_str(retv, inlen, v); } static int get_config_logfile(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->logfile); } static int get_config_fstab(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->fstab); } static int get_config_mount_auto(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; const char *sep = ""; if (!retv) inlen = 0; else memset(retv, 0, inlen); if (!(c->auto_mounts & LXC_AUTO_ALL_MASK)) return 0; switch (c->auto_mounts & LXC_AUTO_PROC_MASK) { case LXC_AUTO_PROC_MIXED: strprint(retv, inlen, "%sproc:mixed", sep); sep = " "; break; case LXC_AUTO_PROC_RW: strprint(retv, inlen, "%sproc:rw", sep); sep = " "; break; default: break; } switch (c->auto_mounts & LXC_AUTO_SYS_MASK) { case LXC_AUTO_SYS_RO: strprint(retv, inlen, "%ssys:ro", sep); sep = " "; break; case LXC_AUTO_SYS_RW: strprint(retv, inlen, "%ssys:rw", sep); sep = " "; break; case LXC_AUTO_SYS_MIXED: strprint(retv, inlen, "%ssys:mixed", sep); sep = " "; break; default: break; } switch (c->auto_mounts & LXC_AUTO_CGROUP_MASK) { case LXC_AUTO_CGROUP_NOSPEC: strprint(retv, inlen, "%scgroup", sep); break; case LXC_AUTO_CGROUP_MIXED: strprint(retv, inlen, "%scgroup:mixed", sep); break; case LXC_AUTO_CGROUP_RO: strprint(retv, inlen, "%scgroup:ro", sep); break; case LXC_AUTO_CGROUP_RW: strprint(retv, inlen, "%scgroup:rw", sep); break; case LXC_AUTO_CGROUP_FULL_NOSPEC: strprint(retv, inlen, "%scgroup-full", sep); break; case LXC_AUTO_CGROUP_FULL_MIXED: strprint(retv, inlen, "%scgroup-full:mixed", sep); break; case LXC_AUTO_CGROUP_FULL_RO: strprint(retv, inlen, "%scgroup-full:ro", sep); break; case LXC_AUTO_CGROUP_FULL_RW: strprint(retv, inlen, "%scgroup-full:rw", sep); break; default: break; } return fulllen; } static int get_config_mount(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->mount_list) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_rootfs(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->rootfs.path); } static int get_config_rootfs_mount(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->rootfs.mount); } static int get_config_rootfs_options(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->rootfs.options); } static int get_config_rootfs_backend(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->rootfs.bdev_type); } static int get_config_pivotdir(const char *key, char *retv, int inlen, struct lxc_conf *c) { return 0; } static int get_config_utsname(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str( retv, inlen, c->utsname ? c->utsname->nodename : NULL); } static int get_config_hooks(const char *key, char *retv, int inlen, struct lxc_conf *c) { char *subkey; int len, fulllen = 0, found = -1; struct lxc_list *it; int i; /* "lxc.hook.mount" */ subkey = strchr(key, '.'); if (subkey) subkey = strchr(subkey + 1, '.'); if (!subkey) return -1; subkey++; if (!*subkey) return -1; for (i = 0; i < NUM_LXC_HOOKS; i++) { if (strcmp(lxchook_names[i], subkey) == 0) { found = i; break; } } if (found == -1) return -1; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->hooks[found]) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_network(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->network) { struct lxc_netdev *n = it->elem; const char *t = lxc_net_type_to_str(n->type); strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); } return fulllen; } /* * lxc.network.0.XXX, where XXX can be: name, type, link, flags, type, * macvlan.mode, veth.pair, vlan, ipv4, ipv6, script.up, hwaddr, mtu, * ipv4.gateway, ipv6.gateway. ipvX.gateway can return 'auto' instead * of an address. ipv4 and ipv6 return lists (newline-separated). * things like veth.pair return '' if invalid (i.e. if called for vlan * type). */ static int get_config_network_item(const char *key, char *retv, int inlen, struct lxc_conf *c) { char *p1; int len, fulllen = 0; struct lxc_netdev *netdev; if (!retv) inlen = 0; else memset(retv, 0, inlen); if (!strncmp(key, "lxc.network.", 12)) key += 12; else return -1; p1 = strchr(key, '.'); if (!p1 || *(p1 + 1) == '\0') return -1; p1++; netdev = get_netdev_from_key(key, &c->network); if (!netdev) return -1; if (strcmp(p1, "name") == 0) { if (netdev->name[0] != '\0') strprint(retv, inlen, "%s", netdev->name); } else if (strcmp(p1, "type") == 0) { strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); } else if (strcmp(p1, "link") == 0) { if (netdev->link[0] != '\0') strprint(retv, inlen, "%s", netdev->link); } else if (strcmp(p1, "flags") == 0) { if (netdev->flags & IFF_UP) strprint(retv, inlen, "up"); } else if (strcmp(p1, "script.up") == 0) { if (netdev->upscript) strprint(retv, inlen, "%s", netdev->upscript); } else if (strcmp(p1, "script.down") == 0) { if (netdev->downscript) strprint(retv, inlen, "%s", netdev->downscript); } else if (strcmp(p1, "hwaddr") == 0) { if (netdev->hwaddr) strprint(retv, inlen, "%s", netdev->hwaddr); } else if (strcmp(p1, "mtu") == 0) { if (netdev->mtu) strprint(retv, inlen, "%s", netdev->mtu); } else if (strcmp(p1, "macvlan.mode") == 0) { if (netdev->type == LXC_NET_MACVLAN) { const char *mode; switch (netdev->priv.macvlan_attr.mode) { case MACVLAN_MODE_PRIVATE: mode = "private"; break; case MACVLAN_MODE_VEPA: mode = "vepa"; break; case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; case MACVLAN_MODE_PASSTHRU: mode = "passthru"; break; default: mode = "(invalid)"; break; } strprint(retv, inlen, "%s", mode); } } else if (strcmp(p1, "veth.pair") == 0) { if (netdev->type == LXC_NET_VETH) { strprint(retv, inlen, "%s", netdev->priv.veth_attr.pair[0] != '\0' ? netdev->priv.veth_attr.pair : netdev->priv.veth_attr.veth1); } } else if (strcmp(p1, "vlan") == 0) { if (netdev->type == LXC_NET_VLAN) { strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); } } else if (strcmp(p1, "ipv4.gateway") == 0) { if (netdev->ipv4_gateway_auto) { strprint(retv, inlen, "auto"); } else if (netdev->ipv4_gateway) { char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); strprint(retv, inlen, "%s", buf); } } else if (strcmp(p1, "ipv4") == 0) { struct lxc_list *it2; lxc_list_for_each(it2, &netdev->ipv4) { struct lxc_inetdev *i = it2->elem; char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); strprint(retv, inlen, "%s/%d\n", buf, i->prefix); } } else if (strcmp(p1, "ipv6.gateway") == 0) { if (netdev->ipv6_gateway_auto) { strprint(retv, inlen, "auto"); } else if (netdev->ipv6_gateway) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); strprint(retv, inlen, "%s", buf); } } else if (strcmp(p1, "ipv6") == 0) { struct lxc_list *it2; lxc_list_for_each(it2, &netdev->ipv6) { struct lxc_inet6dev *i = it2->elem; char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); strprint(retv, inlen, "%s/%d\n", buf, i->prefix); } } return fulllen; } static int get_config_cap_drop(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->caps) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_cap_keep(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->keepcaps) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_console(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->console.path); } static int get_config_console_logfile(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->console.log_path); } static int get_config_seccomp(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->seccomp); } static int get_config_autodev(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->autodev); } static int get_config_haltsignal(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->haltsignal); } static int get_config_rebootsignal(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->rebootsignal); } static int get_config_stopsignal(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->stopsignal); } static int get_config_start(const char *key, char *retv, int inlen, struct lxc_conf *c) { if (strcmp(key + 10, "auto") == 0) return lxc_get_conf_int(c, retv, inlen, c->start_auto); else if (strcmp(key + 10, "delay") == 0) return lxc_get_conf_int(c, retv, inlen, c->start_delay); else if (strcmp(key + 10, "order") == 0) return lxc_get_conf_int(c, retv, inlen, c->start_order); return -1; } static int get_config_monitor(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->monitor_unshare); } static int get_config_group(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->groups) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_environment(const char *key, char *retv, int inlen, struct lxc_conf *c) { int len, fulllen = 0; struct lxc_list *it; if (!retv) inlen = 0; else memset(retv, 0, inlen); lxc_list_for_each(it, &c->environment) { strprint(retv, inlen, "%s\n", (char *)it->elem); } return fulllen; } static int get_config_init_cmd(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_str(retv, inlen, c->init_cmd); } static int get_config_init_uid(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->init_uid); } static int get_config_init_gid(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->init_gid); } static int get_config_ephemeral(const char *key, char *retv, int inlen, struct lxc_conf *c) { return lxc_get_conf_int(c, retv, inlen, c->ephemeral); } /* Callbacks to clear config items. */ static inline int clr_config_personality(const char *key, struct lxc_conf *c, void *data) { c->personality = -1; return 0; } static inline int clr_config_pts(const char *key, struct lxc_conf *c, void *data) { c->pts = 0; return 0; } static inline int clr_config_tty(const char *key, struct lxc_conf *c, void *data) { c->tty = 0; return 0; } static inline int clr_config_ttydir(const char *key, struct lxc_conf *c, void *data) { free(c->ttydir); c->ttydir = NULL; return 0; } static inline int clr_config_kmsg(const char *key, struct lxc_conf *c, void *data) { c->kmsg = 0; return 0; } static inline int clr_config_lsm_aa_profile(const char *key, struct lxc_conf *c, void *data) { free(c->lsm_aa_profile); c->lsm_aa_profile = NULL; return 0; } static inline int clr_config_lsm_aa_incomplete(const char *key, struct lxc_conf *c, void *data) { c->lsm_aa_allow_incomplete = 0; return 0; } static inline int clr_config_lsm_se_context(const char *key, struct lxc_conf *c, void *data) { free(c->lsm_se_context); c->lsm_se_context = NULL; return 0; } static inline int clr_config_cgroup(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_cgroups(c, key); } static inline int clr_config_idmaps(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_idmaps(c); } static inline int clr_config_loglevel(const char *key, struct lxc_conf *c, void *data) { c->loglevel = LXC_LOG_LEVEL_NOTSET; return 0; } static inline int clr_config_logfile(const char *key, struct lxc_conf *c, void *data) { free(c->logfile); c->logfile = NULL; return 0; } static inline int clr_config_mount(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_mount_entries(c); } static inline int clr_config_mount_auto(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_automounts(c); } static inline int clr_config_fstab(const char *key, struct lxc_conf *c, void *data) { free(c->fstab); c->fstab = NULL; return 0; } static inline int clr_config_rootfs(const char *key, struct lxc_conf *c, void *data) { free(c->rootfs.path); c->rootfs.path = NULL; return 0; } static inline int clr_config_rootfs_mount(const char *key, struct lxc_conf *c, void *data) { free(c->rootfs.mount); c->rootfs.mount = NULL; return 0; } static inline int clr_config_rootfs_options(const char *key, struct lxc_conf *c, void *data) { free(c->rootfs.options); c->rootfs.options = NULL; return 0; } static inline int clr_config_rootfs_backend(const char *key, struct lxc_conf *c, void *data) { free(c->rootfs.bdev_type); c->rootfs.bdev_type = NULL; return 0; } static inline int clr_config_pivotdir(const char *key, struct lxc_conf *c, void *data) { return 0; } static inline int clr_config_utsname(const char *key, struct lxc_conf *c, void *data) { free(c->utsname); c->utsname = NULL; return 0; } static inline int clr_config_hooks(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_hooks(c, key); } static inline int clr_config_network_item(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_nic(c, key + 12); } static inline int clr_config_network(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_config_network(c); } static inline int clr_config_cap_drop(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_config_caps(c); } static inline int clr_config_cap_keep(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_config_keepcaps(c); } static inline int clr_config_console(const char *key, struct lxc_conf *c, void *data) { free(c->console.path); c->console.path = NULL; return 0; } static inline int clr_config_console_logfile(const char *key, struct lxc_conf *c, void *data) { free(c->console.log_path); c->console.log_path = NULL; return 0; } static inline int clr_config_seccomp(const char *key, struct lxc_conf *c, void *data) { free(c->seccomp); c->seccomp = NULL; return 0; } static inline int clr_config_autodev(const char *key, struct lxc_conf *c, void *data) { c->autodev = 1; return 0; } static inline int clr_config_haltsignal(const char *key, struct lxc_conf *c, void *data) { c->haltsignal = 0; return 0; } static inline int clr_config_rebootsignal(const char *key, struct lxc_conf *c, void *data) { c->rebootsignal = 0; return 0; } static inline int clr_config_stopsignal(const char *key, struct lxc_conf *c, void *data) { c->stopsignal = 0; return 0; } static inline int clr_config_start(const char *key, struct lxc_conf *c, void *data) { if (strcmp(key + 10, "auto") == 0) c->start_auto = 0; else if (strcmp(key + 10, "delay") == 0) c->start_delay = 0; else if (strcmp(key + 10, "order") == 0) c->start_order = 0; return 0; } static inline int clr_config_monitor(const char *key, struct lxc_conf *c, void *data) { c->monitor_unshare = 0; return 0; } static inline int clr_config_group(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_groups(c); } static inline int clr_config_environment(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_environment(c); } static inline int clr_config_init_cmd(const char *key, struct lxc_conf *c, void *data) { free(c->init_cmd); c->init_cmd = NULL; return 0; } static inline int clr_config_init_uid(const char *key, struct lxc_conf *c, void *data) { c->init_uid = 0; return 0; } static inline int clr_config_init_gid(const char *key, struct lxc_conf *c, void *data) { c->init_gid = 0; return 0; } static inline int clr_config_ephemeral(const char *key, struct lxc_conf *c, void *data) { c->ephemeral = 0; return 0; } static inline int clr_config_includefiles(const char *key, struct lxc_conf *c, void *data) { lxc_clear_includes(c); return 0; } static int get_config_includefiles(const char *key, char *retv, int inlen, struct lxc_conf *c) { return -ENOSYS; } int lxc_list_config_items(char *retv, int inlen) { size_t i; int len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); for (i = 0; i < config_size; i++) { char *s = config[i].name; if (s[strlen(s) - 1] == '.') continue; strprint(retv, inlen, "%s\n", s); } return fulllen; } lxc-2.0.11/src/lxc/rtnl.h0000644061062106075000000000633513435013473012031 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_RTNL_H #define __LXC_RTNL_H /* * Use this as a good size to allocate route netlink messages */ #define RTNLMSG_GOOD_SIZE NLMSG_GOOD_SIZE #define RTNLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + RTNL_HDRLEN)) /* * struct genl_handler : the structure which store the netlink handler * and the family number * * @nlh: the netlink socket handler */ struct rtnl_handler { struct nl_handler nlh; }; /* * struct rtnlmsg : the struct containing the route netlink message * format * * @nlmsghdr: a netlink message header * @rtnlmsghdr: a route netlink message header pointer * */ struct rtnlmsg { struct nlmsghdr nlmsghdr; }; /* * rtnetlink_open : open a route netlink socket * * @handler: a struct rtnl_handler pointer * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_open(struct rtnl_handler *handler); /* * genetlink_close : close a route netlink socket * * @handler: the handler of the socket to be closed * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_close(struct rtnl_handler *handler); /* * rtnetlink_rcv : receive a route netlink socket, it is up * to the caller to manage the allocation of the route netlink message * * @handler: the handler of the route netlink socket * @rtnlmsg: the pointer to a route netlink message pre-allocated * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); /* * rtnetlink_send : send a route netlink socket, it is up * to the caller to manage the allocation of the route netlink message * * @handler: the handler of the route netlink socket * @rtnlmsg: the pointer to a netlink message pre-allocated * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); struct genlmsg *genlmsg_alloc(size_t size); extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg); /* * rtnetlink_transaction : send and receive a route netlink message in one shot * * @handler: the handler of the route netlink socket * @request: a route netlink message containing the request to be sent * @answer: a pre-allocated route netlink message to receive the response * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_transaction(struct rtnl_handler *handler, struct rtnlmsg *request, struct rtnlmsg *answer); #endif /* __LXC_RTNL_H */ lxc-2.0.11/src/lxc/state.h0000644061062106075000000000247013435013473012166 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_STATE_H #define __LXC_STATE_H typedef enum { STOPPED, STARTING, RUNNING, STOPPING, ABORTING, FREEZING, FROZEN, THAWED, MAX_STATE, } lxc_state_t; extern lxc_state_t lxc_getstate(const char *name, const char *lxcpath); 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, const char *lxcpath); #endif lxc-2.0.11/src/lxc/macro.h0000644061062106075000000002215313435013473012147 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_MACRO_H #define __LXC_MACRO_H #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif /* Define __S_ISTYPE if missing from the C library. */ #ifndef __S_ISTYPE #define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) #endif /* capabilities */ #ifndef CAP_SYS_ADMIN #define CAP_SYS_ADMIN 21 #endif #ifndef CAP_SETFCAP #define CAP_SETFCAP 31 #endif #ifndef CAP_MAC_OVERRIDE #define CAP_MAC_OVERRIDE 32 #endif #ifndef CAP_MAC_ADMIN #define CAP_MAC_ADMIN 33 #endif #ifndef CAP_SETUID #define CAP_SETUID 7 #endif #ifndef CAP_SETGID #define CAP_SETGID 6 #endif /* prctl */ #ifndef PR_CAPBSET_READ #define PR_CAPBSET_READ 23 #endif #ifndef PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif /* Control the ambient capability set */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 #endif #ifndef PR_CAP_AMBIENT_IS_SET #define PR_CAP_AMBIENT_IS_SET 1 #endif #ifndef PR_CAP_AMBIENT_RAISE #define PR_CAP_AMBIENT_RAISE 2 #endif #ifndef PR_CAP_AMBIENT_LOWER #define PR_CAP_AMBIENT_LOWER 3 #endif #ifndef PR_CAP_AMBIENT_CLEAR_ALL #define PR_CAP_AMBIENT_CLEAR_ALL 4 #endif #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 #endif #ifndef PR_GET_NO_NEW_PRIVS #define PR_GET_NO_NEW_PRIVS 39 #endif /* filesystem magic values */ #ifndef CGROUP_SUPER_MAGIC #define CGROUP_SUPER_MAGIC 0x27e0eb #endif #ifndef CGROUP2_SUPER_MAGIC #define CGROUP2_SUPER_MAGIC 0x63677270 #endif #ifndef NSFS_MAGIC #define NSFS_MAGIC 0x6e736673 #endif /* current overlayfs */ #ifndef OVERLAY_SUPER_MAGIC #define OVERLAY_SUPER_MAGIC 0x794c7630 #endif /* legacy overlayfs */ #ifndef OVERLAYFS_SUPER_MAGIC #define OVERLAYFS_SUPER_MAGIC 0x794c764f #endif /* Calculate the number of chars needed to represent a given integer as a C * string. Include room for '-' to indicate negative numbers and the \0 byte. * This is based on systemd. */ #define INTTYPE_TO_STRLEN(type) \ (2 + (sizeof(type) <= 1 \ ? 3 \ : sizeof(type) <= 2 \ ? 5 \ : sizeof(type) <= 4 \ ? 10 \ : sizeof(type) <= 8 \ ? 20 \ : sizeof(int[-2 * (sizeof(type) > 8)]))) /* Useful macros */ #define LXC_LINELEN 4096 #define LXC_IDMAPLEN 4096 #define LXC_MAX_BUFFER 4096 #define LXC_NUMSTRLEN64 (INTTYPE_TO_STRLEN(intmax_t)) /* /proc/ = 6 * + * = INTTYPE_TO_STRLEN(pid_t) * + * /fd/ = 4 * + * = INTTYPE_TO_STRLEN(int) * + * \0 = 1 */ #define LXC_PROC_PID_FD_LEN \ (6 + INTTYPE_TO_STRLEN(pid_t) + 4 + INTTYPE_TO_STRLEN(int) + 1) /* /proc/ = 6 * + * = INTTYPE_TO_STRLEN(pid_t) * + * /status = 7 * + * \0 = 1 */ #define LXC_PROC_STATUS_LEN (6 + INTTYPE_TO_STRLEN(pid_t) + 7 + 1) /* /proc/ = 6 * + * = INTTYPE_TO_STRLEN(pid_t) * + * /attr/ = 6 * + * /current = 8 * + * \0 = 1 */ #define LXC_LSMATTRLEN (6 + INTTYPE_TO_STRLEN(pid_t) + 6 + 8 + 1) #define LXC_CMD_DATA_MAX (PATH_MAX * 2) /* loop devices */ #ifndef LO_FLAGS_AUTOCLEAR #define LO_FLAGS_AUTOCLEAR 4 #endif #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif /* memfd_create() */ #ifndef MFD_CLOEXEC #define MFD_CLOEXEC 0x0001U #endif #ifndef MFD_ALLOW_SEALING #define MFD_ALLOW_SEALING 0x0002U #endif /** * BUILD_BUG_ON - break compile if a condition is true. * @condition: the condition which the compiler should know is false. * * If you have some code which relies on certain constants being equal, or * other compile-time-evaluated condition, you should use BUILD_BUG_ON to * detect if someone changes it. * * The implementation uses gcc's reluctance to create a negative array, but * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments * to inline functions). So as a fallback we use the optimizer; if it can't * prove the condition is false, it will cause a link error on the undefined * "__build_bug_on_failed". This error message can be harder to track down * though, hence the two different methods. */ #ifndef __OPTIMIZE__ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) #else extern int __build_bug_on_failed; #define BUILD_BUG_ON(condition) \ do { \ ((void)sizeof(char[1 - 2 * !!(condition)])); \ if (condition) \ __build_bug_on_failed = 1; \ } while (0) #endif #define lxc_iterate_parts(__iterator, __splitme, __separators) \ for (char *__p = NULL, *__it = strtok_r(__splitme, __separators, &__p); \ (__iterator = __it); \ __iterator = __it = strtok_r(NULL, __separators, &__p)) #define prctl_arg(x) ((unsigned long)x) /* networking */ #ifndef NETLINK_DUMP_STRICT_CHK #define NETLINK_DUMP_STRICT_CHK 12 #endif #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #ifndef IFLA_LINKMODE #define IFLA_LINKMODE 17 #endif #ifndef IFLA_LINKINFO #define IFLA_LINKINFO 18 #endif #ifndef IFLA_NET_NS_PID #define IFLA_NET_NS_PID 19 #endif #ifndef IFLA_INFO_KIND #define IFLA_INFO_KIND 1 #endif #ifndef IFLA_VLAN_ID #define IFLA_VLAN_ID 1 #endif #ifndef IFLA_INFO_DATA #define IFLA_INFO_DATA 2 #endif #ifndef VETH_INFO_PEER #define VETH_INFO_PEER 1 #endif #ifndef IFLA_MACVLAN_MODE #define IFLA_MACVLAN_MODE 1 #endif #ifndef IFLA_NEW_NETNSID #define IFLA_NEW_NETNSID 45 #endif #ifdef IFLA_IF_NETNSID #ifndef IFLA_TARGET_NETNSID #define IFLA_TARGET_NETNSID = IFLA_IF_NETNSID #endif #else #define IFLA_IF_NETNSID 46 #define IFLA_TARGET_NETNSID 46 #endif #ifndef IFA_TARGET_NETNSID #define IFA_TARGET_NETNSID 10 #endif #ifndef IFLA_STATS #define IFLA_STATS 7 #endif #ifndef IFLA_STATS64 #define IFLA_STATS64 23 #endif #ifndef RTM_NEWNSID #define RTM_NEWNSID 88 #endif #ifndef RTM_GETNSID #define RTM_GETNSID 90 #endif #ifndef NLMSG_ERROR #define NLMSG_ERROR 0x2 #endif #ifndef MACVLAN_MODE_PRIVATE #define MACVLAN_MODE_PRIVATE 1 #endif #ifndef MACVLAN_MODE_VEPA #define MACVLAN_MODE_VEPA 2 #endif #ifndef MACVLAN_MODE_BRIDGE #define MACVLAN_MODE_BRIDGE 4 #endif #ifndef MACVLAN_MODE_PASSTHRU #define MACVLAN_MODE_PASSTHRU 8 #endif /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ enum { __LXC_NETNSA_NONE, #define __LXC_NETNSA_NSID_NOT_ASSIGNED -1 __LXC_NETNSA_NSID, __LXC_NETNSA_PID, __LXC_NETNSA_FD, __LXC_NETNSA_MAX, }; /* Length of abstract unix domain socket socket address. */ #define LXC_AUDS_ADDR_LEN sizeof(((struct sockaddr_un *)0)->sun_path) /* mount */ #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif #ifndef MS_SLAVE #define MS_SLAVE (1 << 19) #endif #ifndef MS_LAZYTIME #define MS_LAZYTIME (1<<25) #endif #ifndef MS_REC #define MS_REC 16384 #endif /* open */ #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 #endif /* sockets */ #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 02000000 #endif /* pointer conversion macros */ #define PTR_TO_INT(p) ((int)((intptr_t)(p))) #define INT_TO_PTR(u) ((void *)((intptr_t)(u))) #define PTR_TO_INTMAX(p) ((intmax_t)((intptr_t)(p))) #define INTMAX_TO_PTR(u) ((void *)((intptr_t)(u))) #define LXC_INVALID_UID ((uid_t)-1) #define LXC_INVALID_GID ((gid_t)-1) #define STRLITERALLEN(x) (sizeof(""x"") - 1) #define STRARRAYLEN(x) (sizeof(x) - 1) /* Maximum number of bytes sendfile() is able to send in one go. */ #define LXC_SENDFILE_MAX 0x7ffff000 #define move_ptr(ptr) \ ({ \ typeof(ptr) __internal_ptr__ = (ptr); \ (ptr) = NULL; \ __internal_ptr__; \ }) #define move_fd(fd) \ ({ \ int __internal_fd__ = (fd); \ (fd) = -EBADF; \ __internal_fd__; \ }) #endif /* __LXC_MACRO_H */ lxc-2.0.11/src/lxc/nl.c0000644061062106075000000001556213435013473011460 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "nl.h" #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) extern size_t nlmsg_len(const struct nlmsg *nlmsg) { return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN; } extern void *nlmsg_data(struct nlmsg *nlmsg) { char *data = ((char *)nlmsg) + NLMSG_HDRLEN; if (!nlmsg_len(nlmsg)) return NULL; return data; } static int nla_put(struct nlmsg *nlmsg, int attr, const void *data, size_t len) { struct rtattr *rta; size_t rtalen = RTA_LENGTH(len); size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen); if (tlen > nlmsg->cap) return -ENOMEM; rta = NLMSG_TAIL(nlmsg->nlmsghdr); rta->rta_type = attr; rta->rta_len = rtalen; if (data && len) memcpy(RTA_DATA(rta), data, len); nlmsg->nlmsghdr->nlmsg_len = tlen; return 0; } extern int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size) { return nla_put(nlmsg, attr, data, size); } extern int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string) { return nla_put(nlmsg, attr, string, strlen(string) + 1); } extern int nla_put_u32(struct nlmsg *nlmsg, int attr, int value) { return nla_put(nlmsg, attr, &value, sizeof(value)); } extern int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value) { return nla_put(nlmsg, attr, &value, 2); } extern int nla_put_attr(struct nlmsg *nlmsg, int attr) { return nla_put(nlmsg, attr, NULL, 0); } struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr) { struct rtattr *rtattr = NLMSG_TAIL(nlmsg->nlmsghdr); if (nla_put_attr(nlmsg, attr)) return NULL; return rtattr; } void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr) { attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr; } extern struct nlmsg *nlmsg_alloc(size_t size) { struct nlmsg *nlmsg; size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size); nlmsg = (struct nlmsg *)malloc(sizeof(struct nlmsg)); if (!nlmsg) return NULL; nlmsg->nlmsghdr = (struct nlmsghdr *)malloc(len); if (!nlmsg->nlmsghdr) goto errout; memset(nlmsg->nlmsghdr, 0, len); nlmsg->cap = len; nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN; return nlmsg; errout: free(nlmsg); return NULL; } extern void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len) { void *buf; size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len; size_t tlen = NLMSG_ALIGN(len); if (nlmsg_len + tlen > nlmsg->cap) return NULL; buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len; nlmsg->nlmsghdr->nlmsg_len += tlen; if (tlen > len) memset(buf + len, 0, tlen - len); return buf; } extern struct nlmsg *nlmsg_alloc_reserve(size_t size) { struct nlmsg *nlmsg; nlmsg = nlmsg_alloc(size); if (!nlmsg) return NULL; // just set message length to cap directly nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap; return nlmsg; } extern void nlmsg_free(struct nlmsg *nlmsg) { if (!nlmsg) return; free(nlmsg->nlmsghdr); free(nlmsg); } extern int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer) { int ret; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = answer->nlmsghdr, .iov_len = answer->nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; again: ret = recvmsg(handler->fd, &msg, 0); if (ret < 0) { if (errno == EINTR) goto again; return -errno; } if (!ret) return 0; if (msg.msg_flags & MSG_TRUNC && ret == answer->nlmsghdr->nlmsg_len) return -EMSGSIZE; return ret; } extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg) { struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = nlmsg->nlmsghdr, .iov_len = nlmsg->nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; int ret; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; ret = sendmsg(handler->fd, &msg, 0); if (ret < 0) return -errno; return ret; } #ifndef NLMSG_ERROR #define NLMSG_ERROR 0x2 #endif extern int netlink_transaction(struct nl_handler *handler, struct nlmsg *request, struct nlmsg *answer) { int ret; ret = netlink_send(handler, request); if (ret < 0) return ret; ret = netlink_rcv(handler, answer); if (ret < 0) return ret; if (answer->nlmsghdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(answer->nlmsghdr); return err->error; } return 0; } extern int netlink_open(struct nl_handler *handler, int protocol) { socklen_t socklen; int sndbuf = 32768; int rcvbuf = 32768; int err; memset(handler, 0, sizeof(*handler)); handler->fd = socket(AF_NETLINK, SOCK_RAW, protocol); if (handler->fd < 0) return -errno; if (setsockopt(handler->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) goto err_with_errno; if (setsockopt(handler->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,sizeof(rcvbuf)) < 0) goto err_with_errno; memset(&handler->local, 0, sizeof(handler->local)); handler->local.nl_family = AF_NETLINK; handler->local.nl_groups = 0; if (bind(handler->fd, (struct sockaddr*)&handler->local, sizeof(handler->local)) < 0) goto err_with_errno; socklen = sizeof(handler->local); if (getsockname(handler->fd, (struct sockaddr*)&handler->local, &socklen) < 0) goto err_with_errno; if (socklen != sizeof(handler->local)) { err = -EINVAL; goto errclose; } if (handler->local.nl_family != AF_NETLINK) { err = -EINVAL; goto errclose; } handler->seq = time(NULL); return 0; err_with_errno: err = -errno; errclose: close(handler->fd); return err; } extern int netlink_close(struct nl_handler *handler) { close(handler->fd); handler->fd = -1; return 0; } lxc-2.0.11/src/lxc/start.c0000644061062106075000000014336313435013473012205 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBCAP #include #endif #if !HAVE_DECL_PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif #include "af_unix.h" #include "caps.h" #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "confile_utils.h" #include "console.h" #include "error.h" #include "list.h" #include "log.h" #include "lxccontainer.h" #include "lxclock.h" #include "lxcseccomp.h" #include "lxcutmp.h" #include "mainloop.h" #include "monitor.h" #include "namespace.h" #include "network.h" #include "start.h" #include "sync.h" #include "utils.h" #include "lsm/lsm.h" #include "storage/storage.h" #include "storage/storage_utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(lxc_start, lxc); extern void mod_all_rdeps(struct lxc_container *c, bool inc); static bool do_destroy_container(struct lxc_handler *handler); static int lxc_rmdir_onedev_wrapper(void *data); static void lxc_destroy_container_on_signal(struct lxc_handler *handler, const char *name); static void print_top_failing_dir(const char *path) { int ret; size_t len; char *copy, *e, *p, saved; len = strlen(path); copy = alloca(len + 1); (void)strlcpy(copy, path, len + 1); p = copy; e = copy + len; while (p < e) { while (p < e && *p == '/') p++; while (p < e && *p != '/') p++; saved = *p; *p = '\0'; ret = access(copy, X_OK); if (ret != 0) { SYSERROR("Could not access %s. Please grant it x " "access, or add an ACL for the container " "root", copy); return; } *p = saved; } } static void close_ns(int ns_fd[LXC_NS_MAX]) { int i; for (i = 0; i < LXC_NS_MAX; i++) { if (ns_fd[i] < 0) continue; close(ns_fd[i]); ns_fd[i] = -EBADF; } } /* preserve_ns: open /proc/@pid/ns/@ns for each namespace specified * in clone_flags. * Return true on success, false on failure. */ static bool preserve_ns(int ns_fd[LXC_NS_MAX], int clone_flags, pid_t pid) { int i; for (i = 0; i < LXC_NS_MAX; i++) ns_fd[i] = -EBADF; for (i = 0; i < LXC_NS_MAX; i++) { if ((clone_flags & ns_info[i].clone_flag) == 0) continue; ns_fd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name); if (ns_fd[i] < 0) goto error; } return true; error: if (errno == ENOENT) SYSERROR("Kernel does not support attaching to %s namespaces", ns_info[i].proc_name); else SYSERROR("Failed to open file descriptor for %s namespace", ns_info[i].proc_name); close_ns(ns_fd); return false; } static int attach_ns(const int ns_fd[LXC_NS_MAX]) { int i; for (i = 0; i < LXC_NS_MAX; i++) { if (ns_fd[i] < 0) continue; if (setns(ns_fd[i], 0) != 0) goto error; } return 0; error: SYSERROR("Failed to attach %s namespace.", ns_info[i].proc_name); return -1; } static int match_fd(int fd) { return (fd == 0 || fd == 1 || fd == 2); } int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int *fds_to_ignore, size_t len_fds) { int fd, fddir; size_t i; DIR *dir; struct dirent *direntp; if (conf && conf->close_all_fds) closeall = true; restart: dir = opendir("/proc/self/fd"); if (!dir) { WARN("%s - Failed to open directory", strerror(errno)); return -1; } fddir = dirfd(dir); while ((direntp = readdir(dir))) { int ret; if (strcmp(direntp->d_name, ".") == 0) continue; if (strcmp(direntp->d_name, "..") == 0) continue; ret = lxc_safe_int(direntp->d_name, &fd); if (ret < 0) { INFO("Could not parse file descriptor for \"%s\"", direntp->d_name); continue; } for (i = 0; i < len_fds; i++) if (fds_to_ignore[i] == fd) break; if (fd == fddir || fd == lxc_log_fd || (i < len_fds && fd == fds_to_ignore[i])) continue; if (conf) { for (i = 0; i < LXC_NS_MAX; i++) if (conf->inherit_ns_fd[i] == fd) break; if (i < LXC_NS_MAX) continue; } if (current_config && fd == current_config->logfd) continue; if (match_fd(fd)) continue; if (closeall) { close(fd); closedir(dir); INFO("Closed inherited fd: %d.", fd); goto restart; } WARN("Inherited fd: %d.", fd); } closedir(dir); /* cannot fail */ return 0; } static int setup_signal_fd(sigset_t *oldmask) { int ret, sig; sigset_t mask; int signals[] = {SIGBUS, SIGILL, SIGSEGV, SIGWINCH}; /* Block everything except serious error signals. */ ret = sigfillset(&mask); if (ret < 0) return -EBADF; for (sig = 0; sig < (sizeof(signals) / sizeof(signals[0])); sig++) { ret = sigdelset(&mask, signals[sig]); if (ret < 0) return -EBADF; } ret = pthread_sigmask(SIG_BLOCK, &mask, oldmask); if (ret < 0) { SYSERROR("Failed to set signal mask"); return -EBADF; } ret = signalfd(-1, &mask, SFD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create signal file descriptor"); return -EBADF; } TRACE("Created signal file descriptor %d", ret); return ret; } static int signal_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ret; siginfo_t info; struct signalfd_siginfo siginfo; struct lxc_handler *hdlr = data; ret = lxc_read_nointr(fd, &siginfo, sizeof(siginfo)); if (ret < 0) { ERROR("Failed to read signal info from signal file descriptor %d", fd); return -1; } if (ret != sizeof(siginfo)) { ERROR("Unexpected size for struct signalfd_siginfo"); return -EINVAL; } /* Check whether init is running. */ info.si_pid = 0; ret = waitid(P_PID, hdlr->pid, &info, WEXITED | WNOWAIT | WNOHANG); if (ret == 0 && info.si_pid == hdlr->pid) hdlr->init_died = true; /* Try to figure out a reasonable exit status to report. */ if (hdlr->init_died) { switch (info.si_code) { case CLD_EXITED: hdlr->exit_status = info.si_status << 8; break; case CLD_KILLED: case CLD_DUMPED: case CLD_STOPPED: hdlr->exit_status = info.si_status << 8 | 0x7f; break; case CLD_CONTINUED: /* Huh? The waitid() told us it's dead *and* continued? */ WARN("Init %d dead and continued?", hdlr->pid); hdlr->exit_status = 1; break; default: ERROR("Unknown si_code: %d", info.si_code); hdlr->exit_status = 1; } } if (siginfo.ssi_signo == SIGHUP) { kill(hdlr->pid, SIGTERM); INFO("Killing %d since terminal hung up", hdlr->pid); return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } if (siginfo.ssi_signo != SIGCHLD) { kill(hdlr->pid, siginfo.ssi_signo); INFO("Forwarded signal %d to pid %d", siginfo.ssi_signo, hdlr->pid); return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } /* More robustness, protect ourself from a SIGCHLD sent * by a process different from the container init. */ if (siginfo.ssi_pid != hdlr->pid) { NOTICE("Received %d from pid %d instead of container init %d", siginfo.ssi_signo, siginfo.ssi_pid, hdlr->pid); return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } if (siginfo.ssi_code == CLD_STOPPED) { INFO("Container init process was stopped"); return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } else if (siginfo.ssi_code == CLD_CONTINUED) { INFO("Container init process was continued"); return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } DEBUG("Container init process %d exited", hdlr->pid); return LXC_MAINLOOP_CLOSE; } int lxc_serve_state_clients(const char *name, struct lxc_handler *handler, lxc_state_t state) { size_t retlen; ssize_t ret; struct lxc_list *cur, *next; struct state_client *client; struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; if (state == THAWED) handler->state = RUNNING; else handler->state = state; TRACE("Set container state to %s", lxc_state2str(state)); if (lxc_list_empty(&handler->state_clients)) { TRACE("No state clients registered"); return 0; } retlen = strlcpy(msg.name, name, sizeof(msg.name)); if (retlen >= sizeof(msg.name)) return -E2BIG; lxc_list_for_each_safe(cur, &handler->state_clients, next) { client = cur->elem; if (!client->states[state]) { TRACE("State %s not registered for state client %d", lxc_state2str(state), client->clientfd); continue; } TRACE("Sending state %s to state client %d", lxc_state2str(state), client->clientfd); again: ret = send(client->clientfd, &msg, sizeof(msg), 0); if (ret <= 0) { if (errno == EINTR) { TRACE("Caught EINTR; retrying"); goto again; } ERROR("%s - Failed to send message to client", strerror(errno)); } /* kick client from list */ lxc_list_del(cur); close(client->clientfd); free(cur->elem); free(cur); } return 0; } static int lxc_serve_state_socket_pair(const char *name, struct lxc_handler *handler, lxc_state_t state) { ssize_t ret; if (!handler->backgrounded || handler->state_socket_pair[1] < 0 || state == STARTING) return 0; /* Close read end of the socket pair. */ close(handler->state_socket_pair[0]); handler->state_socket_pair[0] = -1; again: ret = lxc_abstract_unix_send_credential(handler->state_socket_pair[1], &(int){state}, sizeof(int)); if (ret != sizeof(int)) { if (errno == EINTR) goto again; SYSERROR("Failed to send state to %d", handler->state_socket_pair[1]); return -1; } TRACE("Sent container state \"%s\" to %d", lxc_state2str(state), handler->state_socket_pair[1]); /* Close write end of the socket pair. */ close(handler->state_socket_pair[1]); handler->state_socket_pair[1] = -1; return 0; } int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state) { int ret; ret = lxc_serve_state_socket_pair(name, handler, state); if (ret < 0) { ERROR("Failed to synchronize via anonymous pair of unix sockets"); return -1; } ret = lxc_serve_state_clients(name, handler, state); if (ret < 0) return -1; /* This function will try to connect to the legacy lxc-monitord state * server and only exists for backwards compatibility. */ lxc_monitor_send_state(name, state, handler->lxcpath); return 0; } int lxc_poll(const char *name, struct lxc_handler *handler) { int ret; bool has_console = true; struct lxc_epoll_descr descr, descr_console; if (handler->conf->console.path && strcmp(handler->conf->console.path, "none") == 0) has_console = false; ret = lxc_mainloop_open(&descr); if (ret < 0) { ERROR("Failed to create mainloop"); goto out_sigfd; } if (has_console) { ret = lxc_mainloop_open(&descr_console); if (ret < 0) { ERROR("Failed to create console mainloop"); goto out_mainloop; } } ret = lxc_mainloop_add_handler(&descr, handler->sigfd, signal_handler, handler); if (ret < 0) { ERROR("Failed to add signal handler for %d to mainloop", handler->sigfd); goto out_mainloop_console; } if (handler->conf->need_utmp_watch) { #if HAVE_LIBCAP if (lxc_utmp_mainloop_add(&descr, handler)) { ERROR("Failed to add utmp handler to LXC mainloop."); goto out_mainloop_console; } #else DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support."); #endif } if (has_console) { struct lxc_console *console = &handler->conf->console; ret = lxc_console_mainloop_add(&descr, console); if (ret < 0) { ERROR("Failed to add console handlers to mainloop"); goto out_mainloop_console; } ret = lxc_console_mainloop_add(&descr_console, console); if (ret < 0) { ERROR("Failed to add console handlers to console mainloop"); goto out_mainloop_console; } handler->conf->console.descr = &descr; } ret = lxc_cmd_mainloop_add(name, &descr, handler); if (ret < 0) { ERROR("Failed to add command handler to mainloop"); goto out_mainloop_console; } TRACE("Mainloop is ready"); ret = lxc_mainloop(&descr, -1); close(descr.epfd); descr.epfd = -EBADF; if (ret < 0 || !handler->init_died) goto out_mainloop_console; if (has_console) ret = lxc_mainloop(&descr_console, 0); out_mainloop_console: if (has_console) { lxc_mainloop_close(&descr_console); TRACE("Closed console mainloop"); } out_mainloop: lxc_mainloop_close(&descr); TRACE("Closed mainloop"); out_sigfd: close(handler->sigfd); TRACE("Closed signal file descriptor %d", handler->sigfd); handler->sigfd = -EBADF; return ret; } void lxc_zero_handler(struct lxc_handler *handler) { int i; memset(handler, 0, sizeof(struct lxc_handler)); handler->clone_flags = -1; handler->pinfd = -1; handler->sigfd = -1; for (i = 0; i < LXC_NS_MAX; i++) handler->nsfd[i] = -1; handler->data_sock[0] = -1; handler->data_sock[1] = -1; handler->state_socket_pair[0] = -1; handler->state_socket_pair[1] = -1; handler->sync_sock[0] = -1; handler->sync_sock[1] = -1; } static void lxc_put_nsfds(struct lxc_handler *handler) { int i; for (i = 0; i < LXC_NS_MAX; i++) { if (handler->nsfd[i] < 0) continue; close(handler->nsfd[i]); handler->nsfd[i] = -EBADF; } } void lxc_free_handler(struct lxc_handler *handler) { if (handler->pinfd >= 0) close(handler->pinfd); if (handler->sigfd >= 0) close(handler->sigfd); lxc_put_nsfds(handler); if (handler->conf && handler->conf->reboot == 0) if (handler->conf->maincmd_fd >= 0) close(handler->conf->maincmd_fd); if (handler->state_socket_pair[0] >= 0) close(handler->state_socket_pair[0]); if (handler->state_socket_pair[1] >= 0) close(handler->state_socket_pair[1]); handler->conf = NULL; free(handler); handler = NULL; } struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, const char *lxcpath, bool daemonize) { int i, ret; struct lxc_handler *handler; handler = malloc(sizeof(*handler)); if (!handler) return NULL; memset(handler, 0, sizeof(*handler)); /* Note that am_guest_unpriv() checks the effective uid. We * probably don't care if we are real root only if we are running * as root so this should be fine. */ handler->am_root = !am_guest_unpriv(); handler->data_sock[0] = handler->data_sock[1] = -1; handler->conf = conf; handler->lxcpath = lxcpath; handler->pinfd = -1; handler->sigfd = -EBADF; handler->init_died = false; handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1; lxc_list_init(&handler->state_clients); for (i = 0; i < LXC_NS_MAX; i++) handler->nsfd[i] = -1; handler->name = name; if (daemonize && !handler->conf->reboot) { /* Create socketpair() to synchronize on daemonized startup. * When the container reboots we don't need to synchronize * again currently so don't open another socketpair(). */ ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, handler->state_socket_pair); if (ret < 0) { ERROR("Failed to create anonymous pair of unix sockets"); goto on_error; } TRACE("Created anonymous pair {%d,%d} of unix sockets", handler->state_socket_pair[0], handler->state_socket_pair[1]); } handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command"); if (handler->conf->maincmd_fd < 0) { ERROR("Failed to set up command socket"); goto on_error; } TRACE("Unix domain socket %d for command server is ready", handler->conf->maincmd_fd); return handler; on_error: lxc_free_handler(handler); return NULL; } int lxc_init(const char *name, struct lxc_handler *handler) { int ret; const char *loglevel; struct lxc_conf *conf = handler->conf; lsm_init(); TRACE("Initialized LSM"); ret = lxc_read_seccomp_config(conf); if (ret < 0) { ERROR("Failed loading seccomp policy"); goto out_close_maincmd_fd; } TRACE("Read seccomp policy"); /* Begin by setting the state to STARTING. */ ret = lxc_set_state(name, handler, STARTING); if (ret < 0) { ERROR("Failed to set state to \"%s\"", lxc_state2str(STARTING)); goto out_close_maincmd_fd; } TRACE("set container state to \"STARTING\""); /* Start of environment variable setup for hooks. */ ret = setenv("LXC_NAME", name, 1); if (ret < 0) SYSERROR("Failed to set environment variable: LXC_NAME=%s", name); if (conf->rcfile) { ret = setenv("LXC_CONFIG_FILE", conf->rcfile, 1); if (ret < 0) SYSERROR("Failed to set environment variable: " "LXC_CONFIG_FILE=%s", conf->rcfile); } if (conf->rootfs.mount) { ret = setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1); if (ret < 0) SYSERROR("Failed to set environment variable: " "LXC_ROOTFS_MOUNT=%s", conf->rootfs.mount); } if (conf->rootfs.path) { ret = setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1); if (ret < 0) SYSERROR("Failed to set environment variable: " "LXC_ROOTFS_PATH=%s", conf->rootfs.path); } if (conf->console.path) { ret = setenv("LXC_CONSOLE", conf->console.path, 1); if (ret < 0) SYSERROR("Failed to set environment variable: " "LXC_CONSOLE=%s", conf->console.path); } if (conf->console.log_path) { ret = setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1); if (ret < 0) SYSERROR("Failed to set environment variable: " "LXC_CONSOLE_LOGPATH=%s", conf->console.log_path); } if (cgns_supported()) { ret = setenv("LXC_CGNS_AWARE", "1", 1); if (ret < 0) SYSERROR("Failed to set environment variable " "LXC_CGNS_AWARE=1"); } loglevel = lxc_log_priority_to_string(lxc_log_get_level()); if (setenv("LXC_LOG_LEVEL", loglevel, 1)) SYSERROR("Failed to set environment variable LXC_LOG_LEVEL=%s", loglevel); TRACE("Set environment variables"); ret = run_lxc_hooks(name, "pre-start", conf, handler->lxcpath, NULL); if (ret < 0) { ERROR("Failed to run lxc.hook.pre-start for container \"%s\"", name); goto out_aborting; } TRACE("Ran pre-start hooks"); /* The signal fd has to be created before forking otherwise if the child * process exits before we setup the signal fd, the event will be lost * and the command will be stuck. */ handler->sigfd = setup_signal_fd(&handler->oldmask); if (handler->sigfd < 0) { ERROR("Failed to setup SIGCHLD fd handler."); goto out_delete_tty; } TRACE("Set up signal fd"); /* Do this after setting up signals since it might unblock SIGWINCH. */ ret = lxc_console_create(conf); if (ret < 0) { ERROR("Failed to create console"); goto out_restore_sigmask; } TRACE("Created console"); ret = lxc_pty_map_ids(conf, &conf->console); if (ret < 0) { ERROR("Failed to chown console"); goto out_restore_sigmask; } TRACE("Chowned console"); INFO("Container \"%s\" is initialized", name); return 0; out_restore_sigmask: (void)pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL); out_delete_tty: lxc_delete_tty(&conf->tty_info); out_aborting: (void)lxc_set_state(name, handler, ABORTING); out_close_maincmd_fd: close(conf->maincmd_fd); conf->maincmd_fd = -1; return -1; } void lxc_fini(const char *name, struct lxc_handler *handler) { int i, ret; struct lxc_list *cur, *next; pid_t self = lxc_raw_getpid(); char *namespaces[LXC_NS_MAX + 1]; size_t namespace_count = 0; /* The STOPPING state is there for future cleanup code which can take * awhile. */ lxc_set_state(name, handler, STOPPING); for (i = 0; i < LXC_NS_MAX; i++) { if (handler->nsfd[i] != -1) { ret = asprintf(&namespaces[namespace_count], "%s:/proc/%d/fd/%d", ns_info[i].proc_name, self, handler->nsfd[i]); if (ret == -1) { SYSERROR("Failed to allocate memory."); break; } ++namespace_count; } } namespaces[namespace_count] = NULL; if (handler->conf->reboot && setenv("LXC_TARGET", "reboot", 1)) SYSERROR("Failed to set environment variable: LXC_TARGET=reboot."); if (!handler->conf->reboot && setenv("LXC_TARGET", "stop", 1)) SYSERROR("Failed to set environment variable: LXC_TARGET=stop."); if (run_lxc_hooks(name, "stop", handler->conf, handler->lxcpath, namespaces)) ERROR("Failed to run lxc.hook.stop for container \"%s\".", name); while (namespace_count--) free(namespaces[namespace_count]); for (i = 0; i < LXC_NS_MAX; i++) { if (handler->nsfd[i] != -1) { close(handler->nsfd[i]); handler->nsfd[i] = -1; } } if (handler->netnsfd >= 0) { close(handler->netnsfd); handler->netnsfd = -1; } cgroup_destroy(handler); /* For all new state clients simply close the command socket. * This will inform all state clients that the container is * STOPPED and also prevents a race between a open()/close() on * the command socket causing a new process to get ECONNREFUSED * because we haven't yet closed the command socket. */ close(handler->conf->maincmd_fd); handler->conf->maincmd_fd = -1; TRACE("Closed command socket"); /* This function will try to connect to the legacy lxc-monitord * state server and only exists for backwards compatibility. */ lxc_monitor_send_state(name, STOPPED, handler->lxcpath); /* The command socket is closed so no one can acces the command * socket anymore so there's no need to lock it. */ handler->state = STOPPED; TRACE("Set container state to \"STOPPED\""); if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) { ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name); if (handler->conf->reboot) { WARN("Container will be stopped instead of rebooted."); handler->conf->reboot = 0; if (setenv("LXC_TARGET", "stop", 1)) WARN("Failed to set environment variable: LXC_TARGET=stop."); } } /* Reset mask set by setup_signal_fd. */ ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL); if (ret < 0) WARN("%s - Failed to restore signal mask", strerror(errno)); lxc_console_delete(&handler->conf->console); lxc_delete_tty(&handler->conf->tty_info); /* The command socket is now closed, no more state clients can register * themselves from now on. So free the list of state clients. */ lxc_list_for_each_safe(cur, &handler->state_clients, next) { struct state_client *client = cur->elem; /* close state client socket */ close(client->clientfd); lxc_list_del(cur); free(cur->elem); free(cur); } if (handler->conf->ephemeral == 1 && handler->conf->reboot != 1) lxc_destroy_container_on_signal(handler, name); lxc_free_handler(handler); } void lxc_abort(const char *name, struct lxc_handler *handler) { int ret, status; lxc_set_state(name, handler, ABORTING); if (handler->pid > 0) { ret = kill(handler->pid, SIGKILL); if (ret < 0) SYSERROR("Failed to send SIGKILL to %d", handler->pid); } while ((ret = waitpid(-1, &status, 0)) > 0) { ; } } #include #include /* reboot(LINUX_REBOOT_CMD_CAD_ON) will return -EINVAL in a child pid namespace * if container reboot support exists. Otherwise, it will either succeed or * return -EPERM. */ static int container_reboot_supported(void *arg) { int *cmd = arg; int ret; ret = reboot(*cmd); if (ret == -1 && errno == EINVAL) return 1; return 0; } static int must_drop_cap_sys_boot(struct lxc_conf *conf) { FILE *f; int ret, cmd, v, flags; long stack_size = 4096; void *stack = alloca(stack_size); int status; pid_t pid; f = fopen("/proc/sys/kernel/ctrl-alt-del", "r"); if (!f) { DEBUG("failed to open /proc/sys/kernel/ctrl-alt-del"); return 1; } ret = fscanf(f, "%d", &v); fclose(f); if (ret != 1) { DEBUG("Failed to read /proc/sys/kernel/ctrl-alt-del."); return 1; } cmd = v ? LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF; flags = CLONE_NEWPID | SIGCHLD; if (!lxc_list_empty(&conf->id_map)) flags |= CLONE_NEWUSER; #ifdef __ia64__ pid = __clone2(container_reboot_supported, stack, stack_size, flags, &cmd); #else stack += stack_size; pid = clone(container_reboot_supported, stack, flags, &cmd); #endif if (pid < 0) { if (flags & CLONE_NEWUSER) ERROR("Failed to clone (%#x): %s (includes CLONE_NEWUSER).", flags, strerror(errno)); else ERROR("Failed to clone (%#x): %s.", flags, strerror(errno)); return -1; } if (wait(&status) < 0) { SYSERROR("Unexpected wait error: %s.", strerror(errno)); return -1; } if (WEXITSTATUS(status) != 1) return 1; return 0; } static int lxc_set_death_signal(int signal) { int ret; pid_t ppid; ret = prctl(PR_SET_PDEATHSIG, signal, 0, 0, 0); /* Check whether we have been orphaned. */ ppid = (pid_t)syscall(SYS_getppid); if (ppid == 1) { pid_t self; self = lxc_raw_getpid(); ret = kill(self, SIGKILL); if (ret < 0) return -1; } if (ret < 0) { SYSERROR("Failed to set PR_SET_PDEATHSIG to %d", signal); return -1; } return 0; } static int do_start(void *data) { int ret; struct lxc_list *iterator; char path[PATH_MAX]; bool have_cap_setgid; uid_t new_uid; gid_t new_gid; int devnull_fd = -1; struct lxc_handler *handler = data; lxc_sync_fini_parent(handler); /* This prctl must be before the synchro, so if the parent dies before * we set the parent death signal, we will detect its death with the * synchro right after, otherwise we have a window where the parent can * exit before we set the pdeath signal leading to a unsupervized * container. */ ret = lxc_set_death_signal(SIGKILL); if (ret < 0) { SYSERROR("Failed to set PR_SET_PDEATHSIG to SIGKILL"); goto out_warn_father; } ret = lxc_ambient_caps_up(); if (ret < 0) { ERROR("Failed to raise ambient capabilities"); goto out_warn_father; } ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL); if (ret < 0) { SYSERROR("Failed to set signal mask"); goto out_warn_father; } /* Don't leak the pinfd to the container. */ if (handler->pinfd >= 0) { close(handler->pinfd); } ret = lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP); if (ret < 0) goto out_warn_father; /* Unshare CLONE_NEWNET after CLONE_NEWUSER. See * https://github.com/lxc/lxd/issues/1978. */ if ((handler->clone_flags & (CLONE_NEWNET | CLONE_NEWUSER)) == (CLONE_NEWNET | CLONE_NEWUSER)) { ret = unshare(CLONE_NEWNET); if (ret < 0) { SYSERROR("Failed to unshare CLONE_NEWNET."); goto out_warn_father; } INFO("Unshared CLONE_NEWNET."); } /* Tell the parent task it can begin to configure the container and wait * for it to finish. */ ret = lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE); if (ret < 0) goto out_warn_father; if (lxc_network_recv_veth_names_from_parent(handler) < 0) { ERROR("Failed to receive veth names from parent"); goto out_warn_father; } /* If we are in a new user namespace, become root there to have * privilege over our namespace. */ if (!lxc_list_empty(&handler->conf->id_map)) { uid_t nsuid = (handler->conf->root_nsuid_map != NULL) ? 0 : handler->conf->init_uid; gid_t nsgid = (handler->conf->root_nsgid_map != NULL) ? 0 : handler->conf->init_gid; ret = lxc_switch_uid_gid(nsuid, nsgid); if (ret < 0) goto out_warn_father; /* Drop groups only after we switched to a valid gid in the new * user namespace. */ ret = lxc_setgroups(0, NULL); if (ret < 0 && (handler->am_root || errno != EPERM)) goto out_warn_father; ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); if (ret < 0) goto out_warn_father; /* set{g,u}id() clears deathsignal */ ret = lxc_set_death_signal(SIGKILL); if (ret < 0) { SYSERROR("Failed to set PR_SET_PDEATHSIG to SIGKILL"); goto out_warn_father; } } if (access(handler->lxcpath, X_OK)) { print_top_failing_dir(handler->lxcpath); goto out_warn_father; } #if HAVE_LIBCAP if (handler->conf->need_utmp_watch) { if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) { SYSERROR("Failed to remove the CAP_SYS_BOOT capability."); goto out_warn_father; } DEBUG("Dropped the CAP_SYS_BOOT capability."); } #endif ret = snprintf(path, sizeof(path), "%s/dev/null", handler->conf->rootfs.mount); if (ret < 0 || ret >= sizeof(path)) goto out_warn_father; /* In order to checkpoint restore, we need to have everything in the * same mount namespace. However, some containers may not have a * reasonable /dev (in particular, they may not have /dev/null), so we * can't set init's std fds to /dev/null by opening it from inside the * container. * * If that's the case, fall back to using the host's /dev/null. This * means that migration won't work, but at least we won't spew output * where it isn't wanted. */ if (handler->backgrounded && !handler->conf->autodev && access(path, F_OK) < 0) { devnull_fd = open_devnull(); if (devnull_fd < 0) goto out_warn_father; WARN("Using /dev/null from the host for container init's " "standard file descriptors. Migration will not work."); } /* Ask father to setup cgroups and wait for him to finish. */ if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP)) goto out_error; /* Unshare cgroup namespace after we have setup our cgroups. If we do it * earlier we end up with a wrong view of /proc/self/cgroup. For * example, assume we unshare(CLONE_NEWCGROUP) first, and then create * the cgroup for the container, say /sys/fs/cgroup/cpuset/lxc/c, then * /proc/self/cgroup would show us: * * 8:cpuset:/lxc/c * * whereas it should actually show * * 8:cpuset:/ */ if (handler->clone_flags & CLONE_NEWCGROUP) { if (unshare(CLONE_NEWCGROUP) < 0) { INFO("Failed to unshare CLONE_NEWCGROUP."); goto out_warn_father; } INFO("Unshared CLONE_NEWCGROUP."); } /* Add the requested environment variables to the current environment to * allow them to be used by the various hooks, such as the start hook * above. */ lxc_list_for_each(iterator, &handler->conf->environment) { if (putenv((char *)iterator->elem)) { SYSERROR("Failed to set environment variable: %s.", (char *)iterator->elem); goto out_warn_father; } } /* Setup the container, ip, names, utsname, ... */ ret = lxc_setup(handler); close(handler->data_sock[1]); close(handler->data_sock[0]); if (ret < 0) { ERROR("Failed to setup container \"%s\".", handler->name); goto out_warn_father; } /* Set the label to change to when we exec(2) the container's init. */ if (lsm_process_label_set(NULL, handler->conf, 1, 1) < 0) goto out_warn_father; /* Some init's such as busybox will set sane tty settings on stdin, * stdout, stderr which it thinks is the console. We already set them * the way we wanted on the real terminal, and we want init to do its * setup on its console ie. the pty allocated in lxc_console_create() so * make sure that that pty is stdin,stdout,stderr. */ if (handler->conf->console.slave >= 0) { if (handler->backgrounded || handler->conf->is_execute == 0) ret = set_stdfds(handler->conf->console.slave); else ret = lxc_console_set_stdfds(handler->conf->console.slave); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to pty file " "descriptor %d", handler->conf->console.slave); goto out_warn_father; } } /* If we mounted a temporary proc, then unmount it now. */ tmp_proc_unmount(handler->conf); if (lxc_seccomp_load(handler->conf) != 0) goto out_warn_father; if (run_lxc_hooks(handler->name, "start", handler->conf, handler->lxcpath, NULL)) { ERROR("Failed to run lxc.hook.start for container \"%s\".", handler->name); goto out_warn_father; } close(handler->sigfd); if (handler->conf->console.slave < 0 && handler->backgrounded) { if (devnull_fd < 0) { devnull_fd = open_devnull(); if (devnull_fd < 0) goto out_warn_father; } ret = set_stdfds(devnull_fd); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to \"/dev/null\""); goto out_warn_father; } } setsid(); if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP_LIMITS)) goto out_warn_father; /* Reset the environment variables the user requested in a clear * environment. */ if (clearenv()) { SYSERROR("Failed to clear environment."); /* Don't error out though. */ } lxc_list_for_each(iterator, &handler->conf->environment) { if (putenv((char *)iterator->elem)) { SYSERROR("Failed to set environment variable: %s.", (char *)iterator->elem); goto out_warn_father; } } if (putenv("container=lxc")) { SYSERROR("Failed to set environment variable: container=lxc."); goto out_warn_father; } if (handler->conf->pty_names) { if (putenv(handler->conf->pty_names)) { SYSERROR("Failed to set environment variable for container ptys."); goto out_warn_father; } } /* The container has been setup. We can now switch to an unprivileged * uid/gid. */ new_uid = handler->conf->init_uid; new_gid = handler->conf->init_gid; /* If we are in a new user namespace we already dropped all * groups when we switched to root in the new user namespace * further above. Only drop groups if we can, so ensure that we * have necessary privilege. */ #if HAVE_LIBCAP have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE); #else have_cap_setgid = false; #endif if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) { if (lxc_setgroups(0, NULL) < 0) goto out_warn_father; } if (lxc_switch_uid_gid(new_uid, new_gid) < 0) goto out_warn_father; ret = lxc_ambient_caps_down(); if (ret < 0) { ERROR("Failed to clear ambient capabilities"); goto out_warn_father; } /* After this call, we are in error because this ops should not return * as it execs. */ handler->ops->start(handler, handler->data); out_warn_father: /* We want the parent to know something went wrong, so we return a * special error code. */ lxc_sync_wake_parent(handler, LXC_SYNC_ERROR); out_error: if (devnull_fd >= 0) close(devnull_fd); return -1; } static int lxc_recv_ttys_from_child(struct lxc_handler *handler) { int i; struct lxc_pty_info *pty_info; int ret = -1; int sock = handler->data_sock[1]; struct lxc_conf *conf = handler->conf; struct lxc_tty_info *tty_info = &conf->tty_info; if (!conf->tty) return 0; tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty); if (!tty_info->pty_info) return -1; for (i = 0; i < conf->tty; i++) { int ttyfds[2]; ret = lxc_abstract_unix_recv_fds(sock, ttyfds, 2, NULL, 0); if (ret < 0) break; pty_info = &tty_info->pty_info[i]; pty_info->busy = 0; pty_info->master = ttyfds[0]; pty_info->slave = ttyfds[1]; TRACE("Received pty with master fd %d and slave fd %d from " "parent", pty_info->master, pty_info->slave); } if (ret < 0) ERROR("Failed to receive %d ttys from child: %s", conf->tty, strerror(errno)); else TRACE("Received %d ttys from child", conf->tty); tty_info->nbtty = conf->tty; return ret; } int resolve_clone_flags(struct lxc_handler *handler) { handler->clone_flags = CLONE_NEWPID | CLONE_NEWNS; if (!lxc_list_empty(&handler->conf->id_map)) handler->clone_flags |= CLONE_NEWUSER; if (handler->conf->inherit_ns_fd[LXC_NS_NET] == -1) { if (!lxc_requests_empty_network(handler)) handler->clone_flags |= CLONE_NEWNET; } else { INFO("Inheriting a net namespace."); } if (handler->conf->inherit_ns_fd[LXC_NS_IPC] == -1) handler->clone_flags |= CLONE_NEWIPC; else INFO("Inheriting an ipc namespace."); if (handler->conf->inherit_ns_fd[LXC_NS_UTS] == -1) handler->clone_flags |= CLONE_NEWUTS; else INFO("Inheriting a uts namespace."); if (handler->conf->inherit_ns_fd[LXC_NS_PID] == -1) handler->clone_flags |= CLONE_NEWPID; else INFO("Inheriting pid namespace"); if (cgns_supported()) { if (handler->conf->inherit_ns_fd[LXC_NS_CGROUP] == -1) handler->clone_flags |= CLONE_NEWCGROUP; else INFO("Inheriting cgroup namespace"); } else if (handler->conf->inherit_ns_fd[LXC_NS_CGROUP] >= 0) { return -EINVAL; } return 0; } /* lxc_spawn() performs crucial setup tasks and clone()s the new process which * exec()s the requested container binary. * Note that lxc_spawn() runs in the parent namespaces. Any operations performed * right here should be double checked if they'd pose a security risk. (For * example, any {u}mount() operations performed here will be reflected on the * host!) */ static int lxc_spawn(struct lxc_handler *handler) { int i, flags, ret; bool wants_to_map_ids; int saved_ns_fd[LXC_NS_MAX]; struct lxc_list *id_map; int preserve_mask = 0; const char *name = handler->name; bool cgroups_connected = false; id_map = &handler->conf->id_map; wants_to_map_ids = !lxc_list_empty(id_map); memset(saved_ns_fd, -1, sizeof(int) * LXC_NS_MAX); for (i = 0; i < LXC_NS_MAX; i++) if (handler->conf->inherit_ns_fd[i] != -1) preserve_mask |= ns_info[i].clone_flag; if (lxc_sync_init(handler)) return -1; ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, handler->data_sock); if (ret < 0) { lxc_sync_fini(handler); return -1; } ret = resolve_clone_flags(handler); if (ret < 0) { lxc_sync_fini(handler); return -1; } if (handler->clone_flags & CLONE_NEWNET) { if (!lxc_list_empty(&handler->conf->network)) { /* Find gateway addresses from the link device, which is * no longer accessible inside the container. Do this * before creating network interfaces, since goto * out_delete_net does not work before lxc_clone. */ if (lxc_find_gateway_addresses(handler)) { ERROR("Failed to find gateway addresses."); lxc_sync_fini(handler); return -1; } /* That should be done before the clone because we will * fill the netdev index and use them in the child. */ if (lxc_create_network_priv(handler)) { ERROR("Failed to create the network."); lxc_sync_fini(handler); return -1; } } } if (!cgroup_init(handler)) { ERROR("Failed initializing cgroup support."); goto out_delete_net; } cgroups_connected = true; if (!cgroup_create(handler)) { ERROR("Failed creating cgroups."); goto out_delete_net; } /* If the rootfs is not a blockdev, prevent the container from marking * it readonly. * If the container is unprivileged then skip rootfs pinning. */ if (!wants_to_map_ids) { handler->pinfd = pin_rootfs(handler->conf->rootfs.path); if (handler->pinfd == -1) INFO("Failed to pin the rootfs for container \"%s\".", handler->name); } if (!preserve_ns(saved_ns_fd, preserve_mask, lxc_raw_getpid())) goto out_delete_net; if (attach_ns(handler->conf->inherit_ns_fd) < 0) goto out_delete_net; /* Create a process in a new set of namespaces. */ flags = handler->clone_flags; if (handler->clone_flags & CLONE_NEWUSER) { /* If CLONE_NEWUSER and CLONE_NEWNET was requested, we need to * clone a new user namespace first and only later unshare our * network namespace to ensure that network devices ownership is * set up correctly. */ flags &= ~CLONE_NEWNET; } handler->pid = lxc_raw_clone_cb(do_start, handler, flags); if (handler->pid < 0) { SYSERROR("Failed to clone a new set of namespaces."); goto out_delete_net; } TRACE("Cloned child process %d", handler->pid); for (i = 0; i < LXC_NS_MAX; i++) if (flags & ns_info[i].clone_flag) INFO("Cloned %s.", ns_info[i].flag_name); if (!preserve_ns(handler->nsfd, handler->clone_flags | preserve_mask, handler->pid)) INFO("Failed to preserve namespace for lxc.hook.stop."); if (attach_ns(saved_ns_fd)) WARN("Failed to restore saved namespaces."); lxc_sync_fini_child(handler); /* Map the container uids. The container became an invalid userid the * moment it was cloned with CLONE_NEWUSER. This call doesn't change * anything immediately, but allows the container to setuid(0) (0 being * mapped to something else on the host.) later to become a valid uid * again. */ if (wants_to_map_ids && lxc_map_ids(id_map, handler->pid)) { ERROR("Failed to set up id mapping."); goto out_delete_net; } if (lxc_sync_wake_child(handler, LXC_SYNC_STARTUP)) goto out_delete_net; if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE)) goto out_delete_net; if (!cgroup_create_legacy(handler)) { ERROR("Failed to setup legacy cgroups for container \"%s\".", name); goto out_delete_net; } if (!cgroup_setup_limits(handler, false)) { ERROR("Failed to setup cgroup limits for container \"%s\".", name); goto out_delete_net; } if (!cgroup_enter(handler)) goto out_delete_net; if (!cgroup_chown(handler)) goto out_delete_net; handler->netnsfd = lxc_preserve_ns(handler->pid, "net"); if (handler->netnsfd < 0) { ERROR("Failed to preserve network namespace"); goto out_delete_net; } /* Create the network configuration. */ if (handler->clone_flags & CLONE_NEWNET) { if (lxc_network_move_created_netdev_priv(handler->lxcpath, handler->name, &handler->conf->network, handler->pid)) { ERROR("Failed to create the configured network."); goto out_delete_net; } if (lxc_create_network_unpriv(handler->lxcpath, handler->name, &handler->conf->network, handler->pid)) { ERROR("Failed to create the configured network."); goto out_delete_net; } } if (lxc_network_send_veth_names_to_child(handler) < 0) { ERROR("Failed to send veth names to child"); goto out_delete_net; } /* Tell the child to continue its initialization. We'll get * LXC_SYNC_CGROUP when it is ready for us to setup cgroups. */ if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE)) goto out_delete_net; if (lxc_sync_barrier_child(handler, LXC_SYNC_CGROUP_UNSHARE)) goto out_delete_net; if (!cgroup_setup_limits(handler, true)) { ERROR("Failed to setup the devices cgroup for container \"%s\".", name); goto out_delete_net; } TRACE("Set up cgroup device limits"); cgroup_disconnect(); cgroups_connected = false; if (handler->clone_flags & CLONE_NEWCGROUP) { /* Now we're ready to preserve the cgroup namespace */ ret = lxc_preserve_ns(handler->pid, "cgroup"); if (ret < 0) { ERROR("%s - Failed to preserve cgroup namespace", strerror(errno)); goto out_delete_net; } handler->nsfd[LXC_NS_CGROUP] = ret; DEBUG("Preserved cgroup namespace via fd %d", ret); } /* Tell the child to complete its initialization and wait for it to exec * or return an error. (The child will never return * LXC_SYNC_POST_CGROUP+1. It will either close the sync pipe, causing * lxc_sync_barrier_child to return success, or return a different * value, causing us to error out). */ if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP)) return -1; if (lxc_network_recv_name_and_ifindex_from_child(handler) < 0) { ERROR("Failed to receive names and ifindices for network " "devices from child"); goto out_delete_net; } /* Now all networks are created, network devices are moved into place, * and the correct names and ifindeces in the respective namespaces have * been recorded. The corresponding structs have now all been filled. So * log them for debugging purposes. */ lxc_log_configured_netdevs(handler->conf); /* Read tty fds allocated by child. */ if (lxc_recv_ttys_from_child(handler) < 0) { ERROR("Failed to receive tty info from child process."); goto out_delete_net; } if (handler->ops->post_start(handler, handler->data)) goto out_abort; if (lxc_set_state(name, handler, RUNNING)) { ERROR("Failed to set state for container \"%s\" to \"%s\".", name, lxc_state2str(RUNNING)); goto out_abort; } lxc_sync_fini(handler); for (i = 0; i < LXC_NS_MAX; i++) if (saved_ns_fd[i] != -1) close(saved_ns_fd[i]); return 0; out_delete_net: for (i = 0; i < LXC_NS_MAX; i++) if (saved_ns_fd[i] != -1) close(saved_ns_fd[i]); if (cgroups_connected) cgroup_disconnect(); if (handler->clone_flags & CLONE_NEWNET) lxc_delete_network(handler); out_abort: lxc_abort(name, handler); lxc_sync_fini(handler); if (handler->pinfd >= 0) { close(handler->pinfd); handler->pinfd = -1; } if (handler->netnsfd >= 0) { close(handler->netnsfd); handler->netnsfd = -1; } return -1; } int __lxc_start(const char *name, struct lxc_handler *handler, struct lxc_operations* ops, void *data, const char *lxcpath, bool backgrounded, int *error_num) { int status; int err = -1; struct lxc_conf *conf = handler->conf; if (lxc_init(name, handler) < 0) { ERROR("Failed to initialize container \"%s\".", name); return -1; } handler->ops = ops; handler->data = data; handler->backgrounded = backgrounded; handler->netnsfd = -1; if (must_drop_cap_sys_boot(handler->conf)) { #if HAVE_LIBCAP DEBUG("Dropping CAP_SYS_BOOT capability."); #else DEBUG("Not dropping CAP_SYS_BOOT capability as capabilities aren't supported."); #endif } else { DEBUG("Not dropping CAP_SYS_BOOT or watching utmp."); handler->conf->need_utmp_watch = 0; } if (!attach_block_device(handler->conf)) { ERROR("Failed to attach block device."); goto out_fini_nonet; } if (geteuid() == 0 && !lxc_list_empty(&conf->id_map)) { /* If the backing store is a device, mount it here and now. */ if (rootfs_is_blockdev(conf)) { if (unshare(CLONE_NEWNS) < 0) { ERROR("Failed to unshare CLONE_NEWNS."); goto out_fini_nonet; } INFO("Unshared CLONE_NEWNS."); remount_all_slave(); if (do_rootfs_setup(conf, name, lxcpath) < 0) { ERROR("Error setting up rootfs mount as root before spawn."); goto out_fini_nonet; } INFO("Set up container rootfs as host root."); } } err = lxc_spawn(handler); if (err) { ERROR("Failed to spawn container \"%s\".", name); goto out_detach_blockdev; } /* close parent side of data socket */ close(handler->data_sock[0]); handler->data_sock[0] = -1; close(handler->data_sock[1]); handler->data_sock[1] = -1; handler->conf->reboot = 0; err = lxc_poll(name, handler); if (err) { ERROR("LXC mainloop exited with error: %d.", err); goto out_abort; } if (!handler->init_died && handler->pid > 0) { ERROR("Child process is not killed"); goto out_abort; } status = lxc_wait_for_pid_status(handler->pid); if (status < 0) SYSERROR("Failed to retrieve status for %d", handler->pid); /* If the child process exited but was not signaled, it didn't call * reboot. This should mean it was an lxc-execute which simply exited. * In any case, treat it as a 'halt'. */ if (WIFSIGNALED(status)) { switch(WTERMSIG(status)) { case SIGINT: /* halt */ DEBUG("Container \"%s\" is halting.", name); break; case SIGHUP: /* reboot */ DEBUG("Container \"%s\" is rebooting.", name); handler->conf->reboot = 1; break; case SIGSYS: /* seccomp */ DEBUG("Container \"%s\" violated its seccomp policy.", name); break; default: DEBUG("Unknown exit status for container \"%s\" init %d.", name, WTERMSIG(status)); break; } } err = lxc_restore_phys_nics_to_netns(handler); if (err < 0) ERROR("Failed to move physical network devices back to parent " "network namespace"); if (handler->pinfd >= 0) { close(handler->pinfd); handler->pinfd = -1; } lxc_monitor_send_exit_code(name, status, handler->lxcpath); lxc_error_set_and_log(handler->pid, status); if (error_num) *error_num = handler->exit_status; out_fini: lxc_delete_network(handler); out_detach_blockdev: detach_block_device(handler->conf); out_fini_nonet: lxc_fini(name, handler); return err; out_abort: lxc_abort(name, handler); goto out_fini; } struct start_args { char *const *argv; }; static int start(struct lxc_handler *handler, void* data) { struct start_args *arg = data; NOTICE("Exec'ing \"%s\"", arg->argv[0]); execvp(arg->argv[0], arg->argv); SYSERROR("Failed to exec \"%s\"", arg->argv[0]); return 0; } static int post_start(struct lxc_handler *handler, void* data) { struct start_args *arg = data; NOTICE("Started \"%s\" with pid \"%d\"", arg->argv[0], handler->pid); return 0; } static struct lxc_operations start_ops = { .start = start, .post_start = post_start }; int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler, const char *lxcpath, bool backgrounded, int *error_num) { struct start_args start_arg = { .argv = argv, }; handler->conf->need_utmp_watch = 1; return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded, error_num); } static void lxc_destroy_container_on_signal(struct lxc_handler *handler, const char *name) { char destroy[MAXPATHLEN]; struct lxc_container *c; int ret = 0; bool bret = true; if (handler->conf->rootfs.path && handler->conf->rootfs.mount) { bret = do_destroy_container(handler); if (!bret) { ERROR("Error destroying rootfs for container \"%s\"", name); return; } } INFO("Destroyed rootfs for container \"%s\"", name); ret = snprintf(destroy, MAXPATHLEN, "%s/%s", handler->lxcpath, name); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("Error destroying directory for container \"%s\"", name); return; } c = lxc_container_new(name, handler->lxcpath); if (c) { if (container_disk_lock(c)) { INFO("Could not update lxc_snapshots file"); lxc_container_put(c); } else { mod_all_rdeps(c, false); container_disk_unlock(c); lxc_container_put(c); } } if (!handler->am_root) ret = userns_exec_full(handler->conf, lxc_rmdir_onedev_wrapper, destroy, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(destroy, NULL); if (ret < 0) { ERROR("Error destroying directory for container \"%s\"", name); return; } INFO("Destroyed directory for container \"%s\"", name); } static int lxc_rmdir_onedev_wrapper(void *data) { char *arg = (char *) data; return lxc_rmdir_onedev(arg, NULL); } static bool do_destroy_container(struct lxc_handler *handler) { int ret; if (!handler->am_root) { ret = userns_exec_full(handler->conf, storage_destroy_wrapper, handler->conf, "storage_destroy_wrapper"); if (ret < 0) return false; return true; } return storage_destroy(handler->conf); } lxc-2.0.11/src/lxc/commands.c0000644061062106075000000007133213435013473012645 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "config.h" #include "confile.h" #include "console.h" #include "log.h" #include "lxc.h" #include "lxclock.h" #include "mainloop.h" #include "monitor.h" #include "start.h" #include "utils.h" /* * This file provides the different functions for clients to query/command the * server. The client is typically some lxc tool and the server is typically the * container (ie. lxc-start). * * Each command is transactional, the clients send a request to the server and * the server answers the request with a message giving the request's status * (zero or a negative errno value). Both the request and response may contain * additional data. * * Each command is wrapped in a ancillary message in order to pass a credential * making possible to the server to check if the client is allowed to ask for * this command or not. * * IMPORTANTLY: Note that semantics for current commands are fixed. If you wish * to make any changes to how, say, LXC_CMD_GET_CONFIG_ITEM works by adding * information to the end of cmd.data, then you must introduce a new * LXC_CMD_GET_CONFIG_ITEM_V2 define with a new number. You may wish to also * mark LXC_CMD_GET_CONFIG_ITEM deprecated in commands.h. * * This is necessary in order to avoid having a newly compiled lxc command * communicating with a running (old) monitor from crashing the running * container. */ lxc_log_define(lxc_commands, lxc); static const char *lxc_cmd_str(lxc_cmd_t cmd) { static const char *const cmdname[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = "console", [LXC_CMD_CONSOLE_WINCH] = "console_winch", [LXC_CMD_STOP] = "stop", [LXC_CMD_GET_STATE] = "get_state", [LXC_CMD_GET_INIT_PID] = "get_init_pid", [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags", [LXC_CMD_GET_CGROUP] = "get_cgroup", [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item", [LXC_CMD_GET_NAME] = "get_name", [LXC_CMD_GET_LXCPATH] = "get_lxcpath", [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client", [LXC_CMD_CONSOLE_LOG] = "console_log", [LXC_CMD_SERVE_STATE_CLIENTS] = "serve_state_clients", }; if (cmd >= LXC_CMD_MAX) return "Invalid request"; return cmdname[cmd]; } /* * lxc_cmd_rsp_recv: Receive a response to a command * * @sock : the socket connected to the container * @cmd : command to put response in * * Returns the size of the response message or < 0 on failure * * Note that if the command response datalen > 0, then data is * a malloc()ed buffer and should be free()ed by the caller. If * the response data is <= a void * worth of data, it will be * stored directly in data and datalen will be 0. * * As a special case, the response for LXC_CMD_CONSOLE is created * here as it contains an fd for the master pty passed through the * unix socket. */ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) { int ret, rspfd; struct lxc_cmd_rsp *rsp = &cmd->rsp; ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); if (ret < 0) { SYSWARN("Failed to receive response for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); if (errno == ECONNRESET) return -1; return -1; } TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd)); if (cmd->req.cmd == LXC_CMD_CONSOLE) { struct lxc_cmd_console_rsp_data *rspdata; /* recv() returns 0 bytes when a tty cannot be allocated, * rsp->ret is < 0 when the peer permission check failed */ if (ret == 0 || rsp->ret < 0) return 0; rspdata = malloc(sizeof(*rspdata)); if (!rspdata) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } rspdata->masterfd = rspfd; rspdata->ttynum = PTR_TO_INT(rsp->data); rsp->data = rspdata; } if (rsp->datalen == 0) { DEBUG("Response data length for command \"%s\" is 0", lxc_cmd_str(cmd->req.cmd)); return ret; } if (rsp->datalen > LXC_CMD_DATA_MAX) { ERROR("Response data for command \"%s\" is too long: %d bytes > %d", lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX); return -1; } rsp->data = malloc(rsp->datalen); if (!rsp->data) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } ret = recv(sock, rsp->data, rsp->datalen, 0); if (ret != rsp->datalen) { SYSERROR("Failed to receive response data for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } return ret; } /* * lxc_cmd_rsp_send: Send a command response * * @fd : file descriptor of socket to send response on * @rsp : response to send * * Returns 0 on success, < 0 on failure */ static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp) { ssize_t ret; errno = EMSGSIZE; ret = send(fd, rsp, sizeof(*rsp), MSG_NOSIGNAL); if (ret < 0 || (size_t)ret != sizeof(*rsp)) { SYSERROR("Failed to send command response %zd", ret); return -1; } if (!rsp->data || rsp->datalen <= 0) return 0; ret = send(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL); if (ret < 0 || ret != (ssize_t)rsp->datalen) { SYSWARN("Failed to send command response data %zd", ret); return -1; } return 0; } static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd, const char *lxcpath, const char *hashed_sock_name) { int client_fd, saved_errno; ssize_t ret = -1; client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name, "command"); if (client_fd < 0) return -1; ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, sizeof(cmd->req)); if (ret < 0 || (size_t)ret != sizeof(cmd->req)) goto on_error; if (cmd->req.datalen <= 0) return client_fd; errno = EMSGSIZE; ret = send(client_fd, (void *)cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL); if (ret < 0 || ret != (ssize_t)cmd->req.datalen) goto on_error; return client_fd; on_error: saved_errno = errno; close(client_fd); errno = saved_errno; return -1; } /* * lxc_cmd: Connect to the specified running container, send it a command * request and collect the response * * @name : name of container to connect to * @cmd : command with initialized request to send * @stopped : output indicator if the container was not running * @lxcpath : the lxcpath in which the container is running * * Returns the size of the response message on success, < 0 on failure * * Note that there is a special case for LXC_CMD_CONSOLE. For this command * the fd cannot be closed because it is used as a placeholder to indicate * that a particular tty slot is in use. The fd is also used as a signal to * the container that when the caller dies or closes the fd, the container * will notice the fd on its side of the socket in its mainloop select and * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be * returned in the cmd response structure. */ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, const char *lxcpath, const char *hashed_sock_name) { int client_fd, saved_errno; int ret = -1; bool stay_connected = false; if (cmd->req.cmd == LXC_CMD_CONSOLE || cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT) stay_connected = true; *stopped = 0; client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name); if (client_fd < 0) { SYSTRACE("Command \"%s\" failed to connect command socket", lxc_cmd_str(cmd->req.cmd)); if (errno == ECONNREFUSED || errno == EPIPE) *stopped = 1; return -1; } ret = lxc_cmd_rsp_recv(client_fd, cmd); if (ret < 0 && errno == ECONNRESET) *stopped = 1; if (!stay_connected || ret <= 0) { saved_errno = errno; close(client_fd); errno = saved_errno; return ret; } if (stay_connected && ret > 0) cmd->rsp.ret = client_fd; return ret; } int lxc_try_cmd(const char *name, const char *lxcpath) { int stopped, ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_INIT_PID }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (stopped) return 0; if (ret > 0 && cmd.rsp.ret < 0) { errno = cmd.rsp.ret; return -1; } if (ret > 0) return 0; /* At this point we weren't denied access, and the container *was* * started. There was some inexplicable error in the protocol. I'm not * clear on whether we should return -1 here, but we didn't receive a * -EACCES, so technically it's not that we're not allowed to control * the container - it's just not behaving. */ return 0; } /* Implementations of the commands and their callbacks */ /* * lxc_cmd_get_init_pid: Get pid of the container's init process * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the pid on success, < 0 on failure */ pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath) { int ret, stopped; intmax_t pid; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_INIT_PID }, .rsp = { .data = INTMAX_TO_PTR((intmax_t){-1}) } }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return -1; pid = PTR_TO_INTMAX(cmd.rsp.data); if (pid < 0) return -1; /* We need to assume that pid_t can actually hold any pid given to us * by the kernel. If it can't it's a libc bug. */ return (pid_t)pid; } static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { intmax_t pid = handler->pid; struct lxc_cmd_rsp rsp = { .data = INTMAX_TO_PTR(pid) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_clone_flags: Get clone flags container was spawned with * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the clone flags on success, < 0 on failure */ int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CLONE_FLAGS }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; return PTR_TO_INT(cmd.rsp.data); } static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->clone_flags) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a * particular subsystem. This is the cgroup path relative to the root * of the cgroup filesystem. * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * @subsystem : the subsystem being asked about * * Returns the path on success, NULL on failure. The caller must free() the * returned path. */ char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, const char *subsystem) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CGROUP, .data = subsystem, .datalen = 0, }, }; cmd.req.data = subsystem; cmd.req.datalen = 0; if (subsystem) cmd.req.datalen = strlen(subsystem) + 1; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return NULL; if (ret == 0) return NULL; if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) return NULL; return cmd.rsp.data; } static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { const char *path; struct lxc_cmd_rsp rsp; if (req->datalen > 0) path = cgroup_get_cgroup(handler, req->data); else path = cgroup_get_cgroup(handler, NULL); if (!path) return -1; rsp.ret = 0; rsp.datalen = strlen(path) + 1; rsp.data = (char *)path; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_config_item: Get config item the running container * * @name : name of container to connect to * @item : the configuration item to retrieve (ex: lxc.network.0.veth.pair) * @lxcpath : the lxcpath in which the container is running * * Returns the item on success, NULL on failure. The caller must free() the * returned item. */ char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM, .data = item, .datalen = strlen(item) + 1, }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int cilen; char *cidata; struct lxc_config_t *item; struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); item = lxc_getconfig(req->data); if (!item) goto err1; cilen = item->get(req->data, NULL, 0, handler->conf); if (cilen <= 0) goto err1; cidata = alloca(cilen + 1); if (item->get(req->data, cidata, cilen + 1, handler->conf) != cilen) goto err1; cidata[cilen] = '\0'; rsp.data = cidata; rsp.datalen = cilen + 1; rsp.ret = 0; goto out; err1: rsp.ret = -1; out: return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_state: Get current state of the container * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the state on success, < 0 on failure */ int lxc_cmd_get_state(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_STATE } }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0 && stopped) return STOPPED; if (ret < 0) return -1; if (!ret) { WARN("Container \"%s\" has stopped before sending its state", name); return -1; } DEBUG("Container \"%s\" is in \"%s\" state", name, lxc_state2str(PTR_TO_INT(cmd.rsp.data))); return PTR_TO_INT(cmd.rsp.data); } static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_stop: Stop the container previously started with lxc_start. All * the processes running inside this container will be killed. * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns 0 on success, < 0 on failure */ int lxc_cmd_stop(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_STOP }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) { if (stopped) { INFO("Container \"%s\" is already stopped", name); return 0; } return -1; } /* We do not expect any answer, because we wait for the connection to be * closed. */ if (ret > 0) { errno = -cmd.rsp.ret; SYSERROR("Failed to stop container \"%s\"", name); return -1; } INFO("Container \"%s\" has stopped", name); return 0; } static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; int stopsignal = SIGKILL; if (handler->conf->stopsignal) stopsignal = handler->conf->stopsignal; memset(&rsp, 0, sizeof(rsp)); rsp.ret = kill(handler->pid, stopsignal); if (!rsp.ret) { /* We can't just use lxc_unfreeze() since we are already in the * context of handling the STOP cmd in lxc-start, and calling * lxc_unfreeze() would do another cmd (GET_CGROUP) which would * deadlock us. */ if (cgroup_unfreeze(handler)) return 0; ERROR("Failed to unfreeze container \"%s\"", handler->name); rsp.ret = -1; } return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_console_winch: To process as if a SIGWINCH were received * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns 0 on success, < 0 on failure */ int lxc_cmd_console_winch(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_CONSOLE_WINCH }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; return 0; } static int lxc_cmd_console_winch_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = 0 }; lxc_console_sigwinch(SIGWINCH); return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_console: Open an fd to a tty in the container * * @name : name of container to connect to * @ttynum : in: the tty to open or -1 for next available * : out: the tty allocated * @fd : out: file descriptor for master side of pty * @lxcpath : the lxcpath in which the container is running * * Returns fd holding tty allocated on success, < 0 on failure */ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) { int ret, stopped; struct lxc_cmd_console_rsp_data *rspdata; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; if (cmd.rsp.ret < 0) { errno = -cmd.rsp.ret; SYSERROR("Denied access to tty"); ret = -1; goto out; } if (ret == 0) { ERROR("tty number %d invalid, busy or all ttys busy", *ttynum); ret = -1; goto out; } rspdata = cmd.rsp.data; if (rspdata->masterfd < 0) { ERROR("Unable to allocate fd for tty %d", rspdata->ttynum); goto out; } ret = cmd.rsp.ret; /* socket fd */ *fd = rspdata->masterfd; *ttynum = rspdata->ttynum; INFO("Alloced fd %d for tty %d via socket %d", *fd, rspdata->ttynum, ret); out: free(cmd.rsp.data); return ret; } static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int masterfd, ret; struct lxc_cmd_rsp rsp; int ttynum = PTR_TO_INT(req->data); masterfd = lxc_console_allocate(handler->conf, fd, &ttynum); if (masterfd < 0) goto out_close; memset(&rsp, 0, sizeof(rsp)); rsp.data = INT_TO_PTR(ttynum); ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)); if (ret < 0) { ERROR("Failed to send tty to client"); lxc_console_free(handler->conf, fd); goto out_close; } return 0; out_close: /* Special indicator to lxc_cmd_handler() to close the fd and do * related cleanup. */ return 1; } /* * lxc_cmd_get_name: Returns the name of the container * * @hashed_sock_name: hashed socket name * * Returns the name on success, NULL on failure. */ char *lxc_cmd_get_name(const char *hashed_sock_name) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_NAME}, }; ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); rsp.data = (char *)handler->name; rsp.datalen = strlen(handler->name) + 1; rsp.ret = 0; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_lxcpath: Returns the lxcpath of the container * * @hashed_sock_name: hashed socket name * * Returns the lxcpath on success, NULL on failure. */ char *lxc_cmd_get_lxcpath(const char *hashed_sock_name) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_LXCPATH}, }; ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); rsp.ret = 0; rsp.data = (char *)handler->lxcpath; rsp.datalen = strlen(handler->lxcpath) + 1; return lxc_cmd_rsp_send(fd, &rsp); } int lxc_cmd_add_state_client(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int *state_client_fd) { int state, stopped; ssize_t ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_ADD_STATE_CLIENT, .data = states, .datalen = (sizeof(lxc_state_t) * MAX_STATE) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (states[STOPPED] != 0 && stopped != 0) return STOPPED; if (ret < 0) { if (errno != ECONNREFUSED) SYSERROR("Failed to execute command"); return -1; } /* We should now be guaranteed to get an answer from the state sending * function. */ if (cmd.rsp.ret < 0) { errno = -cmd.rsp.ret; SYSERROR("Failed to receive socket fd"); return -1; } state = PTR_TO_INT(cmd.rsp.data); if (state < MAX_STATE) { TRACE("Container is already in requested state %s", lxc_state2str(state)); close(cmd.rsp.ret); return state; } *state_client_fd = cmd.rsp.ret; TRACE("Added state client %d to state client list", cmd.rsp.ret); return MAX_STATE; } static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int ret; struct lxc_cmd_rsp rsp = {0}; if (req->datalen < 0) goto reap_client_fd; if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE)) goto reap_client_fd; if (!req->data) goto reap_client_fd; rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data); if (rsp.ret < 0) goto reap_client_fd; rsp.data = INT_TO_PTR(rsp.ret); ret = lxc_cmd_rsp_send(fd, &rsp); if (ret < 0) goto reap_client_fd; return 0; reap_client_fd: /* Special indicator to lxc_cmd_handler() to close the fd and do related * cleanup. */ return 1; } static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = {0}; rsp.ret = -ENOSYS; lxc_cmd_rsp_send(fd, &rsp); return -ENOSYS; } int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, lxc_state_t state) { int stopped; ssize_t ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_SERVE_STATE_CLIENTS, .data = INT_TO_PTR(state) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) { SYSERROR("Failed to execute command"); return -1; } return 0; } static int lxc_cmd_serve_state_clients_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int ret; lxc_state_t state = PTR_TO_INT(req->data); struct lxc_cmd_rsp rsp = {0}; ret = lxc_serve_state_clients(handler->name, handler, state); if (ret < 0) goto reap_client_fd; ret = lxc_cmd_rsp_send(fd, &rsp); if (ret < 0) goto reap_client_fd; return 0; reap_client_fd: /* Special indicator to lxc_cmd_handler() to close the fd and do related * cleanup. */ return 1; } static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *); callback cb[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, [LXC_CMD_CONSOLE_WINCH] = lxc_cmd_console_winch_callback, [LXC_CMD_STOP] = lxc_cmd_stop_callback, [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, [LXC_CMD_CONSOLE_LOG] = lxc_cmd_console_log_callback, [LXC_CMD_SERVE_STATE_CLIENTS] = lxc_cmd_serve_state_clients_callback, }; if (req->cmd >= LXC_CMD_MAX) { ERROR("Undefined command id %d", req->cmd); return -1; } return cb[req->cmd](fd, req, handler); } static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct lxc_epoll_descr *descr, const lxc_cmd_t cmd) { struct state_client *client; struct lxc_list *cur, *next; lxc_console_free(handler->conf, fd); lxc_mainloop_del_handler(descr, fd); if (cmd != LXC_CMD_ADD_STATE_CLIENT) { close(fd); return; } lxc_list_for_each_safe(cur, &handler->state_clients, next) { client = cur->elem; if (client->clientfd != fd) continue; /* kick client from list */ lxc_list_del(cur); close(client->clientfd); free(cur->elem); free(cur); /* No need to walk the whole list. If we found the state client * fd there can't be a second one. */ break; } } static int lxc_cmd_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ret; struct lxc_cmd_req req; struct lxc_handler *handler = data; ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); if (ret < 0) { SYSERROR("Failed to receive data on command socket for command " "\"%s\"", lxc_cmd_str(req.cmd)); if (errno == EACCES) { /* We don't care for the peer, just send and close. */ struct lxc_cmd_rsp rsp = {.ret = ret}; lxc_cmd_rsp_send(fd, &rsp); } goto out_close; } if (ret == 0) goto out_close; if (ret != sizeof(req)) { WARN("Failed to receive full command request. Ignoring request " "for \"%s\"", lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } if (req.datalen > LXC_CMD_DATA_MAX) { ERROR("Received command data length %d is too large for " "command \"%s\"", req.datalen, lxc_cmd_str(req.cmd)); errno = EFBIG; ret = -EFBIG; goto out_close; } if (req.datalen > 0) { void *reqdata; reqdata = alloca(req.datalen); ret = recv(fd, reqdata, req.datalen, 0); if (ret != req.datalen) { WARN("Failed to receive full command request. Ignoring " "request for \"%s\"", lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } req.data = reqdata; } ret = lxc_cmd_process(fd, &req, handler); if (ret) { /* This is not an error, but only a request to close fd. */ ret = 0; goto out_close; } out: return ret; out_close: lxc_cmd_fd_cleanup(fd, handler, descr, req.cmd); goto out; } static int lxc_cmd_accept(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int connection; int opt = 1, ret = -1; connection = accept(fd, NULL, 0); if (connection < 0) { SYSERROR("Failed to accept connection to run command."); return -1; } ret = fcntl(connection, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set close-on-exec on incoming command connection"); goto out_close; } ret = setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); if (ret < 0) { SYSERROR("Failed to enable necessary credentials on command socket"); goto out_close; } ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data); if (ret) { ERROR("Failed to add command handler"); goto out_close; } out: return ret; out_close: close(connection); goto out; } int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix) { int fd, ret; char path[LXC_AUDS_ADDR_LEN] = {0}; ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, NULL, suffix); if (ret < 0) return -1; TRACE("Creating abstract unix socket \"%s\"", &path[1]); fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); if (fd < 0) { SYSERROR("Failed to create command socket %s", &path[1]); if (errno == EADDRINUSE) ERROR("Container \"%s\" appears to be already running", name); return -1; } ret = fcntl(fd, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC on command socket file descriptor"); close(fd); return -1; } return fd; } int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler) { int ret; int fd = handler->conf->maincmd_fd; ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); if (ret < 0) { ERROR("Failed to add handler for command socket"); close(fd); } return ret; } lxc-2.0.11/src/lxc/utils.h0000644061062106075000000003774413435013473012222 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_UTILS_H #define __LXC_UTILS_H /* Properly support loop devices on 32bit systems. */ #define _FILE_OFFSET_BITS 64 #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX_MEMFD_H #include #endif #include "initutils.h" #include "macro.h" /* returns 1 on success, 0 if there were any failures */ extern int lxc_rmdir_onedev(char *path, const char *exclude); extern int get_u16(unsigned short *val, const char *arg, int base); extern int mkdir_p(const char *dir, mode_t mode); extern char *get_rundir(void); /* Define getline() if missing from the C library */ #ifndef HAVE_GETLINE #ifdef HAVE_FGETLN #include <../include/getline.h> #endif #endif #if !defined(__NR_setns) && !defined(__NR_set_ns) #if defined(__x86_64__) #define __NR_setns 308 #elif defined(__i386__) #define __NR_setns 346 #elif defined(__arm__) #define __NR_setns 375 #elif defined(__aarch64__) #define __NR_setns 375 #elif defined(__powerpc__) #define __NR_setns 350 #elif defined(__s390__) #define __NR_setns 339 #endif #endif /* Define setns() if missing from the C library */ #ifndef HAVE_SETNS static inline int setns(int fd, int nstype) { #ifdef __NR_setns return syscall(__NR_setns, fd, nstype); #elif defined(__NR_set_ns) return syscall(__NR_set_ns, fd, nstype); #else errno = ENOSYS; return -1; #endif } #endif /* Define sethostname() if missing from the C library */ #ifndef HAVE_SETHOSTNAME static inline int sethostname(const char * name, size_t len) { #ifdef __NR_sethostname return syscall(__NR_sethostname, name, len); #else errno = ENOSYS; return -1; #endif } #endif /* Define unshare() if missing from the C library */ #ifndef HAVE_UNSHARE static inline int unshare(int flags) { #ifdef __NR_unshare return syscall(__NR_unshare, flags); #else errno = ENOSYS; return -1; #endif } #else int unshare(int); #endif /* Define signalfd() if missing from the C library */ #ifdef HAVE_SYS_SIGNALFD_H # include #else /* assume kernel headers are too old */ #include struct signalfd_siginfo { uint32_t ssi_signo; int32_t ssi_errno; int32_t ssi_code; uint32_t ssi_pid; uint32_t ssi_uid; int32_t ssi_fd; uint32_t ssi_tid; uint32_t ssi_band; uint32_t ssi_overrun; uint32_t ssi_trapno; int32_t ssi_status; int32_t ssi_int; uint64_t ssi_ptr; uint64_t ssi_utime; uint64_t ssi_stime; uint64_t ssi_addr; uint8_t __pad[48]; }; # ifndef __NR_signalfd4 /* assume kernel headers are too old */ # if __i386__ # define __NR_signalfd4 327 # elif __x86_64__ # define __NR_signalfd4 289 # elif __powerpc__ # define __NR_signalfd4 313 # elif __s390x__ # define __NR_signalfd4 322 # elif __arm__ # define __NR_signalfd4 355 # elif __mips__ && _MIPS_SIM == _ABIO32 # define __NR_signalfd4 4324 # elif __mips__ && _MIPS_SIM == _ABI64 # define __NR_signalfd4 5283 # elif __mips__ && _MIPS_SIM == _ABIN32 # define __NR_signalfd4 6287 # endif #endif # ifndef __NR_signalfd /* assume kernel headers are too old */ # if __i386__ # define __NR_signalfd 321 # elif __x86_64__ # define __NR_signalfd 282 # elif __powerpc__ # define __NR_signalfd 305 # elif __s390x__ # define __NR_signalfd 316 # elif __arm__ # define __NR_signalfd 349 # elif __mips__ && _MIPS_SIM == _ABIO32 # define __NR_signalfd 4317 # elif __mips__ && _MIPS_SIM == _ABI64 # define __NR_signalfd 5276 # elif __mips__ && _MIPS_SIM == _ABIN32 # define __NR_signalfd 6280 # endif #endif static inline int signalfd(int fd, const sigset_t *mask, int flags) { int retval; retval = syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags); if (errno == ENOSYS && flags == 0) retval = syscall (__NR_signalfd, fd, mask, _NSIG / 8); return retval; } #endif /* loop devices */ #ifndef LO_FLAGS_AUTOCLEAR #define LO_FLAGS_AUTOCLEAR 4 #endif #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif /* memfd_create() */ #ifndef MFD_CLOEXEC #define MFD_CLOEXEC 0x0001U #endif #ifndef MFD_ALLOW_SEALING #define MFD_ALLOW_SEALING 0x0002U #endif #ifndef F_LINUX_SPECIFIC_BASE #define F_LINUX_SPECIFIC_BASE 1024 #endif #ifndef F_ADD_SEALS #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) #endif #ifndef F_SEAL_SEAL #define F_SEAL_SEAL 0x0001 #define F_SEAL_SHRINK 0x0002 #define F_SEAL_GROW 0x0004 #define F_SEAL_WRITE 0x0008 #endif #ifndef HAVE_MEMFD_CREATE static inline int memfd_create(const char *name, unsigned int flags) { #ifndef __NR_memfd_create #if defined __i386__ #define __NR_memfd_create 356 #elif defined __x86_64__ #define __NR_memfd_create 319 #elif defined __arm__ #define __NR_memfd_create 385 #elif defined __aarch64__ #define __NR_memfd_create 279 #elif defined __s390__ #define __NR_memfd_create 350 #elif defined __powerpc__ #define __NR_memfd_create 360 #elif defined __sparc__ #define __NR_memfd_create 348 #elif defined __blackfin__ #define __NR_memfd_create 390 #elif defined __ia64__ #define __NR_memfd_create 1340 #elif defined _MIPS_SIM #if _MIPS_SIM == _MIPS_SIM_ABI32 #define __NR_memfd_create 4354 #endif #if _MIPS_SIM == _MIPS_SIM_NABI32 #define __NR_memfd_create 6318 #endif #if _MIPS_SIM == _MIPS_SIM_ABI64 #define __NR_memfd_create 5314 #endif #endif #endif #ifdef __NR_memfd_create return syscall(__NR_memfd_create, name, flags); #else errno = ENOSYS; return -1; #endif } #else extern int memfd_create(const char *name, unsigned int flags); #endif static inline int lxc_set_cloexec(int fd) { return fcntl(fd, F_SETFD, FD_CLOEXEC); } /* Struct to carry child pid from lxc_popen() to lxc_pclose(). * Not an opaque struct to allow direct access to the underlying FILE * * (i.e., struct lxc_popen_FILE *file; fgets(buf, sizeof(buf), file->f)) * without additional wrappers. */ struct lxc_popen_FILE { int pipe; FILE *f; pid_t child_pid; }; /* popen(command, "re") replacement that restores default signal mask * via sigprocmask(2) (unblocks all signals) after fork(2) but prior to calling exec(3). * In short, popen(command, "re") does pipe() + fork() + exec() * while lxc_popen(command) does pipe() + fork() + sigprocmask() + exec(). * Returns pointer to struct lxc_popen_FILE, that should be freed with lxc_pclose(). * On error returns NULL. */ extern struct lxc_popen_FILE *lxc_popen(const char *command); /* pclose() replacement to be used on struct lxc_popen_FILE *, * returned by lxc_popen(). * Waits for associated process to terminate, returns its exit status and * frees resources, pointed to by struct lxc_popen_FILE *. */ extern int lxc_pclose(struct lxc_popen_FILE *fp); /* * wait on a child we forked */ extern int wait_for_pid(pid_t pid); extern int lxc_wait_for_pid_status(pid_t pid); /* send and receive buffers completely */ extern ssize_t lxc_write_nointr(int fd, const void* buf, size_t count); extern ssize_t lxc_read_nointr(int fd, void* buf, size_t count); extern ssize_t lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf); #if HAVE_LIBGNUTLS #define SHA_DIGEST_LENGTH 20 extern int sha1sum_file(char *fnam, unsigned char *md_value); #endif /* read and write whole files */ extern int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool add_newline); extern int lxc_read_from_file(const char *filename, void* buf, size_t count); /* convert variadic argument lists to arrays (for execl type argument lists) */ extern char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup); extern const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip); /* Some simple string functions; if they return pointers, they are allocated buffers. */ extern char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack); extern bool lxc_string_in_array(const char *needle, const char **haystack); extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix); /* Normalize and split path: Leading and trailing / are removed, multiple * / are compactified, .. and . are resolved (.. on the top level is considered * identical to .). * Examples: * / -> { NULL } * foo/../bar -> { bar, NULL } * ../../ -> { NULL } * ./bar/baz/.. -> { bar, NULL } * foo//bar -> { foo, bar, NULL } */ extern char **lxc_normalize_path(const char *path); /* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ extern char *lxc_deslashify(const char *path); extern char *lxc_append_paths(const char *first, const char *second); /* Note: the following two functions use strtok(), so they will never * consider an empty element, even if two delimiters are next to * each other. */ extern bool lxc_string_in_list(const char *needle, const char *haystack, char sep); extern char **lxc_string_split(const char *string, char sep); extern char **lxc_string_split_and_trim(const char *string, char sep); /* Append string to NULL-terminated string array. */ extern int lxc_append_string(char ***list, char *entry); /* some simple array manipulation utilities */ typedef void (*lxc_free_fn)(void *); typedef void *(*lxc_dup_fn)(void *); extern int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment); extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); extern size_t lxc_array_len(void **array); extern void **lxc_append_null_to_array(void **array, size_t count); /* mmap() wrapper. lxc_strmmap() will take care to \0-terminate files so that * normal string-handling functions can be used on the buffer. */ extern void *lxc_strmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); /* munmap() wrapper. Use it to free memory mmap()ed with lxc_strmmap(). */ extern int lxc_strmunmap(void *addr, size_t length); /* initialize rand with urandom */ extern int randseed(bool); /* are we unprivileged with respect to our namespaces */ inline static bool am_guest_unpriv(void) { return geteuid() != 0; } /* are we unprivileged with respect to init_user_ns */ inline static bool am_host_unpriv(void) { FILE *f; uid_t user, host, count; int ret; if (geteuid() != 0) return true; /* Now: are we in a user namespace? Because then we're also * unprivileged. */ f = fopen("/proc/self/uid_map", "r"); if (!f) { return false; } ret = fscanf(f, "%u %u %u", &user, &host, &count); fclose(f); if (ret != 3) { return false; } if (user != 0 || host != 0 || count != UINT32_MAX) return true; return false; } /* * parse /proc/self/uid_map to find what @orig maps to */ extern uid_t get_ns_uid(uid_t orig); extern bool dir_exists(const char *path); #define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval); int detect_shared_rootfs(void); bool detect_ramfs_rootfs(void); char *on_path(const char *cmd, const char *rootfs); bool file_exists(const char *f); bool cgns_supported(void); char *choose_init(const char *rootfs); int print_to_file(const char *file, const char *content); bool switch_to_ns(pid_t pid, const char *ns); int is_dir(const char *path); char *get_template_path(const char *t); int safe_mount(const char *src, const char *dest, const char *fstype, unsigned long flags, const void *data, const char *rootfs); int lxc_mount_proc_if_needed(const char *rootfs); int open_devnull(void); int set_stdfds(int fd); int null_stdfds(void); int lxc_count_file_lines(const char *fn); int lxc_preserve_ns(const int pid, const char *ns); /* Check whether a signal is blocked by a process. */ extern bool task_blocks_signal(pid_t pid, int signal); /* Helper functions to parse numbers. */ extern int lxc_safe_uint(const char *numstr, unsigned int *converted); extern int lxc_safe_int(const char *numstr, int *converted); extern int lxc_safe_long(const char *numstr, long int *converted); extern int lxc_safe_long_long(const char *numstr, long long int *converted); extern int lxc_safe_ulong(const char *numstr, unsigned long *converted); extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base); /* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */ extern int parse_byte_size_string(const char *s, int64_t *converted); /* Switch to a new uid and gid. */ int lxc_switch_uid_gid(uid_t uid, gid_t gid); int lxc_setgroups(int size, gid_t list[]); /* Find an unused loop device and associate it with source. */ int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags); /* Clear all mounts on a given node. * >= 0 successfully cleared. The number returned is the number of umounts * performed. * < 0 error umounting. Return -errno. */ int lxc_unstack_mountpoint(const char *path, bool lazy); /* * run_command runs a command and collect it's std{err,out} output in buf. * * @param[out] buf The buffer where the commands std{err,out] output will be * read into. If no output was produced, buf will be memset * to 0. * @param[in] buf_size The size of buf. This function will reserve one byte for * \0-termination. * @param[in] child_fn The function to be run in the child process. This * function must exec. * @param[in] args Arguments to be passed to child_fn. */ int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args); /* Concatenate all passed-in strings into one path. Do not fail. If any piece * is not prefixed with '/', add a '/'. */ __attribute__((sentinel)) extern char *must_make_path(const char *first, ...); __attribute__((sentinel)) extern char *must_append_path(char *first, ...); /* return copy of string @entry; do not fail. */ extern char *must_copy_string(const char *entry); /* Re-alllocate a pointer, do not fail */ extern void *must_realloc(void *orig, size_t sz); /* __typeof__ should be safe to use with all compilers. */ typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic; extern bool has_fs_type(const char *path, fs_type_magic magic_val); extern bool fhas_fs_type(int fd, fs_type_magic magic_val); extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val); extern bool lxc_nic_exists(char *nic); extern int lxc_make_tmpfile(char *template, bool rm); static inline uint64_t lxc_getpagesize(void) { int64_t pgsz; pgsz = sysconf(_SC_PAGESIZE); if (pgsz <= 0) pgsz = 1 << 12; return pgsz; } /* If n is not a power of 2 this function will return the next power of 2 * greater than that number. Note that this function always returns the *next* * power of 2 *greater* that number not the *nearest*. For example, passing 1025 * as argument this function will return 2048 although the closest power of 2 * would be 1024. * If the caller passes in 0 they will receive 0 in return since this is invalid * input and 0 is not a power of 2. */ extern uint64_t lxc_find_next_power2(uint64_t n); static inline pid_t lxc_raw_gettid(void) { #ifdef SYS_gettid return syscall(SYS_gettid); #else return lxc_raw_getpid(); #endif } #endif /* __LXC_UTILS_H */ lxc-2.0.11/src/lxc/lxc.h0000644061062106075000000001130213435013473011626 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXC_H #define __LXC_LXC_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "state.h" struct lxc_msg; struct lxc_conf; struct lxc_arguments; struct lxc_handler; /** Following code is for liblxc. lxc/lxc.h will contain exports of liblxc **/ /* * Start the specified command inside a system container * @name : the name of the container * @argv : an array of char * corresponding to the commande line * @conf : configuration * @backgrounded : whether or not the container is daemonized * Returns 0 on success, < 0 otherwise */ extern int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler, const char *lxcpath, bool backgrounded, int *error_num); /* * Start the specified command inside an application container * @name : the name of the container * @argv : an array of char * corresponding to the commande line * @quiet : if != 0 then lxc-init won't produce any output * @conf : configuration * @backgrounded : whether or not the container is daemonized * Returns 0 on success, < 0 otherwise */ extern int lxc_execute(const char *name, char *const argv[], int quiet, struct lxc_handler *handler, const char *lxcpath, bool backgrounded, int *error_num); /* * Close the fd associated with the monitoring * @fd : the file descriptor provided by lxc_monitor_open * Returns 0 on success, < 0 otherwise */ extern int lxc_monitor_close(int fd); /* * Freeze all the tasks running inside the container * @name : the container name * Returns 0 on success, < 0 otherwise */ extern int lxc_freeze(const char *name, const char *lxcpath); /* * Unfreeze all previously frozen tasks. * @name : the name of the container * Return 0 on success, < 0 otherwise */ extern int lxc_unfreeze(const char *name, const char *lxcpath); /* * Retrieve the container state * @name : the name of the container * Returns the state of the container on success, < 0 otherwise */ extern lxc_state_t lxc_state(const char *name, const char *lxcpath); /* * Set a specified value for a specified subsystem. The specified * subsystem must be fully specified, eg. "cpu.shares" * @filename : the cgroup attribute filename * @value : the value to be set * @name : the name of the container * @lxcpath : lxc config path for container * Returns 0 on success, < 0 otherwise */ extern int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath); /* * Get a specified value for a specified subsystem. The specified * subsystem must be fully specified, eg. "cpu.shares" * @filename : the cgroup attribute filename * @value : the value to be set * @len : the len of the value variable * @name : the name of the container * @lxcpath : lxc config path for container * Returns the number of bytes read, < 0 on error */ extern int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath); /* * Create and return a new lxccontainer struct. */ extern struct lxc_container *lxc_container_new(const char *name, const char *configpath); /* * 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); /* * Add a dependency to a container */ extern int add_rdepend(struct lxc_conf *lxc_conf, char *rdepend); #ifdef __cplusplus } #endif #endif lxc-2.0.11/src/lxc/initutils.h0000644061062106075000000000371313435013473013073 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_INITUTILS_H #define __LXC_INITUTILS_H #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #define DEFAULT_VG "lxc" #define DEFAULT_THIN_POOL "lxc" #define DEFAULT_ZFSROOT "lxc" #define DEFAULT_RBDPOOL "lxc" #ifndef PR_SET_MM #define PR_SET_MM 35 #endif #ifndef PR_SET_MM_MAP #define PR_SET_MM_MAP 14 struct prctl_mm_map { uint64_t start_code; uint64_t end_code; uint64_t start_data; uint64_t end_data; uint64_t start_brk; uint64_t brk; uint64_t start_stack; uint64_t arg_start; uint64_t arg_end; uint64_t env_start; uint64_t env_end; uint64_t *auxv; uint32_t auxv_size; uint32_t exe_fd; }; #endif extern const char *lxc_global_config_value(const char *option_name); /* open a file with O_CLOEXEC */ extern void remove_trailing_slashes(char *p); extern FILE *fopen_cloexec(const char *path, const char *mode); extern int setproctitle(char *title); #endif /* __LXC_INITUTILS_H */ lxc-2.0.11/src/lxc/rexec.h0000644061062106075000000000173313435013473012155 00000000000000/* liblxcapi * * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_REXEC_H #define __LXC_REXEC_H extern int lxc_rexec(const char *memfd_name); #endif /* __LXC_REXEC_H */ lxc-2.0.11/src/lxc/cgroups/0000755061062106075000000000000013435013523012430 500000000000000lxc-2.0.11/src/lxc/cgroups/cgroup.c0000644061062106075000000001310013435013473014012 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "cgroup.h" #include "conf.h" #include "initutils.h" #include "log.h" #include "start.h" lxc_log_define(lxc_cgroup, lxc); static struct cgroup_ops *ops = NULL; extern struct cgroup_ops *cgfs_ops_init(void); extern struct cgroup_ops *cgfsng_ops_init(void); extern struct cgroup_ops *cgm_ops_init(void); __attribute__((constructor)) void cgroup_ops_init(void) { if (ops) { INFO("cgroup driver %s", ops->name); return; } DEBUG("cgroup_init"); #if HAVE_CGMANAGER ops = cgm_ops_init(); #endif if (!ops) ops = cgfsng_ops_init(); if (!ops) ops = cgfs_ops_init(); if (ops) INFO("Initialized cgroup driver %s", ops->name); } bool cgroup_init(struct lxc_handler *handler) { if (handler->cgroup_data) { ERROR("cgroup_init called on already initialized handler"); return true; } if (ops) { INFO("cgroup driver %s initing for %s", ops->name, handler->name); handler->cgroup_data = ops->init(handler); } return handler->cgroup_data != NULL; } void cgroup_destroy(struct lxc_handler *handler) { if (ops) { ops->destroy(handler->cgroup_data, handler->conf); handler->cgroup_data = NULL; } } /* Create the container cgroups for all requested controllers. */ bool cgroup_create(struct lxc_handler *handler) { if (ops) return ops->create(handler->cgroup_data); return false; } /* Enter the container init into its new cgroups for all requested controllers. */ bool cgroup_enter(struct lxc_handler *handler) { if (ops) return ops->enter(handler->cgroup_data, handler->pid); return false; } bool cgroup_create_legacy(struct lxc_handler *handler) { if (ops && ops->create_legacy) return ops->create_legacy(handler->cgroup_data, handler->pid); return true; } const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem) { if (ops) return ops->get_cgroup(handler->cgroup_data, subsystem); return NULL; } bool cgroup_escape(struct lxc_handler *handler) { if (ops) return ops->escape(handler->cgroup_data); return false; } int cgroup_num_hierarchies(void) { if (!ops) return -1; return ops->num_hierarchies(); } bool cgroup_get_hierarchies(int n, char ***out) { if (!ops) return false; return ops->get_hierarchies(n, out); } bool cgroup_unfreeze(struct lxc_handler *handler) { if (ops) return ops->unfreeze(handler->cgroup_data); return false; } bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices) { if (ops) return ops->setup_limits(handler->cgroup_data, handler->conf, with_devices); return false; } bool cgroup_chown(struct lxc_handler *handler) { if (ops && ops->chown) return ops->chown(handler->cgroup_data, handler->conf); return true; } bool cgroup_mount(const char *root, struct lxc_handler *handler, int type) { if (ops) return ops->mount_cgroup(handler, root, type); return false; } int cgroup_nrtasks(struct lxc_handler *handler) { if (ops) { if (ops->nrtasks) return ops->nrtasks(handler->cgroup_data); else WARN("cgroup driver \"%s\" doesn't implement nrtasks", ops->name); } return -1; } bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid) { if (ops) return ops->attach(name, lxcpath, pid); return false; } int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath) { if (ops) return ops->set(filename, value, name, lxcpath); return -1; } int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { if (ops) return ops->get(filename, value, len, name, lxcpath); return -1; } void cgroup_disconnect(void) { if (ops && ops->disconnect) ops->disconnect(); } cgroup_driver_t cgroup_driver(void) { return ops->driver; } #define INIT_SCOPE "/init.scope" void prune_init_scope(char *cg) { char *point; if (!cg) return; point = cg + strlen(cg) - strlen(INIT_SCOPE); if (point < cg) return; if (strcmp(point, INIT_SCOPE) == 0) { if (point == cg) *(point + 1) = '\0'; else *point = '\0'; } } /* Return true if this is a subsystem which we cannot do without. * * systemd is questionable here. The way callers currently use this, if systemd * is not mounted then it will be ignored. But if systemd is mounted, then it * must be setup so that lxc can create cgroups in it, else containers will * fail. * * cgroups listed in lxc.cgroup.use are also treated as crucial * */ bool is_crucial_cgroup_subsystem(const char *s) { const char *cgroup_use; if (strcmp(s, "systemd") == 0) return true; if (strcmp(s, "name=systemd") == 0) return true; if (strcmp(s, "freezer") == 0) return true; cgroup_use = lxc_global_config_value("lxc.cgroup.use"); if (cgroup_use && strstr(cgroup_use, s)) return true; return false; } lxc-2.0.11/src/lxc/cgroups/cgroup_utils.h0000644061062106075000000000346213435013473015251 00000000000000/* * lxc: linux Container library * * Copyright © 2017 Canonical Ltd. * * Authors: * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CGROUP_UTILS_H #define __LXC_CGROUP_UTILS_H #include #include /* Retrieve the cgroup version of a given entry from /proc//mountinfo. */ extern int get_cgroup_version(char *line); /* Check if given entry from /proc//mountinfo is a cgroupfs v1 mount. */ extern bool is_cgroupfs_v1(char *line); /* Check if given entry from /proc//mountinfo is a cgroupfs v2 mount. */ extern bool is_cgroupfs_v2(char *line); /* Given a v1 hierarchy @mountpoint and base @path, verify that we can create * directories underneath it. */ extern bool test_writeable_v1(char *mountpoint, char *path); /* Given a v2 hierarchy @mountpoint and base @path, verify that we can create * directories underneath it and that we have write access to the cgroup's * "cgroup.procs" file. */ extern bool test_writeable_v2(char *mountpoint, char *path); #endif /* __LXC_CGROUP_UTILS_H */ lxc-2.0.11/src/lxc/cgroups/cgfsng.c0000644061062106075000000020102513435013473013767 00000000000000/* * lxc: linux Container library * * Copyright © 2016 Canonical Ltd. * * Authors: * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * cgfs-ng.c: this is a new, simplified implementation of a filesystem * cgroup backend. The original cgfs.c was designed to be as flexible * as possible. It would try to find cgroup filesystems no matter where * or how you had them mounted, and deduce the most usable mount for * each controller. It also was not designed for unprivileged use, as * that was reserved for cgmanager. * * This new implementation assumes that cgroup filesystems are mounted * under /sys/fs/cgroup/clist where clist is either the controller, or * a comman-separated list of controllers. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "caps.h" #include "cgroup.h" #include "cgroup_utils.h" #include "commands.h" #include "conf.h" #include "log.h" #include "storage/storage.h" #include "utils.h" lxc_log_define(lxc_cgfsng, lxc); static struct cgroup_ops cgfsng_ops; /* A descriptor for a mounted hierarchy * * @controllers * - legacy hierarchy * Either NULL, or a null-terminated list of all the co-mounted controllers. * - unified hierarchy * Either NULL, or a null-terminated list of all enabled controllers. * * @mountpoint * - The mountpoint we will use. * - legacy hierarchy * It will be either /sys/fs/cgroup/controller or * /sys/fs/cgroup/controllerlist. * - unified hierarchy * It will either be /sys/fs/cgroup or /sys/fs/cgroup/ * depending on whether this is a hybrid cgroup layout (mix of legacy and * unified hierarchies) or a pure unified cgroup layout. * * @base_cgroup * - The cgroup under which the container cgroup path * is created. This will be either the caller's cgroup (if not root), or * init's cgroup (if root). * * @fullcgpath * - The full path to the containers cgroup. * * @version * - legacy hierarchy * If the hierarchy is a legacy hierarchy this will be set to * CGROUP_SUPER_MAGIC. * - unified hierarchy * If the hierarchy is a legacy hierarchy this will be set to * CGROUP2_SUPER_MAGIC. */ struct hierarchy { char **controllers; char *mountpoint; char *base_cgroup; char *fullcgpath; int version; }; /* The cgroup data which is attached to the lxc_handler. * * @cgroup_pattern * - A copy of lxc.cgroup.pattern. * * @container_cgroup * - If not null, the cgroup which was created for the container. For each * hierarchy, it is created under the @hierarchy->base_cgroup directory. * Relative to the base_cgroup it is the same for all hierarchies. * * @name * - The name of the container. * * @cgroup_layout * - What cgroup layout the container is running with. * - CGROUP_LAYOUT_UNKNOWN * The cgroup layout could not be determined. This should be treated as an * error condition. * - CGROUP_LAYOUT_LEGACY * The container is running with all controllers mounted into legacy cgroup * hierarchies. * - CGROUP_LAYOUT_HYBRID * The container is running with at least one controller mounted into a * legacy cgroup hierarchy and a mountpoint for the unified hierarchy. The * unified hierarchy can be empty (no controllers enabled) or non-empty * (controllers enabled). * - CGROUP_LAYOUT_UNIFIED * The container is running on a pure unified cgroup hierarchy. The unified * hierarchy can be empty (no controllers enabled) or non-empty (controllers * enabled). */ struct cgfsng_handler_data { char *cgroup_pattern; char *container_cgroup; /* cgroup we created for the container */ char *name; /* container name */ cgroup_layout_t cgroup_layout; }; /* @hierarchies * - A NULL-terminated array of struct hierarchy, one per legacy hierarchy. No * duplicates. First sufficient, writeable mounted hierarchy wins. */ struct hierarchy **hierarchies; /* Pointer to the unified hierarchy in the null terminated list @hierarchies. * This is merely a convenience for hybrid cgroup layouts to easily retrieve the * unified hierarchy without iterating throught @hierarchies. */ struct hierarchy *unified; /* * @cgroup_layout * - What cgroup layout the container is running with. * - CGROUP_LAYOUT_UNKNOWN * The cgroup layout could not be determined. This should be treated as an * error condition. * - CGROUP_LAYOUT_LEGACY * The container is running with all controllers mounted into legacy cgroup * hierarchies. * - CGROUP_LAYOUT_HYBRID * The container is running with at least one controller mounted into a * legacy cgroup hierarchy and a mountpoint for the unified hierarchy. The * unified hierarchy can be empty (no controllers enabled) or non-empty * (controllers enabled). * - CGROUP_LAYOUT_UNIFIED * The container is running on a pure unified cgroup hierarchy. The unified * hierarchy can be empty (no controllers enabled) or non-empty (controllers * enabled). */ cgroup_layout_t cgroup_layout; /* What controllers is the container supposed to use. */ char *cgroup_use; /* @lxc_cgfsng_debug * - Whether to print debug info to stdout for the cgfsng driver. */ static bool lxc_cgfsng_debug; #define CGFSNG_DEBUG(format, ...) \ do { \ if (lxc_cgfsng_debug) \ printf("cgfsng: " format, ##__VA_ARGS__); \ } while (0) static void free_string_list(char **clist) { int i; if (!clist) return; for (i = 0; clist[i]; i++) free(clist[i]); free(clist); } /* Allocate a pointer, do not fail. */ static void *must_alloc(size_t sz) { return must_realloc(NULL, sz); } /* Given a pointer to a null-terminated array of pointers, realloc to add one * entry, and point the new entry to NULL. Do not fail. Return the index to the * second-to-last entry - that is, the one which is now available for use * (keeping the list null-terminated). */ static int append_null_to_list(void ***list) { int newentry = 0; if (*list) for (; (*list)[newentry]; newentry++) ; *list = must_realloc(*list, (newentry + 2) * sizeof(void **)); (*list)[newentry + 1] = NULL; return newentry; } /* Given a null-terminated array of strings, check whether @entry is one of the * strings. */ static bool string_in_list(char **list, const char *entry) { int i; if (!list) return false; for (i = 0; list[i]; i++) if (strcmp(list[i], entry) == 0) return true; return false; } /* Return a copy of @entry prepending "name=", i.e. turn "systemd" into * "name=systemd". Do not fail. */ static char *cg_legacy_must_prefix_named(char *entry) { size_t len; char *prefixed; len = strlen(entry); prefixed = must_alloc(len + 6); memcpy(prefixed, "name=", sizeof("name=") - 1); memcpy(prefixed + sizeof("name=") - 1, entry, len); prefixed[len + 5] = '\0'; return prefixed; } /* Append an entry to the clist. Do not fail. @clist must be NULL the first time * we are called. * * We also handle named subsystems here. Any controller which is not a kernel * subsystem, we prefix "name=". Any which is both a kernel and named subsystem, * we refuse to use because we're not sure which we have here. * (TODO: We could work around this in some cases by just remounting to be * unambiguous, or by comparing mountpoint contents with current cgroup.) * * The last entry will always be NULL. */ static void must_append_controller(char **klist, char **nlist, char ***clist, char *entry) { int newentry; char *copy; if (string_in_list(klist, entry) && string_in_list(nlist, entry)) { ERROR("Refusing to use ambiguous controller \"%s\"", entry); ERROR("It is both a named and kernel subsystem"); return; } newentry = append_null_to_list((void ***)clist); if (strncmp(entry, "name=", 5) == 0) copy = must_copy_string(entry); else if (string_in_list(klist, entry)) copy = must_copy_string(entry); else copy = cg_legacy_must_prefix_named(entry); (*clist)[newentry] = copy; } static void free_handler_data(struct cgfsng_handler_data *d) { free(d->cgroup_pattern); free(d->container_cgroup); free(d->name); free(d); } /* Given a handler's cgroup data, return the struct hierarchy for the controller * @c, or NULL if there is none. */ struct hierarchy *get_hierarchy(const char *c) { int i; if (!hierarchies) return NULL; for (i = 0; hierarchies[i]; i++) { if (!c) { /* This is the empty unified hierarchy. */ if (hierarchies[i]->controllers && !hierarchies[i]->controllers[0]) return hierarchies[i]; continue; } if (string_in_list(hierarchies[i]->controllers, c)) return hierarchies[i]; } return NULL; } #define BATCH_SIZE 50 static void batch_realloc(char **mem, size_t oldlen, size_t newlen) { int newbatches = (newlen / BATCH_SIZE) + 1; int oldbatches = (oldlen / BATCH_SIZE) + 1; if (!*mem || newbatches > oldbatches) { *mem = must_realloc(*mem, newbatches * BATCH_SIZE); } } static void append_line(char **dest, size_t oldlen, char *new, size_t newlen) { size_t full = oldlen + newlen; batch_realloc(dest, oldlen, full + 1); memcpy(*dest + oldlen, new, newlen + 1); } /* Slurp in a whole file */ static char *read_file(const char *fnam) { FILE *f; char *line = NULL, *buf = NULL; size_t len = 0, fulllen = 0; int linelen; f = fopen(fnam, "r"); if (!f) return NULL; while ((linelen = getline(&line, &len, f)) != -1) { append_line(&buf, fulllen, line, linelen); fulllen += linelen; } fclose(f); free(line); return buf; } /* Taken over modified from the kernel sources. */ #define NBITS 32 /* bits in uint32_t */ #define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, NBITS) static void set_bit(unsigned bit, uint32_t *bitarr) { bitarr[bit / NBITS] |= (1 << (bit % NBITS)); } static void clear_bit(unsigned bit, uint32_t *bitarr) { bitarr[bit / NBITS] &= ~(1 << (bit % NBITS)); } static bool is_set(unsigned bit, uint32_t *bitarr) { return (bitarr[bit / NBITS] & (1 << (bit % NBITS))) != 0; } /* Create cpumask from cpulist aka turn: * * 0,2-3 * * into bit array * * 1 0 1 1 */ static uint32_t *lxc_cpumask(char *buf, size_t nbits) { char *token; size_t arrlen; uint32_t *bitarr; char *saveptr = NULL; arrlen = BITS_TO_LONGS(nbits); bitarr = calloc(arrlen, sizeof(uint32_t)); if (!bitarr) return NULL; for (; (token = strtok_r(buf, ",", &saveptr)); buf = NULL) { errno = 0; unsigned end, start; char *range; start = strtoul(token, NULL, 0); end = start; range = strchr(token, '-'); if (range) end = strtoul(range + 1, NULL, 0); if (!(start <= end)) { free(bitarr); return NULL; } if (end >= nbits) { free(bitarr); return NULL; } while (start <= end) set_bit(start++, bitarr); } return bitarr; } /* Turn cpumask into simple, comma-separated cpulist. */ static char *lxc_cpumask_to_cpulist(uint32_t *bitarr, size_t nbits) { int ret; size_t i; char **cpulist = NULL; char numstr[LXC_NUMSTRLEN64] = {0}; for (i = 0; i <= nbits; i++) { if (!is_set(i, bitarr)) continue; ret = snprintf(numstr, LXC_NUMSTRLEN64, "%zu", i); if (ret < 0 || (size_t)ret >= LXC_NUMSTRLEN64) { lxc_free_array((void **)cpulist, free); return NULL; } ret = lxc_append_string(&cpulist, numstr); if (ret < 0) { lxc_free_array((void **)cpulist, free); return NULL; } } if (!cpulist) return NULL; return lxc_string_join(",", (const char **)cpulist, false); } static ssize_t get_max_cpus(char *cpulist) { char *c1, *c2; char *maxcpus = cpulist; size_t cpus = 0; c1 = strrchr(maxcpus, ','); if (c1) c1++; c2 = strrchr(maxcpus, '-'); if (c2) c2++; if (!c1 && !c2) c1 = maxcpus; else if (c1 > c2) c2 = c1; else if (c1 < c2) c1 = c2; else if (!c1 && c2) c1 = c2; errno = 0; cpus = strtoul(c1, NULL, 0); if (errno != 0) return -1; return cpus; } #define __ISOL_CPUS "/sys/devices/system/cpu/isolated" static bool cg_legacy_filter_and_set_cpus(char *path, bool am_initialized) { int ret; ssize_t i; char *lastslash, *fpath, oldv; ssize_t maxisol = 0, maxposs = 0; char *cpulist = NULL, *isolcpus = NULL, *posscpus = NULL; uint32_t *isolmask = NULL, *possmask = NULL; bool bret = false, flipped_bit = false; lastslash = strrchr(path, '/'); if (!lastslash) { ERROR("Failed to detect \"/\" in \"%s\"", path); return bret; } oldv = *lastslash; *lastslash = '\0'; fpath = must_make_path(path, "cpuset.cpus", NULL); posscpus = read_file(fpath); if (!posscpus) { SYSERROR("Failed to read file \"%s\"", fpath); goto on_error; } /* Get maximum number of cpus found in possible cpuset. */ maxposs = get_max_cpus(posscpus); if (maxposs < 0) goto on_error; if (!file_exists(__ISOL_CPUS)) { /* This system doesn't expose isolated cpus. */ DEBUG("The path \""__ISOL_CPUS"\" to read isolated cpus from does not exist"); cpulist = posscpus; /* No isolated cpus but we weren't already initialized by * someone. We should simply copy the parents cpuset.cpus * values. */ if (!am_initialized) { DEBUG("Copying cpu settings of parent cgroup"); goto copy_parent; } /* No isolated cpus but we were already initialized by someone. * Nothing more to do for us. */ goto on_success; } isolcpus = read_file(__ISOL_CPUS); if (!isolcpus) { SYSERROR("Failed to read file \""__ISOL_CPUS"\""); goto on_error; } if (!isdigit(isolcpus[0])) { TRACE("No isolated cpus detected"); cpulist = posscpus; /* No isolated cpus but we weren't already initialized by * someone. We should simply copy the parents cpuset.cpus * values. */ if (!am_initialized) { DEBUG("Copying cpu settings of parent cgroup"); goto copy_parent; } /* No isolated cpus but we were already initialized by someone. * Nothing more to do for us. */ goto on_success; } /* Get maximum number of cpus found in isolated cpuset. */ maxisol = get_max_cpus(isolcpus); if (maxisol < 0) goto on_error; if (maxposs < maxisol) maxposs = maxisol; maxposs++; possmask = lxc_cpumask(posscpus, maxposs); if (!possmask) { ERROR("Failed to create cpumask for possible cpus"); goto on_error; } isolmask = lxc_cpumask(isolcpus, maxposs); if (!isolmask) { ERROR("Failed to create cpumask for isolated cpus"); goto on_error; } for (i = 0; i <= maxposs; i++) { if (!is_set(i, isolmask) || !is_set(i, possmask)) continue; flipped_bit = true; clear_bit(i, possmask); } if (!flipped_bit) { DEBUG("No isolated cpus present in cpuset"); goto on_success; } DEBUG("Removed isolated cpus from cpuset"); cpulist = lxc_cpumask_to_cpulist(possmask, maxposs); if (!cpulist) { ERROR("Failed to create cpu list"); goto on_error; } copy_parent: *lastslash = oldv; free(fpath); fpath = must_make_path(path, "cpuset.cpus", NULL); ret = lxc_write_to_file(fpath, cpulist, strlen(cpulist), false); if (ret < 0) { SYSERROR("Failed to write cpu list to \"%s\"", fpath); goto on_error; } on_success: bret = true; on_error: free(fpath); free(isolcpus); free(isolmask); if (posscpus != cpulist) free(posscpus); free(possmask); free(cpulist); return bret; } /* Copy contents of parent(@path)/@file to @path/@file */ static bool copy_parent_file(char *path, char *file) { int ret; char *fpath, *lastslash, oldv; int len = 0; char *value = NULL; lastslash = strrchr(path, '/'); if (!lastslash) { ERROR("Failed to detect \"/\" in \"%s\"", path); return false; } oldv = *lastslash; *lastslash = '\0'; fpath = must_make_path(path, file, NULL); len = lxc_read_from_file(fpath, NULL, 0); if (len <= 0) goto on_error; value = must_alloc(len + 1); ret = lxc_read_from_file(fpath, value, len); if (ret != len) goto on_error; free(fpath); *lastslash = oldv; fpath = must_make_path(path, file, NULL); ret = lxc_write_to_file(fpath, value, len, false); if (ret < 0) SYSERROR("Failed to write \"%s\" to file \"%s\"", value, fpath); free(fpath); free(value); return ret >= 0; on_error: SYSERROR("Failed to read file \"%s\"", fpath); free(fpath); free(value); return false; } /* Initialize the cpuset hierarchy in first directory of @gname and set * cgroup.clone_children so that children inherit settings. Since the * h->base_path is populated by init or ourselves, we know it is already * initialized. */ static bool cg_legacy_handle_cpuset_hierarchy(struct hierarchy *h, char *cgname) { int ret; char v; char *cgpath, *clonechildrenpath, *slash; if (!string_in_list(h->controllers, "cpuset")) return true; if (*cgname == '/') cgname++; slash = strchr(cgname, '/'); if (slash) *slash = '\0'; cgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL); if (slash) *slash = '/'; ret = mkdir(cgpath, 0755); if (ret < 0) { if (errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", cgpath); free(cgpath); return false; } } clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL); /* unified hierarchy doesn't have clone_children */ if (!file_exists(clonechildrenpath)) { free(clonechildrenpath); free(cgpath); return true; } ret = lxc_read_from_file(clonechildrenpath, &v, 1); if (ret < 0) { SYSERROR("Failed to read file \"%s\"", clonechildrenpath); free(clonechildrenpath); free(cgpath); return false; } /* Make sure any isolated cpus are removed from cpuset.cpus. */ if (!cg_legacy_filter_and_set_cpus(cgpath, v == '1')) { SYSERROR("Failed to remove isolated cpus"); free(clonechildrenpath); free(cgpath); return false; } /* Already set for us by someone else. */ if (v == '1') { DEBUG("\"cgroup.clone_children\" was already set to \"1\""); free(clonechildrenpath); free(cgpath); return true; } /* copy parent's settings */ if (!copy_parent_file(cgpath, "cpuset.mems")) { SYSERROR("Failed to copy \"cpuset.mems\" settings"); free(cgpath); free(clonechildrenpath); return false; } free(cgpath); ret = lxc_write_to_file(clonechildrenpath, "1", 1, false); if (ret < 0) { /* Set clone_children so children inherit our settings */ SYSERROR("Failed to write 1 to \"%s\"", clonechildrenpath); free(clonechildrenpath); return false; } free(clonechildrenpath); return true; } /* Given two null-terminated lists of strings, return true if any string is in * both. */ static bool controller_lists_intersect(char **l1, char **l2) { int i; if (!l1 || !l2) return false; for (i = 0; l1[i]; i++) { if (string_in_list(l2, l1[i])) return true; } return false; } /* For a null-terminated list of controllers @clist, return true if any of those * controllers is already listed the null-terminated list of hierarchies @hlist. * Realistically, if one is present, all must be present. */ static bool controller_list_is_dup(struct hierarchy **hlist, char **clist) { int i; if (!hlist) return false; for (i = 0; hlist[i]; i++) if (controller_lists_intersect(hlist[i]->controllers, clist)) return true; return false; } /* Return true if the controller @entry is found in the null-terminated list of * hierarchies @hlist. */ static bool controller_found(struct hierarchy **hlist, char *entry) { int i; if (!hlist) return false; for (i = 0; hlist[i]; i++) if (string_in_list(hlist[i]->controllers, entry)) return true; return false; } /* Return true if all of the controllers which we require have been found. The * required list is freezer and anything in lxc.cgroup.use. */ static bool all_controllers_found(void) { char *p; char *saveptr = NULL; struct hierarchy **hlist = hierarchies; if (!controller_found(hlist, "freezer")) { CGFSNG_DEBUG("No freezer controller mountpoint found\n"); return false; } if (!cgroup_use) return true; for (; (p = strtok_r(cgroup_use, ",", &saveptr)); cgroup_use = NULL) if (!controller_found(hlist, p)) { CGFSNG_DEBUG("No %s controller mountpoint found\n", p); return false; } return true; } /* Get the controllers from a mountinfo line There are other ways we could get * this info. For lxcfs, field 3 is /cgroup/controller-list. For cgroupfs, we * could parse the mount options. But we simply assume that the mountpoint must * be /sys/fs/cgroup/controller-list */ static char **cg_hybrid_get_controllers(char **klist, char **nlist, char *line, int type) { /* The fourth field is /sys/fs/cgroup/comma-delimited-controller-list * for legacy hierarchies. */ int i; char *dup, *p2, *tok; char *p = line, *saveptr = NULL, *sep = ","; char **aret = NULL; for (i = 0; i < 4; i++) { p = strchr(p, ' '); if (!p) return NULL; p++; } /* Note, if we change how mountinfo works, then our caller will need to * verify /sys/fs/cgroup/ in this field. */ if (strncmp(p, "/sys/fs/cgroup/", 15) != 0) { CGFSNG_DEBUG("Found hierarchy not under /sys/fs/cgroup: \"%s\"\n", p); return NULL; } p += 15; p2 = strchr(p, ' '); if (!p2) { CGFSNG_DEBUG("Corrupt mountinfo\n"); return NULL; } *p2 = '\0'; if (type == CGROUP_SUPER_MAGIC) { /* strdup() here for v1 hierarchies. Otherwise strtok_r() will * destroy mountpoints such as "/sys/fs/cgroup/cpu,cpuacct". */ dup = strdup(p); if (!dup) return NULL; for (tok = strtok_r(dup, sep, &saveptr); tok; tok = strtok_r(NULL, sep, &saveptr)) must_append_controller(klist, nlist, &aret, tok); free(dup); } *p2 = ' '; return aret; } static char **cg_unified_make_empty_controller(void) { int newentry; char **aret = NULL; newentry = append_null_to_list((void ***)&aret); aret[newentry] = NULL; return aret; } static char **cg_unified_get_controllers(const char *file) { char *buf, *tok; char *saveptr = NULL, *sep = " \t\n"; char **aret = NULL; buf = read_file(file); if (!buf) return NULL; for (tok = strtok_r(buf, sep, &saveptr); tok; tok = strtok_r(NULL, sep, &saveptr)) { int newentry; char *copy; newentry = append_null_to_list((void ***)&aret); copy = must_copy_string(tok); aret[newentry] = copy; } free(buf); return aret; } static struct hierarchy *add_hierarchy(char **clist, char *mountpoint, char *base_cgroup, int type) { struct hierarchy *new; int newentry; new = must_alloc(sizeof(*new)); new->controllers = clist; new->mountpoint = mountpoint; new->base_cgroup = base_cgroup; new->fullcgpath = NULL; new->version = type; newentry = append_null_to_list((void ***)&hierarchies); hierarchies[newentry] = new; return new; } /* Get a copy of the mountpoint from @line, which is a line from * /proc/self/mountinfo. */ static char *cg_hybrid_get_mountpoint(char *line) { int i; size_t len; char *p2; char *p = line, *sret = NULL; for (i = 0; i < 4; i++) { p = strchr(p, ' '); if (!p) return NULL; p++; } if (strncmp(p, "/sys/fs/cgroup/", 15) != 0) return NULL; p2 = strchr(p + 15, ' '); if (!p2) return NULL; *p2 = '\0'; len = strlen(p); sret = must_alloc(len + 1); memcpy(sret, p, len); sret[len] = '\0'; return sret; } /* Given a multi-line string, return a null-terminated copy of the current line. */ static char *copy_to_eol(char *p) { char *p2 = strchr(p, '\n'), *sret; size_t len; if (!p2) return NULL; len = p2 - p; sret = must_alloc(len + 1); memcpy(sret, p, len); sret[len] = '\0'; return sret; } /* cgline: pointer to character after the first ':' in a line in a \n-terminated * /proc/self/cgroup file. Check whether controller c is present. */ static bool controller_in_clist(char *cgline, char *c) { char *tok, *saveptr = NULL, *eol, *tmp; size_t len; eol = strchr(cgline, ':'); if (!eol) return false; len = eol - cgline; tmp = alloca(len + 1); memcpy(tmp, cgline, len); tmp[len] = '\0'; for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { if (strcmp(tok, c) == 0) return true; } return false; } /* @basecginfo is a copy of /proc/$$/cgroup. Return the current cgroup for * @controller. */ static char *cg_hybrid_get_current_cgroup(char *basecginfo, char *controller, int type) { char *p = basecginfo; for (;;) { bool is_cgv2_base_cgroup = false; /* cgroup v2 entry in "/proc//cgroup": "0::/some/path" */ if ((type == CGROUP2_SUPER_MAGIC) && (*p == '0')) is_cgv2_base_cgroup = true; p = strchr(p, ':'); if (!p) return NULL; p++; if (is_cgv2_base_cgroup || (controller && controller_in_clist(p, controller))) { p = strchr(p, ':'); if (!p) return NULL; p++; return copy_to_eol(p); } p = strchr(p, '\n'); if (!p) return NULL; p++; } } static void must_append_string(char ***list, char *entry) { int newentry; char *copy; newentry = append_null_to_list((void ***)list); copy = must_copy_string(entry); (*list)[newentry] = copy; } static int get_existing_subsystems(char ***klist, char ***nlist) { FILE *f; char *line = NULL; size_t len = 0; f = fopen("/proc/self/cgroup", "r"); if (!f) return -1; while (getline(&line, &len, f) != -1) { char *p, *p2, *tok, *saveptr = NULL; p = strchr(line, ':'); if (!p) continue; p++; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; /* If the kernel has cgroup v2 support, then /proc/self/cgroup * contains an entry of the form: * * 0::/some/path * * In this case we use "cgroup2" as controller name. */ if ((p2 - p) == 0) { must_append_string(klist, "cgroup2"); continue; } for (tok = strtok_r(p, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { if (strncmp(tok, "name=", 5) == 0) must_append_string(nlist, tok); else must_append_string(klist, tok); } } free(line); fclose(f); return 0; } static void trim(char *s) { size_t len; len = strlen(s); while ((len > 1) && (s[len - 1] == '\n')) s[--len] = '\0'; } static void lxc_cgfsng_print_handler_data(const struct cgfsng_handler_data *d) { printf("Cgroup information:\n"); printf(" container name: %s\n", d->name ? d->name : "(null)"); printf(" lxc.cgroup.use: %s\n", cgroup_use ? cgroup_use : "(null)"); printf(" lxc.cgroup.pattern: %s\n", d->cgroup_pattern ? d->cgroup_pattern : "(null)"); printf(" cgroup: %s\n", d->container_cgroup ? d->container_cgroup : "(null)"); } static void lxc_cgfsng_print_hierarchies() { int i; struct hierarchy **it; if (!hierarchies) { printf(" No hierarchies found\n"); return; } printf(" Hierarchies:\n"); for (i = 0, it = hierarchies; it && *it; it++, i++) { int j; char **cit; printf(" %d: base_cgroup: %s\n", i, (*it)->base_cgroup ? (*it)->base_cgroup : "(null)"); printf(" mountpoint: %s\n", (*it)->mountpoint ? (*it)->mountpoint : "(null)"); printf(" controllers:\n"); for (j = 0, cit = (*it)->controllers; cit && *cit; cit++, j++) printf(" %d: %s\n", j, *cit); } } static void lxc_cgfsng_print_basecg_debuginfo(char *basecginfo, char **klist, char **nlist) { int k; char **it; printf("basecginfo is:\n"); printf("%s\n", basecginfo); for (k = 0, it = klist; it && *it; it++, k++) printf("kernel subsystem %d: %s\n", k, *it); for (k = 0, it = nlist; it && *it; it++, k++) printf("named subsystem %d: %s\n", k, *it); } static void lxc_cgfsng_print_debuginfo(const struct cgfsng_handler_data *d) { lxc_cgfsng_print_handler_data(d); lxc_cgfsng_print_hierarchies(); } /* At startup, parse_hierarchies finds all the info we need about cgroup * mountpoints and current cgroups, and stores it in @d. */ static bool cg_hybrid_init(void) { int ret; char *basecginfo; bool will_escape; FILE *f; size_t len = 0; char *line = NULL; char **klist = NULL, **nlist = NULL; /* Root spawned containers escape the current cgroup, so use init's * cgroups as our base in that case. */ will_escape = (geteuid() == 0); if (will_escape) basecginfo = read_file("/proc/1/cgroup"); else basecginfo = read_file("/proc/self/cgroup"); if (!basecginfo) return false; ret = get_existing_subsystems(&klist, &nlist); if (ret < 0) { CGFSNG_DEBUG("Failed to retrieve available legacy cgroup controllers\n"); free(basecginfo); return false; } f = fopen("/proc/self/mountinfo", "r"); if (!f) { CGFSNG_DEBUG("Failed to open \"/proc/self/mountinfo\"\n"); free(basecginfo); return false; } if (lxc_cgfsng_debug) lxc_cgfsng_print_basecg_debuginfo(basecginfo, klist, nlist); while (getline(&line, &len, f) != -1) { int type; bool writeable; struct hierarchy *new; char *base_cgroup = NULL, *mountpoint = NULL; char **controller_list = NULL; type = get_cgroup_version(line); if (type == 0) continue; if (type == CGROUP2_SUPER_MAGIC && unified) continue; if (cgroup_layout == CGROUP_LAYOUT_UNKNOWN) { if (type == CGROUP2_SUPER_MAGIC) cgroup_layout = CGROUP_LAYOUT_UNIFIED; else if (type == CGROUP_SUPER_MAGIC) cgroup_layout = CGROUP_LAYOUT_LEGACY; } else if (cgroup_layout == CGROUP_LAYOUT_UNIFIED) { if (type == CGROUP_SUPER_MAGIC) cgroup_layout = CGROUP_LAYOUT_HYBRID; } else if (cgroup_layout == CGROUP_LAYOUT_LEGACY) { if (type == CGROUP2_SUPER_MAGIC) cgroup_layout = CGROUP_LAYOUT_HYBRID; } controller_list = cg_hybrid_get_controllers(klist, nlist, line, type); if (!controller_list && type == CGROUP_SUPER_MAGIC) continue; if (type == CGROUP_SUPER_MAGIC) if (controller_list_is_dup(hierarchies, controller_list)) goto next; mountpoint = cg_hybrid_get_mountpoint(line); if (!mountpoint) { CGFSNG_DEBUG("Failed parsing mountpoint from \"%s\"\n", line); goto next; } if (type == CGROUP_SUPER_MAGIC) base_cgroup = cg_hybrid_get_current_cgroup(basecginfo, controller_list[0], CGROUP_SUPER_MAGIC); else base_cgroup = cg_hybrid_get_current_cgroup(basecginfo, NULL, CGROUP2_SUPER_MAGIC); if (!base_cgroup) { CGFSNG_DEBUG("Failed to find current cgroup\n"); goto next; } trim(base_cgroup); prune_init_scope(base_cgroup); if (type == CGROUP2_SUPER_MAGIC) writeable = test_writeable_v2(mountpoint, base_cgroup); else writeable = test_writeable_v1(mountpoint, base_cgroup); if (!writeable) goto next; if (type == CGROUP2_SUPER_MAGIC) { char *cgv2_ctrl_path; cgv2_ctrl_path = must_make_path(mountpoint, base_cgroup, "cgroup.controllers", NULL); controller_list = cg_unified_get_controllers(cgv2_ctrl_path); free(cgv2_ctrl_path); if (!controller_list) { controller_list = cg_unified_make_empty_controller(); CGFSNG_DEBUG("No controllers are enabled for " "delegation in the unified hierarchy\n"); } } new = add_hierarchy(controller_list, mountpoint, base_cgroup, type); if (type == CGROUP2_SUPER_MAGIC && !unified) unified = new; continue; next: free_string_list(controller_list); free(mountpoint); free(base_cgroup); } free_string_list(klist); free_string_list(nlist); free(basecginfo); fclose(f); free(line); if (lxc_cgfsng_debug) { printf("Writable cgroup hierarchies:\n"); lxc_cgfsng_print_hierarchies(); } /* verify that all controllers in cgroup.use and all crucial * controllers are accounted for */ if (!all_controllers_found()) return false; return true; } static int cg_is_pure_unified(void) { int ret; struct statfs fs; ret = statfs("/sys/fs/cgroup", &fs); if (ret < 0) return -ENOMEDIUM; if (is_fs_type(&fs, CGROUP2_SUPER_MAGIC)) return CGROUP2_SUPER_MAGIC; return 0; } /* Get current cgroup from /proc/self/cgroup for the cgroupfs v2 hierarchy. */ static char *cg_unified_get_current_cgroup(void) { char *basecginfo, *base_cgroup; bool will_escape; char *copy = NULL; will_escape = (geteuid() == 0); if (will_escape) basecginfo = read_file("/proc/1/cgroup"); else basecginfo = read_file("/proc/self/cgroup"); if (!basecginfo) return NULL; base_cgroup = strstr(basecginfo, "0::/"); if (!base_cgroup) goto cleanup_on_err; base_cgroup = base_cgroup + 3; copy = copy_to_eol(base_cgroup); if (!copy) goto cleanup_on_err; cleanup_on_err: free(basecginfo); if (copy) trim(copy); return copy; } static int cg_unified_init(void) { int ret; char *mountpoint, *subtree_path; char **delegatable; char *base_cgroup = NULL; ret = cg_is_pure_unified(); if (ret == -ENOMEDIUM) return -ENOMEDIUM; if (ret != CGROUP2_SUPER_MAGIC) return 0; base_cgroup = cg_unified_get_current_cgroup(); if (!base_cgroup) return -EINVAL; prune_init_scope(base_cgroup); /* We assume that we have already been given controllers to delegate * further down the hierarchy. If not it is up to the user to delegate * them to us. */ mountpoint = must_copy_string("/sys/fs/cgroup"); subtree_path = must_make_path(mountpoint, base_cgroup, "cgroup.subtree_control", NULL); delegatable = cg_unified_get_controllers(subtree_path); free(subtree_path); if (!delegatable) delegatable = cg_unified_make_empty_controller(); if (!delegatable[0]) CGFSNG_DEBUG("No controllers are enabled for delegation\n"); /* TODO: If the user requested specific controllers via lxc.cgroup.use * we should verify here. The reason I'm not doing it right is that I'm * not convinced that lxc.cgroup.use will be the future since it is a * global property. I much rather have an option that lets you request * controllers per container. */ add_hierarchy(delegatable, mountpoint, base_cgroup, CGROUP2_SUPER_MAGIC); unified = hierarchies[0]; cgroup_layout = CGROUP_LAYOUT_UNIFIED; return CGROUP2_SUPER_MAGIC; } static bool cg_init(void) { int ret; const char *tmp; errno = 0; tmp = lxc_global_config_value("lxc.cgroup.use"); if (!cgroup_use && errno != 0) { /* lxc.cgroup.use can be NULL */ CGFSNG_DEBUG("Failed to retrieve list of cgroups to use\n"); return false; } cgroup_use = must_copy_string(tmp); ret = cg_unified_init(); if (ret < 0) return false; if (ret == CGROUP2_SUPER_MAGIC) return true; return cg_hybrid_init(); } static void *cgfsng_init(struct lxc_handler *handler) { const char *cgroup_pattern; struct cgfsng_handler_data *d; d = must_alloc(sizeof(*d)); memset(d, 0, sizeof(*d)); /* copy container name */ d->name = must_copy_string(handler->name); /* copy system-wide cgroup information */ cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); if (!cgroup_pattern) { /* lxc.cgroup.pattern is only NULL on error. */ ERROR("Failed to retrieve cgroup pattern"); goto out_free; } d->cgroup_pattern = must_copy_string(cgroup_pattern); d->cgroup_layout = cgroup_layout; if (d->cgroup_layout == CGROUP_LAYOUT_LEGACY) TRACE("Running with legacy cgroup layout"); else if (d->cgroup_layout == CGROUP_LAYOUT_HYBRID) TRACE("Running with hybrid cgroup layout"); else if (d->cgroup_layout == CGROUP_LAYOUT_UNIFIED) TRACE("Running with unified cgroup layout"); else WARN("Running with unknown cgroup layout"); if (lxc_cgfsng_debug) lxc_cgfsng_print_debuginfo(d); return d; out_free: free_handler_data(d); return NULL; } static int recursive_destroy(char *dirname) { int ret; struct dirent *direntp; DIR *dir; int r = 0; dir = opendir(dirname); if (!dir) return -1; while ((direntp = readdir(dir))) { char *pathname; struct stat mystat; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; pathname = must_make_path(dirname, direntp->d_name, NULL); ret = lstat(pathname, &mystat); if (ret < 0) { if (!r) WARN("Failed to stat \"%s\"", pathname); r = -1; goto next; } if (!S_ISDIR(mystat.st_mode)) goto next; ret = recursive_destroy(pathname); if (ret < 0) r = -1; next: free(pathname); } ret = rmdir(dirname); if (ret < 0) { if (!r) WARN("%s - Failed to delete \"%s\"", strerror(errno), dirname); r = -1; } ret = closedir(dir); if (ret < 0) { if (!r) WARN("%s - Failed to delete \"%s\"", strerror(errno), dirname); r = -1; } return r; } static int cgroup_rmdir(char *container_cgroup) { int i; if (!container_cgroup || !hierarchies) return 0; for (i = 0; hierarchies[i]; i++) { int ret; struct hierarchy *h = hierarchies[i]; if (!h->fullcgpath) continue; ret = recursive_destroy(h->fullcgpath); if (ret < 0) WARN("Failed to destroy \"%s\"", h->fullcgpath); free(h->fullcgpath); h->fullcgpath = NULL; } return 0; } struct generic_userns_exec_data { struct cgfsng_handler_data *d; struct lxc_conf *conf; uid_t origuid; /* target uid in parent namespace */ char *path; }; static int cgroup_rmdir_wrapper(void *data) { int ret; struct generic_userns_exec_data *arg = data; uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid; gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid; ret = setresgid(nsgid, nsgid, nsgid); if (ret < 0) { SYSERROR("Failed to setresgid(%d, %d, %d)", (int)nsgid, (int)nsgid, (int)nsgid); return -1; } ret = setresuid(nsuid, nsuid, nsuid); if (ret < 0) { SYSERROR("Failed to setresuid(%d, %d, %d)", (int)nsuid, (int)nsuid, (int)nsuid); return -1; } ret = setgroups(0, NULL); if (ret < 0 && errno != EPERM) { SYSERROR("Failed to setgroups(0, NULL)"); return -1; } return cgroup_rmdir(arg->d->container_cgroup); } static void cgfsng_destroy(void *hdata, struct lxc_conf *conf) { int ret; struct cgfsng_handler_data *d = hdata; struct generic_userns_exec_data wrap; if (!d) return; wrap.origuid = 0; wrap.d = hdata; wrap.conf = conf; if (conf && !lxc_list_empty(&conf->id_map)) ret = userns_exec_1(conf, cgroup_rmdir_wrapper, &wrap, "cgroup_rmdir_wrapper"); else ret = cgroup_rmdir(d->container_cgroup); if (ret < 0) { WARN("Failed to destroy cgroups"); return; } free_handler_data(d); } struct cgroup_ops *cgfsng_ops_init(void) { if (getenv("LXC_DEBUG_CGFSNG")) lxc_cgfsng_debug = true; if (!cg_init()) return NULL; return &cgfsng_ops; } static bool cg_unified_create_cgroup(struct hierarchy *h, char *cgname) { size_t i, parts_len; char **it; size_t full_len = 0; char *add_controllers = NULL, *cgroup = NULL; char **parts = NULL; bool bret = false; if (h->version != CGROUP2_SUPER_MAGIC) return true; if (!h->controllers) return true; /* For now we simply enable all controllers that we have detected by * creating a string like "+memory +pids +cpu +io". * TODO: In the near future we might want to support "-" * etc. but whether supporting semantics like this make sense will need * some thinking. */ for (it = h->controllers; it && *it; it++) { full_len += strlen(*it) + 2; add_controllers = must_realloc(add_controllers, full_len + 1); if (h->controllers[0] == *it) add_controllers[0] = '\0'; strcat(add_controllers, "+"); strcat(add_controllers, *it); if ((it + 1) && *(it + 1)) strcat(add_controllers, " "); } parts = lxc_string_split(cgname, '/'); if (!parts) goto on_error; parts_len = lxc_array_len((void **)parts); if (parts_len > 0) parts_len--; cgroup = must_make_path(h->mountpoint, h->base_cgroup, NULL); for (i = 0; i < parts_len; i++) { int ret; char *target; cgroup = must_append_path(cgroup, parts[i], NULL); target = must_make_path(cgroup, "cgroup.subtree_control", NULL); ret = lxc_write_to_file(target, add_controllers, full_len, false); free(target); if (ret < 0) { SYSERROR("Could not enable \"%s\" controllers in the " "unified cgroup \"%s\"", add_controllers, cgroup); goto on_error; } } bret = true; on_error: lxc_free_array((void **)parts, free); free(add_controllers); free(cgroup); return bret; } static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname) { int ret; h->fullcgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL); if (dir_exists(h->fullcgpath)) { ERROR("The cgroup \"%s\" already existed", h->fullcgpath); return false; } if (!cg_legacy_handle_cpuset_hierarchy(h, cgname)) { ERROR("Failed to handle legacy cpuset controller"); return false; } ret = mkdir_p(h->fullcgpath, 0755); if (ret < 0) { ERROR("Failed to create cgroup \"%s\"", h->fullcgpath); return false; } return cg_unified_create_cgroup(h, cgname); } static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname) { int ret; ret = rmdir(h->fullcgpath); if (ret < 0) SYSERROR("Failed to rmdir(\"%s\") from failed creation attempt", h->fullcgpath); free(h->fullcgpath); h->fullcgpath = NULL; } /* Try to create the same cgroup in all hierarchies. Start with cgroup_pattern; * next cgroup_pattern-1, -2, ..., -999. */ static inline bool cgfsng_create(void *hdata) { int i; size_t len; char *container_cgroup, *offset, *tmp; int idx = 0; struct cgfsng_handler_data *d = hdata; if (!d) return false; if (d->container_cgroup) { WARN("cgfsng_create called a second time"); return false; } tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern); if (!tmp) { ERROR("Failed expanding cgroup name pattern"); return false; } len = strlen(tmp) + 5; /* leave room for -NNN\0 */ container_cgroup = must_alloc(len); strcpy(container_cgroup, tmp); free(tmp); offset = container_cgroup + len - 5; again: if (idx == 1000) { ERROR("Too many conflicting cgroup names"); goto out_free; } if (idx) { int ret; ret = snprintf(offset, 5, "-%d", idx); if (ret < 0 || (size_t)ret >= 5) { FILE *f = fopen("/dev/null", "w"); if (f) { fprintf(f, "Workaround for GCC7 bug: " "https://gcc.gnu.org/bugzilla/" "show_bug.cgi?id=78969"); fclose(f); } } } for (i = 0; hierarchies[i]; i++) { if (!create_path_for_hierarchy(hierarchies[i], container_cgroup)) { int j; ERROR("Failed to create cgroup \"%s\"", hierarchies[i]->fullcgpath); free(hierarchies[i]->fullcgpath); hierarchies[i]->fullcgpath = NULL; for (j = 0; j < i; j++) remove_path_for_hierarchy(hierarchies[j], container_cgroup); idx++; goto again; } } d->container_cgroup = container_cgroup; return true; out_free: free(container_cgroup); return false; } static bool cgfsng_enter(void *hdata, pid_t pid) { int i, len; char pidstr[25]; len = snprintf(pidstr, 25, "%d", pid); if (len < 0 || len >= 25) return false; for (i = 0; hierarchies[i]; i++) { int ret; char *fullpath; fullpath = must_make_path(hierarchies[i]->fullcgpath, "cgroup.procs", NULL); ret = lxc_write_to_file(fullpath, pidstr, len, false); if (ret != 0) { SYSERROR("Failed to enter cgroup \"%s\"", fullpath); free(fullpath); return false; } free(fullpath); } return true; } static int chowmod(char *path, uid_t chown_uid, gid_t chown_gid, mode_t chmod_mode) { int ret; ret = chown(path, chown_uid, chown_gid); if (ret < 0) { WARN("%s - Failed to chown(%s, %d, %d)", strerror(errno), path, (int)chown_uid, (int)chown_gid); return -1; } ret = chmod(path, chmod_mode); if (ret < 0) { WARN("%s - Failed to chmod(%s, %d)", strerror(errno), path, (int)chmod_mode); return -1; } return 0; } /* chgrp the container cgroups to container group. We leave * the container owner as cgroup owner. So we must make the * directories 775 so that the container can create sub-cgroups. * * Also chown the tasks and cgroup.procs files. Those may not * exist depending on kernel version. */ static int chown_cgroup_wrapper(void *data) { int i, ret; uid_t destuid; struct generic_userns_exec_data *arg = data; uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid; gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid; ret = setresgid(nsgid, nsgid, nsgid); if (ret < 0) { SYSERROR("Failed to setresgid(%d, %d, %d)", (int)nsgid, (int)nsgid, (int)nsgid); return -1; } ret = setresuid(nsuid, nsuid, nsuid); if (ret < 0) { SYSERROR("Failed to setresuid(%d, %d, %d)", (int)nsuid, (int)nsuid, (int)nsuid); return -1; } ret = setgroups(0, NULL); if (ret < 0 && errno != EPERM) { SYSERROR("Failed to setgroups(0, NULL)"); return -1; } destuid = get_ns_uid(arg->origuid); for (i = 0; hierarchies[i]; i++) { char *fullpath; char *path = hierarchies[i]->fullcgpath; ret = chowmod(path, destuid, nsgid, 0775); if (ret < 0) return -1; /* Failures to chown() these are inconvenient but not * detrimental We leave these owned by the container launcher, * so that container root can write to the files to attach. We * chmod() them 664 so that container systemd can write to the * files (which systemd in wily insists on doing). */ if (hierarchies[i]->version == CGROUP_SUPER_MAGIC) { fullpath = must_make_path(path, "tasks", NULL); (void)chowmod(fullpath, destuid, nsgid, 0664); free(fullpath); } fullpath = must_make_path(path, "cgroup.procs", NULL); (void)chowmod(fullpath, destuid, 0, 0664); free(fullpath); if (hierarchies[i]->version != CGROUP2_SUPER_MAGIC) continue; fullpath = must_make_path(path, "cgroup.subtree_control", NULL); (void)chowmod(fullpath, destuid, nsgid, 0664); free(fullpath); fullpath = must_make_path(path, "cgroup.threads", NULL); (void)chowmod(fullpath, destuid, nsgid, 0664); free(fullpath); } return 0; } static bool cgfsng_chown(void *hdata, struct lxc_conf *conf) { struct cgfsng_handler_data *d = hdata; struct generic_userns_exec_data wrap; if (!d) return false; if (lxc_list_empty(&conf->id_map)) return true; wrap.origuid = geteuid(); wrap.path = NULL; wrap.d = d; wrap.conf = conf; if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap, "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new user namespace"); return false; } return true; } /* cgroup-full:* is done, no need to create subdirs */ static bool cg_mount_needs_subdirs(int type) { if (type >= LXC_AUTO_CGROUP_FULL_RO) return false; return true; } /* After $rootfs/sys/fs/container/controller/the/cg/path has been created, * remount controller ro if needed and bindmount the cgroupfs onto * controll/the/cg/path. */ static int cg_legacy_mount_controllers(int type, struct hierarchy *h, char *controllerpath, char *cgpath, const char *container_cgroup) { int ret, remount_flags; char *sourcepath; int flags = MS_BIND; if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_MIXED) { ret = mount(controllerpath, controllerpath, "cgroup", MS_BIND, NULL); if (ret < 0) { SYSERROR("Failed to bind mount \"%s\" onto \"%s\"", controllerpath, controllerpath); return -1; } remount_flags = add_required_remount_flags(controllerpath, controllerpath, flags | MS_REMOUNT); ret = mount(controllerpath, controllerpath, "cgroup", remount_flags | MS_REMOUNT | MS_BIND | MS_RDONLY, NULL); if (ret < 0) { SYSERROR("Failed to remount \"%s\" ro", controllerpath); return -1; } INFO("Remounted %s read-only", controllerpath); } sourcepath = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL); if (type == LXC_AUTO_CGROUP_RO) flags |= MS_RDONLY; ret = mount(sourcepath, cgpath, "cgroup", flags, NULL); if (ret < 0) { SYSERROR("Failed to mount \"%s\" onto \"%s\"", h->controllers[0], cgpath); free(sourcepath); return -1; } INFO("Mounted \"%s\" onto \"%s\"", h->controllers[0], cgpath); if (flags & MS_RDONLY) { remount_flags = add_required_remount_flags(sourcepath, cgpath, flags | MS_REMOUNT); ret = mount(sourcepath, cgpath, "cgroup", remount_flags, NULL); if (ret < 0) { SYSERROR("Failed to remount \"%s\" ro", cgpath); free(sourcepath); return -1; } INFO("Remounted %s read-only", cgpath); } free(sourcepath); INFO("Completed second stage cgroup automounts for \"%s\"", cgpath); return 0; } /* __cg_mount_direct * * Mount cgroup hierarchies directly without using bind-mounts. The main * uses-cases are mounting cgroup hierarchies in cgroup namespaces and mounting * cgroups for the LXC_AUTO_CGROUP_FULL option. */ static int __cg_mount_direct(int type, struct hierarchy *h, const char *controllerpath) { int ret; char *controllers = NULL; char *fstype = "cgroup2"; unsigned long flags = 0; flags |= MS_NOSUID; flags |= MS_NOEXEC; flags |= MS_NODEV; flags |= MS_RELATIME; if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO) flags |= MS_RDONLY; if (h->version != CGROUP2_SUPER_MAGIC) { controllers = lxc_string_join(",", (const char **)h->controllers, false); if (!controllers) return -ENOMEM; fstype = "cgroup"; } ret = mount("cgroup", controllerpath, fstype, flags, controllers); free(controllers); if (ret < 0) { SYSERROR("Failed to mount \"%s\" with cgroup filesystem type %s", controllerpath, fstype); return -1; } DEBUG("Mounted \"%s\" with cgroup filesystem type %s", controllerpath, fstype); return 0; } static inline int cg_mount_in_cgroup_namespace(int type, struct hierarchy *h, const char *controllerpath) { return __cg_mount_direct(type, h, controllerpath); } static inline int cg_mount_cgroup_full(int type, struct hierarchy *h, const char *controllerpath) { if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) return 0; return __cg_mount_direct(type, h, controllerpath); } static bool cgfsng_mount(void *hdata, const char *root, int type) { int i, ret; char *tmpfspath = NULL; bool has_cgns = false, retval = false, wants_force_mount = false; struct lxc_handler *handler = hdata; struct cgfsng_handler_data *d = handler->cgroup_data; if ((type & LXC_AUTO_CGROUP_MASK) == 0) return true; if (type & LXC_AUTO_CGROUP_FORCE) { type &= ~LXC_AUTO_CGROUP_FORCE; wants_force_mount = true; } if (!wants_force_mount){ if (!lxc_list_empty(&handler->conf->keepcaps)) wants_force_mount = !in_caplist(CAP_SYS_ADMIN, &handler->conf->keepcaps); else wants_force_mount = in_caplist(CAP_SYS_ADMIN, &handler->conf->caps); } has_cgns = cgns_supported(); if (has_cgns && !wants_force_mount) return true; if (type == LXC_AUTO_CGROUP_NOSPEC) type = LXC_AUTO_CGROUP_MIXED; else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) type = LXC_AUTO_CGROUP_FULL_MIXED; /* Mount tmpfs */ tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL); ret = safe_mount(NULL, tmpfspath, "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "size=10240k,mode=755", root); if (ret < 0) goto on_error; for (i = 0; hierarchies[i]; i++) { char *controllerpath, *path2; struct hierarchy *h = hierarchies[i]; char *controller = strrchr(h->mountpoint, '/'); if (!controller) continue; controller++; controllerpath = must_make_path(tmpfspath, controller, NULL); if (dir_exists(controllerpath)) { free(controllerpath); continue; } ret = mkdir(controllerpath, 0755); if (ret < 0) { SYSERROR("Error creating cgroup path: %s", controllerpath); free(controllerpath); goto on_error; } if (has_cgns && wants_force_mount) { /* If cgroup namespaces are supported but the container * will not have CAP_SYS_ADMIN after it has started we * need to mount the cgroups manually. */ ret = cg_mount_in_cgroup_namespace(type, h, controllerpath); free(controllerpath); if (ret < 0) goto on_error; continue; } ret = cg_mount_cgroup_full(type, h, controllerpath); if (ret < 0) { free(controllerpath); goto on_error; } if (!cg_mount_needs_subdirs(type)) { free(controllerpath); continue; } path2 = must_make_path(controllerpath, h->base_cgroup, d->container_cgroup, NULL); ret = mkdir_p(path2, 0755); if (ret < 0) { free(controllerpath); free(path2); goto on_error; } ret = cg_legacy_mount_controllers(type, h, controllerpath, path2, d->container_cgroup); free(controllerpath); free(path2); if (ret < 0) goto on_error; } retval = true; on_error: free(tmpfspath); return retval; } static int recursive_count_nrtasks(char *dirname) { struct dirent *direntp; DIR *dir; int count = 0, ret; char *path; dir = opendir(dirname); if (!dir) return 0; while ((direntp = readdir(dir))) { struct stat mystat; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; path = must_make_path(dirname, direntp->d_name, NULL); if (lstat(path, &mystat)) goto next; if (!S_ISDIR(mystat.st_mode)) goto next; count += recursive_count_nrtasks(path); next: free(path); } path = must_make_path(dirname, "cgroup.procs", NULL); ret = lxc_count_file_lines(path); if (ret != -1) count += ret; free(path); (void)closedir(dir); return count; } static int cgfsng_nrtasks(void *hdata) { int count; char *path; struct cgfsng_handler_data *d = hdata; if (!d || !d->container_cgroup || !hierarchies) return -1; path = must_make_path(hierarchies[0]->fullcgpath, NULL); count = recursive_count_nrtasks(path); free(path); return count; } /* Only root needs to escape to the cgroup of its init. */ static bool cgfsng_escape() { int i; if (geteuid()) return true; for (i = 0; hierarchies[i]; i++) { int ret; char *fullpath; fullpath = must_make_path(hierarchies[i]->mountpoint, hierarchies[i]->base_cgroup, "cgroup.procs", NULL); ret = lxc_write_to_file(fullpath, "0", 2, false); if (ret != 0) { SYSERROR("Failed to escape to cgroup \"%s\"", fullpath); free(fullpath); return false; } free(fullpath); } return true; } static int cgfsng_num_hierarchies(void) { int i; for (i = 0; hierarchies[i]; i++) ; return i; } static bool cgfsng_get_hierarchies(int n, char ***out) { int i; /* sanity check n */ for (i = 0; i < n; i++) if (!hierarchies[i]) return false; *out = hierarchies[i]->controllers; return true; } #define THAWED "THAWED" #define THAWED_LEN (strlen(THAWED)) /* TODO: If the unified cgroup hierarchy grows a freezer controller this needs * to be adapted. */ static bool cgfsng_unfreeze(void *hdata) { int ret; char *fullpath; struct hierarchy *h; h = get_hierarchy("freezer"); if (!h) return false; fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL); ret = lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false); free(fullpath); if (ret < 0) return false; return true; } static const char *cgfsng_get_cgroup(void *hdata, const char *controller) { struct hierarchy *h; h = get_hierarchy(controller); if (!h) { SYSERROR("Failed to find hierarchy for controller \"%s\"", controller ? controller : "(null)"); return NULL; } return h->fullcgpath ? h->fullcgpath + strlen(h->mountpoint) : NULL; } /* Given a cgroup path returned from lxc_cmd_get_cgroup_path, build a full path, * which must be freed by the caller. */ static inline char *build_full_cgpath_from_monitorpath(struct hierarchy *h, const char *inpath, const char *filename) { return must_make_path(h->mountpoint, inpath, filename, NULL); } /* Technically, we're always at a delegation boundary here (This is especially * true when cgroup namespaces are available.). The reasoning is that in order * for us to have been able to start a container in the first place the root * cgroup must have been a leaf node. Now, either the container's init system * has populated the cgroup and kept it as a leaf node or it has created * subtrees. In the former case we will simply attach to the leaf node we * created when we started the container in the latter case we create our own * cgroup for the attaching process. */ static int __cg_unified_attach(const struct hierarchy *h, const char *name, const char *lxcpath, const char *pidstr, size_t pidstr_len, const char *controller) { int ret; size_t len; int fret = -1, idx = 0; char *base_path = NULL, *container_cgroup = NULL, *full_path = NULL; container_cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller); /* not running */ if (!container_cgroup) return 0; base_path = must_make_path(h->mountpoint, container_cgroup, NULL); full_path = must_make_path(base_path, "cgroup.procs", NULL); /* cgroup is populated */ ret = lxc_write_to_file(full_path, pidstr, pidstr_len, false); if (ret < 0 && errno != EBUSY) goto on_error; if (ret == 0) goto on_success; free(full_path); len = strlen(base_path) + sizeof("/lxc-1000") - 1 + sizeof("/cgroup-procs") - 1; full_path = must_alloc(len + 1); do { if (idx) ret = snprintf(full_path, len + 1, "%s/lxc-%d", base_path, idx); else ret = snprintf(full_path, len + 1, "%s/lxc", base_path); if (ret < 0 || (size_t)ret >= len + 1) goto on_error; ret = mkdir_p(full_path, 0755); if (ret < 0 && errno != EEXIST) goto on_error; strcat(full_path, "/cgroup.procs"); ret = lxc_write_to_file(full_path, pidstr, len, false); if (ret == 0) goto on_success; /* this is a non-leaf node */ if (errno != EBUSY) goto on_error; } while (++idx > 0 && idx < 1000); on_success: if (idx < 1000) fret = 0; on_error: free(base_path); free(container_cgroup); free(full_path); return fret; } static bool cgfsng_attach(const char *name, const char *lxcpath, pid_t pid) { int i, len, ret; char pidstr[25]; len = snprintf(pidstr, 25, "%d", pid); if (len < 0 || len >= 25) return false; for (i = 0; hierarchies[i]; i++) { char *path; char *fullpath = NULL; struct hierarchy *h = hierarchies[i]; if (h->version == CGROUP2_SUPER_MAGIC) { ret = __cg_unified_attach(h, name, lxcpath, pidstr, len, h->controllers[0]); if (ret < 0) return false; continue; } path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]); /* not running */ if (!path) continue; fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs"); free(path); ret = lxc_write_to_file(fullpath, pidstr, len, false); if (ret < 0) { SYSERROR("Failed to attach %d to %s", (int)pid, fullpath); free(fullpath); return false; } free(fullpath); } return true; } /* Called externally (i.e. from 'lxc-cgroup') to query cgroup limits. Here we * don't have a cgroup_data set up, so we ask the running container through the * commands API for the cgroup path. */ static int cgfsng_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { int ret = -1; size_t controller_len; char *controller, *p, *path; struct hierarchy *h; controller_len = strlen(filename); controller = alloca(controller_len + 1); strcpy(controller, filename); p = strchr(controller, '.'); if (p) *p = '\0'; path = lxc_cmd_get_cgroup_path(name, lxcpath, controller); /* not running */ if (!path) return -1; h = get_hierarchy(controller); if (h) { char *fullpath; fullpath = build_full_cgpath_from_monitorpath(h, path, filename); ret = lxc_read_from_file(fullpath, value, len); free(fullpath); } free(path); return ret; } /* Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits. Here we * don't have a cgroup_data set up, so we ask the running container through the * commands API for the cgroup path. */ static int cgfsng_set(const char *filename, const char *value, const char *name, const char *lxcpath) { int ret = -1; size_t controller_len; char *controller, *p, *path; struct hierarchy *h; controller_len = strlen(filename); controller = alloca(controller_len + 1); strcpy(controller, filename); p = strchr(controller, '.'); if (p) *p = '\0'; path = lxc_cmd_get_cgroup_path(name, lxcpath, controller); /* not running */ if (!path) return -1; h = get_hierarchy(controller); if (h) { char *fullpath; fullpath = build_full_cgpath_from_monitorpath(h, path, filename); ret = lxc_write_to_file(fullpath, value, strlen(value), false); free(fullpath); } free(path); return ret; } /* take devices cgroup line * /dev/foo rwx * and convert it to a valid * type major:minor mode * line. Return <0 on error. Dest is a preallocated buffer long enough to hold * the output. */ static int convert_devpath(const char *invalue, char *dest) { int n_parts; char *p, *path, type; unsigned long minor, major; struct stat sb; int ret = -EINVAL; char *mode = NULL; path = must_copy_string(invalue); /* Read path followed by mode. Ignore any trailing text. * A ' # comment' would be legal. Technically other text is not * legal, we could check for that if we cared to. */ for (n_parts = 1, p = path; *p && n_parts < 3; p++) { if (*p != ' ') continue; *p = '\0'; if (n_parts != 1) break; p++; n_parts++; while (*p == ' ') p++; mode = p; if (*p == '\0') goto out; } if (n_parts == 1) goto out; ret = stat(path, &sb); if (ret < 0) goto out; mode_t m = sb.st_mode & S_IFMT; switch (m) { case S_IFBLK: type = 'b'; break; case S_IFCHR: type = 'c'; break; default: ERROR("Unsupported device type %i for \"%s\"", m, path); ret = -EINVAL; goto out; } major = MAJOR(sb.st_rdev); minor = MINOR(sb.st_rdev); ret = snprintf(dest, 50, "%c %lu:%lu %s", type, major, minor, mode); if (ret < 0 || ret >= 50) { ERROR("Error on configuration value \"%c %lu:%lu %s\" (max 50 " "chars)", type, major, minor, mode); ret = -ENAMETOOLONG; goto out; } ret = 0; out: free(path); return ret; } /* Called from setup_limits - here we have the container's cgroup_data because * we created the cgroups. */ static int cg_legacy_set_data(const char *filename, const char *value, struct cgfsng_handler_data *d) { size_t len; char *fullpath, *p; /* "b|c <2^64-1>:<2^64-1> r|w|m" = 47 chars max */ char converted_value[50]; struct hierarchy *h; int ret = 0; char *controller = NULL; len = strlen(filename); controller = alloca(len + 1); strcpy(controller, filename); p = strchr(controller, '.'); if (p) *p = '\0'; if (strcmp("devices.allow", filename) == 0 && value[0] == '/') { ret = convert_devpath(value, converted_value); if (ret < 0) return ret; value = converted_value; } h = get_hierarchy(controller); if (!h) { ERROR("Failed to setup limits for the \"%s\" controller. " "The controller seems to be unused by \"cgfsng\" cgroup " "driver or not enabled on the cgroup hierarchy", controller); errno = ENOENT; return -ENOENT; } fullpath = must_make_path(h->fullcgpath, filename, NULL); ret = lxc_write_to_file(fullpath, value, strlen(value), false); free(fullpath); return ret; } static bool __cg_legacy_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices) { struct lxc_list *iterator, *next, *sorted_cgroup_settings; struct lxc_cgroup *cg; struct cgfsng_handler_data *d = hdata; bool ret = false; if (lxc_list_empty(cgroup_settings)) return true; sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings); if (!sorted_cgroup_settings) return false; lxc_list_for_each(iterator, sorted_cgroup_settings) { cg = iterator->elem; if (do_devices == !strncmp("devices", cg->subsystem, 7)) { if (cg_legacy_set_data(cg->subsystem, cg->value, d)) { if (do_devices && (errno == EACCES || errno == EPERM)) { WARN("Failed to set \"%s\" to \"%s\"", cg->subsystem, cg->value); continue; } WARN("Failed to set \"%s\" to \"%s\"", cg->subsystem, cg->value); goto out; } DEBUG("Set controller \"%s\" set to \"%s\"", cg->subsystem, cg->value); } } ret = true; INFO("Limits for the legacy cgroup hierarchies have been setup"); out: lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) { lxc_list_del(iterator); free(iterator); } free(sorted_cgroup_settings); return ret; } static bool __cg_unified_setup_limits(void *hdata, struct lxc_list *cgroup_settings) { INFO("Setting limits on the unified cgroup hierarchy is not supported"); return true; } static bool cgfsng_setup_limits(void *hdata, struct lxc_conf *conf, bool do_devices) { bool bret; bret = __cg_legacy_setup_limits(hdata, &conf->cgroup, do_devices); if (!bret) return false; return __cg_unified_setup_limits(NULL, NULL); } static struct cgroup_ops cgfsng_ops = { .init = cgfsng_init, .destroy = cgfsng_destroy, .create = cgfsng_create, .enter = cgfsng_enter, .escape = cgfsng_escape, .num_hierarchies = cgfsng_num_hierarchies, .get_hierarchies = cgfsng_get_hierarchies, .get_cgroup = cgfsng_get_cgroup, .get = cgfsng_get, .set = cgfsng_set, .unfreeze = cgfsng_unfreeze, .setup_limits = cgfsng_setup_limits, .name = "cgroupfs-ng", .attach = cgfsng_attach, .chown = cgfsng_chown, .mount_cgroup = cgfsng_mount, .nrtasks = cgfsng_nrtasks, .driver = CGFSNG, /* unsupported */ .create_legacy = NULL, }; lxc-2.0.11/src/lxc/cgroups/cgroup.h0000644061062106075000000000655413435013473014036 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CGROUP_H #define __LXC_CGROUP_H #include #include #include struct lxc_handler; struct lxc_conf; struct lxc_list; typedef enum { CGROUP_LAYOUT_UNKNOWN = -1, CGROUP_LAYOUT_LEGACY = 0, CGROUP_LAYOUT_HYBRID = 1, CGROUP_LAYOUT_UNIFIED = 2, } cgroup_layout_t; typedef enum { CGFS, CGMANAGER, CGFSNG, } cgroup_driver_t; struct cgroup_ops { const char *name; void *(*init)(struct lxc_handler *handler); void (*destroy)(void *hdata, struct lxc_conf *conf); bool (*create)(void *hdata); bool (*enter)(void *hdata, pid_t pid); bool (*create_legacy)(void *hdata, pid_t pid); const char *(*get_cgroup)(void *hdata, const char *subsystem); bool (*escape)(); int (*num_hierarchies)(); bool (*get_hierarchies)(int n, char ***out); int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath); int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath); bool (*unfreeze)(void *hdata); bool (*setup_limits)(void *hdata, struct lxc_conf *conf, bool with_devices); bool (*chown)(void *hdata, struct lxc_conf *conf); bool (*attach)(const char *name, const char *lxcpath, pid_t pid); bool (*mount_cgroup)(void *hdata, const char *root, int type); int (*nrtasks)(void *hdata); void (*disconnect)(void); cgroup_driver_t driver; }; extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid); extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type); extern void cgroup_destroy(struct lxc_handler *handler); extern bool cgroup_init(struct lxc_handler *handler); extern bool cgroup_create(struct lxc_handler *handler); extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices); extern bool cgroup_chown(struct lxc_handler *handler); extern bool cgroup_enter(struct lxc_handler *handler); extern void cgroup_cleanup(struct lxc_handler *handler); extern bool cgroup_create_legacy(struct lxc_handler *handler); extern int cgroup_nrtasks(struct lxc_handler *handler); extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem); extern bool cgroup_escape(); extern int cgroup_num_hierarchies(); extern bool cgroup_get_hierarchies(int i, char ***out); extern bool cgroup_unfreeze(struct lxc_handler *handler); extern void cgroup_disconnect(void); extern cgroup_driver_t cgroup_driver(void); extern void prune_init_scope(char *cg); extern bool is_crucial_cgroup_subsystem(const char *s); #endif lxc-2.0.11/src/lxc/cgroups/cgfs.c0000644061062106075000000021632213435013473013450 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "commands.h" #include "list.h" #include "conf.h" #include "utils.h" #include "log.h" #include "cgroup.h" #include "start.h" #include "state.h" #include "storage.h" #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif struct cgroup_hierarchy; struct cgroup_meta_data; struct cgroup_mount_point; /* * cgroup_meta_data: the metadata about the cgroup infrastructure on this * host */ struct cgroup_meta_data { ptrdiff_t ref; /* simple refcount */ struct cgroup_hierarchy **hierarchies; struct cgroup_mount_point **mount_points; int maximum_hierarchy; }; /* * cgroup_hierarchy: describes a single cgroup hierarchy * (may have multiple mount points) */ struct cgroup_hierarchy { int index; bool used; /* false if the hierarchy should be ignored by lxc */ char **subsystems; struct cgroup_mount_point *rw_absolute_mount_point; struct cgroup_mount_point *ro_absolute_mount_point; struct cgroup_mount_point **all_mount_points; size_t all_mount_point_capacity; }; /* * cgroup_mount_point: a mount point to where a hierarchy * is mounted to */ struct cgroup_mount_point { struct cgroup_hierarchy *hierarchy; char *mount_point; char *mount_prefix; bool read_only; bool need_cpuset_init; }; /* * cgroup_process_info: describes the membership of a * process to the different cgroup * hierarchies * * Note this is the per-process info tracked by the cgfs_ops. * This is not used with cgmanager. */ struct cgroup_process_info { struct cgroup_process_info *next; struct cgroup_meta_data *meta_ref; struct cgroup_hierarchy *hierarchy; char *cgroup_path; char *cgroup_path_sub; char **created_paths; size_t created_paths_capacity; size_t created_paths_count; struct cgroup_mount_point *designated_mount_point; }; struct cgfs_data { char *name; const char *cgroup_pattern; struct cgroup_meta_data *meta; struct cgroup_process_info *info; }; lxc_log_define(lxc_cgfs, lxc); static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta); static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list); static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp); static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h); static bool is_valid_cgroup(const char *name); static int create_cgroup(struct cgroup_mount_point *mp, const char *path); static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse, struct lxc_conf *conf); static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *path, const char *suffix); static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem); static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len); static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value); static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow); static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices); static int cgroup_recursive_task_count(const char *cgroup_path); static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path); static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path); static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist); static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data); static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data); /* free process membership information */ static void lxc_cgroup_process_info_free(struct cgroup_process_info *info); static void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info, struct lxc_conf *conf); static struct cgroup_ops cgfs_ops; static int cgroup_rmdir(char *dirname) { struct dirent *direntp; int saved_errno = 0; DIR *dir; int ret, failed=0; char pathname[MAXPATHLEN]; dir = opendir(dirname); if (!dir) { ERROR("Failed to open %s", dirname); return -1; } while ((direntp = readdir(dir))) { struct stat mystat; int rc; if (!direntp) break; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("pathname too long"); failed=1; if (!saved_errno) saved_errno = -ENOMEM; continue; } ret = lstat(pathname, &mystat); if (ret) { SYSERROR("Failed to stat %s", pathname); failed=1; if (!saved_errno) saved_errno = errno; continue; } if (S_ISDIR(mystat.st_mode)) { if (cgroup_rmdir(pathname) < 0) { if (!saved_errno) saved_errno = errno; failed=1; } } } if (rmdir(dirname) < 0) { SYSERROR("Failed to delete %s", dirname); if (!saved_errno) saved_errno = errno; failed=1; } ret = closedir(dir); if (ret) { SYSERROR("Failed to close directory %s", dirname); if (!saved_errno) saved_errno = errno; failed=1; } errno = saved_errno; return failed ? -1 : 0; } static int rmdir_wrapper(void *data) { char *path = data; if (setresgid(0,0,0) < 0) SYSERROR("Failed to setgid to 0"); if (setresuid(0,0,0) < 0) SYSERROR("Failed to setuid to 0"); if (setgroups(0, NULL) < 0) SYSERROR("Failed to clear groups"); return cgroup_rmdir(path); } static struct cgroup_meta_data *lxc_cgroup_load_meta() { const char *cgroup_use = NULL; char **cgroup_use_list = NULL; struct cgroup_meta_data *md = NULL; int saved_errno; errno = 0; cgroup_use = lxc_global_config_value("lxc.cgroup.use"); if (!cgroup_use && errno != 0) return NULL; if (cgroup_use) { cgroup_use_list = lxc_string_split_and_trim(cgroup_use, ','); if (!cgroup_use_list) return NULL; } md = lxc_cgroup_load_meta2((const char **)cgroup_use_list); saved_errno = errno; lxc_free_array((void **)cgroup_use_list, free); errno = saved_errno; return md; } /* Step 1: determine all kernel subsystems */ static bool find_cgroup_subsystems(char ***kernel_subsystems) { FILE *proc_cgroups; bool bret = false; char *line = NULL; size_t sz = 0; size_t kernel_subsystems_count = 0; size_t kernel_subsystems_capacity = 0; int r; proc_cgroups = fopen_cloexec("/proc/cgroups", "r"); if (!proc_cgroups) return false; while (getline(&line, &sz, proc_cgroups) != -1) { char *tab1; char *tab2; int hierarchy_number; if (line[0] == '#') continue; if (!line[0]) continue; tab1 = strchr(line, '\t'); if (!tab1) continue; *tab1++ = '\0'; tab2 = strchr(tab1, '\t'); if (!tab2) continue; *tab2 = '\0'; tab2 = NULL; hierarchy_number = strtoul(tab1, &tab2, 10); if (!tab2 || *tab2) continue; (void)hierarchy_number; r = lxc_grow_array((void ***)kernel_subsystems, &kernel_subsystems_capacity, kernel_subsystems_count + 1, 12); if (r < 0) goto out; (*kernel_subsystems)[kernel_subsystems_count] = strdup(line); if (!(*kernel_subsystems)[kernel_subsystems_count]) goto out; kernel_subsystems_count++; } bret = true; out: fclose(proc_cgroups); free(line); return bret; } /* Step 2: determine all hierarchies (by reading /proc/self/cgroup), * since mount points don't specify hierarchy number and * /proc/cgroups does not contain named hierarchies */ static bool find_cgroup_hierarchies(struct cgroup_meta_data *meta_data, bool all_kernel_subsystems, bool all_named_subsystems, const char **subsystem_whitelist) { FILE *proc_self_cgroup; char *line = NULL; size_t sz = 0; int r; bool bret = false; size_t hierarchy_capacity = 0; proc_self_cgroup = fopen_cloexec("/proc/self/cgroup", "r"); /* if for some reason (because of setns() and pid namespace for example), * /proc/self is not valid, we try /proc/1/cgroup... */ if (!proc_self_cgroup) proc_self_cgroup = fopen_cloexec("/proc/1/cgroup", "r"); if (!proc_self_cgroup) return false; while (getline(&line, &sz, proc_self_cgroup) != -1) { /* file format: hierarchy:subsystems:group, * we only extract hierarchy and subsystems * here */ char *colon1; char *colon2; int hierarchy_number; struct cgroup_hierarchy *h = NULL; char **p; if (!line[0]) continue; colon1 = strchr(line, ':'); if (!colon1) continue; *colon1++ = '\0'; colon2 = strchr(colon1, ':'); if (!colon2) continue; *colon2 = '\0'; colon2 = NULL; /* With cgroupv2 /proc/self/cgroup can contain entries of the * form: 0::/ * These entries need to be skipped. */ if (!strcmp(colon1, "")) continue; hierarchy_number = strtoul(line, &colon2, 10); if (!colon2 || *colon2) continue; if (hierarchy_number > meta_data->maximum_hierarchy) { /* lxc_grow_array will never shrink, so even if we find a lower * hierarchy number here, the array will never be smaller */ r = lxc_grow_array((void ***)&meta_data->hierarchies, &hierarchy_capacity, hierarchy_number + 1, 12); if (r < 0) goto out; meta_data->maximum_hierarchy = hierarchy_number; } /* this shouldn't happen, we had this already */ if (meta_data->hierarchies[hierarchy_number]) goto out; h = calloc(1, sizeof(struct cgroup_hierarchy)); if (!h) goto out; meta_data->hierarchies[hierarchy_number] = h; h->index = hierarchy_number; h->subsystems = lxc_string_split_and_trim(colon1, ','); if (!h->subsystems) goto out; /* see if this hierarchy should be considered */ if (!all_kernel_subsystems || !all_named_subsystems) { for (p = h->subsystems; *p; p++) { if (!strncmp(*p, "name=", 5)) { if (all_named_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) { h->used = true; break; } } else { if (all_kernel_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) { h->used = true; break; } } } } else { /* we want all hierarchy anyway */ h->used = true; } } bret = true; out: fclose(proc_self_cgroup); free(line); return bret; } /* Step 3: determine all mount points of each hierarchy */ static bool find_hierarchy_mountpts( struct cgroup_meta_data *meta_data, char **kernel_subsystems) { bool bret = false; FILE *proc_self_mountinfo; char *line = NULL; size_t sz = 0; char **tokens = NULL; size_t mount_point_count = 0; size_t mount_point_capacity = 0; size_t token_capacity = 0; int r; bool is_cgns = cgns_supported(); proc_self_mountinfo = fopen_cloexec("/proc/self/mountinfo", "r"); /* if for some reason (because of setns() and pid namespace for example), * /proc/self is not valid, we try /proc/1/cgroup... */ if (!proc_self_mountinfo) proc_self_mountinfo = fopen_cloexec("/proc/1/mountinfo", "r"); if (!proc_self_mountinfo) return false; while (getline(&line, &sz, proc_self_mountinfo) != -1) { char *token, *line_tok, *saveptr = NULL; size_t i, j, k; struct cgroup_mount_point *mount_point; struct cgroup_hierarchy *h; char **subsystems; bool is_lxcfs = false; if (line[0] && line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; for (i = 0, line_tok = line; (token = strtok_r(line_tok, " ", &saveptr)); line_tok = NULL) { r = lxc_grow_array((void ***)&tokens, &token_capacity, i + 1, 64); if (r < 0) goto out; tokens[i++] = token; } /* layout of /proc/self/mountinfo: * 0: id * 1: parent id * 2: device major:minor * 3: mount prefix * 4: mount point * 5: per-mount options * [optional X]: additional data * X+7: "-" * X+8: type * X+9: source * X+10: per-superblock options */ for (j = 6; j < i && tokens[j]; j++) if (!strcmp(tokens[j], "-")) break; /* could not find separator */ if (j >= i || !tokens[j]) continue; /* there should be exactly three fields after * the separator */ if (i != j + 4) continue; /* not a cgroup filesystem */ if (strcmp(tokens[j + 1], "cgroup") != 0) { if (strcmp(tokens[j + 1], "fuse.lxcfs") != 0) continue; if (strncmp(tokens[4], "/sys/fs/cgroup/", 15) != 0) continue; is_lxcfs = true; char *curtok = tokens[4] + 15; subsystems = subsystems_from_mount_options(curtok, kernel_subsystems); } else subsystems = subsystems_from_mount_options(tokens[j + 3], kernel_subsystems); if (!subsystems) goto out; h = NULL; for (k = 0; k <= meta_data->maximum_hierarchy; k++) { if (meta_data->hierarchies[k] && meta_data->hierarchies[k]->subsystems[0] && lxc_string_in_array(meta_data->hierarchies[k]->subsystems[0], (const char **)subsystems)) { /* TODO: we could also check if the lists really match completely, * just to have an additional sanity check */ h = meta_data->hierarchies[k]; break; } } lxc_free_array((void **)subsystems, free); if (!h) goto out; r = lxc_grow_array((void ***)&meta_data->mount_points, &mount_point_capacity, mount_point_count + 1, 12); if (r < 0) goto out; /* create mount point object */ mount_point = calloc(1, sizeof(*mount_point)); if (!mount_point) goto out; meta_data->mount_points[mount_point_count++] = mount_point; mount_point->hierarchy = h; if (is_lxcfs || is_cgns) mount_point->mount_prefix = strdup("/"); else mount_point->mount_prefix = strdup(tokens[3]); mount_point->mount_point = strdup(tokens[4]); if (!mount_point->mount_point || !mount_point->mount_prefix) goto out; mount_point->read_only = !lxc_string_in_list("rw", tokens[5], ','); if (!strcmp(mount_point->mount_prefix, "/")) { if (mount_point->read_only) { if (!h->ro_absolute_mount_point) h->ro_absolute_mount_point = mount_point; } else { if (!h->rw_absolute_mount_point) h->rw_absolute_mount_point = mount_point; } } if (h) k = lxc_array_len((void **)h->all_mount_points); else k = 0; r = lxc_grow_array((void ***)&h->all_mount_points, &h->all_mount_point_capacity, k + 1, 4); if (r < 0) goto out; h->all_mount_points[k] = mount_point; } bret = true; out: fclose(proc_self_mountinfo); free(tokens); free(line); return bret; } static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist) { bool all_kernel_subsystems = true; bool all_named_subsystems = false; struct cgroup_meta_data *meta_data = NULL; char **kernel_subsystems = NULL; int saved_errno = 0; /* if the subsystem whitelist is not specified, include all * hierarchies that contain kernel subsystems by default but * no hierarchies that only contain named subsystems * * if it is specified, the specifier @all will select all * hierarchies, @kernel will select all hierarchies with * kernel subsystems and @named will select all named * hierarchies */ all_kernel_subsystems = subsystem_whitelist ? (lxc_string_in_array("@kernel", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) : true; all_named_subsystems = subsystem_whitelist ? (lxc_string_in_array("@named", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) : true; meta_data = calloc(1, sizeof(struct cgroup_meta_data)); if (!meta_data) return NULL; meta_data->ref = 1; if (!find_cgroup_subsystems(&kernel_subsystems)) goto out_error; if (!find_cgroup_hierarchies(meta_data, all_kernel_subsystems, all_named_subsystems, subsystem_whitelist)) goto out_error; if (!find_hierarchy_mountpts(meta_data, kernel_subsystems)) goto out_error; /* oops, we couldn't find anything */ if (!meta_data->hierarchies || !meta_data->mount_points) { errno = EINVAL; goto out_error; } lxc_free_array((void **)kernel_subsystems, free); return meta_data; out_error: saved_errno = errno; lxc_free_array((void **)kernel_subsystems, free); lxc_cgroup_put_meta(meta_data); errno = saved_errno; return NULL; } static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data) { meta_data->ref++; return meta_data; } static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data) { size_t i; if (!meta_data) return NULL; if (--meta_data->ref > 0) return meta_data; lxc_free_array((void **)meta_data->mount_points, (lxc_free_fn)lxc_cgroup_mount_point_free); if (meta_data->hierarchies) for (i = 0; i <= meta_data->maximum_hierarchy; i++) if (meta_data->hierarchies[i]) lxc_cgroup_hierarchy_free(meta_data->hierarchies[i]); free(meta_data->hierarchies); free(meta_data); return NULL; } static struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem) { size_t i; for (i = 0; i <= meta_data->maximum_hierarchy; i++) { struct cgroup_hierarchy *h = meta_data->hierarchies[i]; if (!h) continue; if (h && lxc_string_in_array(subsystem, (const char **)h->subsystems)) return h; } return NULL; } static bool mountpoint_is_accessible(struct cgroup_mount_point *mp) { return mp && access(mp->mount_point, F_OK) == 0; } static struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable) { struct cgroup_mount_point **mps; struct cgroup_mount_point *current_result = NULL; ssize_t quality = -1; /* trivial case */ if (mountpoint_is_accessible(hierarchy->rw_absolute_mount_point)) return hierarchy->rw_absolute_mount_point; if (!should_be_writable && mountpoint_is_accessible(hierarchy->ro_absolute_mount_point)) return hierarchy->ro_absolute_mount_point; for (mps = hierarchy->all_mount_points; mps && *mps; mps++) { struct cgroup_mount_point *mp = *mps; size_t prefix_len = mp->mount_prefix ? strlen(mp->mount_prefix) : 0; if (prefix_len == 1 && mp->mount_prefix[0] == '/') prefix_len = 0; if (!mountpoint_is_accessible(mp)) continue; if (should_be_writable && mp->read_only) continue; if (!prefix_len || (strncmp(group, mp->mount_prefix, prefix_len) == 0 && (group[prefix_len] == '\0' || group[prefix_len] == '/'))) { /* search for the best quality match, i.e. the match with the * shortest prefix where this group is still contained */ if (quality == -1 || prefix_len < quality) { current_result = mp; quality = prefix_len; } } } if (!current_result) errno = ENOENT; return current_result; } static char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix) { struct cgroup_meta_data *meta_data; struct cgroup_hierarchy *h; struct cgroup_mount_point *mp; char *result; int saved_errno; meta_data = lxc_cgroup_load_meta(); if (!meta_data) return NULL; h = lxc_cgroup_find_hierarchy(meta_data, subsystem); if (!h) goto out_error; mp = lxc_cgroup_find_mount_point(h, group, should_be_writable); if (!mp) goto out_error; result = cgroup_to_absolute_path(mp, group, suffix); if (!result) goto out_error; lxc_cgroup_put_meta(meta_data); return result; out_error: saved_errno = errno; lxc_cgroup_put_meta(meta_data); errno = saved_errno; return NULL; } static struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta) { char pid_buf[32]; snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid); return lxc_cgroup_process_info_getx(pid_buf, meta); } static struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta) { return lxc_cgroup_process_info_get(1, meta); } static struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta) { struct cgroup_process_info *i; i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta); if (!i) i = lxc_cgroup_process_info_get(lxc_raw_getpid(), meta); return i; } /* * If a controller has ns cgroup mounted, then in that cgroup the handler->pid * is already in a new cgroup named after the pid. 'mnt' is passed in as * the full current cgroup. Say that is /sys/fs/cgroup/lxc/2975 and the container * name is c1. . We want to rename the cgroup directory to /sys/fs/cgroup/lxc/c1, * and return the string /sys/fs/cgroup/lxc/c1. */ static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, pid_t pid, const char *name) { char *dir, *fulloldpath; char *newname, *fullnewpath; int len, newlen, ret; /* * if cgroup is mounted at /cgroup and task is in cgroup /ab/, pid 2375 and * name is c1, * dir: /ab * fulloldpath = /cgroup/ab/2375 * fullnewpath = /cgroup/ab/c1 * newname = /ab/c1 */ dir = alloca(strlen(oldname) + 1); strcpy(dir, oldname); len = strlen(oldname) + strlen(mountpath) + 22; fulloldpath = alloca(len); ret = snprintf(fulloldpath, len, "%s/%s/%lu", mountpath, oldname, (unsigned long)pid); if (ret < 0 || ret >= len) return NULL; len = strlen(dir) + strlen(name) + 2; newname = malloc(len); if (!newname) { SYSERROR("Out of memory"); return NULL; } ret = snprintf(newname, len, "%s/%s", dir, name); if (ret < 0 || ret >= len) { free(newname); return NULL; } newlen = strlen(mountpath) + len + 2; fullnewpath = alloca(newlen); ret = snprintf(fullnewpath, newlen, "%s/%s", mountpath, newname); if (ret < 0 || ret >= newlen) { free(newname); return NULL; } if (access(fullnewpath, F_OK) == 0) { if (rmdir(fullnewpath) != 0) { SYSERROR("container cgroup %s already exists.", fullnewpath); free(newname); return NULL; } } if (rename(fulloldpath, fullnewpath)) { SYSERROR("failed to rename cgroup %s->%s", fulloldpath, fullnewpath); free(newname); return NULL; } DEBUG("'%s' renamed to '%s'", oldname, newname); return newname; } static bool is_crucial_hierarchy(struct cgroup_hierarchy *h) { char **p; for (p = h->subsystems; *p; p++) { if (is_crucial_cgroup_subsystem(*p)) return true; } return false; } /* create a new cgroup */ static struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern) { char **cgroup_path_components = NULL; char **p = NULL; char *path_so_far = NULL; char **new_cgroup_paths = NULL; char **new_cgroup_paths_sub = NULL; struct cgroup_mount_point *mp; struct cgroup_hierarchy *h; struct cgroup_process_info *base_info = NULL; struct cgroup_process_info *info_ptr; int saved_errno; int r; unsigned suffix = 0; bool had_sub_pattern = false; size_t i; if (!is_valid_cgroup(name)) { ERROR("Invalid cgroup name: '%s'", name); errno = EINVAL; return NULL; } if (!strstr(path_pattern, "%n")) { ERROR("Invalid cgroup path pattern: '%s'; contains no %%n for specifying container name", path_pattern); errno = EINVAL; return NULL; } /* we will modify the result of this operation directly, * so we don't have to copy the data structure */ base_info = (path_pattern[0] == '/') ? lxc_cgroup_process_info_get_init(meta_data) : lxc_cgroup_process_info_get_self(meta_data); if (!base_info) return NULL; new_cgroup_paths = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *)); if (!new_cgroup_paths) goto out_initial_error; new_cgroup_paths_sub = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *)); if (!new_cgroup_paths_sub) goto out_initial_error; /* find mount points we can use */ for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) { h = info_ptr->hierarchy; if (!h) continue; mp = lxc_cgroup_find_mount_point(h, info_ptr->cgroup_path, true); if (!mp) { ERROR("Could not find writable mount point for cgroup hierarchy %d while trying to create cgroup.", h->index); goto out_initial_error; } info_ptr->designated_mount_point = mp; if (lxc_string_in_array("ns", (const char **)h->subsystems)) continue; if (handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) { ERROR("Could not set clone_children to 1 for cpuset hierarchy in parent cgroup."); goto out_initial_error; } } /* normalize the path */ cgroup_path_components = lxc_normalize_path(path_pattern); if (!cgroup_path_components) goto out_initial_error; /* go through the path components to see if we can create them */ for (p = cgroup_path_components; *p || (sub_pattern && !had_sub_pattern); p++) { /* we only want to create the same component with -1, -2, etc. * if the component contains the container name itself, otherwise * it's not an error if it already exists */ char *p_eff = *p ? *p : (char *)sub_pattern; bool contains_name = strstr(p_eff, "%n"); char *current_component = NULL; char *current_subpath = NULL; char *current_entire_path = NULL; char *parts[3]; size_t j = 0; i = 0; /* if we are processing the subpattern, we want to make sure * loop is ended the next time around */ if (!*p) { had_sub_pattern = true; p--; } goto find_name_on_this_level; cleanup_name_on_this_level: /* This is reached if we found a name clash. * In that case, remove the cgroup from all previous hierarchies */ for (j = 0, info_ptr = base_info; j < i && info_ptr; info_ptr = info_ptr->next, j++) { if (info_ptr->created_paths_count < 1) continue; r = remove_cgroup(info_ptr->designated_mount_point, info_ptr->created_paths[info_ptr->created_paths_count - 1], false, NULL); if (r < 0) WARN("could not clean up cgroup we created when trying to create container"); free(info_ptr->created_paths[info_ptr->created_paths_count - 1]); info_ptr->created_paths[--info_ptr->created_paths_count] = NULL; } if (current_component != current_subpath) free(current_subpath); if (current_component != p_eff) free(current_component); current_component = current_subpath = NULL; /* try again with another suffix */ ++suffix; find_name_on_this_level: /* determine name of the path component we should create */ if (contains_name && suffix > 0) { char *buf = calloc(strlen(name) + 32, 1); if (!buf) goto out_initial_error; snprintf(buf, strlen(name) + 32, "%s-%u", name, suffix); current_component = lxc_string_replace("%n", buf, p_eff); free(buf); } else { current_component = contains_name ? lxc_string_replace("%n", name, p_eff) : p_eff; } parts[0] = path_so_far; parts[1] = current_component; parts[2] = NULL; current_subpath = path_so_far ? lxc_string_join("/", (const char **)parts, false) : current_component; /* Now go through each hierarchy and try to create the * corresponding cgroup */ for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) { char *parts2[3]; if (!info_ptr->hierarchy) continue; if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems)) continue; current_entire_path = NULL; parts2[0] = !strcmp(info_ptr->cgroup_path, "/") ? "" : info_ptr->cgroup_path; parts2[1] = current_subpath; parts2[2] = NULL; current_entire_path = lxc_string_join("/", (const char **)parts2, false); if (!*p) { /* we are processing the subpath, so only update that one */ free(new_cgroup_paths_sub[i]); new_cgroup_paths_sub[i] = strdup(current_entire_path); if (!new_cgroup_paths_sub[i]) goto cleanup_from_error; } else { /* remember which path was used on this controller */ free(new_cgroup_paths[i]); new_cgroup_paths[i] = strdup(current_entire_path); if (!new_cgroup_paths[i]) goto cleanup_from_error; } r = create_cgroup(info_ptr->designated_mount_point, current_entire_path); if (r < 0 && errno == EEXIST && contains_name) { /* name clash => try new name with new suffix */ free(current_entire_path); current_entire_path = NULL; goto cleanup_name_on_this_level; } else if (r < 0 && errno != EEXIST) { if (is_crucial_hierarchy(info_ptr->hierarchy)) { SYSERROR("Could not create cgroup '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point); goto cleanup_from_error; } goto skip; } else if (r == 0) { /* successfully created */ r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8); if (r < 0) goto cleanup_from_error; if (!init_cpuset_if_needed(info_ptr->designated_mount_point, current_entire_path)) { ERROR("Failed to initialize cpuset for '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point); goto cleanup_from_error; } info_ptr->created_paths[info_ptr->created_paths_count++] = current_entire_path; } else { /* if we didn't create the cgroup, then we have to make sure that * further cgroups will be created properly */ if (handle_cgroup_settings(info_ptr->designated_mount_point, info_ptr->cgroup_path) < 0) { ERROR("Could not set clone_children to 1 for cpuset hierarchy in pre-existing cgroup."); goto cleanup_from_error; } if (!init_cpuset_if_needed(info_ptr->designated_mount_point, info_ptr->cgroup_path)) { ERROR("Failed to initialize cpuset in pre-existing '%s'.", info_ptr->cgroup_path); goto cleanup_from_error; } skip: /* already existed but path component of pattern didn't contain '%n', * so this is not an error; but then we don't need current_entire_path * anymore... */ free(current_entire_path); current_entire_path = NULL; } } /* save path so far */ free(path_so_far); path_so_far = strdup(current_subpath); if (!path_so_far) goto cleanup_from_error; /* cleanup */ if (current_component != current_subpath) free(current_subpath); if (current_component != p_eff) free(current_component); current_component = current_subpath = NULL; continue; cleanup_from_error: /* called if an error occurred in the loop, so we * do some additional cleanup here */ saved_errno = errno; if (current_component != current_subpath) free(current_subpath); if (current_component != p_eff) free(current_component); free(current_entire_path); errno = saved_errno; goto out_initial_error; } /* we're done, now update the paths */ for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) { if (!info_ptr->hierarchy) continue; /* ignore legacy 'ns' subsystem here, lxc_cgroup_create_legacy * will take care of it * Since we do a continue in above loop, new_cgroup_paths[i] is * unset anyway, as is new_cgroup_paths_sub[i] */ if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems)) continue; free(info_ptr->cgroup_path); info_ptr->cgroup_path = new_cgroup_paths[i]; info_ptr->cgroup_path_sub = new_cgroup_paths_sub[i]; } /* don't use lxc_free_array since we used the array members * to store them in our result... */ free(new_cgroup_paths); free(new_cgroup_paths_sub); free(path_so_far); lxc_free_array((void **)cgroup_path_components, free); return base_info; out_initial_error: saved_errno = errno; free(path_so_far); lxc_cgroup_process_info_free_and_remove(base_info, NULL); lxc_free_array((void **)new_cgroup_paths, free); lxc_free_array((void **)new_cgroup_paths_sub, free); lxc_free_array((void **)cgroup_path_components, free); errno = saved_errno; return NULL; } static int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid) { struct cgroup_process_info *info_ptr; int r; for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) { if (!info_ptr->hierarchy) continue; if (!lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems)) continue; /* * For any path which has ns cgroup mounted, handler->pid is already * moved into a container called '%d % (handler->pid)'. Rename it to * the cgroup name and record that. */ char *tmp = cgroup_rename_nsgroup((const char *)info_ptr->designated_mount_point->mount_point, info_ptr->cgroup_path, pid, name); if (!tmp) return -1; free(info_ptr->cgroup_path); info_ptr->cgroup_path = tmp; r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8); if (r < 0) return -1; tmp = strdup(tmp); if (!tmp) return -1; info_ptr->created_paths[info_ptr->created_paths_count++] = tmp; } return 0; } /* get the cgroup membership of a given container */ static struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data) { struct cgroup_process_info *result = NULL; int saved_errno = 0; size_t i; struct cgroup_process_info **cptr = &result; struct cgroup_process_info *entry = NULL; char *path = NULL; for (i = 0; i <= meta_data->maximum_hierarchy; i++) { struct cgroup_hierarchy *h = meta_data->hierarchies[i]; if (!h || !h->used) continue; /* use the command interface to look for the cgroup */ path = lxc_cmd_get_cgroup_path(name, lxcpath, h->subsystems[0]); if (!path) { h->used = false; continue; } entry = calloc(1, sizeof(struct cgroup_process_info)); if (!entry) goto out_error; entry->meta_ref = lxc_cgroup_get_meta(meta_data); entry->hierarchy = h; entry->cgroup_path = path; path = NULL; /* it is not an error if we don't find anything here, * it is up to the caller to decide what to do in that * case */ entry->designated_mount_point = lxc_cgroup_find_mount_point(h, entry->cgroup_path, true); *cptr = entry; cptr = &entry->next; entry = NULL; } return result; out_error: saved_errno = errno; free(path); lxc_cgroup_process_info_free(result); lxc_cgroup_process_info_free(entry); errno = saved_errno; return NULL; } /* move a processs to the cgroups specified by the membership */ static int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) { char pid_buf[32]; char *cgroup_tasks_fn; int r; struct cgroup_process_info *info_ptr; snprintf(pid_buf, 32, "%lu", (unsigned long)pid); for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) { if (!info_ptr->hierarchy) continue; char *cgroup_path = (enter_sub && info_ptr->cgroup_path_sub) ? info_ptr->cgroup_path_sub : info_ptr->cgroup_path; if (!info_ptr->designated_mount_point) { info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, cgroup_path, true); if (!info_ptr->designated_mount_point) { SYSERROR("Could not add pid %lu to cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", (unsigned long)pid, cgroup_path); return -1; } } cgroup_tasks_fn = cgroup_to_absolute_path(info_ptr->designated_mount_point, cgroup_path, "/tasks"); if (!cgroup_tasks_fn) { SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path); return -1; } r = lxc_write_to_file(cgroup_tasks_fn, pid_buf, strlen(pid_buf), false); free(cgroup_tasks_fn); if (r < 0 && is_crucial_hierarchy(info_ptr->hierarchy)) { SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path); return -1; } } return 0; } /* free process membership information */ void lxc_cgroup_process_info_free(struct cgroup_process_info *info) { struct cgroup_process_info *next; if (!info) return; next = info->next; lxc_cgroup_put_meta(info->meta_ref); free(info->cgroup_path); free(info->cgroup_path_sub); lxc_free_array((void **)info->created_paths, free); free(info); lxc_cgroup_process_info_free(next); } /* free process membership information and remove cgroups that were created */ void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info, struct lxc_conf *conf) { struct cgroup_process_info *next; char **pp; if (!info) return; next = info->next; { struct cgroup_mount_point *mp = info->designated_mount_point; if (!mp) mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true); if (mp) /* ignore return value here, perhaps we created the * '/lxc' cgroup in this container but another container * is still running (for example) */ (void)remove_cgroup(mp, info->cgroup_path, true, conf); } for (pp = info->created_paths; pp && *pp; pp++); for ((void)(pp && --pp); info->created_paths && pp >= info->created_paths; --pp) { free(*pp); } free(info->created_paths); lxc_cgroup_put_meta(info->meta_ref); free(info->cgroup_path); free(info->cgroup_path_sub); free(info); lxc_cgroup_process_info_free_and_remove(next, conf); } static char *lxc_cgroup_get_hierarchy_path_data(const char *subsystem, struct cgfs_data *d) { struct cgroup_process_info *info = d->info; info = find_info_for_subsystem(info, subsystem); if (!info) return NULL; prune_init_scope(info->cgroup_path); return info->cgroup_path; } static char *lxc_cgroup_get_hierarchy_abs_path_data(const char *subsystem, struct cgfs_data *d) { struct cgroup_process_info *info = d->info; struct cgroup_mount_point *mp = NULL; info = find_info_for_subsystem(info, subsystem); if (!info) return NULL; if (info->designated_mount_point) { mp = info->designated_mount_point; } else { mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true); if (!mp) return NULL; } return cgroup_to_absolute_path(mp, info->cgroup_path, NULL); } static char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath) { struct cgroup_meta_data *meta; struct cgroup_process_info *base_info, *info; struct cgroup_mount_point *mp; char *result = NULL; meta = lxc_cgroup_load_meta(); if (!meta) return NULL; base_info = lxc_cgroup_get_container_info(name, lxcpath, meta); if (!base_info) goto out1; info = find_info_for_subsystem(base_info, subsystem); if (!info) goto out2; if (info->designated_mount_point) { mp = info->designated_mount_point; } else { mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true); if (!mp) goto out3; } result = cgroup_to_absolute_path(mp, info->cgroup_path, NULL); out3: out2: lxc_cgroup_process_info_free(base_info); out1: lxc_cgroup_put_meta(meta); return result; } static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfs_data *d) { char *subsystem = NULL, *p, *path; int ret = -1; subsystem = alloca(strlen(filename) + 1); strcpy(subsystem, filename); if ((p = strchr(subsystem, '.')) != NULL) *p = '\0'; errno = ENOENT; path = lxc_cgroup_get_hierarchy_abs_path_data(subsystem, d); if (path) { ret = do_cgroup_set(path, filename, value); int saved_errno = errno; free(path); errno = saved_errno; } return ret; } static int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; subsystem = alloca(strlen(filename) + 1); strcpy(subsystem, filename); if ((p = strchr(subsystem, '.')) != NULL) *p = '\0'; path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath); if (path) { ret = do_cgroup_set(path, filename, value); free(path); } return ret; } static int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; subsystem = alloca(strlen(filename) + 1); strcpy(subsystem, filename); if ((p = strchr(subsystem, '.')) != NULL) *p = '\0'; path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath); if (path) { ret = do_cgroup_get(path, filename, value, len); free(path); } return ret; } static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type) { size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup"); char *path = NULL; char **parts = NULL; char *dirname = NULL; char *abs_path = NULL; char *abs_path2 = NULL; struct cgfs_data *cgfs_d; struct cgroup_process_info *info, *base_info; int r, saved_errno = 0; struct lxc_handler *handler = hdata; if (cgns_supported()) return true; cgfs_d = handler->cgroup_data; if (!cgfs_d) return false; base_info = cgfs_d->info; /* If we get passed the _NOSPEC types, we default to _MIXED, since we don't * have access to the lxc_conf object at this point. It really should be up * to the caller to fix this, but this doesn't really hurt. */ if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) type = LXC_AUTO_CGROUP_FULL_MIXED; else if (type == LXC_AUTO_CGROUP_NOSPEC) type = LXC_AUTO_CGROUP_MIXED; if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) { ERROR("could not mount cgroups into container: invalid type specified internally"); errno = EINVAL; return false; } path = calloc(1, bufsz); if (!path) return false; snprintf(path, bufsz, "%s/sys/fs/cgroup", root); r = safe_mount("cgroup_root", path, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME, "size=10240k,mode=755", root); if (r < 0) { SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container"); return false; } /* now mount all the hierarchies we care about */ for (info = base_info; info; info = info->next) { size_t subsystem_count, i; struct cgroup_mount_point *mp = info->designated_mount_point; if (!info->hierarchy) continue; if (!mountpoint_is_accessible(mp)) mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true); if (!mp) { SYSERROR("could not find original mount point for cgroup hierarchy while trying to mount cgroup filesystem"); goto out_error; } subsystem_count = lxc_array_len((void **)info->hierarchy->subsystems); parts = calloc(subsystem_count + 1, sizeof(char *)); if (!parts) goto out_error; for (i = 0; i < subsystem_count; i++) { if (!strncmp(info->hierarchy->subsystems[i], "name=", 5)) parts[i] = info->hierarchy->subsystems[i] + 5; else parts[i] = info->hierarchy->subsystems[i]; } dirname = lxc_string_join(",", (const char **)parts, false); if (!dirname) goto out_error; /* create subsystem directory */ abs_path = lxc_append_paths(path, dirname); if (!abs_path) goto out_error; r = mkdir_p(abs_path, 0755); if (r < 0 && errno != EEXIST) { SYSERROR("could not create cgroup subsystem directory /sys/fs/cgroup/%s", dirname); goto out_error; } abs_path2 = lxc_append_paths(abs_path, info->cgroup_path); if (!abs_path2) goto out_error; if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_RW || type == LXC_AUTO_CGROUP_FULL_MIXED) { /* bind-mount the cgroup entire filesystem there */ if (strcmp(mp->mount_prefix, "/") != 0) { /* FIXME: maybe we should just try to remount the entire hierarchy * with a regular mount command? may that works? */ ERROR("could not automatically mount cgroup-full to /sys/fs/cgroup/%s: host has no mount point for this cgroup filesystem that has access to the root cgroup", dirname); goto out_error; } r = mount(mp->mount_point, abs_path, "none", MS_BIND, 0); if (r < 0) { SYSERROR("error bind-mounting %s to %s", mp->mount_point, abs_path); goto out_error; } /* main cgroup path should be read-only */ if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_MIXED) { r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL); if (r < 0) { SYSERROR("error re-mounting %s readonly", abs_path); goto out_error; } } /* own cgroup should be read-write */ if (type == LXC_AUTO_CGROUP_FULL_MIXED) { r = mount(abs_path2, abs_path2, NULL, MS_BIND, NULL); if (r < 0) { SYSERROR("error bind-mounting %s onto itself", abs_path2); goto out_error; } r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND, NULL); if (r < 0) { SYSERROR("error re-mounting %s readwrite", abs_path2); goto out_error; } } } else { /* create path for container's cgroup */ r = mkdir_p(abs_path2, 0755); if (r < 0 && errno != EEXIST) { SYSERROR("could not create cgroup directory /sys/fs/cgroup/%s%s", dirname, info->cgroup_path); goto out_error; } /* for read-only and mixed cases, we have to bind-mount the tmpfs directory * that points to the hierarchy itself (i.e. /sys/fs/cgroup/cpu etc.) onto * itself and then bind-mount it read-only, since we keep the tmpfs itself * read-write (see comment below) */ if (type == LXC_AUTO_CGROUP_MIXED || type == LXC_AUTO_CGROUP_RO) { r = mount(abs_path, abs_path, NULL, MS_BIND, NULL); if (r < 0) { SYSERROR("error bind-mounting %s onto itself", abs_path); goto out_error; } r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL); if (r < 0) { SYSERROR("error re-mounting %s readonly", abs_path); goto out_error; } } free(abs_path); abs_path = NULL; /* bind-mount container's cgroup to that directory */ abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL); if (!abs_path) goto out_error; r = mount(abs_path, abs_path2, "none", MS_BIND, 0); if (r < 0 && is_crucial_hierarchy(info->hierarchy)) { SYSERROR("error bind-mounting %s to %s", abs_path, abs_path2); goto out_error; } if (type == LXC_AUTO_CGROUP_RO) { r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL); if (r < 0) { SYSERROR("error re-mounting %s readonly", abs_path2); goto out_error; } } } free(abs_path); free(abs_path2); abs_path = NULL; abs_path2 = NULL; /* add symlinks for every single subsystem */ if (subsystem_count > 1) { for (i = 0; i < subsystem_count; i++) { abs_path = lxc_append_paths(path, parts[i]); if (!abs_path) goto out_error; r = symlink(dirname, abs_path); if (r < 0) WARN("could not create symlink %s -> %s in /sys/fs/cgroup of container", parts[i], dirname); free(abs_path); abs_path = NULL; } } free(dirname); free(parts); dirname = NULL; parts = NULL; } /* We used to remount the entire tmpfs readonly if any :ro or * :mixed mode was specified. However, Ubuntu's mountall has the * unfortunate behavior to block bootup if /sys/fs/cgroup is * mounted read-only and cannot be remounted read-write. * (mountall reads /lib/init/fstab and tries to (re-)mount all of * these if they are not already mounted with the right options; * it contains an entry for /sys/fs/cgroup. In case it can't do * that, it prompts for the user to either manually fix it or * boot anyway. But without user input, booting of the container * hangs.) * * Instead of remounting the entire tmpfs readonly, we only * remount the paths readonly that are part of the cgroup * hierarchy. */ free(path); return true; out_error: saved_errno = errno; free(path); free(dirname); free(parts); free(abs_path); free(abs_path2); errno = saved_errno; return false; } static int cgfs_nrtasks(void *hdata) { struct cgfs_data *d = hdata; struct cgroup_process_info *info; struct cgroup_mount_point *mp = NULL; char *abs_path = NULL; int ret; if (!d) { errno = ENOENT; return -1; } info = d->info; if (!info) { errno = ENOENT; return -1; } if (info->designated_mount_point) { mp = info->designated_mount_point; } else { mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, false); if (!mp) return -1; } abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL); if (!abs_path) return -1; ret = cgroup_recursive_task_count(abs_path); free(abs_path); return ret; } static struct cgroup_process_info * lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta) { struct cgroup_process_info *result = NULL; FILE *proc_pid_cgroup = NULL; char *line = NULL; size_t sz = 0; int saved_errno = 0; struct cgroup_process_info **cptr = &result; struct cgroup_process_info *entry = NULL; proc_pid_cgroup = fopen_cloexec(proc_pid_cgroup_str, "r"); if (!proc_pid_cgroup) return NULL; while (getline(&line, &sz, proc_pid_cgroup) != -1) { /* file format: hierarchy:subsystems:group */ char *colon1; char *colon2; char *endptr; int hierarchy_number; struct cgroup_hierarchy *h = NULL; if (!line[0]) continue; if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; colon1 = strchr(line, ':'); if (!colon1) continue; *colon1++ = '\0'; colon2 = strchr(colon1, ':'); if (!colon2) continue; *colon2++ = '\0'; endptr = NULL; /* With cgroupv2 /proc/self/cgroup can contain entries of the * form: 0::/ * These entries need to be skipped. */ if (!strcmp(colon1, "")) continue; hierarchy_number = strtoul(line, &endptr, 10); if (!endptr || *endptr) continue; if (hierarchy_number > meta->maximum_hierarchy) { /* we encountered a hierarchy we didn't have before, * so probably somebody remounted some stuff in the * mean time... */ errno = EAGAIN; goto out_error; } h = meta->hierarchies[hierarchy_number]; if (!h) { /* we encountered a hierarchy that was thought to be * dead before, so probably somebody remounted some * stuff in the mean time... */ errno = EAGAIN; goto out_error; } /* we are told that we should ignore this hierarchy */ if (!h->used) continue; entry = calloc(1, sizeof(struct cgroup_process_info)); if (!entry) goto out_error; entry->meta_ref = lxc_cgroup_get_meta(meta); entry->hierarchy = h; entry->cgroup_path = strdup(colon2); if (!entry->cgroup_path) goto out_error; prune_init_scope(entry->cgroup_path); *cptr = entry; cptr = &entry->next; entry = NULL; } fclose(proc_pid_cgroup); free(line); return result; out_error: saved_errno = errno; if (proc_pid_cgroup) fclose(proc_pid_cgroup); lxc_cgroup_process_info_free(result); lxc_cgroup_process_info_free(entry); free(line); errno = saved_errno; return NULL; } static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list) { char *token, *str, *saveptr = NULL; char **result = NULL; size_t result_capacity = 0; size_t result_count = 0; int saved_errno; int r; str = alloca(strlen(mount_options)+1); strcpy(str, mount_options); for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) { /* we have a subsystem if it's either in the list of * subsystems provided by the kernel OR if it starts * with name= for named hierarchies */ r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 12); if (r < 0) goto out_free; result[result_count + 1] = NULL; if (strncmp(token, "name=", 5) && !lxc_string_in_array(token, (const char **)kernel_list)) { /* this is eg 'systemd' but the mount will be * 'name=systemd' */ result[result_count] = malloc(strlen(token) + 6); if (result[result_count]) sprintf(result[result_count], "name=%s", token); } else result[result_count] = strdup(token); if (!result[result_count]) goto out_free; result_count++; } return result; out_free: saved_errno = errno; lxc_free_array((void**)result, free); errno = saved_errno; return NULL; } static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp) { if (!mp) return; free(mp->mount_point); free(mp->mount_prefix); free(mp); } static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h) { if (!h) return; if (h->subsystems) { lxc_free_array((void **)h->subsystems, free); h->subsystems = NULL; } if (h->all_mount_points) { free(h->all_mount_points); h->all_mount_points = NULL; } free(h); h = NULL; } static bool is_valid_cgroup(const char *name) { const char *p; for (p = name; *p; p++) { /* Use the ASCII printable characters range(32 - 127) * is reasonable, we kick out 32(SPACE) because it'll * break legacy lxc-ls */ if (*p <= 32 || *p >= 127 || *p == '/') return false; } return strcmp(name, ".") != 0 && strcmp(name, "..") != 0; } static int create_or_remove_cgroup(bool do_remove, struct cgroup_mount_point *mp, const char *path, int recurse, struct lxc_conf *conf) { int r, saved_errno = 0; char *buf = cgroup_to_absolute_path(mp, path, NULL); if (!buf) return -1; /* create or remove directory */ if (do_remove) { if (!dir_exists(buf)) return 0; if (recurse) { if (conf && !lxc_list_empty(&conf->id_map)) r = userns_exec_1(conf, rmdir_wrapper, buf, "rmdir_wrapper"); else r = cgroup_rmdir(buf); } else r = rmdir(buf); } else r = mkdir_p(buf, 0777); saved_errno = errno; free(buf); errno = saved_errno; return r; } static int create_cgroup(struct cgroup_mount_point *mp, const char *path) { return create_or_remove_cgroup(false, mp, path, false, NULL); } static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse, struct lxc_conf *conf) { return create_or_remove_cgroup(true, mp, path, recurse, conf); } static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *path, const char *suffix) { /* first we have to make sure we subtract the mount point's prefix */ char *prefix = mp->mount_prefix; char *buf; ssize_t len, rv; /* we want to make sure only absolute paths to cgroups are passed to us */ if (path[0] != '/') { errno = EINVAL; return NULL; } if (prefix && !strcmp(prefix, "/")) prefix = NULL; /* prefix doesn't match */ if (prefix && strncmp(prefix, path, strlen(prefix)) != 0) { errno = EINVAL; return NULL; } /* if prefix is /foo and path is /foobar */ if (prefix && path[strlen(prefix)] != '/' && path[strlen(prefix)] != '\0') { errno = EINVAL; return NULL; } /* remove prefix from path */ path += prefix ? strlen(prefix) : 0; len = strlen(mp->mount_point) + strlen(path) + (suffix ? strlen(suffix) : 0); buf = calloc(len + 1, 1); if (!buf) return NULL; rv = snprintf(buf, len + 1, "%s%s%s", mp->mount_point, path, suffix ? suffix : ""); if (rv > len) { free(buf); errno = ENOMEM; return NULL; } return buf; } static struct cgroup_process_info * find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem) { struct cgroup_process_info *info_ptr; for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) { struct cgroup_hierarchy *h = info_ptr->hierarchy; if (!h) continue; if (lxc_string_in_array(subsystem, (const char **)h->subsystems)) return info_ptr; } errno = ENOENT; return NULL; } static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len) { const char *parts[3] = { cgroup_path, sub_filename, NULL }; char *filename; int ret, saved_errno; filename = lxc_string_join("/", parts, false); if (!filename) return -1; ret = lxc_read_from_file(filename, value, len); saved_errno = errno; free(filename); errno = saved_errno; return ret; } static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value) { const char *parts[3] = { cgroup_path, sub_filename, NULL }; char *filename; int ret, saved_errno; filename = lxc_string_join("/", parts, false); if (!filename) return -1; ret = lxc_write_to_file(filename, value, strlen(value), false); saved_errno = errno; free(filename); errno = saved_errno; return ret; } static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices) { struct lxc_list *iterator, *sorted_cgroup_settings, *next; struct lxc_cgroup *cg; int ret = -1; if (lxc_list_empty(cgroup_settings)) return 0; sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings); if (!sorted_cgroup_settings) { return -1; } lxc_list_for_each(iterator, sorted_cgroup_settings) { cg = iterator->elem; if (do_devices == !strncmp("devices", cg->subsystem, 7)) { if (strcmp(cg->subsystem, "devices.deny") == 0 && cgroup_devices_has_allow_or_deny(d, cg->value, false)) continue; if (strcmp(cg->subsystem, "devices.allow") == 0 && cgroup_devices_has_allow_or_deny(d, cg->value, true)) continue; if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) { if (do_devices && (errno == EACCES || errno == EPERM)) { WARN("Error setting %s to %s for %s", cg->subsystem, cg->value, d->name); continue; } SYSERROR("Error setting %s to %s for %s", cg->subsystem, cg->value, d->name); goto out; } } DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); } ret = 0; INFO("cgroup has been setup"); out: lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) { lxc_list_del(iterator); free(iterator); } free(sorted_cgroup_settings); return ret; } static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow) { char *path; FILE *devices_list; char *line = NULL; size_t sz = 0; bool ret = !for_allow; const char *parts[3] = { NULL, "devices.list", NULL }; /* XXX FIXME if users could use something other than 'lxc.devices.deny = * a'. not sure they ever do, but they *could* right now, I'm assuming * they do NOT */ if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0) return false; parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_data("devices", d); if (!parts[0]) return false; path = lxc_string_join("/", parts, false); if (!path) { free((void *)parts[0]); return false; } devices_list = fopen_cloexec(path, "r"); if (!devices_list) { free(path); return false; } while (getline(&line, &sz, devices_list) != -1) { size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') line[len-1] = '\0'; if (strcmp(line, "a *:* rwm") == 0) { ret = for_allow; goto out; } else if (for_allow && strcmp(line, v) == 0) { ret = true; goto out; } } out: fclose(devices_list); free(line); free(path); return ret; } static int cgroup_recursive_task_count(const char *cgroup_path) { DIR *d; struct dirent *dent; int n = 0, r; d = opendir(cgroup_path); if (!d) return 0; while ((dent = readdir(d))) { const char *parts[3] = { cgroup_path, dent->d_name, NULL }; char *sub_path; struct stat st; if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; sub_path = lxc_string_join("/", parts, false); if (!sub_path) { closedir(d); return -1; } r = stat(sub_path, &st); if (r < 0) { closedir(d); free(sub_path); return -1; } if (S_ISDIR(st.st_mode)) { r = cgroup_recursive_task_count(sub_path); if (r >= 0) n += r; } else if (!strcmp(dent->d_name, "tasks")) { r = lxc_count_file_lines(sub_path); if (r >= 0) n += r; } free(sub_path); } closedir(d); return n; } static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path) { int r, saved_errno = 0; char buf[2]; mp->need_cpuset_init = false; /* If this is the memory cgroup, we want to enforce hierarchy. * But don't fail if for some reason we can't. */ if (lxc_string_in_array("memory", (const char **)mp->hierarchy->subsystems)) { char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/memory.use_hierarchy"); if (cc_path) { r = lxc_read_from_file(cc_path, buf, 1); if (r < 1 || buf[0] != '1') { r = lxc_write_to_file(cc_path, "1", 1, false); if (r < 0) SYSERROR("failed to set memory.use_hierarchy to 1; continuing"); } free(cc_path); } } /* if this is a cpuset hierarchy, we have to set cgroup.clone_children in * the base cgroup, otherwise containers will start with an empty cpuset.mems * and cpuset.cpus and then */ if (lxc_string_in_array("cpuset", (const char **)mp->hierarchy->subsystems)) { char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/cgroup.clone_children"); struct stat sb; if (!cc_path) return -1; /* cgroup.clone_children is not available when running under * older kernel versions; in this case, we'll initialize * cpuset.cpus and cpuset.mems later, after the new cgroup * was created */ if (stat(cc_path, &sb) != 0 && errno == ENOENT) { mp->need_cpuset_init = true; free(cc_path); return 0; } r = lxc_read_from_file(cc_path, buf, 1); if (r == 1 && buf[0] == '1') { free(cc_path); return 0; } r = lxc_write_to_file(cc_path, "1", 1, false); saved_errno = errno; free(cc_path); errno = saved_errno; return r < 0 ? -1 : 0; } return 0; } static int cgroup_read_from_file(const char *fn, char buf[], size_t bufsize) { int ret = lxc_read_from_file(fn, buf, bufsize); if (ret < 0) { SYSERROR("failed to read %s", fn); return ret; } if (ret == bufsize) { if (bufsize > 0) { /* obviously this wasn't empty */ buf[bufsize-1] = '\0'; return ret; } /* Callers don't do this, but regression/sanity check */ ERROR("was not expecting 0 bufsize"); return -1; } buf[ret] = '\0'; return ret; } static bool do_init_cpuset_file(struct cgroup_mount_point *mp, const char *path, const char *name) { char value[1024]; char *childfile, *parentfile = NULL, *tmp; int ret; bool ok = false; childfile = cgroup_to_absolute_path(mp, path, name); if (!childfile) return false; /* don't overwrite a non-empty value in the file */ ret = cgroup_read_from_file(childfile, value, sizeof(value)); if (ret < 0) goto out; if (value[0] != '\0' && value[0] != '\n') { ok = true; goto out; } /* path to the same name in the parent cgroup */ parentfile = strdup(path); if (!parentfile) goto out; tmp = strrchr(parentfile, '/'); if (!tmp) goto out; if (tmp == parentfile) tmp++; /* keep the '/' at the start */ *tmp = '\0'; tmp = parentfile; parentfile = cgroup_to_absolute_path(mp, tmp, name); free(tmp); if (!parentfile) goto out; /* copy from parent to child cgroup */ ret = cgroup_read_from_file(parentfile, value, sizeof(value)); if (ret < 0) goto out; if (ret == sizeof(value)) { /* If anyone actually sees this error, we can address it */ ERROR("parent cpuset value too long"); goto out; } ok = (lxc_write_to_file(childfile, value, strlen(value), false) >= 0); if (!ok) SYSERROR("failed writing %s", childfile); out: free(parentfile); free(childfile); return ok; } static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path) { /* the files we have to handle here are only in cpuset hierarchies */ if (!lxc_string_in_array("cpuset", (const char **)mp->hierarchy->subsystems)) return true; if (!mp->need_cpuset_init) return true; return (do_init_cpuset_file(mp, path, "/cpuset.cpus") && do_init_cpuset_file(mp, path, "/cpuset.mems") ); } static void print_cgfs_init_debuginfo(struct cgfs_data *d) { int i; if (!getenv("LXC_DEBUG_CGFS")) return; DEBUG("Cgroup information:"); DEBUG(" container name: %s", d->name); if (!d->meta || !d->meta->hierarchies) { DEBUG(" No hierarchies found."); return; } DEBUG(" Controllers:"); for (i = 0; i <= d->meta->maximum_hierarchy; i++) { char **p; struct cgroup_hierarchy *h = d->meta->hierarchies[i]; if (!h) { DEBUG(" Empty hierarchy number %d.", i); continue; } for (p = h->subsystems; p && *p; p++) { DEBUG(" %2d: %s", i, *p); } } } struct cgroup_ops *cgfs_ops_init(void) { return &cgfs_ops; } static void *cgfs_init(struct lxc_handler *handler) { struct cgfs_data *d; d = malloc(sizeof(*d)); if (!d) return NULL; memset(d, 0, sizeof(*d)); d->name = strdup(handler->name); if (!d->name) goto err1; d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); d->meta = lxc_cgroup_load_meta(); if (!d->meta) { ERROR("cgroupfs failed to detect cgroup metadata"); goto err2; } print_cgfs_init_debuginfo(d); return d; err2: free(d->name); err1: free(d); return NULL; } static void cgfs_destroy(void *hdata, struct lxc_conf *conf) { struct cgfs_data *d = hdata; if (!d) return; free(d->name); lxc_cgroup_process_info_free_and_remove(d->info, conf); lxc_cgroup_put_meta(d->meta); free(d); } static inline bool cgfs_create(void *hdata) { struct cgfs_data *d = hdata; struct cgroup_process_info *i; struct cgroup_meta_data *md; if (!d) return false; md = d->meta; i = lxc_cgroupfs_create(d->name, d->cgroup_pattern, md, NULL); if (!i) return false; d->info = i; return true; } static inline bool cgfs_enter(void *hdata, pid_t pid) { struct cgfs_data *d = hdata; struct cgroup_process_info *i; int ret; if (!d) return false; i = d->info; ret = lxc_cgroupfs_enter(i, pid, false); return ret == 0; } static inline bool cgfs_create_legacy(void *hdata, pid_t pid) { struct cgfs_data *d = hdata; struct cgroup_process_info *i; if (!d) return false; i = d->info; if (lxc_cgroup_create_legacy(i, d->name, pid) < 0) { ERROR("failed to create legacy ns cgroups for '%s'", d->name); return false; } return true; } static const char *cgfs_get_cgroup(void *hdata, const char *subsystem) { struct cgfs_data *d = hdata; if (!d) return NULL; return lxc_cgroup_get_hierarchy_path_data(subsystem, d); } static bool cgfs_escape(void *hdata) { struct cgroup_meta_data *md; int i; bool ret = false; md = lxc_cgroup_load_meta(); if (!md) return false; for (i = 0; i <= md->maximum_hierarchy; i++) { struct cgroup_hierarchy *h = md->hierarchies[i]; struct cgroup_mount_point *mp; char *tasks; FILE *f; int written; if (!h) { WARN("not escaping hierarchy %d", i); continue; } mp = lxc_cgroup_find_mount_point(h, "/", true); if (!mp) goto out; tasks = cgroup_to_absolute_path(mp, "/", "tasks"); if (!tasks) goto out; f = fopen(tasks, "a"); free(tasks); if (!f) goto out; written = fprintf(f, "%d\n", lxc_raw_getpid()); fclose(f); if (written < 0) { SYSERROR("writing tasks failed\n"); goto out; } } ret = true; out: lxc_cgroup_put_meta(md); return ret; } static int cgfs_num_hierarchies(void) { /* not implemented */ return -1; } static bool cgfs_get_hierarchies(int i, char ***out) { /* not implemented */ return false; } static bool cgfs_unfreeze(void *hdata) { struct cgfs_data *d = hdata; char *cgabspath, *cgrelpath; int ret; if (!d) return false; cgrelpath = lxc_cgroup_get_hierarchy_path_data("freezer", d); cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL); if (!cgabspath) return false; ret = do_cgroup_set(cgabspath, "freezer.state", "THAWED"); free(cgabspath); return ret == 0; } static bool cgroupfs_setup_limits(void *hdata, struct lxc_conf *conf, bool with_devices) { struct cgfs_data *d = hdata; if (!d) return false; return do_setup_cgroup_limits(d, &conf->cgroup, with_devices) == 0; } static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid) { struct cgroup_meta_data *meta_data; struct cgroup_process_info *container_info; int ret; meta_data = lxc_cgroup_load_meta(); if (!meta_data) { ERROR("could not move attached process %d to cgroup of container", pid); return false; } container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data); lxc_cgroup_put_meta(meta_data); if (!container_info) { ERROR("could not move attached process %d to cgroup of container", pid); return false; } ret = lxc_cgroupfs_enter(container_info, pid, false); lxc_cgroup_process_info_free(container_info); if (ret < 0) { ERROR("could not move attached process %d to cgroup of container", pid); return false; } return true; } struct chown_data { const char *cgroup_path; uid_t origuid; }; /* * TODO - someone should refactor this to unshare once passing all the paths * to be chowned in one go */ static int chown_cgroup_wrapper(void *data) { struct chown_data *arg = data; uid_t destuid; char *fpath; if (setresgid(0,0,0) < 0) SYSERROR("Failed to setgid to 0"); if (setresuid(0,0,0) < 0) SYSERROR("Failed to setuid to 0"); if (setgroups(0, NULL) < 0) SYSERROR("Failed to clear groups"); destuid = get_ns_uid(arg->origuid); if (chown(arg->cgroup_path, destuid, 0) < 0) SYSERROR("Failed chowning %s to %d", arg->cgroup_path, (int)destuid); fpath = lxc_append_paths(arg->cgroup_path, "tasks"); if (!fpath) return -1; if (chown(fpath, destuid, 0) < 0) SYSERROR("Error chowning %s\n", fpath); free(fpath); fpath = lxc_append_paths(arg->cgroup_path, "cgroup.procs"); if (!fpath) return -1; if (chown(fpath, destuid, 0) < 0) SYSERROR("Error chowning %s", fpath); free(fpath); return 0; } static bool do_cgfs_chown(char *cgroup_path, struct lxc_conf *conf) { struct chown_data data; char *fpath; if (!dir_exists(cgroup_path)) return true; if (lxc_list_empty(&conf->id_map)) /* If there's no mapping then we don't need to chown */ return true; data.cgroup_path = cgroup_path; data.origuid = geteuid(); /* Unpriv users can't chown it themselves, so chown from * a child namespace mapping both our own and the target uid */ if (userns_exec_1(conf, chown_cgroup_wrapper, &data, "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new namespace"); return false; } /* * Now chmod 775 the directory else the container cannot create cgroups. * This can't be done in the child namespace because it only group-owns * the cgroup */ if (chmod(cgroup_path, 0775) < 0) { SYSERROR("Error chmoding %s\n", cgroup_path); return false; } fpath = lxc_append_paths(cgroup_path, "tasks"); if (!fpath) return false; if (chmod(fpath, 0664) < 0) SYSERROR("Error chmoding %s\n", fpath); free(fpath); fpath = lxc_append_paths(cgroup_path, "cgroup.procs"); if (!fpath) return false; if (chmod(fpath, 0664) < 0) SYSERROR("Error chmoding %s\n", fpath); free(fpath); return true; } static bool cgfs_chown(void *hdata, struct lxc_conf *conf) { struct cgfs_data *d = hdata; struct cgroup_process_info *info_ptr; char *cgpath; bool r = true; if (!d) return false; for (info_ptr = d->info; info_ptr; info_ptr = info_ptr->next) { if (!info_ptr->hierarchy) continue; if (!info_ptr->designated_mount_point) { info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, info_ptr->cgroup_path, true); if (!info_ptr->designated_mount_point) { SYSERROR("Could not chown cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", info_ptr->cgroup_path); return false; } } cgpath = cgroup_to_absolute_path(info_ptr->designated_mount_point, info_ptr->cgroup_path, NULL); if (!cgpath) { SYSERROR("Could not chown cgroup %s: internal error", info_ptr->cgroup_path); continue; } r = do_cgfs_chown(cgpath, conf); if (!r && is_crucial_hierarchy(info_ptr->hierarchy)) { ERROR("Failed chowning %s\n", cgpath); free(cgpath); return false; } free(cgpath); } return true; } static struct cgroup_ops cgfs_ops = { .init = cgfs_init, .destroy = cgfs_destroy, .create = cgfs_create, .enter = cgfs_enter, .create_legacy = cgfs_create_legacy, .get_cgroup = cgfs_get_cgroup, .escape = cgfs_escape, .num_hierarchies = cgfs_num_hierarchies, .get_hierarchies = cgfs_get_hierarchies, .get = lxc_cgroupfs_get, .set = lxc_cgroupfs_set, .unfreeze = cgfs_unfreeze, .setup_limits = cgroupfs_setup_limits, .name = "cgroupfs", .attach = lxc_cgroupfs_attach, .chown = cgfs_chown, .mount_cgroup = cgroupfs_mount_cgroup, .nrtasks = cgfs_nrtasks, .driver = CGFS, }; lxc-2.0.11/src/lxc/cgroups/cgmanager.c0000644061062106075000000011614713435013473014456 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "commands.h" #include "list.h" #include "namespace.h" #include "conf.h" #include "utils.h" #include "log.h" #include "cgroup.h" #include "start.h" #include "state.h" #include "storage.h" #define CGM_SUPPORTS_GET_ABS 3 #define CGM_SUPPORTS_NAMED 4 #define CGM_SUPPORTS_MULT_CONTROLLERS 10 #ifdef HAVE_CGMANAGER lxc_log_define(lxc_cgmanager, lxc); #include #include #include #include #include struct cgm_data { char *name; char *cgroup_path; const char *cgroup_pattern; }; static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER; static void lock_mutex(pthread_mutex_t *l) { int ret; if ((ret = pthread_mutex_lock(l)) != 0) { fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret)); exit(1); } } static void unlock_mutex(pthread_mutex_t *l) { int ret; if ((ret = pthread_mutex_unlock(l)) != 0) { fprintf(stderr, "%s: pthread_mutex_unlock returned:%d %s\n", __FILE__, ret, strerror(ret)); exit(1); } } void cgm_lock(void) { lock_mutex(&cgm_mutex); } void cgm_unlock(void) { unlock_mutex(&cgm_mutex); } #ifdef HAVE_PTHREAD_ATFORK __attribute__((constructor)) static void process_lock_setup_atfork(void) { pthread_atfork(cgm_lock, cgm_unlock, cgm_unlock); } #endif static NihDBusProxy *cgroup_manager = NULL; static int32_t api_version; static struct cgroup_ops cgmanager_ops; static int nr_subsystems; static char **subsystems, **subsystems_inone; static bool dbus_threads_initialized = false; static void cull_user_controllers(void); static void cgm_dbus_disconnect(void) { if (cgroup_manager) { dbus_connection_flush(cgroup_manager->connection); dbus_connection_close(cgroup_manager->connection); nih_free(cgroup_manager); } cgroup_manager = NULL; cgm_unlock(); } #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock" static bool cgm_dbus_connect(void) { DBusError dbus_error; static DBusConnection *connection; cgm_lock(); if (!dbus_threads_initialized) { /* tell dbus to do struct locking for thread safety */ dbus_threads_init_default(); dbus_threads_initialized = true; } dbus_error_init(&dbus_error); connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error); if (!connection) { DEBUG("Failed opening dbus connection: %s: %s", dbus_error.name, dbus_error.message); dbus_error_free(&dbus_error); cgm_unlock(); return false; } dbus_connection_set_exit_on_disconnect(connection, FALSE); dbus_error_free(&dbus_error); cgroup_manager = nih_dbus_proxy_new(NULL, connection, NULL /* p2p */, "/org/linuxcontainers/cgmanager", NULL, NULL); dbus_connection_unref(connection); if (!cgroup_manager) { NihError *nerr; nerr = nih_error_get(); ERROR("Error opening cgmanager proxy: %s", nerr->message); nih_free(nerr); cgm_dbus_disconnect(); return false; } /* get the api version */ if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("Error cgroup manager api version: %s", nerr->message); nih_free(nerr); cgm_dbus_disconnect(); return false; } if (api_version < CGM_SUPPORTS_NAMED) cull_user_controllers(); return true; } static bool cgm_all_controllers_same; /* * Check whether we can use "all" when talking to cgmanager. * We check two things: * 1. whether cgmanager is new enough to support this. * 2. whether the task we are interested in is in the same * cgroup for all controllers. * In cgm_init (before an lxc-start) we care about our own * cgroup. In cgm_attach, we care about the target task's * cgroup. */ static void check_supports_multiple_controllers(pid_t pid) { FILE *f; char *line = NULL, *prevpath = NULL; size_t sz = 0; char path[100]; cgm_all_controllers_same = false; if (pid == -1) sprintf(path, "/proc/self/cgroup"); else sprintf(path, "/proc/%d/cgroup", pid); f = fopen(path, "r"); if (!f) return; cgm_all_controllers_same = true; while (getline(&line, &sz, f) != -1) { /* file format: hierarchy:subsystems:group */ char *colon; if (!line[0]) continue; colon = strchr(line, ':'); if (!colon) continue; colon = strchr(colon+1, ':'); if (!colon) continue; colon++; if (!prevpath) { prevpath = alloca(strlen(colon)+1); strcpy(prevpath, colon); continue; } if (strcmp(prevpath, colon) != 0) { cgm_all_controllers_same = false; break; } } fclose(f); free(line); } static int send_creds(int sock, int rpid, int ruid, int rgid) { struct msghdr msg = { 0 }; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred = { .pid = rpid, .uid = ruid, .gid = rgid, }; char cmsgbuf[CMSG_SPACE(sizeof(cred))]; char buf[1]; buf[0] = 'p'; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (sendmsg(sock, &msg, 0) < 0) return -1; return 0; } static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed) { bool ret = true; if ( cgmanager_create_sync(NULL, cgroup_manager, controller, cgroup_path, existed) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_create_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Failed to create %s:%s", controller, cgroup_path); ret = false; } return ret; } /* * Escape to the root cgroup if we are root, so that the container will * be in "/lxc/c1" rather than "/user/..../c1" * called internally with connection already open */ static bool cgm_escape(void *hdata) { bool ret = true, cgm_needs_disconnect = false; pid_t me = lxc_raw_getpid(); char **slist = subsystems; int i; if (!cgroup_manager) { if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } cgm_needs_disconnect = true; } if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager, slist[i], "/", me) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s", slist[i], nerr->message); nih_free(nerr); ret = false; break; } } if (cgm_needs_disconnect) cgm_dbus_disconnect(); return ret; } static int cgm_num_hierarchies(void) { /* not implemented */ return -1; } static bool cgm_get_hierarchies(int i, char ***out) { /* not implemented */ return false; } struct chown_data { const char *cgroup_path; uid_t origuid; }; static int do_chown_cgroup(const char *controller, const char *cgroup_path, uid_t newuid) { int sv[2] = {-1, -1}, optval = 1, ret = -1; pid_t pid_self; char buf[1]; struct pollfd fds; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) { SYSERROR("Error creating socketpair"); goto out; } if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { SYSERROR("setsockopt failed"); goto out; } if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { SYSERROR("setsockopt failed"); goto out; } if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller, cgroup_path, sv[1]) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message); nih_free(nerr); goto out; } /* now send credentials */ fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } if (read(sv[0], &buf, 1) != 1) { ERROR("Error getting reply from server over socketpair"); goto out; } pid_self = lxc_raw_getpid(); if (send_creds(sv[0], pid_self, getuid(), getgid())) { SYSERROR("Error sending pid over SCM_CREDENTIAL"); goto out; } fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } if (read(sv[0], &buf, 1) != 1) { ERROR("Error getting reply from server over socketpair"); goto out; } if (send_creds(sv[0], pid_self, newuid, 0)) { SYSERROR("Error sending pid over SCM_CREDENTIAL"); goto out; } fds.fd = sv[0]; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, -1) <= 0) { ERROR("Error getting go-ahead from server: %s", strerror(errno)); goto out; } ret = read(sv[0], buf, 1); out: close(sv[0]); close(sv[1]); if (ret == 1 && *buf == '1') return 0; return -1; } static int chown_cgroup_wrapper(void *data) { struct chown_data *arg = data; char **slist = subsystems; int i, ret = -1; uid_t destuid; if (setresgid(0,0,0) < 0) SYSERROR("Failed to setgid to 0"); if (setresuid(0,0,0) < 0) SYSERROR("Failed to setuid to 0"); if (setgroups(0, NULL) < 0) SYSERROR("Failed to clear groups"); cgm_dbus_disconnect(); if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return -1; } destuid = get_ns_uid(arg->origuid); if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) { ERROR("Failed to chown %s:%s to container root", slist[i], arg->cgroup_path); goto fail; } } ret = 0; fail: cgm_dbus_disconnect(); return ret; } /* Internal helper. Must be called with the cgmanager dbus socket open */ static bool lxc_cgmanager_chmod(const char *controller, const char *cgroup_path, const char *file, int mode) { if (cgmanager_chmod_sync(NULL, cgroup_manager, controller, cgroup_path, file, mode) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message); nih_free(nerr); return false; } return true; } /* Internal helper. Must be called with the cgmanager dbus socket open */ static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf) { struct chown_data data; char **slist = subsystems; int i; if (lxc_list_empty(&conf->id_map)) /* If there's no mapping then we don't need to chown */ return true; data.cgroup_path = cgroup_path; data.origuid = geteuid(); /* Unpriv users can't chown it themselves, so chown from * a child namespace mapping both our own and the target uid */ if (userns_exec_1(conf, chown_cgroup_wrapper, &data, "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new namespace"); return false; } /* * Now chmod 775 the directory else the container cannot create cgroups. * This can't be done in the child namespace because it only group-owns * the cgroup */ if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775)) return false; if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0664)) return false; if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0664)) return false; } return true; } #define CG_REMOVE_RECURSIVE 1 /* Internal helper. Must be called with the cgmanager dbus socket open */ static void cgm_remove_cgroup(const char *controller, const char *path) { int existed; if ( cgmanager_remove_sync(NULL, cgroup_manager, controller, path, CG_REMOVE_RECURSIVE, &existed) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_remove_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Error removing %s:%s", controller, path); } if (existed == -1) INFO("cgroup removal attempt: %s:%s did not exist", controller, path); } static void *cgm_init(struct lxc_handler *handler) { struct cgm_data *d; d = malloc(sizeof(*d)); if (!d) return NULL; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); goto err1; } memset(d, 0, sizeof(*d)); d->name = strdup(handler->name); if (!d->name) { cgm_dbus_disconnect(); goto err1; } d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); /* cgm_create immediately gets called so keep the connection open */ return d; err1: free(d); return NULL; } /* Called after a failed container startup */ static void cgm_destroy(void *hdata, struct lxc_conf *conf) { struct cgm_data *d = hdata; char **slist = subsystems; int i; if (!d || !d->cgroup_path) return; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return; } if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) cgm_remove_cgroup(slist[i], d->cgroup_path); free(d->name); free(d->cgroup_path); free(d); cgm_dbus_disconnect(); } /* * remove all the cgroups created * called internally with dbus connection open */ static inline void cleanup_cgroups(char *path) { int i; char **slist = subsystems; if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) cgm_remove_cgroup(slist[i], path); } static inline bool cgm_create(void *hdata) { struct cgm_data *d = hdata; char **slist = subsystems; int i, index=0, baselen, ret; int32_t existed; char result[MAXPATHLEN], *tmp, *cgroup_path; if (!d) return false; /* XXX we should send a hint to the cgmanager that when these cgroups * become empty they should be deleted. Requires a cgmanager extension. */ memset(result, 0, MAXPATHLEN); tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern); if (!tmp) goto bad; if (strlen(tmp) >= MAXPATHLEN) { free(tmp); goto bad; } strcpy(result, tmp); baselen = strlen(result); free(tmp); tmp = result; while (*tmp == '/') tmp++; again: if (index == 100) { /* turn this into a warn later */ ERROR("cgroup error? 100 cgroups with this name already running"); goto bad; } if (index) { ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index); if (ret < 0 || ret >= MAXPATHLEN-baselen) goto bad; } existed = 0; if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (!lxc_cgmanager_create(slist[i], tmp, &existed)) { ERROR("Error creating cgroup %s:%s", slist[i], result); cleanup_cgroups(tmp); goto bad; } if (existed == 1) goto next; } /* success */ cgroup_path = strdup(tmp); if (!cgroup_path) { cleanup_cgroups(tmp); goto bad; } d->cgroup_path = cgroup_path; cgm_dbus_disconnect(); return true; next: index++; goto again; bad: cgm_dbus_disconnect(); return false; } /* * Use the cgmanager to move a task into a cgroup for a particular * hierarchy. * All the subsystems in this hierarchy are co-mounted, so we only * need to transition the task into one of the cgroups * * Internal helper, must be called with cgmanager dbus socket open */ static bool lxc_cgmanager_enter(pid_t pid, const char *controller, const char *cgroup_path, bool abs) { int ret; if (abs) ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager, controller, cgroup_path, pid); else ret = cgmanager_move_pid_sync(NULL, cgroup_manager, controller, cgroup_path, pid); if (ret != 0) { NihError *nerr; nerr = nih_error_get(); WARN("call to cgmanager_move_pid_%ssync failed: %s", abs ? "abs_" : "", nerr->message); nih_free(nerr); return false; } return true; } static inline bool cgm_enter(void *hdata, pid_t pid) { struct cgm_data *d = hdata; char **slist = subsystems; bool ret = false; int i; if (!d || !d->cgroup_path) return false; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } if (cgm_all_controllers_same) slist = subsystems_inone; for (i = 0; slist[i]; i++) { if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false)) goto out; } ret = true; out: cgm_dbus_disconnect(); return ret; } static const char *cgm_get_cgroup(void *hdata, const char *subsystem) { struct cgm_data *d = hdata; if (!d || !d->cgroup_path) return NULL; return d->cgroup_path; } #if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC static inline bool abs_cgroup_supported(void) { return api_version >= CGM_SUPPORTS_GET_ABS; } #else static inline bool abs_cgroup_supported(void) { return false; } #define cgmanager_get_pid_cgroup_abs_sync(...) -1 #endif static char *try_get_abs_cgroup(const char *name, const char *lxcpath, const char *controller) { char *cgroup = NULL; if (abs_cgroup_supported()) { /* get the container init pid and ask for its abs cgroup */ pid_t pid = lxc_cmd_get_init_pid(name, lxcpath); if (pid < 0) return NULL; if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager, controller, pid, &cgroup) != 0) { cgroup = NULL; NihError *nerr; nerr = nih_error_get(); nih_free(nerr); } else prune_init_scope(cgroup); return cgroup; } /* use the command interface to look for the cgroup */ return lxc_cmd_get_cgroup_path(name, lxcpath, controller); } /* * nrtasks is called by the utmp helper by the container monitor. * cgmanager socket was closed after cgroup setup was complete, so we need * to reopen here. * * Return -1 on error. */ static int cgm_get_nrtasks(void *hdata) { struct cgm_data *d = hdata; int32_t *pids; size_t pids_len; if (!d || !d->cgroup_path) return -1; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return -1; } if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0], d->cgroup_path, &pids, &pids_len) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message); nih_free(nerr); pids_len = -1; goto out; } nih_free(pids); out: cgm_dbus_disconnect(); return pids_len; } #if HAVE_CGMANAGER_LIST_CONTROLLERS static bool lxc_list_controllers(char ***list) { if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } if (cgmanager_list_controllers_sync(NULL, cgroup_manager, list) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_list_controllers_sync failed: %s", nerr->message); nih_free(nerr); cgm_dbus_disconnect(); return false; } cgm_dbus_disconnect(); return true; } #else static bool lxc_list_controllers(char ***list) { return false; } #endif static inline void free_abs_cgroup(char *cgroup) { if (!cgroup) return; if (abs_cgroup_supported()) nih_free(cgroup); else free(cgroup); } static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue) { char *controller, *key, *cgroup = NULL, *cglast; int len = -1; int ret; nih_local char *result = NULL; controller = alloca(strlen(filename)+1); strcpy(controller, filename); key = strchr(controller, '.'); if (!key) { ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } *key = '\0'; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } cgroup = try_get_abs_cgroup(name, lxcpath, controller); if (!cgroup) { cgm_dbus_disconnect(); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } cglast = strrchr(cgroup, '/'); if (!cglast) { cgm_dbus_disconnect(); free_abs_cgroup(cgroup); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } *cglast = '\0'; if (!lxc_cgmanager_enter(lxc_raw_getpid(), controller, cgroup, abs_cgroup_supported())) { WARN("Failed to enter container cgroup %s:%s", controller, cgroup); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); cgm_dbus_disconnect(); free_abs_cgroup(cgroup); exit(1); } if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) { NihError *nerr; nerr = nih_error_get(); nih_free(nerr); free_abs_cgroup(cgroup); cgm_dbus_disconnect(); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) WARN("Failed to warn cgm_get of error; parent may hang"); exit(1); } free_abs_cgroup(cgroup); cgm_dbus_disconnect(); len = strlen(result); ret = write(outp, &len, sizeof(len)); if (ret != sizeof(len)) { WARN("Failed to send length to parent"); exit(1); } if (!len || !sendvalue) { exit(0); } ret = write(outp, result, len); if (ret < 0) exit(1); exit(0); } /* cgm_get is called to get container cgroup settings, not during startup */ static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { pid_t pid; int p[2], ret, newlen, readlen; if (pipe(p) < 0) return -1; if ((pid = fork()) < 0) { close(p[0]); close(p[1]); return -1; } if (!pid) /* do_cgm_get exits */ do_cgm_get(name, lxcpath, filename, p[1], len && value); close(p[1]); ret = read(p[0], &newlen, sizeof(newlen)); if (ret != sizeof(newlen)) { close(p[0]); ret = -1; goto out; } if (!len || !value) { close(p[0]); ret = newlen; goto out; } memset(value, 0, len); if (newlen < 0) { /* child is reporting an error */ close(p[0]); ret = -1; goto out; } if (newlen == 0) { /* empty read */ close(p[0]); ret = 0; goto out; } readlen = newlen > len ? len : newlen; ret = read(p[0], value, readlen); close(p[0]); if (ret != readlen) { ret = -1; goto out; } if (newlen >= len) { value[len-1] = '\0'; newlen = len-1; } else if (newlen+1 < len) { /* cgmanager doesn't add eol to last entry */ value[newlen++] = '\n'; value[newlen] = '\0'; } ret = newlen; out: if (wait_for_pid(pid)) WARN("do_cgm_get exited with error"); return ret; } static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp) { char *controller, *key, *cgroup = NULL; int retval = 0; /* value we are sending to the parent over outp */ int ret; char *cglast; controller = alloca(strlen(filename)+1); strcpy(controller, filename); key = strchr(controller, '.'); if (!key) { ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } *key = '\0'; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } cgroup = try_get_abs_cgroup(name, lxcpath, controller); if (!cgroup) { cgm_dbus_disconnect(); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } cglast = strrchr(cgroup, '/'); if (!cglast) { cgm_dbus_disconnect(); free_abs_cgroup(cgroup); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } *cglast = '\0'; if (!lxc_cgmanager_enter(lxc_raw_getpid(), controller, cgroup, abs_cgroup_supported())) { ERROR("Failed to enter container cgroup %s:%s", controller, cgroup); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); cgm_dbus_disconnect(); free_abs_cgroup(cgroup); exit(1); } if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); free_abs_cgroup(cgroup); cgm_dbus_disconnect(); ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) WARN("Failed to warn cgm_set of error; parent may hang"); exit(1); } free_abs_cgroup(cgroup); cgm_dbus_disconnect(); /* tell parent that we are done */ retval = 1; ret = write(outp, &retval, sizeof(retval)); if (ret != sizeof(retval)) { exit(1); } exit(0); } /* cgm_set is called to change cgroup settings, not during startup */ static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) { pid_t pid; int p[2], ret, v; if (pipe(p) < 0) return -1; if ((pid = fork()) < 0) { close(p[1]); close(p[0]); return -1; } if (!pid) /* do_cgm_set exits */ do_cgm_set(name, lxcpath, filename, value, p[1]); close(p[1]); ret = read(p[0], &v, sizeof(v)); close(p[0]); if (wait_for_pid(pid)) WARN("do_cgm_set exited with error"); if (ret != sizeof(v) || !v) return -1; return 0; } static void free_subsystems(void) { int i; for (i = 0; i < nr_subsystems; i++) free(subsystems[i]); free(subsystems); subsystems = NULL; nr_subsystems = 0; } static void cull_user_controllers(void) { int i, j; for (i = 0; i < nr_subsystems; i++) { if (strncmp(subsystems[i], "name=", 5) != 0) continue; for (j = i; j < nr_subsystems-1; j++) subsystems[j] = subsystems[j+1]; nr_subsystems--; } } /* * return true if inword is in the comma-delimited list cgroup_use */ static bool in_comma_list(const char *inword, const char *cgroup_use) { char *e; size_t inlen = strlen(inword), len; do { e = strchr(cgroup_use, ','); len = e ? e - cgroup_use : strlen(cgroup_use); if (len == inlen && strncmp(inword, cgroup_use, len) == 0) return true; cgroup_use = e + 1; } while (e); return false; } /* * inlist is a comma-delimited list of cgroups; so is checklist. Return * true if any member of inlist is in checklist. */ static bool any_in_comma_list(const char *inlist, const char *checklist) { char *tmp = alloca(strlen(inlist) + 1), *tok, *saveptr = NULL; strcpy(tmp, inlist); for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { if (in_comma_list(tok, checklist)) return true; } return false; } static bool in_subsystem_list(const char *c) { int i; for (i = 0; i < nr_subsystems; i++) { if (strcmp(c, subsystems[i]) == 0) return true; } return false; } /* * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory", * then clear out any other subsystems, and make sure that freezer * and memory are both enabled */ static bool verify_and_prune(const char *cgroup_use) { const char *p; char *e; int i, j; for (p = cgroup_use; p && *p; p = e + 1) { e = strchr(p, ','); if (e) *e = '\0'; if (!in_subsystem_list(p)) { ERROR("Controller %s required by lxc.cgroup.use but not available\n", p); return false; } if (e) *e = ','; if (!e) break; } for (i = 0; i < nr_subsystems;) { if (in_comma_list(subsystems[i], cgroup_use)) { i++; continue; } free(subsystems[i]); for (j = i; j < nr_subsystems-1; j++) subsystems[j] = subsystems[j+1]; subsystems[nr_subsystems-1] = NULL; nr_subsystems--; } return true; } static void drop_subsystem(int which) { int i; if (which < 0 || which >= nr_subsystems) { ERROR("code error: dropping invalid subsystem index\n"); exit(1); } free(subsystems[which]); /* note - we have nr_subsystems+1 entries, last one a NULL */ for (i = which; i < nr_subsystems; i++) subsystems[i] = subsystems[i+1]; nr_subsystems -= 1; } /* * Check whether we can create the cgroups we would want */ static bool subsys_is_writeable(const char *controller, const char *probe) { int32_t existed; bool ret = true; if ( cgmanager_create_sync(NULL, cgroup_manager, controller, probe, &existed) != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_create_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Failed to create %s:%s", controller, probe); ret = false; } return ret; } static char *get_last_controller_in_list(char *list) { char *p; while ((p = strchr(list, ',')) != NULL) list = p + 1; return list; } /* * Make sure that all the controllers are writeable. * If any are not, then * - if they are listed in lxc.cgroup.use, refuse to start * - else if they are crucial subsystems, refuse to start * - else warn and do not use them */ static bool verify_final_subsystems(const char *cgroup_use) { int i; bool dropped_any = false; bool bret = false; const char *cgroup_pattern; char tmpnam[50], *probe; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); i = snprintf(tmpnam, 50, "lxcprobe-%d", lxc_raw_getpid()); if (i < 0 || i >= 50) { ERROR("Attack - format string modified?"); return false; } probe = lxc_string_replace("%n", tmpnam, cgroup_pattern); if (!probe) goto out; i = 0; while (i < nr_subsystems) { char *p = get_last_controller_in_list(subsystems[i]); if (!subsys_is_writeable(p, probe)) { if (is_crucial_cgroup_subsystem(p)) { ERROR("Cannot write to crucial subsystem %s\n", subsystems[i]); goto out; } if (cgroup_use && any_in_comma_list(subsystems[i], cgroup_use)) { ERROR("Cannot write to subsystem %s which is requested in lxc.cgroup.use\n", subsystems[i]); goto out; } WARN("Cannot write to subsystem %s, continuing with out it\n", subsystems[i]); dropped_any = true; drop_subsystem(i); } else { cgm_remove_cgroup(subsystems[i], probe); i++; } } if (dropped_any) cgm_all_controllers_same = false; bret = true; out: free(probe); cgm_dbus_disconnect(); return bret; } static bool collect_subsystems(void) { char *line = NULL; nih_local char **cgm_subsys_list = NULL; size_t sz = 0; FILE *f = NULL; if (subsystems) /* already initialized */ return true; subsystems_inone = malloc(2 * sizeof(char *)); if (!subsystems_inone) return false; subsystems_inone[0] = "all"; subsystems_inone[1] = NULL; if (lxc_list_controllers(&cgm_subsys_list)) { while (cgm_subsys_list[nr_subsystems]) { char **tmp = NIH_MUST( realloc(subsystems, (nr_subsystems+2)*sizeof(char *)) ); tmp[nr_subsystems] = NIH_MUST( strdup(cgm_subsys_list[nr_subsystems++]) ); subsystems = tmp; } if (nr_subsystems) subsystems[nr_subsystems] = NULL; goto collected; } INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups"); f = fopen_cloexec("/proc/self/cgroup", "r"); if (!f) { f = fopen_cloexec("/proc/1/cgroup", "r"); if (!f) return false; } while (getline(&line, &sz, f) != -1) { /* file format: hierarchy:subsystems:group, * with multiple subsystems being ,-separated */ char *slist, *end, *p, *saveptr = NULL, **tmp; if (!line[0]) continue; slist = strchr(line, ':'); if (!slist) continue; slist++; end = strchr(slist, ':'); if (!end) continue; *end = '\0'; for (p = strtok_r(slist, ",", &saveptr); p; p = strtok_r(NULL, ",", &saveptr)) { tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *)); if (!tmp) goto out_free; subsystems = tmp; tmp[nr_subsystems] = strdup(p); tmp[nr_subsystems+1] = NULL; if (!tmp[nr_subsystems]) goto out_free; nr_subsystems++; } } fclose(f); f = NULL; free(line); line = NULL; collected: if (!nr_subsystems) { ERROR("No cgroup subsystems found"); return false; } /* make sure that cgroup.use can be and is honored */ const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use"); if (!cgroup_use && errno != 0) goto final_verify; if (cgroup_use) { if (!verify_and_prune(cgroup_use)) { free_subsystems(); return false; } subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) ); cgm_all_controllers_same = false; } final_verify: return verify_final_subsystems(cgroup_use); out_free: free(line); if (f) fclose(f); free_subsystems(); return false; } /* * called during cgroup.c:cgroup_ops_init(), at startup. No threads. * We check whether we can talk to cgmanager, escape to root cgroup if * we are root, then close the connection. */ struct cgroup_ops *cgm_ops_init(void) { check_supports_multiple_controllers(-1); if (!collect_subsystems()) return NULL; if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS) cgm_all_controllers_same = false; /* if root, try to escape to root cgroup */ if (geteuid() == 0 && !cgm_escape(NULL)) { free_subsystems(); return NULL; } return &cgmanager_ops; } /* unfreeze is called by the command api after killing a container. */ static bool cgm_unfreeze(void *hdata) { struct cgm_data *d = hdata; bool ret = true; if (!d || !d->cgroup_path) return false; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path, "freezer.state", "THAWED") != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Error unfreezing %s", d->cgroup_path); ret = false; } cgm_dbus_disconnect(); return ret; } static bool cgm_setup_limits(void *hdata, struct lxc_conf *conf, bool do_devices) { struct cgm_data *d = hdata; struct lxc_list *iterator, *sorted_cgroup_settings, *next; struct lxc_cgroup *cg; struct lxc_list *cgroup_settings = &conf->cgroup; bool ret = false; if (lxc_list_empty(cgroup_settings)) return true; if (!d || !d->cgroup_path) return false; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings); if (!sorted_cgroup_settings) { return false; } lxc_list_for_each(iterator, sorted_cgroup_settings) { char controller[100], *p; cg = iterator->elem; if (do_devices != !strncmp("devices", cg->subsystem, 7)) continue; if (strlen(cg->subsystem) > 100) /* i smell a rat */ goto out; strcpy(controller, cg->subsystem); p = strchr(controller, '.'); if (p) *p = '\0'; if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, d->cgroup_path, cg->subsystem, cg->value) != 0) { NihError *nerr; nerr = nih_error_get(); if (do_devices) { WARN("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); WARN("Error setting cgroup %s:%s limit type %s", controller, d->cgroup_path, cg->subsystem); continue; } ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); ERROR("Error setting cgroup %s:%s limit type %s", controller, d->cgroup_path, cg->subsystem); goto out; } DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); } ret = true; INFO("cgroup limits have been setup"); out: lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) { lxc_list_del(iterator); free(iterator); } free(sorted_cgroup_settings); cgm_dbus_disconnect(); return ret; } static bool cgm_chown(void *hdata, struct lxc_conf *conf) { struct cgm_data *d = hdata; if (!d || !d->cgroup_path) return false; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } if (!chown_cgroup(d->cgroup_path, conf)) WARN("Failed to chown %s to container root", d->cgroup_path); cgm_dbus_disconnect(); return true; } /* * TODO: this should be re-written to use the get_config_item("lxc.idmap") * cmd api instead of getting the idmap from c->lxc_conf. The reason is * that the id_maps may be different if the container was started with a * -f or -s argument. * The reason I'm punting on that is because we'll need to parse the * idmap results. */ static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid) { bool pass = true; char *cgroup = NULL; char **slist = subsystems; int i; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return false; } for (i = 0; slist[i]; i++) { cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]); if (!cgroup) { ERROR("Failed to get cgroup for controller %s", slist[i]); cgm_dbus_disconnect(); return false; } if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) { pass = false; break; } } cgm_dbus_disconnect(); if (!pass) ERROR("Failed to enter group %s", cgroup); free_abs_cgroup(cgroup); return pass; } static bool cgm_bind_dir(const char *root, const char *dirname) { nih_local char *cgpath = NULL; /* /sys should have been mounted by now */ cgpath = NIH_MUST( nih_strdup(NULL, root) ); NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") ); if (!dir_exists(cgpath)) { ERROR("%s does not exist", cgpath); return false; } /* mount a tmpfs there so we can create subdirs */ if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) { SYSERROR("Failed to mount tmpfs at %s", cgpath); return false; } NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") ); if (mkdir(cgpath, 0755) < 0) { SYSERROR("Failed to create %s", cgpath); return false; } if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) { SYSERROR("Failed to bind mount %s to %s", dirname, cgpath); return false; } return true; } /* * cgm_mount_cgroup: * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to * /sys/fs/cgroup/cgmanager/ in the container. * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that. * Else do nothing */ #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower" #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager" static bool cgm_mount_cgroup(void *hdata, const char *root, int type) { if (dir_exists(CGMANAGER_LOWER_SOCK)) return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK); if (dir_exists(CGMANAGER_UPPER_SOCK)) return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK); /* Host doesn't have cgmanager running? Then how did we get here? */ return false; } static struct cgroup_ops cgmanager_ops = { .init = cgm_init, .destroy = cgm_destroy, .create = cgm_create, .enter = cgm_enter, .create_legacy = NULL, .get_cgroup = cgm_get_cgroup, .escape = cgm_escape, .num_hierarchies = cgm_num_hierarchies, .get_hierarchies = cgm_get_hierarchies, .get = cgm_get, .set = cgm_set, .unfreeze = cgm_unfreeze, .setup_limits = cgm_setup_limits, .name = "cgmanager", .chown = cgm_chown, .attach = cgm_attach, .mount_cgroup = cgm_mount_cgroup, .nrtasks = cgm_get_nrtasks, .disconnect = NULL, .driver = CGMANAGER, }; #endif lxc-2.0.11/src/lxc/cgroups/cgroup_utils.c0000644061062106075000000000544513435013473015247 00000000000000/* * lxc: linux Container library * * Copyright © 2017 Canonical Ltd. * * Authors: * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include "cgroup_utils.h" #include "utils.h" int get_cgroup_version(char *line) { if (is_cgroupfs_v1(line)) return CGROUP_SUPER_MAGIC; if (is_cgroupfs_v2(line)) return CGROUP2_SUPER_MAGIC; return 0; } bool is_cgroupfs_v1(char *line) { char *p = strstr(line, " - "); if (!p) return false; return strncmp(p, " - cgroup ", 10) == 0; } bool is_cgroupfs_v2(char *line) { char *p = strstr(line, " - "); if (!p) return false; return strncmp(p, " - cgroup2 ", 11) == 0; } bool test_writeable_v1(char *mountpoint, char *path) { char *fullpath = must_make_path(mountpoint, path, NULL); int ret; ret = access(fullpath, W_OK); free(fullpath); return ret == 0; } bool test_writeable_v2(char *mountpoint, char *path) { /* In order to move ourselves into an appropriate sub-cgroup we need to * have write access to the parent cgroup's "cgroup.procs" file, i.e. we * need to have write access to the our current cgroups's "cgroup.procs" * file. */ int ret; char *cgroup_path, *cgroup_procs_file, *cgroup_threads_file; cgroup_path = must_make_path(mountpoint, path, NULL); cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL); ret = access(cgroup_path, W_OK); if (ret < 0) { free(cgroup_path); free(cgroup_procs_file); return false; } ret = access(cgroup_procs_file, W_OK); free(cgroup_procs_file); if (ret < 0) { free(cgroup_path); return false; } /* Newer versions of cgroup2 now also require write access to the * "cgroup.threads" file. */ cgroup_threads_file = must_make_path(cgroup_path, "cgroup.threads", NULL); free(cgroup_path); if (!file_exists(cgroup_threads_file)) { free(cgroup_threads_file); return true; } ret = access(cgroup_threads_file, W_OK); free(cgroup_threads_file); if (ret < 0) return false; return ret == 0; } lxc-2.0.11/src/lxc/lxccontainer.h0000644061062106075000000007676613435013473013562 00000000000000/*! \file * * liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONTAINER_H #define __LXC_CONTAINER_H #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define LXC_CLONE_KEEPNAME (1 << 0) /*!< Do not edit the rootfs to change the hostname */ #define LXC_CLONE_KEEPMACADDR (1 << 1) /*!< Do not change the MAC address on network interfaces */ #define LXC_CLONE_SNAPSHOT (1 << 2) /*!< Snapshot the original filesystem(s) */ #define LXC_CLONE_KEEPBDEVTYPE (1 << 3) /*!< Use the same bdev type */ #define LXC_CLONE_MAYBE_SNAPSHOT (1 << 4) /*!< Snapshot only if bdev supports it, else copy */ #define LXC_CLONE_MAXFLAGS (1 << 5) /*!< Number of \c LXC_CLONE_* flags */ #define LXC_CREATE_QUIET (1 << 0) /*!< Redirect \c stdin to \c /dev/zero and \c stdout and \c stderr to \c /dev/null */ #define LXC_CREATE_MAXFLAGS (1 << 1) /*!< Number of \c LXC_CREATE* flags */ struct bdev_specs; struct lxc_snapshot; struct lxc_lock; struct migrate_opts; /*! * An LXC container. * * Note that changing the order of struct members is an API change, as callers * will end up having the wrong offset when calling a function. So when making * changes, whenever possible stick to simply appending new members. */ struct lxc_container { // private fields /*! * \private * Name of container. */ char *name; /*! * \private * Full path to configuration file. */ char *configfile; /*! * \private * File to store pid. */ char *pidfile; /*! * \private * Container semaphore lock. */ struct lxc_lock *slock; /*! * \private * Container private lock. */ struct lxc_lock *privlock; /*! * \private * Number of references to this container. * \note protected by privlock. */ int numthreads; /*! * \private * Container configuration. * * \internal FIXME: do we want the whole lxc_handler? */ struct lxc_conf *lxc_conf; // public fields /*! Human-readable string representing last error */ char *error_string; /*! Last error number */ int error_num; /*! Whether container wishes to be daemonized */ bool daemonize; /*! Full path to configuration file */ char *config_path; /*! * \brief Determine if \c /var/lib/lxc/$name/config exists. * * \param c Container. * * \return \c true if container is defined, else \c false. */ bool (*is_defined)(struct lxc_container *c); /*! * \brief Determine state of container. * * \param c Container. * * \return Static upper-case string representing state of container. * * \note Returned string must not be freed. */ const char *(*state)(struct lxc_container *c); /*! * \brief Determine if container is running. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*is_running)(struct lxc_container *c); /*! * \brief Freeze running container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*freeze)(struct lxc_container *c); /*! * \brief Thaw a frozen container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*unfreeze)(struct lxc_container *c); /*! * \brief Determine process ID of the containers init process. * * \param c Container. * * \return pid of init process as seen from outside the * container. */ pid_t (*init_pid)(struct lxc_container *c); /*! * \brief Load the specified configuration for the container. * * \param c Container. * \param alt_file Full path to alternate configuration file, or * \c NULL to use the default configuration file. * * \return \c true on success, else \c false. */ bool (*load_config)(struct lxc_container *c, const char *alt_file); /*! * \brief Start the container. * * \param c Container. * \param useinit Use lxcinit rather than \c /sbin/init. * \param argv Array of arguments to pass to init. * * \return \c true on success, else \c false. */ bool (*start)(struct lxc_container *c, int useinit, char * const argv[]); /*! * \brief Start the container (list variant). * * \param c Container. * \param useinit Use lxcinit rather than \c /sbin/init. * \param ... Command-line to pass to init (must end in \c NULL). * * \return \c true on success, else \c false. * * \note Identical to \ref start except that that the init * arguments are specified via a list rather than an array of * pointers. */ bool (*startl)(struct lxc_container *c, int useinit, ...); /*! * \brief Stop the container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*stop)(struct lxc_container *c); /*! * \brief Change whether the container wants to run disconnected * from the terminal. * * \param c Container. * \param state Value for the daemonize bit (0 or 1). * * \return \c true on success, else \c false. */ bool (*want_daemonize)(struct lxc_container *c, bool state); /*! * \brief Change whether the container wishes all file descriptors * to be closed on startup. * * \param c Container. * \param state Value for the close_all_fds bit (0 or 1). * * \return \c true on success, else \c false. */ bool (*want_close_all_fds)(struct lxc_container *c, bool state); /*! * \brief Return current config file name. * * \param c Container. * * \return config file name, or \c NULL on error. * * \note The result is allocated, so the caller must free the result. */ char *(*config_file_name)(struct lxc_container *c); /*! * \brief Wait for container to reach a particular state. * * \param c Container. * \param state State to wait for. * \param timeout Timeout in seconds. * * \return \c true if state reached within \p timeout, else \c false. * * \note A \p timeout of \c -1 means wait forever. A \p timeout * of \c 0 means do not wait. */ bool (*wait)(struct lxc_container *c, const char *state, int timeout); /*! * \brief Set a key/value configuration option. * * \param c Container. * \param key Name of option to set. * \param value Value of \p name to set. * * \return \c true on success, else \c false. */ bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value); /*! * \brief Delete the container. * * \param c Container. * * \return \c true on success, else \c false. * * \note Container must be stopped and have no dependent snapshots. */ bool (*destroy)(struct lxc_container *c); /*! * \brief Save configuaration to a file. * * \param c Container. * \param alt_file Full path to file to save configuration in. * * \return \c true on success, else \c false. */ bool (*save_config)(struct lxc_container *c, const char *alt_file); /*! * \brief Create a container. * * \param c Container (with lxcpath, name and a starting * configuration set). * \param t Template to execute to instantiate the root * filesystem and adjust the configuration. * \param bdevtype Backing store type to use (if \c NULL, \c dir will be used). * \param specs Additional parameters for the backing store (for * example LVM volume group to use). * \param flags \c LXC_CREATE_* options (currently only \ref * LXC_CREATE_QUIET is supported). * \param argv Arguments to pass to the template, terminated by \c NULL (if no * arguments are required, just pass \c NULL). * * \return \c true on success, else \c false. */ bool (*create)(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]); /*! * \brief Create a container (list variant). * * \param c Container (with lxcpath, name and a starting * configuration set). * \param t Template to execute to instantiate the root * filesystem and adjust the configuration. * \param bdevtype Backing store type to use (if \c NULL, \c dir will be used). * \param specs Additional parameters for the backing store (for * example LVM volume group to use). * \param flags \c LXC_CREATE_* options (currently only \ref * LXC_CREATE_QUIET is supported). * \param ... Command-line to pass to init (must end in \c NULL). * * \return \c true on success, else \c false. * * \note Identical to \ref create except that the template * arguments are specified as a list rather than an array of * pointers. */ bool (*createl)(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, ...); /*! * \brief Rename a container * * \param c Container. * \param newname New name to be used for the container. * * \return \c true on success, else \c false. */ bool (*rename)(struct lxc_container *c, const char *newname); /*! * \brief Request the container reboot by sending it \c SIGINT. * * \param c Container. * * \return \c true if reboot request successful, else \c false. */ bool (*reboot)(struct lxc_container *c); /*! * \brief Request the container shutdown by sending it \c * SIGPWR. * * \param c Container. * \param timeout Seconds to wait before returning false. * (-1 to wait forever, 0 to avoid waiting). * * \return \c true if the container was shutdown successfully, else \c false. */ bool (*shutdown)(struct lxc_container *c, int timeout); /*! * \brief Completely clear the containers in-memory configuration. * * \param c Container. */ void (*clear_config)(struct lxc_container *c); /*! * \brief Clear a configuration item. * * \param c Container. * \param key Name of option to clear. * * \return \c true on success, else \c false. * * \note Analog of \ref set_config_item. */ bool (*clear_config_item)(struct lxc_container *c, const char *key); /*! * \brief Retrieve the value of a config item. * * \param c Container. * \param key Name of option to get. * \param[out] retv Caller-allocated buffer to write value of \p key * into (or \c NULL to determine length of value). * \param inlen Length of \p retv (may be zero). * * \return Length of config items value, or < 0 on error. * * \note The caller can (and should) determine how large a buffer to allocate for * \p retv by initially passing its value as \c NULL and considering the return value. * This function can then be called again passing a newly-allocated suitably-sized buffer. * \note If \p retv is NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, nothing will be written to \p retv and still return * the length of config item value. */ int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen); /*! * \brief Retrieve the value of a config item from running container. * * \param c Container. * \param key Name of option to get. * * \return the item or NULL on error. * * \note Returned string must be freed by the caller. */ char* (*get_running_config_item)(struct lxc_container *c, const char *key); /*! * \brief Retrieve a list of config item keys given a key * prefix. * * \param c Container. * \param key Name of option to get. * \param[out] retv Caller-allocated buffer to write list of keys to * (or \c NULL to determine overall length of keys list). * \param inlen Length of \p retv (may be zero). * * \return Length of keys list, or < 0 on error. * * \note The list values written to \p retv are separated by * a newline character ('\\n'). * \note The caller can (and should) determine how large a buffer to allocate for * \p retv by initially passing its value as \c NULL and considering the return value. * This function can then be called again passing a newly-allocated suitably-sized buffer. * \note If \p retv is NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, the value written * to \p retv will be truncated. */ int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen); /*! * \brief Obtain a list of network interfaces. * \param c Container. * * \return Newly-allocated array of network interfaces, or \c * NULL on error. * * \note The returned array is allocated, so the caller must free it. * \note The returned array is terminated with a \c NULL entry. */ char** (*get_interfaces)(struct lxc_container *c); /*! * \brief Determine the list of container IP addresses. * * \param c Container. * \param interface Network interface name to consider. * \param family Network family (for example "inet", "inet6"). * \param scope IPv6 scope id (ignored if \p family is not "inet6"). * * \return Newly-allocated array of network interfaces, or \c * NULL on error. * * \note The returned array is allocated, so the caller must free it. * \note The returned array is terminated with a \c NULL entry. */ char** (*get_ips)(struct lxc_container *c, const char* interface, const char* family, int scope); /*! * \brief Retrieve the specified cgroup subsystem value for the container. * * \param c Container. * \param subsys cgroup subsystem to retrieve. * \param[out] retv Caller-allocated buffer to write value of \p * subsys into (or \c NULL to determine length of value). * \param inlen length of \p retv (may be zero). * * \return Length of \p subsys value, or < 0 on error. * * \note If \p retv is \c NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, the value written * to \p retv will be truncated. */ int (*get_cgroup_item)(struct lxc_container *c, const char *subsys, char *retv, int inlen); /*! * \brief Set the specified cgroup subsystem value for the container. * * \param c Container. * \param subsys cgroup subsystem to consider. * \param value Value to set for \p subsys. * * \return \c true on success, else \c false. */ bool (*set_cgroup_item)(struct lxc_container *c, const char *subsys, const char *value); /*! * \brief Determine full path to the containers configuration file. * Each container can have a custom configuration path. However * by default it will be set to either the \c LXCPATH configure * variable, or the lxcpath value in the \c LXC_GLOBAL_CONF configuration * file (i.e. \c /etc/lxc/lxc.conf). * The value for a specific container can be changed using * \ref set_config_path. There is no other way to specify this in general at the moment. * * \param c Container. * * \return Static string representing full path to configuration * file. * * \note Returned string must not be freed. */ const char *(*get_config_path)(struct lxc_container *c); /*! * \brief Set the full path to the containers configuration * file. * * \param c Container. * \param path Full path to configuration file. * * \return \c true on success, else \c false. */ bool (*set_config_path)(struct lxc_container *c, const char *path); /*! * \brief Copy a stopped container. * * \param c Original container. * \param newname New name for the container. If \c NULL, the same * name is used and a new lxcpath MUST be specified. * \param lxcpath lxcpath in which to create the new container. If * \c NULL, the original container's lxcpath will be used. * (XXX: should we use the default instead?) * \param flags Additional \c LXC_CLONE* flags to change the cloning behaviour: * - \ref LXC_CLONE_KEEPNAME * - \ref LXC_CLONE_KEEPMACADDR * - \ref LXC_CLONE_SNAPSHOT * \param bdevtype Optionally force the cloned bdevtype to a specified plugin. * By default the original is used (subject to snapshot requirements). * \param bdevdata Information about how to create the new storage * (i.e. fstype and fsdata). * \param newsize In case of a block device backing store, an * optional size. If \c 0, the original backing store's size will * be used if possible. Note this only applies to the rootfs. For * any other filesystems, the original size will be duplicated. * \param hookargs Additional arguments to pass to the clone hook script. * * \return Newly-allocated copy of container \p c, or \p NULL on * error. * * \note If devtype was not specified, and \p flags contains \ref * LXC_CLONE_SNAPSHOT then use the native \p bdevtype if possible, * else use an overlayfs. */ struct lxc_container *(*clone)(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs); /*! * \brief Allocate a console tty for the container. * * \param c Container. * \param[in,out] ttynum Terminal number to attempt to allocate, * or \c -1 to allocate the first available tty. * \param[out] masterfd File descriptor referring to the master side of the pty. * * \return tty file descriptor number on success, or \c -1 on * failure. * * \note On successful return, \p ttynum will contain the tty number * that was allocated. * \note The returned file descriptor is used to keep the tty * allocated. The caller should call close(2) on the returned file * descriptor when no longer required so that it may be allocated * by another caller. */ int (*console_getfd)(struct lxc_container *c, int *ttynum, int *masterfd); /*! * \brief Allocate and run a console tty. * * \param c Container. * \param ttynum Terminal number to attempt to allocate, \c -1 to * allocate the first available tty or \c 0 to allocate the * console. * \param stdinfd File descriptor to read input from. * \param stdoutfd File descriptor to write output to. * \param stderrfd File descriptor to write error output to. * \param escape The escape character (1 == 'a', 2 == 'b', ...). * * \return \c 0 on success, \c -1 on failure. * * \note This function will not return until the console has been * exited by the user. */ int (*console)(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape); /*! * \brief Create a sub-process attached to a container and run * a function inside it. * * \param c Container. * \param exec_function Function to run. * \param exec_payload Data to pass to \p exec_function. * \param options \ref lxc_attach_options_t. * \param[out] attached_process Process ID of process running inside * container \p c that is running \p exec_function. * * \return \c 0 on success, \c -1 on error. */ int (*attach)(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process); /*! * \brief Run a program inside a container and wait for it to exit. * * \param c Container. * \param options See \ref attach options. * \param program Full path inside container of program to run. * \param argv Array of arguments to pass to \p program. * * \return \c waitpid(2) status of exited process that ran \p * program, or \c -1 on error. */ int (*attach_run_wait)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]); /*! * \brief Run a program inside a container and wait for it to exit (list variant). * * \param c Container. * \param options See \ref attach options. * \param program Full path inside container of program to run. * \param ... Command-line to pass to \p program (must end in \c NULL). * * \return \c waitpid(2) status of exited process that ran \p * program, or \c -1 on error. */ int (*attach_run_waitl)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...); /*! * \brief Create a container snapshot. * * Assuming default paths, snapshots will be created as * \c /var/lib/lxc/\/snaps/snap\ * where \c \ represents the container name and \c \ * represents the zero-based snapshot number. * * \param c Container. * \param commentfile Full path to file containing a description * of the snapshot. * * \return -1 on error, or zero-based snapshot number. * * \note \p commentfile may be \c NULL but this is discouraged. */ int (*snapshot)(struct lxc_container *c, const char *commentfile); /*! * \brief Obtain a list of container snapshots. * * \param c Container. * \param[out] snapshots Dynamically-allocated Array of lxc_snapshot's. * * \return Number of snapshots. * * \note The array returned in \p snapshots is allocated, so the caller must free it. * \note To free an individual snapshot as returned in \p * snapshots, call the snapshots \c free function (see \c src/tests/snapshot.c for an example). */ int (*snapshot_list)(struct lxc_container *c, struct lxc_snapshot **snapshots); /*! * \brief Create a new container based on a snapshot. * * The restored container will be a copy (not snapshot) of the snapshot, * and restored in the lxcpath of the original container. * \param c Container. * \param snapname Name of snapshot. * \param newname Name to be used for the restored snapshot. * \return \c true on success, else \c false. * \warning If \p newname is the same as the current container * name, the container will be destroyed. However, this will * fail if the snapshot is overlay-based, since the snapshots * will pin the original container. * \note As an example, if the container exists as \c /var/lib/lxc/c1, snapname might be \c 'snap0' * (representing \c /var/lib/lxc/c1/snaps/snap0). If \p newname is \p c2, * then \c snap0 will be copied to \c /var/lib/lxc/c2. */ bool (*snapshot_restore)(struct lxc_container *c, const char *snapname, const char *newname); /*! * \brief Destroy the specified snapshot. * * \param c Container. * \param snapname Name of snapshot. * * \return \c true on success, else \c false. */ bool (*snapshot_destroy)(struct lxc_container *c, const char *snapname); /*! * \brief Determine if the caller may control the container. * * \param c Container. * * \return \c false if there is a control socket for the * container monitor and the caller may not access it, otherwise * returns \c true. */ bool (*may_control)(struct lxc_container *c); /*! * \brief Add specified device to the container. * * \param c Container. * \param src_path Full path of the device. * \param dest_path Alternate path in the container (or \p NULL * to use \p src_path). * * \return \c true on success, else \c false. */ bool (*add_device_node)(struct lxc_container *c, const char *src_path, const char *dest_path); /*! * \brief Remove specified device from the container. * * \param c Container. * \param src_path Full path of the device. * \param dest_path Alternate path in the container (or \p NULL * to use \p src_path). * * \return \c true on success, else \c false. */ bool (*remove_device_node)(struct lxc_container *c, const char *src_path, const char *dest_path); /* Post LXC-1.0 additions */ /*! * \brief Add specified netdev to the container. * * \param c Container. * \param dev name of net device. * * \return \c true on success, else \c false. */ bool (*attach_interface)(struct lxc_container *c, const char *dev, const char *dst_dev); /*! * \brief Remove specified netdev from the container. * * \param c Container. * \param dev name of net device. * * \return \c true on success, else \c false. */ bool (*detach_interface)(struct lxc_container *c, const char *dev, const char *dst_dev); /*! * \brief Checkpoint a container. * * \param c Container. * \param directory The directory to dump the container to. * \param stop Whether or not to stop the container after checkpointing. * \param verbose Enable criu's verbose logs. * * \return \c true on success, else \c false. * present at compile time). */ bool (*checkpoint)(struct lxc_container *c, char *directory, bool stop, bool verbose); /*! * \brief Restore a container from a checkpoint. * * \param c Container. * \param directory The directory to restore the container from. * \param verbose Enable criu's verbose logs. * * \return \c true on success, else \c false. * */ bool (*restore)(struct lxc_container *c, char *directory, bool verbose); /*! * \brief Delete the container and all its snapshots. * * \param c Container. * * \return \c true on success, else \c false. * * \note Container must be stopped. */ bool (*destroy_with_snapshots)(struct lxc_container *c); /*! * \brief Destroy all the container's snapshot. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*snapshot_destroy_all)(struct lxc_container *c); /* Post LXC-1.1 additions */ /*! * \brief An API call to perform various migration operations * * \param cmd One of the MIGRATE_ contstants. * \param opts A migrate_opts struct filled with relevant options. * \param size The size of the migrate_opts struct, i.e. sizeof(struct migrate_opts). * * \return \c 0 on success, nonzero on failure. */ int (*migrate)(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size); }; /*! * \brief An LXC container snapshot. */ struct lxc_snapshot { char *name; /*!< Name of snapshot */ char *comment_pathname; /*!< Full path to snapshots comment file (may be \c NULL) */ char *timestamp; /*!< Time snapshot was created */ char *lxcpath; /*!< Full path to LXCPATH for snapshot */ /*! * \brief De-allocate the snapshot. * \param s snapshot. */ void (*free)(struct lxc_snapshot *s); }; /*! * \brief Specifications for how to create a new backing store */ struct bdev_specs { char *fstype; /*!< Filesystem type */ uint64_t fssize; /*!< Filesystem size in bytes */ struct { char *zfsroot; /*!< ZFS root path */ } zfs; struct { char *vg; /*!< LVM Volume Group name */ char *lv; /*!< LVM Logical Volume name */ char *thinpool; /*!< LVM thin pool to use, if any */ } lvm; char *dir; /*!< Directory path */ struct { char *rbdname; /*!< RBD image name */ char *rbdpool; /*!< Ceph pool name */ } rbd; }; /*! * \brief Commands for the migrate API call. */ enum { MIGRATE_PRE_DUMP, MIGRATE_DUMP, MIGRATE_RESTORE, }; /*! * \brief Options for the migrate API call. */ struct migrate_opts { /* new members should be added at the end */ char *directory; bool verbose; bool stop; /* stop the container after dump? */ char *predump_dir; /* relative to directory above */ char *pageserver_address; /* where should memory pages be send? */ char *pageserver_port; /* This flag indicates whether or not the container's rootfs will have * the same inodes on checkpoint and restore. In the case of e.g. zfs * send or btrfs send, or an LVM snapshot, this will be true, but it * won't if e.g. you rsync the filesystems between two machines. */ bool preserves_inodes; /* Path to an executable script that will be registered as a criu * "action script" */ char *action_script; /* If CRIU >= 2.4 is detected the option to skip in-flight connections * will be enabled by default. The flag 'disable_skip_in_flight' will * unconditionally disable this feature. In-flight connections are * not fully established TCP connections: SYN, SYN-ACK */ bool disable_skip_in_flight; /* This is the maximum file size for deleted files (which CRIU calls * "ghost" files) that will be handled. 0 indicates the CRIU default, * which at this time is 1MB. */ uint64_t ghost_limit; }; /*! * \brief Create a new container. * * \param name Name to use for container. * \param configpath Full path to configuration file to use. * * \return Newly-allocated container, or \c NULL on error. */ struct lxc_container *lxc_container_new(const char *name, const char *configpath); /*! * \brief Add a reference to the specified container. * * \param c Container. * * \return \c true on success, \c false on error. */ int lxc_container_get(struct lxc_container *c); /*! * \brief Drop a reference to the specified container. * * \param c Container. * * \return \c 0 on success, \c 1 if reference was successfully dropped * and container has been freed, and \c -1 on error. * * \warning If \c 1 is returned, \p c is no longer valid. */ int lxc_container_put(struct lxc_container *c); /*! * \brief Obtain a list of all container states. * \param[out] states Caller-allocated array to hold all states (may be \c NULL). * * \return Number of container states. * * \note Passing \c NULL for \p states allows the caller to first * calculate how many states there are before calling the function again, the second time * providing a suitably-sized array to store the static string pointers * in. * \note The \p states array should be freed by the caller, but not the strings the elements point to. */ int lxc_get_wait_states(const char **states); /*! * \brief Get the value for a global config key * * \param key The name of the config key * * \return String representing the current value for the key. */ const char *lxc_get_global_config_item(const char *key); /*! * \brief Determine version of LXC. * \return Static string representing version of LXC in use. * * \note Returned string must not be freed. */ const char *lxc_get_version(void); /*! * \brief Get a list of defined containers in a lxcpath. * * \param lxcpath lxcpath under which to look. * \param names If not \c NULL, then a list of container names will be returned here. * \param cret If not \c NULL, then a list of lxc_containers will be returned here. * * \return Number of containers found, or \c -1 on error. * * \note Values returned in \p cret are sorted by container name. */ int list_defined_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); /*! * \brief Get a list of active containers for a given lxcpath. * * \param lxcpath Full \c LXCPATH path to consider. * \param[out] names Dynamically-allocated array of container names. * \param[out] cret Dynamically-allocated list of containers. * * \return Number of containers found, or -1 on error. * * \note Some of the containers may not be "defined". * \note Values returned in \p cret are sorted by container name. * \note \p names and \p cret may both (or either) be specified as \c NULL. * \note \p names and \p cret must be freed by the caller. */ int list_active_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); /*! * \brief Get a complete list of all containers for a given lxcpath. * * \param lxcpath Full \c LXCPATH path to consider. * \param[out] names Dynamically-allocated array of container name. * \param[out] cret Dynamically-allocated list of containers. * * \return Number of containers, or -1 on error. * * \note Some of the containers may not be "defined". * \note Values returned in \p cret are sorted by container name. * \note \p names and \p cret may both (or either) be specified as \c NULL. * \note \p names and \p cret must be freed by the caller. */ int list_all_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); /*! * \brief Close log file. */ void lxc_log_close(void); #ifdef __cplusplus } #endif #endif lxc-2.0.11/src/lxc/af_unix.c0000644061062106075000000001437713435013473012503 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(lxc_af_unix, lxc); int lxc_abstract_unix_open(const char *path, int type, int flags) { int fd, ret; size_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, type, 0); if (fd < 0) return -1; /* Clear address structure */ memset(&addr, 0, sizeof(addr)); if (!path) return fd; addr.sun_family = AF_UNIX; len = strlen(&path[1]); /* do not enforce \0-termination */ if (len >= sizeof(addr.sun_path)) { close(fd); errno = ENAMETOOLONG; return -1; } /* do not enforce \0-termination */ memcpy(&addr.sun_path[1], &path[1], len); ret = bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { int tmp = errno; close(fd); errno = tmp; return -1; } if (type == SOCK_STREAM) { ret = listen(fd, 100); if (ret < 0) { int tmp = errno; close(fd); errno = tmp; return -1; } } return fd; } int lxc_abstract_unix_close(int fd) { close(fd); return 0; } int lxc_abstract_unix_connect(const char *path) { int fd, ret; size_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; len = strlen(&path[1]); /* do not enforce \0-termination */ if (len >= sizeof(addr.sun_path)) { close(fd); errno = ENAMETOOLONG; return -1; } /* do not enforce \0-termination */ memcpy(&addr.sun_path[1], &path[1], len); ret = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { close(fd); return -1; } return fd; } int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) return -1; msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = sendmsg(fd, &msg, MSG_NOSIGNAL); free(cmsgbuf); return ret; } int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) return -1; msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(fd, &msg, 0); if (ret <= 0) goto out; cmsg = CMSG_FIRSTHDR(&msg); memset(recvfds, -1, num_recvfds * sizeof(int)); if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); } out: free(cmsgbuf); return ret; } int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred = { .pid = lxc_raw_getpid(), .uid = getuid(), .gid = getgid(), }; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; return sendmsg(fd, &msg, MSG_NOSIGNAL); } int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred; int ret; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(fd, &msg, 0); if (ret <= 0) goto out; cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); if (cred.uid && (cred.uid != getuid() || cred.gid != getgid())) { INFO("message denied for '%d/%d'", cred.uid, cred.gid); return -EACCES; } } out: return ret; } lxc-2.0.11/src/lxc/caps.c0000644061062106075000000001765713435013473012004 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include "caps.h" #include "log.h" lxc_log_define(lxc_caps, lxc); #if HAVE_LIBCAP #ifndef PR_CAPBSET_READ #define PR_CAPBSET_READ 23 #endif /* Control the ambient capability set */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 #endif #ifndef PR_CAP_AMBIENT_IS_SET #define PR_CAP_AMBIENT_IS_SET 1 #endif #ifndef PR_CAP_AMBIENT_RAISE #define PR_CAP_AMBIENT_RAISE 2 #endif #ifndef PR_CAP_AMBIENT_LOWER #define PR_CAP_AMBIENT_LOWER 3 #endif #ifndef PR_CAP_AMBIENT_CLEAR_ALL #define PR_CAP_AMBIENT_CLEAR_ALL 4 #endif int lxc_caps_down(void) { cap_t caps; int ret; /* when we are run as root, we don't want to play * with the capabilities */ if (!getuid()) return 0; caps = cap_get_proc(); if (!caps) { ERROR("failed to cap_get_proc: %s", strerror(errno)); return -1; } ret = cap_clear_flag(caps, CAP_EFFECTIVE); if (ret) { ERROR("failed to cap_clear_flag: %s", strerror(errno)); goto out; } ret = cap_set_proc(caps); if (ret) { ERROR("failed to cap_set_proc: %s", strerror(errno)); goto out; } out: cap_free(caps); return 0; } int lxc_caps_up(void) { cap_t caps; cap_value_t cap; int ret; /* when we are run as root, we don't want to play * with the capabilities */ if (!getuid()) return 0; caps = cap_get_proc(); if (!caps) { ERROR("failed to cap_get_proc: %s", strerror(errno)); return -1; } for (cap = 0; cap <= CAP_LAST_CAP; cap++) { cap_flag_value_t flag; ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); if (ret) { if (errno == EINVAL) { INFO("Last supported cap was %d", cap-1); break; } else { ERROR("failed to cap_get_flag: %s", strerror(errno)); goto out; } } ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); if (ret) { ERROR("failed to cap_set_flag: %s", strerror(errno)); goto out; } } ret = cap_set_proc(caps); if (ret) { ERROR("failed to cap_set_proc: %s", strerror(errno)); goto out; } out: cap_free(caps); return 0; } int lxc_ambient_caps_up(void) { int ret; cap_t caps; cap_value_t cap; int last_cap = CAP_LAST_CAP; char *cap_names = NULL; if (!getuid() || geteuid()) return 0; caps = cap_get_proc(); if (!caps) { SYSERROR("Failed to retrieve capabilities"); return -1; } for (cap = 0; cap <= CAP_LAST_CAP; cap++) { cap_flag_value_t flag; ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); if (ret < 0) { if (errno == EINVAL) { last_cap = (cap - 1); INFO("Last supported cap was %d", last_cap); break; } SYSERROR("Failed to retrieve capability flag"); goto out; } ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag); if (ret < 0) { SYSERROR("Failed to set capability flag"); goto out; } } ret = cap_set_proc(caps); if (ret < 0) { SYSERROR("Failed to set capabilities"); goto out; } for (cap = 0; cap <= last_cap; cap++) { ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0); if (ret < 0) { WARN("%s - Failed to raise ambient capability %d", strerror(errno), cap); goto out; } } cap_names = cap_to_text(caps, NULL); if (!cap_names) { SYSWARN("Failed to convert capabilities %d", cap); goto out; } TRACE("Raised %s in inheritable and ambient capability set", cap_names); out: cap_free(cap_names); cap_free(caps); return 0; } int lxc_ambient_caps_down(void) { int ret; cap_t caps; cap_value_t cap; if (!getuid() || geteuid()) return 0; ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); if (ret < 0) { SYSERROR("Failed to clear ambient capability set"); return -1; } caps = cap_get_proc(); if (!caps) { SYSERROR("Failed to retrieve capabilities"); return -1; } for (cap = 0; cap <= CAP_LAST_CAP; cap++) { ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR); if (ret < 0) { SYSERROR("Failed to remove capability from inheritable set"); goto out; } } ret = cap_set_proc(caps); if (ret < 0) { SYSERROR("Failed to set capabilities"); goto out; } out: cap_free(caps); return 0; } int lxc_caps_init(void) { uid_t uid = getuid(); gid_t gid = getgid(); uid_t euid = geteuid(); if (!uid) { INFO("command is run as 'root'"); return 0; } if (uid && !euid) { INFO("command is run as setuid root (uid : %d)", uid); if (prctl(PR_SET_KEEPCAPS, 1)) { ERROR("failed to 'PR_SET_KEEPCAPS': %s", strerror(errno)); return -1; } if (setresgid(gid, gid, gid)) { ERROR("failed to change gid to '%d': %s", gid, strerror(errno)); return -1; } if (setresuid(uid, uid, uid)) { ERROR("failed to change uid to '%d': %s", uid, strerror(errno)); return -1; } if (lxc_caps_up()) { ERROR("failed to restore capabilities: %s", strerror(errno)); return -1; } } if (uid == euid) INFO("command is run as user '%d'", uid); return 0; } static int _real_caps_last_cap(void) { int fd; int result = -1; /* try to get the maximum capability over the kernel * interface introduced in v3.2 */ fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY); if (fd >= 0) { char buf[32]; char *ptr; int n; again: n = read(fd, buf, 31); if (n < 0 && errno == EINTR) { goto again; } else if (n >= 0) { buf[n] = '\0'; errno = 0; result = strtol(buf, &ptr, 10); if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) result = -1; } close(fd); } /* try to get it manually by trying to get the status of * each capability indiviually from the kernel */ if (result < 0) { int cap = 0; while (prctl(PR_CAPBSET_READ, cap) >= 0) cap++; result = cap - 1; } return result; } int lxc_caps_last_cap(void) { static int last_cap = -1; if (last_cap < 0) last_cap = _real_caps_last_cap(); return last_cap; } static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) { int ret; cap_flag_value_t flagval; ret = cap_get_flag(caps, cap, flag, &flagval); if (ret < 0) { ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno)); return false; } return flagval == CAP_SET; } bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) { #if LIBCAP_SUPPORTS_FILE_CAPABILITIES bool cap_is_set; cap_t caps; caps = cap_get_file(path); if (!caps) { /* This is undocumented in the manpage but the source code show * that cap_get_file() may return NULL when successful for the * case where it didn't detect any file capabilities. In this * case errno will be set to ENODATA. */ if (errno != ENODATA) ERROR("Failed to perform cap_get_file(): %s.\n", strerror(errno)); return false; } cap_is_set = lxc_cap_is_set(caps, cap, flag); cap_free(caps); return cap_is_set; #else errno = ENODATA; return false; #endif } bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) { bool cap_is_set; cap_t caps; caps = cap_get_proc(); if (!caps) { ERROR("Failed to perform cap_get_proc(): %s.\n", strerror(errno)); return false; } cap_is_set = lxc_cap_is_set(caps, cap, flag); cap_free(caps); return cap_is_set; } #endif lxc-2.0.11/src/lxc/version.h.in0000644061062106075000000000223413435013473013136 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_VERSION_H #define __LXC_VERSION_H #define LXC_DEVEL @LXC_DEVEL@ #define LXC_VERSION_MAJOR @LXC_VERSION_MAJOR@ #define LXC_VERSION_MINOR @LXC_VERSION_MINOR@ #define LXC_VERSION_MICRO @LXC_VERSION_MICRO@ #define LXC_VERSION_ABI "@LXC_ABI@" #define LXC_VERSION "@LXC_VERSION@" #endif lxc-2.0.11/src/lxc/lxclock.c0000644061062106075000000001553313435013473012504 00000000000000/* liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lxclock.h" #include "utils.h" #include "log.h" #ifdef MUTEX_DEBUGGING #include #endif #define MAX_STACKDEPTH 25 lxc_log_define(lxc_lock, lxc); #ifdef MUTEX_DEBUGGING static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; static inline void dump_stacktrace(void) { void *array[MAX_STACKDEPTH]; size_t size; char **strings; size_t i; size = backtrace(array, MAX_STACKDEPTH); strings = backtrace_symbols(array, size); /* Using fprintf here as our logging module is not thread safe. */ fprintf(stderr, "\tObtained %zu stack frames\n", size); for (i = 0; i < size; i++) fprintf(stderr, "\t\t%s\n", strings[i]); free(strings); } #else static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; static inline void dump_stacktrace(void) {;} #endif static void lock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_lock(l); if (ret != 0) { SYSERROR("Failed to acquire mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static void unlock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_unlock(l); if (ret != 0) { SYSERROR("Failed to release mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static char *lxclock_name(const char *p, const char *n) { int ret; int len; char *dest; char *rundir; /* lockfile will be: * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root * or * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root */ /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */ len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 3; rundir = get_rundir(); if (!rundir) return NULL; len += strlen(rundir); if ((dest = malloc(len)) == NULL) { free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p); if (ret < 0 || ret >= len) { free(dest); free(rundir); return NULL; } ret = mkdir_p(dest, 0755); if (ret < 0) { free(dest); free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n); free(rundir); if (ret < 0 || ret >= len) { free(dest); return NULL; } return dest; } 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) { free(s); return NULL; } return s; } struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) { struct lxc_lock *l; l = malloc(sizeof(*l)); if (!l) goto out; if (!name) { l->type = LXC_LOCK_ANON_SEM; l->u.sem = lxc_new_unnamed_sem(); if (!l->u.sem) { free(l); l = NULL; } goto out; } l->type = LXC_LOCK_FLOCK; l->u.f.fname = lxclock_name(lxcpath, name); if (!l->u.f.fname) { free(l); l = NULL; goto out; } l->u.f.fd = -1; out: return l; } int lxclock(struct lxc_lock *l, int timeout) { int ret = -1, saved_errno = errno; struct flock lk; switch(l->type) { case LXC_LOCK_ANON_SEM: if (!timeout) { ret = sem_wait(l->u.sem); if (ret < 0) saved_errno = errno; } else { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { ret = -2; goto out; } ts.tv_sec += timeout; ret = sem_timedwait(l->u.sem, &ts); if (ret < 0) saved_errno = errno; } break; case LXC_LOCK_FLOCK: ret = -2; if (timeout) { ERROR("Error: timeout not supported with flock"); goto out; } if (!l->u.f.fname) { ERROR("Error: filename not set for flock"); goto out; } if (l->u.f.fd == -1) { l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR); if (l->u.f.fd == -1) { ERROR("Error opening %s", l->u.f.fname); saved_errno = errno; goto out; } } memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX); saved_errno = errno; } break; } out: errno = saved_errno; return ret; } int lxcunlock(struct lxc_lock *l) { int ret = 0, saved_errno = errno; struct flock lk; switch(l->type) { case LXC_LOCK_ANON_SEM: if (!l->u.sem) ret = -2; else { ret = sem_post(l->u.sem); saved_errno = errno; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_UNLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB); saved_errno = errno; } close(l->u.f.fd); l->u.f.fd = -1; } else ret = -2; break; } errno = saved_errno; return ret; } /* * lxc_putlock() is only called when a container_new() fails, * or during container_put(), which is already guaranteed to * only be done by one task. * So the only exclusion we need to provide here is for regular * thread safety (i.e. file descriptor table changes). */ void lxc_putlock(struct lxc_lock *l) { if (!l) return; switch(l->type) { case LXC_LOCK_ANON_SEM: if (l->u.sem) { sem_destroy(l->u.sem); free(l->u.sem); l->u.sem = NULL; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { close(l->u.f.fd); l->u.f.fd = -1; } free(l->u.f.fname); l->u.f.fname = NULL; break; } free(l); } void process_lock(void) { lock_mutex(&thread_mutex); } void process_unlock(void) { unlock_mutex(&thread_mutex); } int container_mem_lock(struct lxc_container *c) { return lxclock(c->privlock, 0); } void container_mem_unlock(struct lxc_container *c) { lxcunlock(c->privlock); } int container_disk_lock(struct lxc_container *c) { int ret; if ((ret = lxclock(c->privlock, 0))) return ret; if ((ret = lxclock(c->slock, 0))) { lxcunlock(c->privlock); return ret; } return 0; } void container_disk_unlock(struct lxc_container *c) { lxcunlock(c->slock); lxcunlock(c->privlock); } lxc-2.0.11/src/lxc/console.c0000644061062106075000000004724213435013473012511 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "caps.h" #include "commands.h" #include "conf.h" #include "console.h" #include "log.h" #include "lxclock.h" #include "mainloop.h" #include "start.h" /* for struct lxc_handler */ #include "utils.h" #if HAVE_PTY_H #include #else #include <../include/openpty.h> #endif #define LXC_CONSOLE_BUFFER_SIZE 1024 lxc_log_define(console, lxc); static struct lxc_list lxc_ttys; typedef void (*sighandler_t)(int); __attribute__((constructor)) void lxc_console_init(void) { lxc_list_init(&lxc_ttys); } void lxc_console_winsz(int srcfd, int dstfd) { int ret; struct winsize wsz; if (!isatty(srcfd)) return; ret = ioctl(srcfd, TIOCGWINSZ, &wsz); if (ret < 0) { WARN("Failed to get window size"); return; } ret = ioctl(dstfd, TIOCSWINSZ, &wsz); if (ret < 0) WARN("Failed to set window size"); else DEBUG("Set window size to %d columns and %d rows", wsz.ws_col, wsz.ws_row); return; } static void lxc_console_winch(struct lxc_tty_state *ts) { lxc_console_winsz(ts->stdinfd, ts->masterfd); if (ts->winch_proxy) lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); } void lxc_console_sigwinch(int sig) { struct lxc_list *it; struct lxc_tty_state *ts; lxc_list_for_each(it, &lxc_ttys) { ts = it->elem; lxc_console_winch(ts); } } int lxc_console_cb_signal_fd(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { ssize_t ret; struct signalfd_siginfo siginfo; struct lxc_tty_state *ts = cbdata; ret = read(fd, &siginfo, sizeof(siginfo)); if (ret < 0 || (size_t)ret < sizeof(siginfo)) { ERROR("Failed to read signal info"); return -1; } if (siginfo.ssi_signo == SIGTERM) { DEBUG("Received SIGTERM. Detaching from the console"); return LXC_MAINLOOP_CLOSE; } if (siginfo.ssi_signo == SIGWINCH) lxc_console_winch(ts); return 0; } struct lxc_tty_state *lxc_console_signal_init(int srcfd, int dstfd) { int ret; bool istty; sigset_t mask; struct lxc_tty_state *ts; ts = malloc(sizeof(*ts)); if (!ts) return NULL; memset(ts, 0, sizeof(*ts)); ts->stdinfd = srcfd; ts->masterfd = dstfd; ts->sigfd = -1; sigemptyset(&mask); istty = isatty(srcfd) == 1; if (!istty) { INFO("fd %d does not refer to a tty device", srcfd); } else { /* Add tty to list to be scanned at SIGWINCH time. */ lxc_list_add_elem(&ts->node, ts); lxc_list_add_tail(&lxc_ttys, &ts->node); sigaddset(&mask, SIGWINCH); } /* Exit the mainloop cleanly on SIGTERM. */ sigaddset(&mask, SIGTERM); ret = sigprocmask(SIG_BLOCK, &mask, &ts->oldmask); if (ret < 0) { WARN("Failed to block signals"); goto on_error; } ts->sigfd = signalfd(-1, &mask, SFD_CLOEXEC); if (ts->sigfd < 0) { WARN("Failed to create signal fd"); sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); goto on_error; } DEBUG("Created signal fd %d", ts->sigfd); return ts; on_error: ERROR("Failed to create signal fd"); if (ts->sigfd >= 0) { close(ts->sigfd); ts->sigfd = -1; } if (istty) lxc_list_del(&ts->node); return ts; } void lxc_console_signal_fini(struct lxc_tty_state *ts) { if (ts->sigfd >= 0) { close(ts->sigfd); if (sigprocmask(SIG_SETMASK, &ts->oldmask, NULL) < 0) WARN("%s - Failed to restore signal mask", strerror(errno)); } if (isatty(ts->stdinfd)) lxc_list_del(&ts->node); free(ts); } int lxc_console_cb_con(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { struct lxc_console *console = (struct lxc_console *)data; char buf[LXC_CONSOLE_BUFFER_SIZE]; int r, w, w_log; w = r = lxc_read_nointr(fd, buf, sizeof(buf)); if (r <= 0) { INFO("Console client on fd %d has exited", fd); lxc_mainloop_del_handler(descr, fd); if (fd == console->master) { console->master = -EBADF; } else if (fd == console->peer) { if (console->tty_state) { lxc_console_signal_fini(console->tty_state); console->tty_state = NULL; } console->peer = -EBADF; } else { ERROR("Handler received unexpected file descriptor"); } close(fd); return LXC_MAINLOOP_CLOSE; } if (fd == console->peer) w = lxc_write_nointr(console->master, buf, r); w_log = 0; if (fd == console->master) { /* write to peer first */ if (console->peer >= 0) w = lxc_write_nointr(console->peer, buf, r); /* write to console log */ if (console->log_fd >= 0) w_log = lxc_write_nointr(console->log_fd, buf, r); } if (w != r) WARN("Console short write r:%d != w:%d", r, w); if (w_log < 0) TRACE("Failed to write %d bytes to console log", r); return 0; } static int lxc_console_mainloop_add_peer(struct lxc_console *console) { int ret; if (console->peer >= 0) { ret = lxc_mainloop_add_handler(console->descr, console->peer, lxc_console_cb_con, console); if (ret < 0) { WARN("Failed to add console peer handler to mainloop"); return -1; } } if (!console->tty_state || console->tty_state->sigfd < 0) return 0; ret = lxc_mainloop_add_handler(console->descr, console->tty_state->sigfd, lxc_console_cb_signal_fd, console->tty_state); if (ret < 0) { WARN("Failed to add signal handler to mainloop"); return -1; } return 0; } int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_console *console) { int ret; if (console->master < 0) { INFO("no console"); return 0; } ret = lxc_mainloop_add_handler(descr, console->master, lxc_console_cb_con, console); if (ret < 0) { ERROR("Failed to add handler for %d to mainloop", console->master); return -1; } /* We cache the descr so that we can add an fd to it when someone * does attach to it in lxc_console_allocate(). */ console->descr = descr; ret = lxc_console_mainloop_add_peer(console); if (ret < 0) return -1; return 0; } int lxc_setup_tios(int fd, struct termios *oldtios) { struct termios newtios; if (!isatty(fd)) { ERROR("'%d' is not a tty", fd); return -1; } /* Get current termios */ if (tcgetattr(fd, oldtios)) { SYSERROR("failed to get current terminal settings"); return -1; } /* ensure we don't end up in an endless loop: * The kernel might fire SIGTTOU while an * ioctl() in tcsetattr() is executed. When the ioctl() * is resumed and retries, the signal handler interrupts it again. */ signal (SIGTTIN, SIG_IGN); signal (SIGTTOU, SIG_IGN); newtios = *oldtios; /* We use the same settings that ssh does. */ newtios.c_iflag |= IGNPAR; newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); #ifdef IUCLC newtios.c_iflag &= ~IUCLC; #endif newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); #ifdef IEXTEN newtios.c_lflag &= ~IEXTEN; #endif newtios.c_oflag &= ~OPOST; newtios.c_cc[VMIN] = 1; newtios.c_cc[VTIME] = 0; /* Set new attributes. */ if (tcsetattr(fd, TCSAFLUSH, &newtios)) { ERROR("failed to set new terminal settings"); return -1; } return 0; } static void lxc_console_peer_proxy_free(struct lxc_console *console) { if (console->tty_state) { lxc_console_signal_fini(console->tty_state); console->tty_state = NULL; } close(console->peerpty.master); close(console->peerpty.slave); console->peerpty.master = -1; console->peerpty.slave = -1; console->peerpty.busy = -1; console->peerpty.name[0] = '\0'; console->peer = -1; } static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd) { struct termios oldtermio; struct lxc_tty_state *ts; int ret; if (console->master < 0) { ERROR("console not set up"); return -1; } if (console->peerpty.busy != -1 || console->peer != -1) { NOTICE("console already in use"); return -1; } if (console->tty_state) { ERROR("console already has tty_state"); return -1; } /* this is the proxy pty that will be given to the client, and that * the real pty master will send to / recv from */ ret = openpty(&console->peerpty.master, &console->peerpty.slave, console->peerpty.name, NULL, NULL); if (ret) { SYSERROR("failed to create proxy pty"); return -1; } if (lxc_setup_tios(console->peerpty.slave, &oldtermio) < 0) goto err1; ts = lxc_console_signal_init(console->peerpty.master, console->master); if (!ts) goto err1; console->tty_state = ts; console->peer = console->peerpty.slave; console->peerpty.busy = sockfd; ret = lxc_console_mainloop_add_peer(console); if (ret < 0) goto err1; DEBUG("%d %s peermaster:%d sockfd:%d", lxc_raw_getpid(), __FUNCTION__, console->peerpty.master, sockfd); return 0; err1: lxc_console_peer_proxy_free(console); return -1; } int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) { int masterfd = -1, ttynum; struct lxc_tty_info *tty_info = &conf->tty_info; struct lxc_console *console = &conf->console; if (*ttyreq == 0) { if (lxc_console_peer_proxy_alloc(console, sockfd) < 0) goto out; masterfd = console->peerpty.master; goto out; } if (*ttyreq > 0) { if (*ttyreq > tty_info->nbtty) goto out; if (tty_info->pty_info[*ttyreq - 1].busy) goto out; /* the requested tty is available */ ttynum = *ttyreq; goto out_tty; } /* search for next available tty, fixup index tty1 => [0] */ for (ttynum = 1; ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; ttynum++) ; /* we didn't find any available slot for tty */ if (ttynum > tty_info->nbtty) goto out; *ttyreq = ttynum; out_tty: tty_info->pty_info[ttynum - 1].busy = sockfd; masterfd = tty_info->pty_info[ttynum - 1].master; out: return masterfd; } void lxc_console_free(struct lxc_conf *conf, int fd) { int i; struct lxc_tty_info *tty_info = &conf->tty_info; struct lxc_console *console = &conf->console; for (i = 0; i < tty_info->nbtty; i++) { if (tty_info->pty_info[i].busy == fd) tty_info->pty_info[i].busy = 0; } if (console->peerpty.busy == fd) { lxc_mainloop_del_handler(console->descr, console->peerpty.slave); lxc_console_peer_proxy_free(console); } } static int lxc_console_peer_default(struct lxc_console *console) { struct lxc_tty_state *ts; const char *path = console->path; int fd; int ret = 0; /* If no console was given, try current controlling terminal, there * won't be one if we were started as a daemon (-d). */ if (!path && !access("/dev/tty", F_OK)) { fd = open("/dev/tty", O_RDWR); if (fd >= 0) { close(fd); path = "/dev/tty"; } } if (!path) { errno = ENOTTY; DEBUG("process does not have a controlling terminal"); goto out; } console->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC)); if (console->peer < 0) { ERROR("Failed to open \"%s\": %s", path, strerror(errno)); return -ENOTTY; } DEBUG("using \"%s\" as peer tty device", path); if (!isatty(console->peer)) { ERROR("file descriptor for file \"%s\" does not refer to a tty device", path); goto on_error1; } ts = lxc_console_signal_init(console->peer, console->master); console->tty_state = ts; if (!ts) { WARN("Failed to install signal handler"); goto on_error1; } lxc_console_winsz(console->peer, console->master); console->tios = malloc(sizeof(*console->tios)); if (!console->tios) { SYSERROR("failed to allocate memory"); goto on_error1; } if (lxc_setup_tios(console->peer, console->tios) < 0) goto on_error2; else goto out; on_error2: free(console->tios); console->tios = NULL; on_error1: close(console->peer); console->peer = -1; ret = -ENOTTY; out: return ret; } void lxc_console_delete(struct lxc_console *console) { int ret; if (console->tios && console->peer >= 0) { ret = tcsetattr(console->peer, TCSAFLUSH, console->tios); if (ret < 0) WARN("%s - Failed to set old terminal settings", strerror(errno)); } free(console->tios); console->tios = NULL; if (console->peer >= 0) close(console->peer); console->peer = -1; if (console->master >= 0) close(console->master); console->master = -1; if (console->slave >= 0) close(console->slave); console->slave = -1; if (console->log_fd >= 0) close(console->log_fd); console->log_fd = -1; } /** * This is the console log file. Please note that the console log file is * (implementation wise not content wise) independent of the console ringbuffer. */ int lxc_console_create_log_file(struct lxc_console *console) { if (!console->log_path) return 0; console->log_fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600)); if (console->log_fd < 0) { SYSERROR("Failed to open console log file \"%s\"", console->log_path); return -1; } DEBUG("Using \"%s\" as console log file", console->log_path); return 0; } int lxc_pty_create(struct lxc_console *console) { int ret, saved_errno; ret = openpty(&console->master, &console->slave, console->name, NULL, NULL); saved_errno = errno; if (ret < 0) { ERROR("%s - Failed to allocate a pty", strerror(saved_errno)); return -1; } ret = fcntl(console->master, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on console master"); goto err; } ret = fcntl(console->slave, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on console slave"); goto err; } ret = lxc_console_peer_default(console); if (ret < 0) { ERROR("Failed to allocate a peer pty device"); goto err; } return 0; err: lxc_console_delete(console); return -ENODEV; } int lxc_console_create(struct lxc_conf *conf) { int ret; struct lxc_console *console = &conf->console; if (console->path && !strcmp(console->path, "none")) { INFO("No console was requested"); return 0; } ret = lxc_pty_create(console); if (ret < 0) return -1; /* create console log file */ ret = lxc_console_create_log_file(console); if (ret < 0) goto err; return 0; err: lxc_console_delete(console); return -ENODEV; } int lxc_console_set_stdfds(int fd) { if (fd < 0) return 0; if (isatty(STDIN_FILENO)) if (dup2(fd, STDIN_FILENO) < 0) { SYSERROR("failed to duplicate stdin."); return -1; } if (isatty(STDOUT_FILENO)) if (dup2(fd, STDOUT_FILENO) < 0) { SYSERROR("failed to duplicate stdout."); return -1; } if (isatty(STDERR_FILENO)) if (dup2(fd, STDERR_FILENO) < 0) { SYSERROR("failed to duplicate stderr."); return -1; } return 0; } int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { struct lxc_tty_state *ts = cbdata; char c; if (fd != ts->stdinfd) return LXC_MAINLOOP_CLOSE; if (lxc_read_nointr(ts->stdinfd, &c, 1) <= 0) return LXC_MAINLOOP_CLOSE; if (ts->escape >= 1) { /* we want to exit the console with Ctrl+a q */ if (c == ts->escape && !ts->saw_escape) { ts->saw_escape = 1; return 0; } if (c == 'q' && ts->saw_escape) return LXC_MAINLOOP_CLOSE; ts->saw_escape = 0; } if (lxc_write_nointr(ts->masterfd, &c, 1) <= 0) return LXC_MAINLOOP_CLOSE; return 0; } int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { struct lxc_tty_state *ts = cbdata; char buf[LXC_CONSOLE_BUFFER_SIZE]; int r, w; if (fd != ts->masterfd) return LXC_MAINLOOP_CLOSE; r = lxc_read_nointr(fd, buf, sizeof(buf)); if (r <= 0) return LXC_MAINLOOP_CLOSE; w = lxc_write_nointr(ts->stdoutfd, buf, r); if (w <= 0) { return LXC_MAINLOOP_CLOSE; } else if (w != r) { SYSERROR("Failed to write"); return 1; } return 0; } int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path); } int lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { int ret, ttyfd, masterfd; struct lxc_epoll_descr descr; struct termios oldtios; struct lxc_tty_state *ts; int istty = 0; ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); if (ttyfd < 0) return -1; ret = setsid(); if (ret < 0) TRACE("Process is already group leader"); ts = lxc_console_signal_init(stdinfd, masterfd); if (!ts) { ret = -1; goto close_fds; } ts->escape = escape; ts->winch_proxy = c->name; ts->winch_proxy_lxcpath = c->config_path; ts->stdoutfd = stdoutfd; istty = isatty(stdinfd); if (istty) { lxc_console_winsz(stdinfd, masterfd); lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); } else { INFO("File descriptor %d does not refer to a tty device", stdinfd); } ret = lxc_mainloop_open(&descr); if (ret) { ERROR("Failed to create mainloop"); goto sigwinch_fini; } if (ts->sigfd != -1) { ret = lxc_mainloop_add_handler(&descr, ts->sigfd, lxc_console_cb_signal_fd, ts); if (ret < 0) { ERROR("Failed to add signal handler to mainloop"); goto close_mainloop; } } ret = lxc_mainloop_add_handler(&descr, ts->stdinfd, lxc_console_cb_tty_stdin, ts); if (ret < 0) { ERROR("Failed to add stdin handler"); goto close_mainloop; } ret = lxc_mainloop_add_handler(&descr, ts->masterfd, lxc_console_cb_tty_master, ts); if (ret < 0) { ERROR("Failed to add master handler"); goto close_mainloop; } if (ts->escape >= 1) { fprintf(stderr, "\n" "Connected to tty %1$d\n" "Type to exit the console, " " to enter Ctrl+%2$c itself\n", ttynum, 'a' + escape - 1); } if (istty) { ret = lxc_setup_tios(stdinfd, &oldtios); if (ret < 0) goto close_mainloop; } ret = lxc_mainloop(&descr, -1); if (ret < 0) { ERROR("The mainloop returned an error"); goto restore_tios; } ret = 0; restore_tios: if (istty) { istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios); if (istty < 0) WARN("%s - Failed to restore terminal properties", strerror(errno)); } close_mainloop: lxc_mainloop_close(&descr); sigwinch_fini: lxc_console_signal_fini(ts); close_fds: close(masterfd); close(ttyfd); return ret; } int lxc_make_controlling_pty(int fd) { int ret; setsid(); ret = ioctl(fd, TIOCSCTTY, (char *)NULL); if (ret < 0) return -1; return 0; } int lxc_login_pty(int fd) { int ret; ret = lxc_make_controlling_pty(fd); if (ret < 0) return -1; ret = lxc_console_set_stdfds(fd); if (ret < 0) return -1; if (fd > STDERR_FILENO) close(fd); return 0; } void lxc_pty_info_init(struct lxc_pty_info *pty) { pty->name[0] = '\0'; pty->master = -EBADF; pty->slave = -EBADF; pty->busy = -1; } void lxc_pty_init(struct lxc_console *pty) { memset(pty, 0, sizeof(*pty)); pty->slave = -EBADF; pty->master = -EBADF; pty->peer = -EBADF; pty->log_fd = -EBADF; lxc_pty_info_init(&pty->peerpty); } void lxc_pty_conf_free(struct lxc_console *console) { free(console->log_path); free(console->path); } int lxc_pty_map_ids(struct lxc_conf *c, struct lxc_console *pty) { int ret; if (lxc_list_empty(&c->id_map)) return 0; ret = strcmp(pty->name, ""); if (ret == 0) return 0; ret = chown_mapped_root(pty->name, c); if (ret < 0) { ERROR("Failed to chown \"%s\"", pty->name); return -1; } TRACE("Chowned \"%s\"", pty->name); return 0; } lxc-2.0.11/src/lxc/commands_utils.h0000644061062106075000000000652213435013473014071 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 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. */ #ifndef __LXC_COMMANDS_UTILS_H #define __LXC_COMMANDS_UTILS_H #include #include "state.h" #include "commands.h" int lxc_make_abstract_socket_name(char *path, size_t pathlen, const char *lxcname, const char *lxcpath, const char *hashed_sock_name, const char *suffix); /* lxc_cmd_sock_get_state Register a new state client fd in the container's * in-memory handler and retrieve the requested * states. * * @param[in] name Name of container to connect to. * @param[in] lxcpath The lxcpath in which the container is running. * @param[in] states The states to wait for. * @return Return < 0 on error * < MAX_STATE current container state */ extern int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int timeout); /* lxc_cmd_sock_rcv_state Retrieve the requested state from a state client * fd registerd in the container's in-memory * handler. * * @param[int] state_client_fd The state client fd from which the state can be * received. * @return Return < 0 on error * < MAX_STATE current container state */ extern int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout); /* lxc_add_state_client Add a new state client to the container's * in-memory handler. * * @param[int] state_client_fd The state client fd to add. * @param[int] handler The container's in-memory handler. * @param[in] states The states to wait for. * * @return Return < 0 on error * 0 on success */ extern int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, lxc_state_t states[MAX_STATE]); /* lxc_cmd_connect Connect to the container's command socket. * * @param[in] name Name of container to connect to. * @param[in] lxcpath The lxcpath in which the container is running. * @param[in] hashed_sock_name The hashed name of the socket (optional). Can be * NULL. * * @return Return < 0 on error * >= 0 client fd */ extern int lxc_cmd_connect(const char *name, const char *lxcpath, const char *hashed_sock_name, const char *suffix); #endif /* __LXC_COMMANDS_UTILS_H */ lxc-2.0.11/src/lxc/attach.h0000644061062106075000000000266313435013473012316 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ATTACH_H #define __LXC_ATTACH_H #include #include #include #include "namespace.h" struct lxc_conf; struct lxc_proc_context_info { char *lsm_label; struct lxc_container *container; signed long personality; unsigned long long capability_mask; int ns_inherited; int ns_fd[LXC_NS_MAX]; }; extern int lxc_attach(const char *name, const char *lxcpath, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process); #endif /* __LXC_ATTACH_H */ lxc-2.0.11/src/lxc/confile_utils.c0000644061062106075000000001727613435013473013712 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 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 "config.h" #include #include #include #include "conf.h" #include "confile.h" #include "confile_utils.h" #include "error.h" #include "log.h" #include "list.h" #include "network.h" #include "parse.h" #include "utils.h" lxc_log_define(lxc_confile_utils, lxc); int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, unsigned long *hostid, unsigned long *range) { int ret = -1; unsigned long tmp_hostid, tmp_nsid, tmp_range; char tmp_type; char *window, *slide; char *dup = NULL; /* Duplicate string. */ dup = strdup(idmap); if (!dup) goto on_error; /* A prototypical idmap entry would be: "u 1000 1000000 65536" */ /* align */ slide = window = dup; /* skip whitespace */ slide += strspn(slide, " \t\r"); if (slide != window && *slide == '\0') goto on_error; /* Validate type. */ if (*slide != 'u' && *slide != 'g') { ERROR("Invalid id mapping type: %c", *slide); goto on_error; } /* Assign type. */ tmp_type = *slide; /* move beyond type */ slide++; /* align */ window = slide; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* There must be whitespace. */ if (slide == window) goto on_error; /* Mark beginning of nsid. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window || *slide == '\0') goto on_error; /* Mark end of nsid. */ *slide = '\0'; /* Parse nsid. */ if (lxc_safe_ulong(window, &tmp_nsid) < 0) { ERROR("Failed to parse nsid: %s", window); goto on_error; } /* Move beyond \0. */ slide++; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* If there was only one whitespace then we whiped it with our \0 above. * So only ensure that we're not at the end of the string. */ if (*slide == '\0') goto on_error; /* Mark beginning of hostid. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window || *slide == '\0') goto on_error; /* Mark end of nsid. */ *slide = '\0'; /* Parse hostid. */ if (lxc_safe_ulong(window, &tmp_hostid) < 0) { ERROR("Failed to parse hostid: %s", window); goto on_error; } /* Move beyond \0. */ slide++; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* If there was only one whitespace then we whiped it with our \0 above. * So only ensure that we're not at the end of the string. */ if (*slide == '\0') goto on_error; /* Mark beginning of range. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window) goto on_error; /* The range is the last valid entry we expect. So make sure that there * is no trailing garbage and if there is, error out. */ if (*(slide + strspn(slide, " \t\r\n")) != '\0') goto on_error; /* Mark end of range. */ *slide = '\0'; /* Parse range. */ if (lxc_safe_ulong(window, &tmp_range) < 0) { ERROR("Failed to parse id mapping range: %s", window); goto on_error; } *type = tmp_type; *nsid = tmp_nsid; *hostid = tmp_hostid; *range = tmp_range; /* Yay, we survived. */ ret = 0; on_error: free(dup); return ret; } bool lxc_config_value_empty(const char *value) { if (value && strlen(value) > 0) return false; return true; } struct lxc_netdev *lxc_find_netdev_by_idx(struct lxc_conf *conf, unsigned int idx) { struct lxc_netdev *netdev = NULL; struct lxc_list *networks = &conf->network; struct lxc_list *insert = networks; /* lookup network */ if (lxc_list_empty(networks)) return NULL; lxc_list_for_each(insert, networks) { netdev = insert->elem; if (netdev->idx >= idx) break; } /* network already exists */ if (netdev->idx == idx) return netdev; return NULL; } /* Takes care of finding the correct netdev struct in the networks list or * allocates a new one if it couldn't be found. */ struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf, unsigned int idx) { struct lxc_list *newlist; struct lxc_netdev *netdev = NULL; struct lxc_list *networks = &conf->network; struct lxc_list *insert = networks; /* lookup network */ netdev = lxc_find_netdev_by_idx(conf, idx); if (netdev) return netdev; /* network does not exist */ netdev = malloc(sizeof(*netdev)); if (!netdev) return NULL; memset(netdev, 0, sizeof(*netdev)); lxc_list_init(&netdev->ipv4); lxc_list_init(&netdev->ipv6); /* give network a unique index */ netdev->idx = idx; /* prepare new list */ newlist = malloc(sizeof(*newlist)); if (!newlist) { free(netdev); return NULL; } lxc_list_init(newlist); newlist->elem = netdev; /* Insert will now point to the correct position to insert the new * netdev. */ lxc_list_add_tail(insert, newlist); return netdev; } void lxc_log_configured_netdevs(const struct lxc_conf *conf) { struct lxc_netdev *netdev; struct lxc_list *it = (struct lxc_list *)&conf->network;; if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) && (lxc_log_get_level() != LXC_LOG_LEVEL_TRACE)) return; if (lxc_list_empty(it)) { TRACE("container has no networks configured"); return; } lxc_list_for_each(it, &conf->network) { netdev = it->elem; TRACE("index: %zd", netdev->idx); TRACE("ifindex: %d", netdev->ifindex); switch (netdev->type) { case LXC_NET_VETH: TRACE("type: veth"); if (netdev->priv.veth_attr.pair[0] != '\0') TRACE("veth pair: %s", netdev->priv.veth_attr.pair); if (netdev->priv.veth_attr.veth1[0] != '\0') TRACE("veth1 : %s", netdev->priv.veth_attr.veth1); if (netdev->priv.veth_attr.ifindex > 0) TRACE("host side ifindex for veth device: %d", netdev->priv.veth_attr.ifindex); break; case LXC_NET_MACVLAN: TRACE("type: macvlan"); break; case LXC_NET_VLAN: TRACE("type: vlan"); break; case LXC_NET_PHYS: TRACE("type: phys"); if (netdev->priv.phys_attr.ifindex > 0) { TRACE("host side ifindex for phys device: %d", netdev->priv.phys_attr.ifindex); } break; case LXC_NET_EMPTY: TRACE("type: empty"); break; case LXC_NET_NONE: TRACE("type: none"); break; default: ERROR("invalid network type %d", netdev->type); return; } TRACE("flags: %s", netdev->flags == IFF_UP ? "up" : "none"); if (netdev->link[0] != '\0') TRACE("link: %s", netdev->link); if (netdev->name[0] != '\0') TRACE("name: %s", netdev->name); if (netdev->hwaddr) TRACE("hwaddr: %s", netdev->hwaddr); if (netdev->mtu) TRACE("mtu: %s", netdev->mtu); if (netdev->upscript) TRACE("upscript: %s", netdev->upscript); if (netdev->downscript) TRACE("downscript: %s", netdev->downscript); } } int network_ifname(char *valuep, const char *value) { if (strlen(value) >= IFNAMSIZ) { ERROR("Network devie name \"%s\" is too long (>= %zu)", value, (size_t)IFNAMSIZ); } strcpy(valuep, value); return 0; } lxc-2.0.11/src/lxc/namespace.h0000644061062106075000000001301713435013473013001 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_NAMESPACE_H #define __LXC_NAMESPACE_H #include #include #include #include "config.h" #ifndef CLONE_PARENT_SETTID #define CLONE_PARENT_SETTID 0x00100000 #endif #ifndef CLONE_CHILD_CLEARTID #define CLONE_CHILD_CLEARTID 0x00200000 #endif #ifndef CLONE_CHILD_SETTID #define CLONE_CHILD_SETTID 0x01000000 #endif #ifndef CLONE_VFORK #define CLONE_VFORK 0x00004000 #endif #ifndef CLONE_THREAD #define CLONE_THREAD 0x00010000 #endif #ifndef CLONE_SETTLS #define CLONE_SETTLS 0x00080000 #endif #ifndef CLONE_VM #define CLONE_VM 0x00000100 #endif #ifndef CLONE_FILES #define CLONE_FILES 0x00000400 #endif #ifndef CLONE_FS # define CLONE_FS 0x00000200 #endif #ifndef CLONE_NEWNS # define CLONE_NEWNS 0x00020000 #endif #ifndef CLONE_NEWCGROUP # define CLONE_NEWCGROUP 0x02000000 #endif #ifndef CLONE_NEWUTS # define CLONE_NEWUTS 0x04000000 #endif #ifndef CLONE_NEWIPC # define CLONE_NEWIPC 0x08000000 #endif #ifndef CLONE_NEWUSER # define CLONE_NEWUSER 0x10000000 #endif #ifndef CLONE_NEWPID # define CLONE_NEWPID 0x20000000 #endif #ifndef CLONE_NEWNET # define CLONE_NEWNET 0x40000000 #endif enum { LXC_NS_USER, LXC_NS_MNT, LXC_NS_PID, LXC_NS_UTS, LXC_NS_IPC, LXC_NS_NET, LXC_NS_CGROUP, LXC_NS_MAX }; extern const struct ns_info { const char *proc_name; int clone_flag; const char *flag_name; } ns_info[LXC_NS_MAX]; #if defined(__ia64__) int __clone2(int (*__fn) (void *__arg), void *__child_stack_base, size_t __child_stack_size, int __flags, void *__arg, ...); #else int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ ); #endif /** * lxc_clone() - create a new process * * - allocate stack: * This function allocates a new stack the size of page and passes it to the * kernel. * * - support all CLONE_*flags: * This function supports all CLONE_* flags. If in doubt or not sufficiently * familiar with process creation in the kernel and interactions with libcs * this function should be used. * * - pthread_atfork() handlers depending on libc: * Whether this function runs pthread_atfork() handlers depends on the * corresponding libc wrapper. glibc currently does not run pthread_atfork() * handlers but does not guarantee that they are not. Other libcs might or * might not run pthread_atfork() handlers. If you require guarantees please * refer to the lxc_raw_clone*() functions below. * * - should call lxc_raw_getpid(): * The child should use lxc_raw_getpid() to retrieve its pid. */ extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags); /** * lxc_raw_clone() - create a new process * * - fork() behavior: * This function returns 0 in the child and > 0 in the parent. * * - copy-on-write: * This function does not allocate a new stack and relies on copy-on-write * semantics. * * - supports subset of ClONE_* flags: * lxc_raw_clone() intentionally only supports a subset of the flags available * to the actual system call. Please refer to the implementation what flags * cannot be used. Also, please don't assume that just because a flag isn't * explicitly checked for as being unsupported that it is supported. If in * doubt or not sufficiently familiar with process creation in the kernel and * interactions with libcs this function should be used. * * - no pthread_atfork() handlers: * This function circumvents - as much as this this is possible - any libc * wrappers and thus does not run any pthread_atfork() handlers. Make sure * that this is safe to do in the context you are trying to call this * function. * * - must call lxc_raw_getpid(): * The child must use lxc_raw_getpid() to retrieve its pid. */ extern pid_t lxc_raw_clone(unsigned long flags); /** * lxc_raw_clone_cb() - create a new process * * - non-fork() behavior: * Function does return pid of the child or -1 on error. Pass in a callback * function via the "fn" argument that gets executed in the child process. The * "args" argument is passed to "fn". * * All other comments that apply to lxc_raw_clone() apply to lxc_raw_clone_cb() * as well. */ extern pid_t lxc_raw_clone_cb(int (*fn)(void *), void *args, unsigned long flags); extern int lxc_namespace_2_cloneflag(char *namespace); extern int lxc_fill_namespace_flags(char *flaglist, int *flags); /** * Because of older glibc's pid cache (up to 2.25) whenever clone() is called * the child must must retrieve it's own pid via lxc_raw_getpid(). */ static inline pid_t lxc_raw_getpid(void) { return (pid_t) syscall(SYS_getpid); } #endif lxc-2.0.11/src/lxc/storage/0000755061062106075000000000000013435013523012412 500000000000000lxc-2.0.11/src/lxc/storage/storage.c0000644061062106075000000003470113435013473014153 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aufs.h" #include "btrfs.h" #include "conf.h" #include "config.h" #include "dir.h" #include "error.h" #include "log.h" #include "loop.h" #include "lvm.h" #include "lxc.h" #include "lxclock.h" #include "nbd.h" #include "namespace.h" #include "overlay.h" #include "parse.h" #include "rbd.h" #include "rsync.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" #include "zfs.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12, 114, size_t) #endif lxc_log_define(storage, lxc); /* aufs */ static const struct lxc_storage_ops aufs_ops = { .detect = &aufs_detect, .mount = &aufs_mount, .umount = &aufs_umount, .clone_paths = &aufs_clonepaths, .destroy = &aufs_destroy, .create = &aufs_create, .can_snapshot = true, .can_backup = true, }; /* btrfs */ static const struct lxc_storage_ops btrfs_ops = { .detect = &btrfs_detect, .mount = &btrfs_mount, .umount = &btrfs_umount, .clone_paths = &btrfs_clonepaths, .destroy = &btrfs_destroy, .create = &btrfs_create, .can_snapshot = true, .can_backup = true, }; /* dir */ static const struct lxc_storage_ops dir_ops = { .detect = &dir_detect, .mount = &dir_mount, .umount = &dir_umount, .clone_paths = &dir_clonepaths, .destroy = &dir_destroy, .create = &dir_create, .can_snapshot = false, .can_backup = true, }; /* loop */ static const struct lxc_storage_ops loop_ops = { .detect = &loop_detect, .mount = &loop_mount, .umount = &loop_umount, .clone_paths = &loop_clonepaths, .destroy = &loop_destroy, .create = &loop_create, .can_snapshot = false, .can_backup = true, }; /* lvm */ static const struct lxc_storage_ops lvm_ops = { .detect = &lvm_detect, .mount = &lvm_mount, .umount = &lvm_umount, .clone_paths = &lvm_clonepaths, .destroy = &lvm_destroy, .create = &lvm_create, .can_snapshot = true, .can_backup = false, }; /* nbd */ const struct lxc_storage_ops nbd_ops = { .detect = &nbd_detect, .mount = &nbd_mount, .umount = &nbd_umount, .clone_paths = &nbd_clonepaths, .destroy = &nbd_destroy, .create = &nbd_create, .can_snapshot = true, .can_backup = false, }; /* overlay */ static const struct lxc_storage_ops ovl_ops = { .detect = &ovl_detect, .mount = &ovl_mount, .umount = &ovl_umount, .clone_paths = &ovl_clonepaths, .destroy = &ovl_destroy, .create = &ovl_create, .can_snapshot = true, .can_backup = true, }; /* rbd */ static const struct lxc_storage_ops rbd_ops = { .detect = &rbd_detect, .mount = &rbd_mount, .umount = &rbd_umount, .clone_paths = &rbd_clonepaths, .destroy = &rbd_destroy, .create = &rbd_create, .can_snapshot = false, .can_backup = false, }; /* zfs */ static const struct lxc_storage_ops zfs_ops = { .detect = &zfs_detect, .mount = &zfs_mount, .umount = &zfs_umount, .clone_paths = &zfs_clonepaths, .destroy = &zfs_destroy, .create = &zfs_create, .can_snapshot = true, .can_backup = true, }; struct lxc_storage_type { const char *name; const struct lxc_storage_ops *ops; }; static const struct lxc_storage_type bdevs[] = { { .name = "zfs", .ops = &zfs_ops, }, { .name = "lvm", .ops = &lvm_ops, }, { .name = "rbd", .ops = &rbd_ops, }, { .name = "btrfs", .ops = &btrfs_ops, }, { .name = "dir", .ops = &dir_ops, }, { .name = "aufs", .ops = &aufs_ops, }, { .name = "overlayfs", .ops = &ovl_ops, }, { .name = "loop", .ops = &loop_ops, }, { .name = "nbd", .ops = &nbd_ops, }, }; static const size_t numbdevs = sizeof(bdevs) / sizeof(struct lxc_storage_type); static const struct lxc_storage_type *get_storage_by_name(const char *name) { size_t i, cmplen; cmplen = strcspn(name, ":"); if (cmplen == 0) return NULL; for (i = 0; i < numbdevs; i++) if (strncmp(bdevs[i].name, name, cmplen) == 0) break; if (i == numbdevs) return NULL; DEBUG("Detected rootfs type \"%s\"", bdevs[i].name); return &bdevs[i]; } const struct lxc_storage_type *storage_query(struct lxc_conf *conf, const char *src) { size_t i; const struct lxc_storage_type *bdev; bdev = get_storage_by_name(src); if (bdev) return bdev; for (i = 0; i < numbdevs; i++) if (bdevs[i].ops->detect(src)) break; if (i == numbdevs) return NULL; DEBUG("Detected rootfs type \"%s\"", bdevs[i].name); return &bdevs[i]; } struct lxc_storage *storage_get(const char *type) { size_t i; struct lxc_storage *bdev; for (i = 0; i < numbdevs; i++) { if (strcmp(bdevs[i].name, type) == 0) break; } if (i == numbdevs) return NULL; bdev = malloc(sizeof(struct lxc_storage)); if (!bdev) return NULL; memset(bdev, 0, sizeof(struct lxc_storage)); bdev->ops = bdevs[i].ops; bdev->type = bdevs[i].name; return bdev; } static struct lxc_storage *do_storage_create(const char *dest, const char *type, const char *cname, struct bdev_specs *specs) { struct lxc_storage *bdev; if (!type) type = "dir"; bdev = storage_get(type); if (!bdev) return NULL; if (bdev->ops->create(bdev, dest, cname, specs) < 0) { storage_put(bdev); return NULL; } return bdev; } bool storage_can_backup(struct lxc_conf *conf) { struct lxc_storage *bdev = storage_init(conf, NULL, NULL, NULL); bool ret; if (!bdev) return false; ret = bdev->ops->can_backup; storage_put(bdev); return ret; } /* If we're not snaphotting, then storage_copy becomes a simple case of mount * the original, mount the new, and rsync the contents. */ struct lxc_storage *storage_copy(struct lxc_container *c0, const char *cname, const char *lxcpath, const char *bdevtype, int flags, const char *bdevdata, uint64_t newsize, int *needs_rdep) { struct lxc_storage *orig, *new; pid_t pid; int ret; bool snap = flags & LXC_CLONE_SNAPSHOT; bool maybe_snap = flags & LXC_CLONE_MAYBE_SNAPSHOT; bool keepbdevtype = flags & LXC_CLONE_KEEPBDEVTYPE; const char *src = c0->lxc_conf->rootfs.path; const char *oldname = c0->name; const char *oldpath = c0->config_path; struct rsync_data data; /* If the container name doesn't show up in the rootfs path, then we * don't know how to come up with a new name. */ if (!src) { ERROR("No rootfs specified"); return NULL; } if (strstr(src, oldname) == NULL) { ERROR( "original rootfs path %s doesn't include container name %s", src, oldname); return NULL; } orig = storage_init(c0->lxc_conf, src, NULL, NULL); if (!orig) { ERROR("failed to detect blockdev type for %s", src); return NULL; } if (!orig->dest) { int ret; size_t len; struct stat sb; len = strlen(oldpath) + strlen(oldname) + strlen("/rootfs") + 2; orig->dest = malloc(len); if (!orig->dest) { ERROR("Failed to allocate memory"); storage_put(orig); return NULL; } ret = snprintf(orig->dest, len, "%s/%s/rootfs", oldpath, oldname); if (ret < 0 || (size_t)ret >= len) { ERROR("Failed to create string"); storage_put(orig); return NULL; } ret = stat(orig->dest, &sb); if (ret < 0 && errno == ENOENT) if (mkdir_p(orig->dest, 0755) < 0) WARN("Error creating '%s', continuing.", orig->dest); } /* * special case for snapshot - if caller requested maybe_snapshot and * keepbdevtype and backing store is directory, then proceed with a copy * clone rather than returning error */ if (maybe_snap && keepbdevtype && !bdevtype && !orig->ops->can_snapshot) snap = false; /* If newtype is NULL and snapshot is set, then use overlayfs. */ if (!bdevtype && !keepbdevtype && snap && (!strcmp(orig->type, "dir") || !strcmp(orig->type, "overlayfs"))) bdevtype = "overlayfs"; if (am_guest_unpriv() && !unpriv_snap_allowed(orig, bdevtype, snap, maybe_snap)) { ERROR("Unsupported snapshot type \"%s\" for unprivileged users", bdevtype ? bdevtype : "(null)"); storage_put(orig); return NULL; } *needs_rdep = 0; if (bdevtype && strcmp(orig->type, "dir") == 0 && (strcmp(bdevtype, "aufs") == 0 || strcmp(bdevtype, "overlayfs") == 0)) { *needs_rdep = 1; } else if (snap && strcmp(orig->type, "lvm") == 0 && !lvm_is_thin_volume(orig->src)) { *needs_rdep = 1; } if (strcmp(oldpath, lxcpath) && !bdevtype && strcmp(orig->type, "overlayfs")) bdevtype = "dir"; else if (!bdevtype) bdevtype = orig->type; /* get new bdev type */ new = storage_get(bdevtype); if (!new) { ERROR("no such block device type: %s", bdevtype ? bdevtype : orig->type); storage_put(orig); return NULL; } if (new->ops->clone_paths(orig, new, oldname, cname, oldpath, lxcpath, snap, newsize, c0->lxc_conf) < 0) { ERROR("failed getting pathnames for cloned storage: %s", src); goto err; } if (am_guest_unpriv() && chown_mapped_root(new->src, c0->lxc_conf) < 0) WARN("Failed to update ownership of %s", new->dest); if (snap) return new; /* * https://github.com/lxc/lxc/issues/131 * Use btrfs snapshot feature instead of rsync to restore if both orig * and new are btrfs */ if (bdevtype && strcmp(orig->type, "btrfs") == 0 && strcmp(new->type, "btrfs") == 0 && btrfs_same_fs(orig->dest, new->dest) == 0) { if (btrfs_destroy(new) < 0) { ERROR("Error destroying %s subvolume", new->dest); goto err; } if (mkdir_p(new->dest, 0755) < 0) { ERROR("Error creating %s directory", new->dest); goto err; } if (btrfs_snapshot(orig->dest, new->dest) < 0) { ERROR("Error restoring %s to %s", orig->dest, new->dest); goto err; } storage_put(orig); return new; } pid = fork(); if (pid < 0) { SYSERROR("fork"); goto err; } if (pid > 0) { int ret = wait_for_pid(pid); storage_put(orig); if (ret < 0) { storage_put(new); return NULL; } return new; } data.orig = orig; data.new = new; if (am_guest_unpriv()) ret = userns_exec_full(c0->lxc_conf, rsync_rootfs_wrapper, &data, "rsync_rootfs_wrapper"); else ret = rsync_rootfs(&data); exit(ret == 0 ? 0 : 1); err: storage_put(orig); storage_put(new); return NULL; } /* Create a backing store for a container. * If successful, return a struct bdev *, with the bdev mounted and ready * for use. Before completing, the caller will need to call the * umount operation and storage_put(). * @dest: the mountpoint (i.e. /var/lib/lxc/$name/rootfs) * @type: the bdevtype (dir, btrfs, zfs, rbd, etc) * @cname: the container name * @specs: details about the backing store to create, like fstype */ struct lxc_storage *storage_create(const char *dest, const char *type, const char *cname, struct bdev_specs *specs) { int ret; struct lxc_storage *bdev; char *best_options[] = {"btrfs", "zfs", "lvm", "dir", "rbd", NULL}; if (!type) return do_storage_create(dest, "dir", cname, specs); ret = strcmp(type, "best"); if (ret == 0) { int i; /* Try for the best backing store type, according to our * opinionated preferences. */ for (i = 0; best_options[i]; i++) { bdev = do_storage_create(dest, best_options[i], cname, specs); if (bdev) return bdev; } return NULL; } /* -B lvm,dir */ if (strchr(type, ',')) { char *dup, *token; char *saveptr = NULL; size_t len; len = strlen(type); dup = alloca(len + 1); (void)strlcpy(dup, type, len + 1); for (token = strtok_r(dup, ",", &saveptr); token; token = strtok_r(NULL, ",", &saveptr)) { bdev = do_storage_create(dest, token, cname, specs); if (bdev) return bdev; } } return do_storage_create(dest, type, cname, specs); } bool storage_destroy(struct lxc_conf *conf) { struct lxc_storage *r; bool ret = false; r = storage_init(conf, conf->rootfs.path, conf->rootfs.mount, NULL); if (!r) return ret; if (r->ops->destroy(r) == 0) ret = true; storage_put(r); return ret; } struct lxc_storage *storage_init(struct lxc_conf *conf, const char *src, const char *dst, const char *mntopts) { struct lxc_storage *bdev; const struct lxc_storage_type *q; if (!src) src = conf->rootfs.path; if (!src) return NULL; q = storage_query(conf, src); if (!q) return NULL; bdev = malloc(sizeof(struct lxc_storage)); if (!bdev) return NULL; memset(bdev, 0, sizeof(struct lxc_storage)); bdev->ops = q->ops; bdev->type = q->name; if (mntopts) bdev->mntopts = strdup(mntopts); if (src) bdev->src = strdup(src); if (dst) bdev->dest = strdup(dst); if (strcmp(bdev->type, "nbd") == 0) bdev->nbd_idx = conf->nbd_idx; return bdev; } bool storage_is_dir(struct lxc_conf *conf, const char *path) { struct lxc_storage *orig; bool bret = false; orig = storage_init(conf, path, NULL, NULL); if (!orig) return bret; if (strcmp(orig->type, "dir") == 0) bret = true; storage_put(orig); return bret; } void storage_put(struct lxc_storage *bdev) { free(bdev->mntopts); free(bdev->src); free(bdev->dest); free(bdev); } bool rootfs_is_blockdev(struct lxc_conf *conf) { const struct lxc_storage_type *q; struct stat st; int ret; if (!conf->rootfs.path || strcmp(conf->rootfs.path, "/") == 0 || strlen(conf->rootfs.path) == 0) return false; ret = stat(conf->rootfs.path, &st); if (ret == 0 && S_ISBLK(st.st_mode)) return true; q = storage_query(conf, conf->rootfs.path); if (!q) return false; if (strcmp(q->name, "lvm") == 0 || strcmp(q->name, "loop") == 0 || strcmp(q->name, "nbd") == 0) return true; return false; } lxc-2.0.11/src/lxc/storage/rbd.c0000644061062106075000000001046013435013473013252 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include /* Required for PRIu64 to work. */ #include #include #include #include #include "log.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" lxc_log_define(rbd, lxc); int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { ERROR("rbd clonepaths not implemented"); return -1; } int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { const char *rbdpool, *rbdname = n, *fstype; uint64_t size; int ret, len; char sz[24]; pid_t pid; const char *cmd_args[2]; char cmd_output[MAXPATHLEN]; if (!specs) return -1; rbdpool = specs->rbd.rbdpool; if (!rbdpool) rbdpool = lxc_global_config_value("lxc.bdev.rbd.rbdpool"); if (specs->rbd.rbdname) rbdname = specs->rbd.rbdname; /* source device /dev/rbd/lxc/ctn */ len = strlen(rbdpool) + strlen(rbdname) + 11; bdev->src = malloc(len); if (!bdev->src) return -1; ret = snprintf(bdev->src, len, "/dev/rbd/%s/%s", rbdpool, rbdname); if (ret < 0 || ret >= len) return -1; // fssize is in bytes. size = specs->fssize; if (!size) size = DEFAULT_FS_SIZE; // in megabytes for rbd tool ret = snprintf(sz, 24, "%"PRIu64, size / 1024 / 1024 ); if (ret < 0 || ret >= 24) exit(1); if ((pid = fork()) < 0) return -1; if (!pid) { execlp("rbd", "rbd", "create" , "--pool", rbdpool, rbdname, "--size", sz, (char *)NULL); exit(1); } if (wait_for_pid(pid) < 0) return -1; if ((pid = fork()) < 0) return -1; if (!pid) { execlp("rbd", "rbd", "map", "--pool", rbdpool, rbdname, (char *)NULL); exit(1); } if (wait_for_pid(pid) < 0) return -1; fstype = specs->fstype; if (!fstype) fstype = DEFAULT_FSTYPE; cmd_args[0] = fstype; cmd_args[1] = bdev->src; ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, (void *)cmd_args); if (ret < 0) return -1; if (!(bdev->dest = strdup(dest))) return -1; if (mkdir_p(bdev->dest, 0755) < 0 && errno != EEXIST) { ERROR("Error creating %s", bdev->dest); return -1; } return 0; } int rbd_destroy(struct lxc_storage *orig) { pid_t pid; char *rbdfullname; if ( file_exists(orig->src) ) { if ((pid = fork()) < 0) return -1; if (!pid) { execlp("rbd", "rbd", "unmap" , orig->src, (char *)NULL); exit(1); } if (wait_for_pid(pid) < 0) return -1; } if ((pid = fork()) < 0) return -1; if (!pid) { rbdfullname = alloca(strlen(orig->src) - 8); strcpy( rbdfullname, &orig->src[9] ); execlp("rbd", "rbd", "rm" , rbdfullname, (char *)NULL); exit(1); } return wait_for_pid(pid); } int rbd_detect(const char *path) { if ( memcmp(path, "/dev/rbd/", 9) == 0) return 1; return 0; } int rbd_mount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "rbd")) return -22; if (!bdev->src || !bdev->dest) return -22; if ( !file_exists(bdev->src) ) { // if blkdev does not exist it should be mapped, because it is not persistent on reboot ERROR("Block device %s is not mapped.", bdev->src); return -1; } return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts); } int rbd_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "rbd")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } lxc-2.0.11/src/lxc/storage/overlay.c0000644061062106075000000004555513435013473014201 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include "conf.h" #include "confile.h" #include "log.h" #include "lxccontainer.h" #include "overlay.h" #include "rsync.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" lxc_log_define(overlay, lxc); static char *ovl_name; static char *ovl_version[] = {"overlay", "overlayfs"}; /* defined in lxccontainer.c: needs to become common helper */ extern char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath); static char *ovl_detect_name(void); static int ovl_do_rsync(struct lxc_storage *orig, struct lxc_storage *new, struct lxc_conf *conf); static int ovl_rsync(struct rsync_data *data); static int ovl_rsync_wrapper(void *data); static int ovl_remount_on_enodev(const char *lower, const char *target, const char *name, unsigned long mountflags, const void *options); int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { if (!snap) { ERROR("overlayfs is only for snapshot clones"); return -22; } if (!orig->src || !orig->dest) return -1; new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); if (!new->dest) return -1; if (mkdir_p(new->dest, 0755) < 0) return -1; if (am_guest_unpriv() && chown_mapped_root(new->dest, conf) < 0) WARN("Failed to update ownership of %s", new->dest); if (strcmp(orig->type, "dir") == 0) { char *delta, *lastslash; char *work; int ret, len, lastslashidx; /* * if we have * /var/lib/lxc/c2/rootfs * then delta will be * /var/lib/lxc/c2/delta0 */ lastslash = strrchr(new->dest, '/'); if (!lastslash) return -22; if (strlen(lastslash) < 7) return -22; lastslash++; lastslashidx = lastslash - new->dest; delta = malloc(lastslashidx + 7); if (!delta) return -1; strncpy(delta, new->dest, lastslashidx + 1); strcpy(delta + lastslashidx, "delta0"); if ((ret = mkdir(delta, 0755)) < 0) { SYSERROR("error: mkdir %s", delta); free(delta); return -1; } if (am_guest_unpriv() && chown_mapped_root(delta, conf) < 0) WARN("Failed to update ownership of %s", delta); /* * Make workdir for overlayfs.v22 or higher: * The workdir will be * /var/lib/lxc/c2/olwork * and is used to prepare files before they are atomically * switched to the overlay destination. Workdirs need to be on * the same filesystem as the upperdir so it's OK for it to be * empty. */ work = malloc(lastslashidx + 7); if (!work) { free(delta); return -1; } strncpy(work, new->dest, lastslashidx + 1); strcpy(work + lastslashidx, "olwork"); if (mkdir(work, 0755) < 0) { SYSERROR("error: mkdir %s", work); free(delta); free(work); return -1; } if (am_guest_unpriv() && chown_mapped_root(work, conf) < 0) WARN("Failed to update ownership of %s", work); free(work); // the src will be 'overlayfs:lowerdir:upperdir' len = strlen(delta) + strlen(orig->src) + 12; new->src = malloc(len); if (!new->src) { free(delta); return -ENOMEM; } ret = snprintf(new->src, len, "overlayfs:%s:%s", orig->src, delta); free(delta); if (ret < 0 || ret >= len) return -ENOMEM; } else if (strcmp(orig->type, "overlayfs") == 0) { /* * What exactly do we want to do here? I think we want to use * the original lowerdir, with a private delta which is * originally rsynced from the original delta */ char *osrc, *odelta, *nsrc, *ndelta, *work; char *lastslash; int len, ret, lastslashidx; if (!(osrc = strdup(orig->src))) return -22; nsrc = strchr(osrc, ':') + 1; if (nsrc != osrc + 10 || (odelta = strchr(nsrc, ':')) == NULL) { free(osrc); return -22; } *odelta = '\0'; odelta++; ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath); if (!ndelta) { free(osrc); return -ENOMEM; } if ((ret = mkdir(ndelta, 0755)) < 0 && errno != EEXIST) { SYSERROR("error: mkdir %s", ndelta); free(osrc); free(ndelta); return -1; } if (am_guest_unpriv() && chown_mapped_root(ndelta, conf) < 0) WARN("Failed to update ownership of %s", ndelta); /* * make workdir for overlayfs.v22 or higher (see comment further * up) */ lastslash = strrchr(ndelta, '/'); if (!lastslash) { free(osrc); free(ndelta); return -1; } lastslash++; lastslashidx = lastslash - ndelta; work = malloc(lastslashidx + 7); if (!work) { free(osrc); free(ndelta); return -1; } strncpy(work, ndelta, lastslashidx + 1); strcpy(work + lastslashidx, "olwork"); if ((mkdir(work, 0755) < 0) && errno != EEXIST) { SYSERROR("error: mkdir %s", work); free(osrc); free(ndelta); free(work); return -1; } if (am_guest_unpriv() && chown_mapped_root(work, conf) < 0) WARN("Failed to update ownership of %s", work); free(work); len = strlen(nsrc) + strlen(ndelta) + 12; new->src = malloc(len); if (!new->src) { free(osrc); free(ndelta); return -ENOMEM; } ret = snprintf(new->src, len, "overlayfs:%s:%s", nsrc, ndelta); free(osrc); free(ndelta); if (ret < 0 || ret >= len) return -ENOMEM; return ovl_do_rsync(orig, new, conf); } else { ERROR("overlayfs clone of %s container is not yet supported", orig->type); /* * Note, supporting this will require ovl_mount supporting * mounting of the underlay. No big deal, just needs to be done. */ return -1; } return 0; } /* * to say 'lxc-create -t ubuntu -n o1 -B overlayfs' means you want * $lxcpath/$lxcname/rootfs to have the created container, while all * changes after starting the container are written to * $lxcpath/$lxcname/delta0 */ int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { char *delta; int ret, len = strlen(dest), newlen; if (len < 8 || strcmp(dest + len - 7, "/rootfs") != 0) return -1; if (!(bdev->dest = strdup(dest))) { ERROR("Out of memory"); return -1; } delta = alloca(strlen(dest) + 1); strcpy(delta, dest); strcpy(delta + len - 6, "delta0"); if (mkdir_p(delta, 0755) < 0) { ERROR("Error creating %s", delta); return -1; } // overlayfs:lower:upper newlen = (2 * len) + strlen("overlayfs:") + 2; bdev->src = malloc(newlen); if (!bdev->src) { ERROR("Out of memory"); return -1; } ret = snprintf(bdev->src, newlen, "overlayfs:%s:%s", dest, delta); if (ret < 0 || ret >= newlen) return -1; if (mkdir_p(bdev->dest, 0755) < 0) { ERROR("Error creating %s", bdev->dest); return -1; } return 0; } int ovl_destroy(struct lxc_storage *orig) { char *upper; if (strncmp(orig->src, "overlayfs:", 10) != 0) return -22; upper = strchr(orig->src + 10, ':'); if (!upper) return -22; upper++; return lxc_rmdir_onedev(upper, NULL); } int ovl_detect(const char *path) { if (strncmp(path, "overlayfs:", 10) == 0) return 1; // take their word for it return 0; } char *ovl_getlower(char *p) { char *p1 = strchr(p, ':'); if (p1) *p1 = '\0'; return p; } int ovl_mount(struct lxc_storage *bdev) { char *tmp, *options, *dup, *lower, *upper; char *options_work, *work, *lastslash; int lastslashidx; int len, len2; unsigned long mntflags; char *mntdata; int ret, ret2; if (strcmp(bdev->type, "overlayfs")) return -22; if (!bdev->src || !bdev->dest) return -22; if (!ovl_name) ovl_name = ovl_detect_name(); /* * separately mount it first: * mount -t overlayfs * -oupperdir=${upper},lowerdir=${lower} lower dest */ dup = alloca(strlen(bdev->src) + 1); strcpy(dup, bdev->src); /* support multiple lower layers */ if (!(lower = strstr(dup, ":/"))) return -22; lower++; upper = lower; while ((tmp = strstr(++upper, ":/"))) { upper = tmp; } if (--upper == lower) return -22; *upper = '\0'; upper++; // if delta doesn't yet exist, create it if (mkdir_p(upper, 0755) < 0 && errno != EEXIST) return -22; /* * overlayfs.v22 or higher needs workdir option: * if upper is * /var/lib/lxc/c2/delta0 * then workdir is * /var/lib/lxc/c2/olwork */ lastslash = strrchr(upper, '/'); if (!lastslash) return -22; lastslash++; lastslashidx = lastslash - upper; work = alloca(lastslashidx + 7); strncpy(work, upper, lastslashidx + 7); strcpy(work + lastslashidx, "olwork"); if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { free(mntdata); return -22; } if (mkdir_p(work, 0755) < 0 && errno != EEXIST) { free(mntdata); return -22; } /* * TODO: * We should check whether bdev->src is a blockdev but for now only * support overlays of a basic directory */ if (mntdata) { len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=,") + strlen(mntdata) + 1; options = alloca(len); ret = snprintf(options, len, "upperdir=%s,lowerdir=%s,%s", upper, lower, mntdata); len2 = strlen(lower) + strlen(upper) + strlen(work) + strlen("upperdir=,lowerdir=,workdir=") + strlen(mntdata) + 1; options_work = alloca(len2); ret2 = snprintf(options, len2, "upperdir=%s,lowerdir=%s,workdir=%s,%s", upper, lower, work, mntdata); } else { len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=") + 1; options = alloca(len); ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, lower); len2 = strlen(lower) + strlen(upper) + strlen(work) + strlen("upperdir=,lowerdir=,workdir=") + 1; options_work = alloca(len2); ret2 = snprintf(options_work, len2, "upperdir=%s,lowerdir=%s,workdir=%s", upper, lower, work); } if (ret < 0 || ret >= len || ret2 < 0 || ret2 >= len2) { free(mntdata); return -1; } /* Assume we need a workdir as we are on a overlay version >= v22. */ ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, MS_MGC_VAL | mntflags, options_work); if (ret < 0) { INFO("Overlayfs: Error mounting %s onto %s with options %s. " "Retrying without workdir: %s.", lower, bdev->dest, options_work, strerror(errno)); /* Assume we cannot use a workdir as we are on a version <= v21. */ ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, MS_MGC_VAL | mntflags, options); if (ret < 0) SYSERROR("Overlayfs: Error mounting %s onto %s with " "options %s: %s.", lower, bdev->dest, options, strerror(errno)); else INFO("Overlayfs: Mounted %s onto %s with options %s.", lower, bdev->dest, options); } else { INFO("Overlayfs: Mounted %s onto %s with options %s.", lower, bdev->dest, options_work); } return ret; } int ovl_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "overlayfs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen) { char *rootfsdir = NULL; char *s1 = NULL; char *s2 = NULL; char *s3 = NULL; if (!rootfs_path || !rootfslen) return NULL; s1 = strdup(rootfs_path); if (!s1) return NULL; if ((s2 = strstr(s1, ":/"))) { s2 = s2 + 1; if ((s3 = strstr(s2, ":/"))) *s3 = '\0'; rootfsdir = strdup(s2); if (!rootfsdir) { free(s1); return NULL; } } if (!rootfsdir) rootfsdir = s1; else free(s1); *rootfslen = strlen(rootfsdir); return rootfsdir; } int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { char lxcpath[MAXPATHLEN]; char *rootfs_path = NULL; char *rootfsdir = NULL; char *upperdir = NULL; char *workdir = NULL; char **opts = NULL; int fret = -1; int ret = 0; size_t arrlen = 0; size_t dirlen = 0; size_t i; size_t len = 0; size_t rootfslen = 0; /* When rootfs == NULL we have a container without a rootfs. */ if (rootfs && rootfs->path) rootfs_path = rootfs->path; opts = lxc_string_split(mntent->mnt_opts, ','); if (opts) arrlen = lxc_array_len((void **)opts); else goto err; for (i = 0; i < arrlen; i++) { if (strstr(opts[i], "upperdir=") && (strlen(opts[i]) > (len = strlen("upperdir=")))) upperdir = opts[i] + len; else if (strstr(opts[i], "workdir=") && (strlen(opts[i]) > (len = strlen("workdir=")))) workdir = opts[i] + len; } if (rootfs_path) { ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); if (ret < 0 || ret >= MAXPATHLEN) goto err; rootfsdir = ovl_get_rootfs(rootfs_path, &rootfslen); if (!rootfsdir) goto err; dirlen = strlen(lxcpath); } /* * We neither allow users to create upperdirs and workdirs outside the * containerdir nor inside the rootfs. The latter might be debatable. * When we have a container without a rootfs we skip the checks. */ ret = 0; if (upperdir) { if (!rootfs_path) ret = mkdir_p(upperdir, 0755); else if ((strncmp(upperdir, lxcpath, dirlen) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) ret = mkdir_p(upperdir, 0755); if (ret < 0) WARN("Failed to create upperdir"); } ret = 0; if (workdir) { if (!rootfs_path) ret = mkdir_p(workdir, 0755); else if ((strncmp(workdir, lxcpath, dirlen) == 0) && (strncmp(workdir, rootfsdir, rootfslen) != 0)) ret = mkdir_p(workdir, 0755); if (ret < 0) WARN("Failed to create workdir"); } fret = 0; err: free(rootfsdir); lxc_free_array((void **)opts, free); return fret; } /* * To be called from lxcapi_clone() in lxccontainer.c: When we clone a container * with overlay lxc.mount.entry entries we need to update absolute paths for * upper- and workdir. This update is done in two locations: * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done * independent of each other since lxc_conf->mountlist may container more mount * entries (e.g. from other included files) than lxc_conf->unexpanded_config . */ int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, const char *lxc_name, const char *newpath, const char *newname) { char new_upper[MAXPATHLEN]; char new_work[MAXPATHLEN]; char old_upper[MAXPATHLEN]; char old_work[MAXPATHLEN]; char *cleanpath = NULL; size_t i; int fret = -1; int ret = 0; struct lxc_list *iterator; const char *ovl_dirs[] = {"br", "upperdir", "workdir"}; cleanpath = strdup(newpath); if (!cleanpath) goto err; remove_trailing_slashes(cleanpath); /* * We have to update lxc_conf->unexpanded_config separately from * lxc_conf->mount_list. */ for (i = 0; i < sizeof(ovl_dirs) / sizeof(ovl_dirs[0]); i++) { if (!clone_update_unexp_ovl_paths(lxc_conf, lxc_path, newpath, lxc_name, newname, ovl_dirs[i])) goto err; } ret = snprintf(old_work, MAXPATHLEN, "workdir=%s/%s", lxc_path, lxc_name); if (ret < 0 || ret >= MAXPATHLEN) goto err; ret = snprintf(new_work, MAXPATHLEN, "workdir=%s/%s", cleanpath, newname); if (ret < 0 || ret >= MAXPATHLEN) goto err; lxc_list_for_each(iterator, &lxc_conf->mount_list) { char *mnt_entry = NULL; char *new_mnt_entry = NULL; char *tmp = NULL; char *tmp_mnt_entry = NULL; mnt_entry = iterator->elem; if (strstr(mnt_entry, "overlay")) tmp = "upperdir"; else if (strstr(mnt_entry, "aufs")) tmp = "br"; if (!tmp) continue; ret = snprintf(old_upper, MAXPATHLEN, "%s=%s/%s", tmp, lxc_path, lxc_name); if (ret < 0 || ret >= MAXPATHLEN) goto err; ret = snprintf(new_upper, MAXPATHLEN, "%s=%s/%s", tmp, cleanpath, newname); if (ret < 0 || ret >= MAXPATHLEN) goto err; if (strstr(mnt_entry, old_upper)) { tmp_mnt_entry = lxc_string_replace(old_upper, new_upper, mnt_entry); } if (strstr(mnt_entry, old_work)) { if (tmp_mnt_entry) new_mnt_entry = lxc_string_replace(old_work, new_work, tmp_mnt_entry); else new_mnt_entry = lxc_string_replace(old_work, new_work, mnt_entry); } if (new_mnt_entry) { free(iterator->elem); iterator->elem = strdup(new_mnt_entry); } else if (tmp_mnt_entry) { free(iterator->elem); iterator->elem = strdup(tmp_mnt_entry); } free(new_mnt_entry); free(tmp_mnt_entry); } fret = 0; err: free(cleanpath); return fret; } static int ovl_remount_on_enodev(const char *lower, const char *target, const char *name, unsigned long mountflags, const void *options) { int ret; ret = mount(lower, target, ovl_name, MS_MGC_VAL | mountflags, options); if (ret < 0 && errno == ENODEV) /* Try other module name. */ ret = mount(lower, target, ovl_name == ovl_version[0] ? ovl_version[1] : ovl_version[0], MS_MGC_VAL | mountflags, options); return ret; } static int ovl_rsync(struct rsync_data *data) { int ret; if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (unshare(CLONE_NEWNS) < 0) { SYSERROR("Unable to unshare mounts ns"); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } if (ovl_mount(data->orig) < 0) { ERROR("Failed mounting original container fs"); return -1; } if (ovl_mount(data->new) < 0) { ERROR("Failed mounting new container fs"); return -1; } ret = do_rsync(data->orig->dest, data->new->dest); ovl_umount(data->new); ovl_umount(data->orig); if (ret < 0) { ERROR("rsyncing %s to %s", data->orig->dest, data->new->dest); return -1; } return 0; } static char *ovl_detect_name(void) { char *v = ovl_version[0]; char *line = NULL; size_t len = 0; FILE *f = fopen("/proc/filesystems", "r"); if (!f) return v; while (getline(&line, &len, f) != -1) { if (strcmp(line, "nodev\toverlayfs\n") == 0) { v = ovl_version[1]; break; } } fclose(f); free(line); return v; } static int ovl_do_rsync(struct lxc_storage *orig, struct lxc_storage *new, struct lxc_conf *conf) { int ret = -1; struct rsync_data rdata; rdata.orig = orig; rdata.new = new; if (am_guest_unpriv()) ret = userns_exec_full(conf, ovl_rsync_wrapper, &rdata, "ovl_rsync_wrapper"); else ret = ovl_rsync(&rdata); if (ret) ERROR("copying overlayfs delta"); return ret; } static int ovl_rsync_wrapper(void *data) { struct rsync_data *arg = data; return ovl_rsync(arg); } lxc-2.0.11/src/lxc/storage/nbd.c0000644061062106075000000001525513435013473013255 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "log.h" #include "nbd.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(nbd, lxc); struct nbd_attach_data { const char *nbd; const char *path; }; static bool clone_attach_nbd(const char *nbd, const char *path); static int do_attach_nbd(void *d); static bool nbd_busy(int idx); static void nbd_detach(const char *path); static int nbd_get_partition(const char *src); static bool wait_for_partition(const char *path); bool attach_nbd(char *src, struct lxc_conf *conf) { char *orig, *p, path[50]; int i = 0; size_t len; len = strlen(src); orig = alloca(len + 1); (void)strlcpy(orig, src, len + 1); /* if path is followed by a partition, drop that for now */ p = strchr(orig, ':'); if (p) *p = '\0'; while (1) { sprintf(path, "/dev/nbd%d", i); if (!file_exists(path)) return false; if (nbd_busy(i)) { i++; continue; } if (!clone_attach_nbd(path, orig)) return false; conf->nbd_idx = i; return true; } } void detach_nbd_idx(int idx) { int ret; char path[50]; ret = snprintf(path, 50, "/dev/nbd%d", idx); if (ret < 0 || ret >= 50) return; nbd_detach(path); } int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { return -ENOSYS; } int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { return -ENOSYS; } int nbd_destroy(struct lxc_storage *orig) { return -ENOSYS; } int nbd_detect(const char *path) { if (strncmp(path, "nbd:", 4) == 0) return 1; return 0; } int nbd_mount(struct lxc_storage *bdev) { int ret = -1, partition; char path[50]; if (strcmp(bdev->type, "nbd")) return -22; if (!bdev->src || !bdev->dest) return -22; /* nbd_idx should have been copied by bdev_init from the lxc_conf */ if (bdev->nbd_idx < 0) return -22; partition = nbd_get_partition(bdev->src); if (partition) ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx, partition); else ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx); if (ret < 0 || ret >= 50) { ERROR("Error setting up nbd device path"); return ret; } /* It might take awhile for the partition files to show up */ if (partition) { if (!wait_for_partition(path)) return -2; } ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts); if (ret < 0) ERROR("Error mounting %s", bdev->src); return ret; } int nbd_umount(struct lxc_storage *bdev) { int ret; if (strcmp(bdev->type, "nbd")) return -22; if (!bdev->src || !bdev->dest) return -22; ret = umount(bdev->dest); return ret; } bool requires_nbd(const char *path) { if (strncmp(path, "nbd:", 4) == 0) return true; return false; } static int do_attach_nbd(void *d) { struct nbd_attach_data *data = d; const char *nbd, *path; pid_t pid; sigset_t mask; int sfd; ssize_t s; struct signalfd_siginfo fdsi; sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGCHLD); nbd = data->nbd; path = data->path; if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { SYSERROR("Error blocking signals for nbd watcher"); exit(1); } sfd = signalfd(-1, &mask, 0); if (sfd == -1) { SYSERROR("Error opening signalfd for nbd task"); exit(1); } if (prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) < 0) SYSERROR("Error setting parent death signal for nbd watcher"); pid = fork(); if (pid) { for (;;) { s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); if (s != sizeof(struct signalfd_siginfo)) SYSERROR("Error reading from signalfd"); if (fdsi.ssi_signo == SIGHUP) { /* container has exited */ nbd_detach(nbd); exit(0); } else if (fdsi.ssi_signo == SIGCHLD) { int status; /* If qemu-nbd fails, or is killed by a signal, * then exit */ while (waitpid(-1, &status, WNOHANG) > 0) { if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) || WIFSIGNALED(status)) { nbd_detach(nbd); exit(1); } } } } } close(sfd); if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) WARN("Warning: unblocking signals for nbd watcher"); execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL); SYSERROR("Error executing qemu-nbd"); exit(1); } static bool clone_attach_nbd(const char *nbd, const char *path) { pid_t pid; struct nbd_attach_data data; data.nbd = nbd; data.path = path; pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID); if (pid < 0) return false; return true; } static bool nbd_busy(int idx) { char path[100]; int ret; ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx); if (ret < 0 || ret >= 100) return true; return file_exists(path); } static void nbd_detach(const char *path) { int ret; pid_t pid = fork(); if (pid < 0) { SYSERROR("Error forking to detach nbd"); return; } if (pid) { ret = wait_for_pid(pid); if (ret < 0) ERROR("nbd disconnect returned an error"); return; } execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL); SYSERROR("Error executing qemu-nbd"); exit(1); } /* * Pick the partition # off the end of a nbd:file:p * description. Return 1-9 for the partition id, or 0 * for no partition. */ static int nbd_get_partition(const char *src) { char *p = strchr(src, ':'); if (!p) return 0; p = strchr(p+1, ':'); if (!p) return 0; p++; if (*p < '1' || *p > '9') return 0; return *p - '0'; } static bool wait_for_partition(const char *path) { int count = 0; while (count < 5) { if (file_exists(path)) return true; sleep(1); count++; } ERROR("Device %s did not show up after 5 seconds", path); return false; } lxc-2.0.11/src/lxc/storage/storage_utils.h0000644061062106075000000000370213435013473015375 00000000000000/* * lxc: linux Container library * * Copyright © 2017 Canonical Ltd. * * Authors: * Christian Brauner * * This program 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 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 */ #ifndef __LXC_STORAGE_UTILS_H #define __LXC_STORAGE_UTILS_H #include "config.h" #include #include #include #include "conf.h" struct lxc_storage; struct lxc_conf; extern char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath); extern bool attach_block_device(struct lxc_conf *conf); extern void detach_block_device(struct lxc_conf *conf); extern int blk_getsize(struct lxc_storage *bdev, uint64_t *size); extern int detect_fs(struct lxc_storage *bdev, char *type, int len); extern int do_mkfs_exec_wrapper(void *args); extern int is_blktype(struct lxc_storage *b); extern int mount_unknown_fs(const char *rootfs, const char *target, const char *options); extern int find_fstype_cb(char *buffer, void *data); extern char *linkderef(char *path, char *dest); extern bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, bool maybesnap); extern bool is_valid_storage_type(const char *type); extern int storage_destroy_wrapper(void *data); #endif /* __LXC_STORAGE_UTILS_H */ lxc-2.0.11/src/lxc/storage/loop.h0000644061062106075000000000307213435013473013462 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LOOP_H #define __LXC_LOOP_H #define _GNU_SOURCE #include struct lxc_storage; struct bdev_specs; struct lxc_conf; extern int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int loop_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int loop_destroy(struct lxc_storage *orig); extern int loop_detect(const char *path); extern int loop_mount(struct lxc_storage *bdev); extern int loop_umount(struct lxc_storage *bdev); #endif /* __LXC_LOOP_H */ lxc-2.0.11/src/lxc/storage/dir.c0000644061062106075000000000670613435013473013271 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include "log.h" #include "storage.h" #include "utils.h" lxc_log_define(dir, lxc); /* For a simple directory bind mount, we substitute the old container name and * paths for the new. */ int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { int len, ret; if (snap) { ERROR("directories cannot be snapshotted. Try aufs or overlayfs."); return -1; } if (!orig->dest || !orig->src) return -1; len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; new->src = malloc(len); if (!new->src) return -1; ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); if (ret < 0 || ret >= len) return -1; if ((new->dest = strdup(new->src)) == NULL) return -1; return 0; } int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { if (specs && specs->dir) bdev->src = strdup(specs->dir); else bdev->src = strdup(dest); bdev->dest = strdup(dest); if (!bdev->src || !bdev->dest) { ERROR("Out of memory"); return -1; } if (mkdir_p(bdev->src, 0755) < 0) { ERROR("Error creating %s", bdev->src); return -1; } if (mkdir_p(bdev->dest, 0755) < 0) { ERROR("Error creating %s", bdev->dest); return -1; } return 0; } int dir_destroy(struct lxc_storage *orig) { if (lxc_rmdir_onedev(orig->src, NULL) < 0) return -1; return 0; } int dir_detect(const char *path) { if (strncmp(path, "dir:", 4) == 0) return 1; // take their word for it if (is_dir(path)) return 1; return 0; } int dir_mount(struct lxc_storage *bdev) { unsigned long mntflags; char *mntdata; int ret; unsigned long mflags; if (strcmp(bdev->type, "dir")) return -22; if (!bdev->src || !bdev->dest) return -22; if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { free(mntdata); return -22; } ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata); if ((0 == ret) && (mntflags & MS_RDONLY)) { DEBUG("remounting %s on %s with readonly options", bdev->src ? bdev->src : "(none)", bdev->dest ? bdev->dest : "(none)"); mflags = add_required_remount_flags(bdev->src, bdev->dest, MS_BIND | MS_REC | mntflags | MS_REMOUNT); ret = mount(bdev->src, bdev->dest, "bind", mflags, mntdata); } free(mntdata); return ret; } int dir_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "dir")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } lxc-2.0.11/src/lxc/storage/rsync.c0000644061062106075000000000612313435013473013642 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "log.h" #include "rsync.h" #include "storage.h" #include "utils.h" lxc_log_define(rsync, lxc); /* the bulk of this needs to become a common helper */ int do_rsync(const char *src, const char *dest) { // call out to rsync pid_t pid; char *s; size_t l; pid = fork(); if (pid < 0) return -1; if (pid > 0) return wait_for_pid(pid); l = strlen(src) + 2; s = malloc(l); if (!s) exit(1); strcpy(s, src); s[l-2] = '/'; s[l-1] = '\0'; execlp("rsync", "rsync", "-aHXS", "--delete", s, dest, (char *)NULL); exit(1); } int rsync_delta(struct rsync_data_char *data) { if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (do_rsync(data->src, data->dest) < 0) { ERROR("rsyncing %s to %s", data->src, data->dest); return -1; } return 0; } int rsync_delta_wrapper(void *data) { struct rsync_data_char *arg = data; return rsync_delta(arg); } int rsync_rootfs(struct rsync_data *data) { struct lxc_storage *orig = data->orig, *new = data->new; if (unshare(CLONE_NEWNS) < 0) { SYSERROR("unshare CLONE_NEWNS"); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } // If not a snapshot, copy the fs. if (orig->ops->mount(orig) < 0) { ERROR("failed mounting %s onto %s", orig->src, orig->dest); return -1; } if (new->ops->mount(new) < 0) { ERROR("failed mounting %s onto %s", new->src, new->dest); return -1; } if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (do_rsync(orig->dest, new->dest) < 0) { ERROR("rsyncing %s to %s", orig->src, new->src); return -1; } return 0; } int rsync_rootfs_wrapper(void *data) { struct rsync_data *arg = data; return rsync_rootfs(arg); } lxc-2.0.11/src/lxc/storage/aufs.c0000644061062106075000000002470113435013473013444 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include "aufs.h" #include "log.h" #include "aufs.h" #include "rsync.h" #include "sync.h" #include "storage.h" #include "utils.h" lxc_log_define(aufs, lxc); /* the bulk of this needs to become a common helper */ extern char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath); int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { if (!snap) { ERROR("aufs is only for snapshot clones"); return -22; } if (!orig->src || !orig->dest) return -1; new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); if (!new->dest) return -1; if (mkdir_p(new->dest, 0755) < 0) return -1; if (am_guest_unpriv() && chown_mapped_root(new->dest, conf) < 0) WARN("Failed to update ownership of %s", new->dest); if (strcmp(orig->type, "dir") == 0) { char *delta, *lastslash; int ret, len, lastslashidx; // if we have /var/lib/lxc/c2/rootfs, then delta will be // /var/lib/lxc/c2/delta0 lastslash = strrchr(new->dest, '/'); if (!lastslash) return -22; if (strlen(lastslash) < 7) return -22; lastslash++; lastslashidx = lastslash - new->dest; delta = malloc(lastslashidx + 7); if (!delta) return -1; strncpy(delta, new->dest, lastslashidx+1); strcpy(delta+lastslashidx, "delta0"); if ((ret = mkdir(delta, 0755)) < 0) { SYSERROR("error: mkdir %s", delta); free(delta); return -1; } if (am_guest_unpriv() && chown_mapped_root(delta, conf) < 0) WARN("Failed to update ownership of %s", delta); // the src will be 'aufs:lowerdir:upperdir' len = strlen(delta) + strlen(orig->src) + 12; new->src = malloc(len); if (!new->src) { free(delta); return -ENOMEM; } ret = snprintf(new->src, len, "aufs:%s:%s", orig->src, delta); free(delta); if (ret < 0 || ret >= len) return -ENOMEM; } else if (strcmp(orig->type, "aufs") == 0) { // What exactly do we want to do here? // I think we want to use the original lowerdir, with a // private delta which is originally rsynced from the // original delta char *osrc, *odelta, *nsrc, *ndelta; int len, ret; if (!(osrc = strdup(orig->src))) return -22; nsrc = strchr(osrc, ':') + 1; if (nsrc != osrc + 5 || (odelta = strchr(nsrc, ':')) == NULL) { free(osrc); return -22; } *odelta = '\0'; odelta++; ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath); if (!ndelta) { free(osrc); return -ENOMEM; } if ((ret = mkdir(ndelta, 0755)) < 0 && errno != EEXIST) { SYSERROR("error: mkdir %s", ndelta); free(osrc); free(ndelta); return -1; } if (am_guest_unpriv() && chown_mapped_root(ndelta, conf) < 0) WARN("Failed to update ownership of %s", ndelta); struct rsync_data_char rdata; rdata.src = odelta; rdata.dest = ndelta; if (am_guest_unpriv()) ret = userns_exec_full(conf, rsync_delta_wrapper, &rdata, "rsync_delta_wrapper"); else ret = rsync_delta(&rdata); if (ret) { free(osrc); free(ndelta); ERROR("copying aufs delta"); return -1; } len = strlen(nsrc) + strlen(ndelta) + 12; new->src = malloc(len); if (!new->src) { free(osrc); free(ndelta); return -ENOMEM; } ret = snprintf(new->src, len, "aufs:%s:%s", nsrc, ndelta); free(osrc); free(ndelta); if (ret < 0 || ret >= len) return -ENOMEM; } else { ERROR("aufs clone of %s container is not yet supported", orig->type); // Note, supporting this will require aufs_mount supporting // mounting of the underlay. No big deal, just needs to be done. return -1; } return 0; } /* * to say 'lxc-create -t ubuntu -n o1 -B aufs' means you want * $lxcpath/$lxcname/rootfs to have the created container, while all * changes after starting the container are written to * $lxcpath/$lxcname/delta0 */ int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { char *delta; int ret, len = strlen(dest), newlen; if (len < 8 || strcmp(dest+len-7, "/rootfs") != 0) return -1; if (!(bdev->dest = strdup(dest))) { ERROR("Out of memory"); return -1; } delta = alloca(strlen(dest)+1); strcpy(delta, dest); strcpy(delta+len-6, "delta0"); if (mkdir_p(delta, 0755) < 0) { ERROR("Error creating %s", delta); return -1; } /* aufs:lower:upper */ newlen = (2 * len) + strlen("aufs:") + 2; bdev->src = malloc(newlen); if (!bdev->src) { ERROR("Out of memory"); return -1; } ret = snprintf(bdev->src, newlen, "aufs:%s:%s", dest, delta); if (ret < 0 || ret >= newlen) return -1; if (mkdir_p(bdev->dest, 0755) < 0) { ERROR("Error creating %s", bdev->dest); return -1; } return 0; } int aufs_destroy(struct lxc_storage *orig) { char *upper; if (strncmp(orig->src, "aufs:", 5) != 0) return -22; upper = strchr(orig->src + 5, ':'); if (!upper) return -22; upper++; return lxc_rmdir_onedev(upper, NULL); } int aufs_detect(const char *path) { if (strncmp(path, "aufs:", 5) == 0) return 1; // take their word for it return 0; } int aufs_mount(struct lxc_storage *bdev) { char *tmp, *options, *dup, *lower, *upper; int len; unsigned long mntflags; char *mntdata; int ret; const char *xinopath = "/dev/shm/aufs.xino"; if (strcmp(bdev->type, "aufs")) return -22; if (!bdev->src || !bdev->dest) return -22; // separately mount it first // mount -t aufs -obr=${upper}=rw:${lower}=ro lower dest dup = alloca(strlen(bdev->src)+1); strcpy(dup, bdev->src); /* support multiple lower layers */ if (!(lower = strstr(dup, ":/"))) return -22; lower++; upper = lower; while ((tmp = strstr(++upper, ":/"))) { upper = tmp; } if (--upper == lower) return -22; *upper = '\0'; upper++; if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { free(mntdata); return -22; } // TODO We should check whether bdev->src is a blockdev, and if so // but for now, only support aufs of a basic directory // AUFS does not work on top of certain filesystems like (XFS or Btrfs) // so add xino=/dev/shm/aufs.xino parameter to mount options. // The same xino option can be specified to multiple aufs mounts, and // a xino file is not shared among multiple aufs mounts. // // see http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg02587.html // http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05126.html if (mntdata) { len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,,xino=") + strlen(mntdata) + 1; options = alloca(len); ret = snprintf(options, len, "br=%s=rw:%s=ro,%s,xino=%s", upper, lower, mntdata, xinopath); } else { len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,xino=") + 1; options = alloca(len); ret = snprintf(options, len, "br=%s=rw:%s=ro,xino=%s", upper, lower, xinopath); } if (ret < 0 || ret >= len) { free(mntdata); return -1; } ret = mount(lower, bdev->dest, "aufs", MS_MGC_VAL | mntflags, options); if (ret < 0) SYSERROR("aufs: error mounting %s onto %s options %s", lower, bdev->dest, options); else INFO("aufs: mounted %s onto %s options %s", lower, bdev->dest, options); return ret; } int aufs_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "aufs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen) { char *rootfsdir = NULL; char *s1 = NULL; char *s2 = NULL; char *s3 = NULL; if (!rootfs_path || !rootfslen) return NULL; s1 = strdup(rootfs_path); if (!s1) return NULL; if ((s2 = strstr(s1, ":/"))) { s2 = s2 + 1; if ((s3 = strstr(s2, ":/"))) *s3 = '\0'; rootfsdir = strdup(s2); if (!rootfsdir) { free(s1); return NULL; } } if (!rootfsdir) rootfsdir = s1; else free(s1); *rootfslen = strlen(rootfsdir); return rootfsdir; } int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { char lxcpath[MAXPATHLEN]; char *rootfs_path = NULL; char *rootfsdir = NULL; char *scratch = NULL; char *tmp = NULL; char *upperdir = NULL; char **opts = NULL; int fret = -1; int ret = 0; size_t arrlen = 0; size_t i; size_t len = 0; size_t rootfslen = 0; /* When rootfs == NULL we have a container without a rootfs. */ if (rootfs && rootfs->path) rootfs_path = rootfs->path; opts = lxc_string_split(mntent->mnt_opts, ','); if (opts) arrlen = lxc_array_len((void **)opts); else goto err; for (i = 0; i < arrlen; i++) { if (strstr(opts[i], "br=") && (strlen(opts[i]) > (len = strlen("br=")))) tmp = opts[i] + len; } if (!tmp) goto err; upperdir = strtok_r(tmp, ":=", &scratch); if (!upperdir) goto err; if (rootfs_path) { ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); if (ret < 0 || ret >= MAXPATHLEN) goto err; rootfsdir = aufs_get_rootfs(rootfs->path, &rootfslen); if (!rootfsdir) goto err; } /* * We neither allow users to create upperdirs and workdirs outside the * containerdir nor inside the rootfs. The latter might be debatable. * When we have a container without a rootfs we skip the checks. */ ret = 0; if (!rootfs_path) ret = mkdir_p(upperdir, 0755); else if ((strncmp(upperdir, lxcpath, strlen(lxcpath)) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) ret = mkdir_p(upperdir, 0755); if (ret < 0) WARN("Failed to create upperdir"); fret = 0; err: free(rootfsdir); lxc_free_array((void **)opts, free); return fret; } lxc-2.0.11/src/lxc/storage/btrfs.h0000644061062106075000000002755413435013473013644 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_BTRFS_H #define __LXC_BTRFS_H #define _GNU_SOURCE #include /* __le64, __l32 ... */ #include #include #include #ifndef BTRFS_SUPER_MAGIC # define BTRFS_SUPER_MAGIC 0x9123683E #endif typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; struct btrfs_ioctl_space_info { unsigned long long flags; unsigned long long total_bytes; unsigned long long used_bytes; }; struct btrfs_ioctl_space_args { unsigned long long space_slots; unsigned long long total_spaces; struct btrfs_ioctl_space_info spaces[]; }; #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long) #define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ struct btrfs_ioctl_space_args) #define BTRFS_FSID_SIZE 16 struct btrfs_ioctl_fs_info_args { unsigned long long max_id; unsigned long long num_devices; char fsid[BTRFS_FSID_SIZE]; unsigned long long reserved[124]; }; #define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ struct btrfs_ioctl_fs_info_args) #define BTRFS_SUBVOL_NAME_MAX 4039 #define BTRFS_PATH_NAME_MAX 4087 struct btrfs_ioctl_vol_args { signed long long fd; char name[BTRFS_PATH_NAME_MAX + 1]; }; #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \ struct btrfs_ioctl_vol_args_v2) #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ struct btrfs_ioctl_vol_args_v2) #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ struct btrfs_ioctl_vol_args) #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ struct btrfs_ioctl_vol_args) #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) struct btrfs_ioctl_vol_args_v2 { signed long long fd; unsigned long long transid; unsigned long long flags; union { struct { unsigned long long size; //struct btrfs_qgroup_inherit *qgroup_inherit; void *qgroup_inherit; }; unsigned long long unused[4]; }; char name[BTRFS_SUBVOL_NAME_MAX + 1]; }; /* * root backrefs tie subvols and snapshots to the directory entries that * reference them */ #define BTRFS_ROOT_BACKREF_KEY 144 /* * root items point to tree roots. There are typically in the root * tree used by the super block to find all the other trees */ #define BTRFS_ROOT_ITEM_KEY 132 /* * root refs make a fast index for listing all of the snapshots and * subvolumes referenced by a given root. They point directly to the * directory item in the root that references the subvol */ #define BTRFS_ROOT_REF_KEY 156 #define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL #define BTRFS_DIR_ITEM_KEY 84 /* * * this is used for both forward and backward root refs * */ struct btrfs_root_ref { __le64 dirid; __le64 sequence; __le16 name_len; } __attribute__ ((__packed__)); struct btrfs_disk_key { __le64 objectid; u8 type; __le64 offset; } __attribute__ ((__packed__)); struct btrfs_dir_item { struct btrfs_disk_key location; __le64 transid; __le16 data_len; __le16 name_len; u8 type; } __attribute__ ((__packed__)); #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 #define BTRFS_PATH_NAME_MAX 4087 struct btrfs_ioctl_search_key { /* which root are we searching. 0 is the tree of tree roots */ __u64 tree_id; /* keys returned will be >= min and <= max */ __u64 min_objectid; __u64 max_objectid; /* keys returned will be >= min and <= max */ __u64 min_offset; __u64 max_offset; /* max and min transids to search for */ __u64 min_transid; __u64 max_transid; /* keys returned will be >= min and <= max */ __u32 min_type; __u32 max_type; /* * how many items did userland ask for, and how many are we * returning */ __u32 nr_items; /* align to 64 bits */ __u32 unused; /* some extra for later */ __u64 unused1; __u64 unused2; __u64 unused3; __u64 unused4; }; struct btrfs_ioctl_search_header { __u64 transid; __u64 objectid; __u64 offset; __u32 type; __u32 len; } __attribute__((may_alias)); #define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) /* * the buf is an array of search headers where * each header is followed by the actual item * the type field is expanded to 32 bits for alignment */ struct btrfs_ioctl_search_args { struct btrfs_ioctl_search_key key; char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; }; #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ struct btrfs_ioctl_search_args) #define BTRFS_UUID_SIZE 16 struct btrfs_timespec { __le64 sec; __le32 nsec; } __attribute__ ((__packed__)); struct btrfs_inode_item { /* nfs style generation number */ __le64 generation; /* transid that last touched this inode */ __le64 transid; __le64 size; __le64 nbytes; __le64 block_group; __le32 nlink; __le32 uid; __le32 gid; __le32 mode; __le64 rdev; __le64 flags; /* modification sequence number for NFS */ __le64 sequence; /* * a little future expansion, for more than this we can * just grow the inode item and version it */ __le64 reserved[4]; struct btrfs_timespec atime; struct btrfs_timespec ctime; struct btrfs_timespec mtime; struct btrfs_timespec otime; } __attribute__ ((__packed__)); struct btrfs_root_item_v0 { struct btrfs_inode_item inode; __le64 generation; __le64 root_dirid; __le64 bytenr; __le64 byte_limit; __le64 bytes_used; __le64 last_snapshot; __le64 flags; __le32 refs; struct btrfs_disk_key drop_progress; u8 drop_level; u8 level; } __attribute__ ((__packed__)); struct btrfs_root_item { struct btrfs_inode_item inode; __le64 generation; __le64 root_dirid; __le64 bytenr; __le64 byte_limit; __le64 bytes_used; __le64 last_snapshot; __le64 flags; __le32 refs; struct btrfs_disk_key drop_progress; u8 drop_level; u8 level; /* * The following fields appear after subvol_uuids+subvol_times * were introduced. */ /* * This generation number is used to test if the new fields are valid * and up to date while reading the root item. Every time the root item * is written out, the "generation" field is copied into this field. If * anyone ever mounted the fs with an older kernel, we will have * mismatching generation values here and thus must invalidate the * new fields. See btrfs_update_root and btrfs_find_last_root for * details. * the offset of generation_v2 is also used as the start for the memset * when invalidating the fields. */ __le64 generation_v2; u8 uuid[BTRFS_UUID_SIZE]; u8 parent_uuid[BTRFS_UUID_SIZE]; u8 received_uuid[BTRFS_UUID_SIZE]; __le64 ctransid; /* updated when an inode changes */ __le64 otransid; /* trans when created */ __le64 stransid; /* trans when sent. non-zero for received subvol */ __le64 rtransid; /* trans when received. non-zero for received subvol */ struct btrfs_timespec ctime; struct btrfs_timespec otime; struct btrfs_timespec stime; struct btrfs_timespec rtime; __le64 reserved[8]; /* for future */ } __attribute__ ((__packed__)); #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ struct btrfs_ioctl_ino_lookup_args) #define BTRFS_INO_LOOKUP_PATH_MAX 4080 struct btrfs_ioctl_ino_lookup_args { __u64 treeid; __u64 objectid; char name[BTRFS_INO_LOOKUP_PATH_MAX]; }; /* * All files have objectids in this range. */ #define BTRFS_FIRST_FREE_OBJECTID 256ULL #define BTRFS_LAST_FREE_OBJECTID -256ULL #define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL /* * The followings are macro for correctly getting member of * structures in both low and big endian platforms as per * btrfs-progs */ #ifdef __CHECKER__ #define __force __attribute__((force)) #else #define __force #endif #if __BYTE_ORDER == __BIG_ENDIAN #define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) #define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) #define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) #define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) #define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) #define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) #else #define cpu_to_le64(x) ((__force __le64)(u64)(x)) #define le64_to_cpu(x) ((__force u64)(__le64)(x)) #define cpu_to_le32(x) ((__force __le32)(u32)(x)) #define le32_to_cpu(x) ((__force u32)(__le32)(x)) #define cpu_to_le16(x) ((__force __le16)(u16)(x)) #define le16_to_cpu(x) ((__force u16)(__le16)(x)) #endif #define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \ static inline u##bits btrfs_##name(type *s) \ { \ return le##bits##_to_cpu(s->member); \ } \ static inline void btrfs_set_##name(type *s, u##bits val) \ { \ s->member = cpu_to_le##bits(val); \ } /* defined as btrfs_stack_root_ref_dirid */ BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64); /* defined as btrfs_stack_root_ref_sequence */ BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64); /* defined as btrfs_stack_root_ref_name_len */ BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16); struct lxc_storage; struct bdev_specs; struct lxc_conf; struct mytree_node { u64 objid; u64 parentid; char *name; char *dirname; }; struct my_btrfs_tree { struct mytree_node *nodes; int num; }; extern int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int btrfs_destroy(struct lxc_storage *orig); extern int btrfs_detect(const char *path); extern int btrfs_mount(struct lxc_storage *bdev); extern int btrfs_umount(struct lxc_storage *bdev); extern char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name, u16 name_len); extern int btrfs_list_get_path_rootid(int fd, u64 *treeid); extern bool is_btrfs_fs(const char *path); extern int is_btrfs_subvol(const char *path); extern bool btrfs_try_remove_subvol(const char *path); extern int btrfs_same_fs(const char *orig, const char *new); extern int btrfs_snapshot(const char *orig, const char *new); extern int btrfs_snapshot_wrapper(void *data); extern bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); extern bool btrfs_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); #endif /* __LXC_BTRFS_H */ lxc-2.0.11/src/lxc/storage/lvm.h0000644061062106075000000000405413435013473013310 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LVM_H #define __LXC_LVM_H #define _GNU_SOURCE #include struct lxc_storage; struct bdev_specs; struct lxc_conf; extern int lvm_detect(const char *path); extern int lvm_mount(struct lxc_storage *bdev); extern int lvm_umount(struct lxc_storage *bdev); extern int lvm_compare_lv_attr(const char *path, int pos, const char expected); extern int lvm_is_thin_volume(const char *path); extern int lvm_is_thin_pool(const char *path); extern int lvm_snapshot(const char *orig, const char *path, uint64_t size); extern int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int lvm_destroy(struct lxc_storage *orig); extern int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); extern bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); #endif /* __LXC_LVM_H */ lxc-2.0.11/src/lxc/storage/zfs.c0000644061062106075000000001622013435013473013305 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include "config.h" #include "log.h" #include "storage.h" #include "utils.h" #include "zfs.h" lxc_log_define(zfs, lxc); /* * zfs ops: * There are two ways we could do this. We could always specify the 'zfs device' * (i.e. tank/lxc lxc/container) as rootfs. But instead (at least right now) we * have lxc-create specify $lxcpath/$lxcname/rootfs as the mountpoint, so that * it is always mounted. That means 'mount' is really never needed and could be * noop, but for the sake of flexibility let's always bind-mount. */ int zfs_list_entry(const char *path, char *output, size_t inlen) { struct lxc_popen_FILE *f; int found=0; f = lxc_popen("zfs list 2> /dev/null"); if (f == NULL) { SYSERROR("popen failed"); return 0; } while (fgets(output, inlen, f->f)) { if (strstr(output, path)) { found = 1; break; } } (void) lxc_pclose(f); return found; } int zfs_detect(const char *path) { char *output = malloc(LXC_LOG_BUFFER_SIZE); if (!output) { ERROR("out of memory"); return 0; } int found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); free(output); return found; } int zfs_mount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "zfs")) return -22; if (!bdev->src || !bdev->dest) return -22; char *mntdata; unsigned long mntflags; if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { free(mntdata); return -22; } int ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata); free(mntdata); return ret; } int zfs_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "zfs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } int zfs_clone(const char *opath, const char *npath, const char *oname, const char *nname, const char *lxcpath, int snapshot) { // use the 'zfs list | grep opath' entry to get the zfsroot char output[MAXPATHLEN], option[MAXPATHLEN]; char *p; const char *zfsroot = output; int ret; pid_t pid; if (zfs_list_entry(opath, output, MAXPATHLEN)) { // zfsroot is output up to ' ' if ((p = strchr(output, ' ')) == NULL) return -1; *p = '\0'; if ((p = strrchr(output, '/')) == NULL) return -1; *p = '\0'; } else { zfsroot = lxc_global_config_value("lxc.bdev.zfs.root"); } ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", lxcpath, nname); if (ret < 0 || ret >= MAXPATHLEN) return -1; // zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname if (!snapshot) { if ((pid = fork()) < 0) return -1; if (!pid) { char dev[MAXPATHLEN]; ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, nname); if (ret < 0 || ret >= MAXPATHLEN) exit(EXIT_FAILURE); execlp("zfs", "zfs", "create", option, dev, (char *)NULL); exit(EXIT_FAILURE); } return wait_for_pid(pid); } else { // if snapshot, do // 'zfs snapshot zfsroot/oname@nname // zfs clone zfsroot/oname@nname zfsroot/nname char path1[MAXPATHLEN], path2[MAXPATHLEN]; ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", zfsroot, oname, nname); if (ret < 0 || ret >= MAXPATHLEN) return -1; (void) snprintf(path2, MAXPATHLEN, "%s/%s", zfsroot, nname); // if the snapshot exists, delete it if ((pid = fork()) < 0) return -1; if (!pid) { int dev0 = open("/dev/null", O_WRONLY); if (dev0 >= 0) dup2(dev0, STDERR_FILENO); execlp("zfs", "zfs", "destroy", path1, (char *)NULL); exit(EXIT_FAILURE); } // it probably doesn't exist so destroy probably will fail. (void) wait_for_pid(pid); // run first (snapshot) command if ((pid = fork()) < 0) return -1; if (!pid) { execlp("zfs", "zfs", "snapshot", path1, (char *)NULL); exit(EXIT_FAILURE); } if (wait_for_pid(pid) < 0) return -1; // run second (clone) command if ((pid = fork()) < 0) return -1; if (!pid) { execlp("zfs", "zfs", "clone", option, path1, path2, (char *)NULL); exit(EXIT_FAILURE); } return wait_for_pid(pid); } } int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { int len, ret; if (!orig->src || !orig->dest) return -1; if (snap && strcmp(orig->type, "zfs")) { ERROR("zfs snapshot from %s backing store is not supported", orig->type); return -1; } len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; new->src = malloc(len); if (!new->src) return -1; ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); if (ret < 0 || ret >= len) return -1; if ((new->dest = strdup(new->src)) == NULL) return -1; return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); } /* * TODO: detect whether this was a clone, and if so then also delete the * snapshot it was based on, so that we don't hold the original * container busy. */ int zfs_destroy(struct lxc_storage *orig) { pid_t pid; char output[MAXPATHLEN]; char *p; if ((pid = fork()) < 0) return -1; if (pid) return wait_for_pid(pid); if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) { ERROR("Error: zfs entry for %s not found", orig->src); return -1; } // zfs mount is output up to ' ' if ((p = strchr(output, ' ')) == NULL) return -1; *p = '\0'; execlp("zfs", "zfs", "destroy", "-r", output, (char *)NULL); exit(EXIT_FAILURE); } int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { const char *zfsroot; char option[MAXPATHLEN]; int ret; pid_t pid; if (!specs || !specs->zfs.zfsroot) zfsroot = lxc_global_config_value("lxc.bdev.zfs.root"); else zfsroot = specs->zfs.zfsroot; if (!(bdev->dest = strdup(dest))) { ERROR("No mount target specified or out of memory"); return -1; } if (!(bdev->src = strdup(bdev->dest))) { ERROR("out of memory"); return -1; } ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s", bdev->dest); if (ret < 0 || ret >= MAXPATHLEN) return -1; if ((pid = fork()) < 0) return -1; if (pid) return wait_for_pid(pid); char dev[MAXPATHLEN]; ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, n); if (ret < 0 || ret >= MAXPATHLEN) exit(EXIT_FAILURE); execlp("zfs", "zfs", "create", option, dev, (char *)NULL); exit(EXIT_FAILURE); } lxc-2.0.11/src/lxc/storage/nbd.h0000644061062106075000000000331613435013473013255 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_NBD_H #define __LXC_NBD_H #define _GNU_SOURCE #include #include struct lxc_storage; struct bdev_specs; struct lxc_conf; extern int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int nbd_destroy(struct lxc_storage *orig); extern int nbd_detect(const char *path); extern int nbd_mount(struct lxc_storage *bdev); extern int nbd_umount(struct lxc_storage *bdev); extern bool attach_nbd(char *src, struct lxc_conf *conf); extern void detach_nbd_idx(int idx); extern bool requires_nbd(const char *path); #endif /* __LXC_NBD_H */ lxc-2.0.11/src/lxc/storage/storage.h0000644061062106075000000001002013435013473014144 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_STORAGE_H #define __LXC_STORAGE_H #include "config.h" #include #include #include #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MNT_DETACH #define MNT_DETACH 2 #endif #ifndef MS_SLAVE #define MS_SLAVE (1 << 19) #endif #ifndef MS_RELATIME #define MS_RELATIME (1 << 21) #endif #ifndef MS_STRICTATIME #define MS_STRICTATIME (1 << 24) #endif #define DEFAULT_FS_SIZE 1073741824 #define DEFAULT_FSTYPE "ext4" struct lxc_storage; struct lxc_storage_ops { /* detect whether path is of this bdev type */ int (*detect)(const char *path); // mount requires src and dest to be set. int (*mount)(struct lxc_storage *bdev); int (*umount)(struct lxc_storage *bdev); int (*destroy)(struct lxc_storage *bdev); int (*create)(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); /* given original mount, rename the paths for cloned container */ int (*clone_paths)(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); bool can_snapshot; bool can_backup; }; /* When lxc is mounting a rootfs, then src will be the "lxc.rootfs.path" value, * dest will be the mount dir (i.e. "/lxc") When clone or create is * doing so, then dest will be "//rootfs", since we may need * to rsync from one to the other. */ struct lxc_storage { const struct lxc_storage_ops *ops; const char *type; char *src; char *dest; char *mntopts; /* Turn the following into a union if need be. */ /* lofd is the open fd for the mounted loopback file. */ int lofd; /* index for the connected nbd device. */ int nbd_idx; }; extern bool storage_is_dir(struct lxc_conf *conf, const char *path); extern bool storage_can_backup(struct lxc_conf *conf); /* Instantiate a lxc_storage object. The src is used to determine which blockdev * type this should be. The dst and data are optional, and will be used in case * of mount/umount. * * The source will be "dir:/var/lib/lxc/c1" or "lvm:/dev/lxc/c1". For other * backing stores, this will allow additional options. In particular, * "overlayfs:/var/lib/lxc/canonical/rootfs:/var/lib/lxc/c1/delta" will mean use * /var/lib/lxc/canonical/rootfs as lower dir, and /var/lib/lxc/c1/delta as the * upper, writeable layer. */ extern struct lxc_storage *storage_init(struct lxc_conf *conf, const char *src, const char *dst, const char *data); extern struct lxc_storage *storage_copy(struct lxc_container *c0, const char *cname, const char *lxcpath, const char *bdevtype, int flags, const char *bdevdata, uint64_t newsize, int *needs_rdep); extern struct lxc_storage *storage_create(const char *dest, const char *type, const char *cname, struct bdev_specs *specs); extern void storage_put(struct lxc_storage *bdev); extern bool storage_destroy(struct lxc_conf *conf); extern bool rootfs_is_blockdev(struct lxc_conf *conf); #endif // __LXC_STORAGE_H lxc-2.0.11/src/lxc/storage/rbd.h0000644061062106075000000000305513435013473013261 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_RDB_H #define __LXC_RDB_H #define _GNU_SOURCE #include struct lxc_storage; struct bdev_specs; struct lxc_conf; extern int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int rbd_destroy(struct lxc_storage *orig); extern int rbd_detect(const char *path); extern int rbd_mount(struct lxc_storage *bdev); extern int rbd_umount(struct lxc_storage *bdev); #endif /* __LXC_RDB_H */ lxc-2.0.11/src/lxc/storage/overlay.h0000644061062106075000000000541713435013473014177 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_OVERLAY_H #define __LXC_OVERLAY_H #include #include #include #include #include #include "storage.h" struct lxc_storage; struct bdev_specs; struct lxc_conf; struct lxc_rootfs; extern int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int ovl_destroy(struct lxc_storage *orig); extern int ovl_detect(const char *path); extern int ovl_mount(struct lxc_storage *bdev); extern int ovl_umount(struct lxc_storage *bdev); /* To be called from lxcapi_clone() in lxccontainer.c: When we clone a container * with overlay lxc.mount.entry entries we need to update absolute paths for * upper- and workdir. This update is done in two locations: * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done * independent of each other since lxc_conf->mountlist may container more mount * entries (e.g. from other included files) than lxc_conf->unexpanded_config . */ extern int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, const char *lxc_name, const char *newpath, const char *newname); /* To be called from functions in lxccontainer.c: Get lower directory for * overlay rootfs. */ extern char *ovl_get_lower(const char *rootfs_path); /* Get rootfs path for overlay backed containers. Allocated memory must be freed * by caller. */ extern char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen); /* Create upper- and workdirs for overlay mounts. */ extern int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path); #endif /* __LXC_OVERLAY_H */ lxc-2.0.11/src/lxc/storage/zfs.h0000644061062106075000000000355113435013473013315 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ZFS_H #define __LXC_ZFS_H #define _GNU_SOURCE #include #include struct lxc_storage; /* defined in lxccontainer.h */ struct bdev_specs; /* defined conf.h */ struct lxc_conf; extern int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int zfs_destroy(struct lxc_storage *orig); extern int zfs_detect(const char *path); extern int zfs_mount(struct lxc_storage *bdev); extern int zfs_umount(struct lxc_storage *bdev); extern bool zfs_copy(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); extern bool zfs_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, struct lxc_storage *new, uint64_t newsize); #endif /* __LXC_ZFS_H */ lxc-2.0.11/src/lxc/storage/aufs.h0000644061062106075000000000362513435013473013453 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_AUFS_H #define __LXC_AUFS_H #define _GNU_SOURCE #include #include #include "storage.h" struct lxc_storage; struct bdev_specs; struct lxc_conf; struct lxc_rootfs; int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); int aufs_destroy(struct lxc_storage *orig); int aufs_detect(const char *path); int aufs_mount(struct lxc_storage *bdev); int aufs_umount(struct lxc_storage *bdev); /* Get rootfs path for aufs backed containers. Allocated memory must be freed by * caller. */ char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen); /* * Create directories for aufs mounts. */ int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path); #endif /* __LXC_AUFS_H */ lxc-2.0.11/src/lxc/storage/btrfs.c0000644061062106075000000004175313435013473013634 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "btrfs.h" #include "rsync.h" #include "storage.h" #include "utils.h" #ifndef HAVE_STRLCAT #include "include/strlcat.h" #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(btrfs, lxc); /* defined in lxccontainer.c: needs to become common helper */ extern char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath); /* * Return the full path of objid under dirid. Let's say dirid is * /lxc/c1/rootfs, and objid is /lxc/c1/rootfs/a/b/c. Then we will * return a/b/c. If instead objid is for /lxc/c1/rootfs/a, we will * simply return a. */ char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name, u16 name_len) { struct btrfs_ioctl_ino_lookup_args args; int ret; size_t len, retlen; char *retpath; memset(&args, 0, sizeof(args)); args.treeid = dir_id; args.objectid = objid; ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); if (ret) { SYSERROR("Failed to lookup path for %llu %llu %s", (unsigned long long) dir_id, (unsigned long long) objid, name); return NULL; } else INFO("Got path for %llu %llu - %s", (unsigned long long) objid, (unsigned long long) dir_id, name); if (args.name[0]) { /* * we're in a subdirectory of ref_tree, the kernel ioctl * puts a / in there for us */ len = strlen(args.name) + name_len + 2; retpath = malloc(len); if (!retpath) return NULL; (void)strlcpy(retpath, args.name, len); (void)strlcat(retpath, "/", len); retlen = strlcat(retpath, name, len); if (retlen >= len) { ERROR("Failed to append name - %s", name); free(retpath); return NULL; } } else { /* we're at the root of ref_tree */ len = name_len + 1; retpath = malloc(len); if (!retpath) return NULL; *retpath = '\0'; retlen = strlcat(retpath, name, len); if (retlen >= len) { ERROR("Failed to append name - %s", name); free(retpath); return NULL; } } return retpath; } // // btrfs ops // int btrfs_list_get_path_rootid(int fd, u64 *treeid) { int ret; struct btrfs_ioctl_ino_lookup_args args; memset(&args, 0, sizeof(args)); args.objectid = BTRFS_FIRST_FREE_OBJECTID; ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); if (ret < 0) { WARN("Warning: can't perform the search -%s\n", strerror(errno)); return ret; } *treeid = args.treeid; return 0; } bool is_btrfs_fs(const char *path) { int fd, ret; struct btrfs_ioctl_space_args sargs; // make sure this is a btrfs filesystem fd = open(path, O_RDONLY); if (fd < 0) return false; sargs.space_slots = 0; sargs.total_spaces = 0; ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, &sargs); close(fd); if (ret < 0) return false; return true; } /* * Taken from btrfs toolsuite. Test if path is a subvolume. * return 0; path exists but it is not a subvolume * return 1; path exists and it is a subvolume * return < 0; error */ int is_btrfs_subvol(const char *path) { struct stat st; struct statfs stfs; int ret; ret = stat(path, &st); if (ret < 0) return -errno; if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) return 0; ret = statfs(path, &stfs); if (ret < 0) return -errno; return stfs.f_type == BTRFS_SUPER_MAGIC; } int btrfs_detect(const char *path) { struct stat st; int ret; if (!is_btrfs_fs(path)) return 0; // and make sure it's a subvolume. ret = stat(path, &st); if (ret < 0) return 0; if (st.st_ino == 256 && S_ISDIR(st.st_mode)) return 1; return 0; } int btrfs_mount(struct lxc_storage *bdev) { unsigned long mntflags; char *mntdata; int ret; if (strcmp(bdev->type, "btrfs")) return -22; if (!bdev->src || !bdev->dest) return -22; if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { free(mntdata); return -22; } ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata); free(mntdata); return ret; } int btrfs_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "btrfs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } static int btrfs_subvolume_create(const char *path) { int ret, saved_errno; size_t retlen; struct btrfs_ioctl_vol_args args; char *p, *newfull; int fd = -1; newfull = strdup(path); if (!newfull) { errno = ENOMEM; return -ENOMEM; } p = strrchr(newfull, '/'); if (!p) { free(newfull); errno = EINVAL; return -EINVAL; } *p = '\0'; fd = open(newfull, O_RDONLY); if (fd < 0) { free(newfull); return -errno; } memset(&args, 0, sizeof(args)); retlen = strlcpy(args.name, p + 1, BTRFS_SUBVOL_NAME_MAX); if (retlen >= BTRFS_SUBVOL_NAME_MAX) { free(newfull); close(fd); return -E2BIG; } ret = ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args); saved_errno = errno; close(fd); free(newfull); errno = saved_errno; return ret; } int btrfs_same_fs(const char *orig, const char *new) { int fd_orig = -1, fd_new = -1, ret = -1; struct btrfs_ioctl_fs_info_args orig_args, new_args; fd_orig = open(orig, O_RDONLY); if (fd_orig < 0) { SYSERROR("Error opening original rootfs %s", orig); goto out; } ret = ioctl(fd_orig, BTRFS_IOC_FS_INFO, &orig_args); if (ret < 0) { SYSERROR("BTRFS_IOC_FS_INFO %s", orig); goto out; } fd_new = open(new, O_RDONLY); if (fd_new < 0) { SYSERROR("Error opening new container dir %s", new); ret = -1; goto out; } ret = ioctl(fd_new, BTRFS_IOC_FS_INFO, &new_args); if (ret < 0) { SYSERROR("BTRFS_IOC_FS_INFO %s", new); goto out; } if (strncmp(orig_args.fsid, new_args.fsid, BTRFS_FSID_SIZE) != 0) { ret = -1; goto out; } ret = 0; out: if (fd_new != -1) close(fd_new); if (fd_orig != -1) close(fd_orig); return ret; } int btrfs_snapshot(const char *orig, const char *new) { size_t retlen; struct btrfs_ioctl_vol_args_v2 args; char *newdir, *newname; char *newfull = NULL; int saved_errno = -1; int fd = -1, fddst = -1, ret = -1; newfull = strdup(new); if (!newfull) goto out; ret = rmdir(newfull); if (ret < 0 && errno != ENOENT) goto out; newname = basename(newfull); fd = open(orig, O_RDONLY); if (fd < 0) goto out; newdir = dirname(newfull); fddst = open(newdir, O_RDONLY); if (fddst < 0) goto out; memset(&args, 0, sizeof(args)); args.fd = fd; retlen = strlcpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); if (retlen >= BTRFS_SUBVOL_NAME_MAX) goto out; ret = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); saved_errno = errno; out: if (fddst != -1) close(fddst); if (fd != -1) close(fd); free(newfull); if (saved_errno >= 0) errno = saved_errno; return ret; } int btrfs_snapshot_wrapper(void *data) { struct rsync_data_char *arg = data; if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } return btrfs_snapshot(arg->src, arg->dest); } int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { if (!orig->dest || !orig->src) return -1; if (strcmp(orig->type, "btrfs")) { int len, ret; if (snap) { ERROR("btrfs snapshot from %s backing store is not supported", orig->type); return -1; } len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; new->src = malloc(len); if (!new->src) return -1; ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); if (ret < 0 || ret >= len) return -1; } else { // in case rootfs is in custom path, reuse it if ((new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath)) == NULL) return -1; } if ((new->dest = strdup(new->src)) == NULL) return -1; if (orig->mntopts && (new->mntopts = strdup(orig->mntopts)) == NULL) return -1; if (snap) { struct rsync_data_char sdata; if (!am_guest_unpriv()) return btrfs_snapshot(orig->dest, new->dest); sdata.dest = new->dest; sdata.src = orig->dest; return userns_exec_full(conf, btrfs_snapshot_wrapper, &sdata, "btrfs_snapshot_wrapper"); } if (rmdir(new->dest) < 0 && errno != ENOENT) { SYSERROR("removing %s", new->dest); return -1; } return btrfs_subvolume_create(new->dest); } static int btrfs_do_destroy_subvol(const char *path) { int ret, fd = -1; size_t retlen; struct btrfs_ioctl_vol_args args; char *p, *newfull = strdup(path); if (!newfull) { ERROR("Error: out of memory"); return -1; } p = strrchr(newfull, '/'); if (!p) { ERROR("bad path: %s", path); free(newfull); return -1; } *p = '\0'; fd = open(newfull, O_RDONLY); if (fd < 0) { SYSERROR("Error opening %s", newfull); free(newfull); return -1; } memset(&args, 0, sizeof(args)); retlen = strlcpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); if (retlen >= BTRFS_SUBVOL_NAME_MAX) { free(newfull); close(fd); return -E2BIG; } ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); INFO("btrfs: snapshot destroy ioctl returned %d for %s", ret, path); if (ret < 0 && errno == EPERM) ERROR("Is the rootfs mounted with -o user_subvol_rm_allowed?"); free(newfull); close(fd); return ret; } static int get_btrfs_tree_idx(struct my_btrfs_tree *tree, u64 id) { int i; if (!tree) return -1; for (i = 0; i < tree->num; i++) { if (tree->nodes[i].objid == id) return i; } return -1; } static struct my_btrfs_tree *create_my_btrfs_tree(u64 id, const char *path, int name_len) { struct my_btrfs_tree *tree; tree = malloc(sizeof(struct my_btrfs_tree)); if (!tree) return NULL; tree->nodes = malloc(sizeof(struct mytree_node)); if (!tree->nodes) { free(tree); return NULL; } tree->num = 1; tree->nodes[0].dirname = NULL; tree->nodes[0].name = strdup(path); if (!tree->nodes[0].name) { free(tree->nodes); free(tree); return NULL; } tree->nodes[0].parentid = 0; tree->nodes[0].objid = id; return tree; } static bool update_tree_node(struct mytree_node *n, u64 id, u64 parent, char *name, int name_len, char *dirname) { if (id) n->objid = id; if (parent) n->parentid = parent; if (name) { n->name = malloc(name_len + 1); if (!n->name) return false; (void)strlcpy(n->name, name, name_len + 1); } if (dirname) { size_t len; len = strlen(dirname); n->dirname = malloc(len + 1); if (!n->dirname) { free(n->name); return false; } (void)strlcpy(n->dirname, dirname, len + 1); } return true; } static bool add_btrfs_tree_node(struct my_btrfs_tree *tree, u64 id, u64 parent, char *name, int name_len, char *dirname) { struct mytree_node *tmp; int i = get_btrfs_tree_idx(tree, id); if (i != -1) return update_tree_node(&tree->nodes[i], id, parent, name, name_len, dirname); tmp = realloc(tree->nodes, (tree->num+1) * sizeof(struct mytree_node)); if (!tmp) return false; tree->nodes = tmp; memset(&tree->nodes[tree->num], 0, sizeof(struct mytree_node)); if (!update_tree_node(&tree->nodes[tree->num], id, parent, name, name_len, dirname)) return false; tree->num++; return true; } static void free_btrfs_tree(struct my_btrfs_tree *tree) { int i; if (!tree) return; for (i = 0; i < tree->num; i++) { free(tree->nodes[i].name); free(tree->nodes[i].dirname); } free(tree->nodes); free(tree); } /* * Given a @tree of subvolumes under @path, ask btrfs to remove each * subvolume */ static bool do_remove_btrfs_children(struct my_btrfs_tree *tree, u64 root_id, const char *path) { int i, ret; char *newpath; size_t len; for (i = 0; i < tree->num; i++) { if (tree->nodes[i].parentid == root_id) { if (!tree->nodes[i].dirname) { WARN("Odd condition: child objid with no name under %s\n", path); continue; } len = strlen(path) + strlen(tree->nodes[i].dirname) + 2; newpath = malloc(len); if (!newpath) { ERROR("Out of memory"); return false; } ret = snprintf(newpath, len, "%s/%s", path, tree->nodes[i].dirname); if (ret < 0 || ret >= len) { free(newpath); return false; } if (!do_remove_btrfs_children(tree, tree->nodes[i].objid, newpath)) { ERROR("Failed to prune %s\n", tree->nodes[i].name); free(newpath); return false; } if (btrfs_do_destroy_subvol(newpath) != 0) { ERROR("Failed to remove %s\n", newpath); free(newpath); return false; } free(newpath); } } return true; } static int btrfs_recursive_destroy(const char *path) { u64 root_id; int fd; struct btrfs_ioctl_search_args args; struct btrfs_ioctl_search_key *sk = &args.key; struct btrfs_ioctl_search_header sh; struct btrfs_root_ref *ref; struct my_btrfs_tree *tree; int ret, e, i; unsigned long off = 0; int name_len; char *name; char *tmppath; u64 dir_id; fd = open(path, O_RDONLY); if (fd < 0) { ERROR("Failed to open %s\n", path); return -1; } if (btrfs_list_get_path_rootid(fd, &root_id)) { e = errno; close(fd); if (e == EPERM || e == EACCES) { WARN("Will simply try removing"); goto ignore_search; } return -1; } tree = create_my_btrfs_tree(root_id, path, strlen(path)); if (!tree) { ERROR("Out of memory\n"); close(fd); return -1; } /* Walk all subvols looking for any under this id */ memset(&args, 0, sizeof(args)); /* search in the tree of tree roots */ sk->tree_id = 1; sk->max_type = BTRFS_ROOT_REF_KEY; sk->min_type = BTRFS_ROOT_ITEM_KEY; sk->min_objectid = 0; sk->max_objectid = (u64)-1; sk->max_offset = (u64)-1; sk->min_offset = 0; sk->max_transid = (u64)-1; sk->nr_items = 4096; while(1) { ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); e = errno; if (ret < 0) { close(fd); free_btrfs_tree(tree); if (e == EPERM || e == EACCES) { WARN("Warn: can't perform the search under %s. Will simply try removing", path); goto ignore_search; } ERROR("Error: can't perform the search under %s\n", path); return -1; } if (sk->nr_items == 0) break; off = 0; for (i = 0; i < sk->nr_items; i++) { memcpy(&sh, args.buf + off, sizeof(sh)); off += sizeof(sh); /* * A backref key with the name and dirid of the parent * comes followed by the reoot ref key which has the * name of the child subvol in question. */ if (sh.objectid != root_id && sh.type == BTRFS_ROOT_BACKREF_KEY) { ref = (struct btrfs_root_ref *)(args.buf + off); name_len = btrfs_stack_root_ref_name_len(ref); name = (char *)(ref + 1); dir_id = btrfs_stack_root_ref_dirid(ref); tmppath = get_btrfs_subvol_path(fd, sh.offset, dir_id, name, name_len); if (!add_btrfs_tree_node(tree, sh.objectid, sh.offset, name, name_len, tmppath)) { ERROR("Out of memory"); free_btrfs_tree(tree); free(tmppath); close(fd); return -1; } free(tmppath); } off += sh.len; /* * record the mins in sk so we can make sure the * next search doesn't repeat this root */ sk->min_objectid = sh.objectid; sk->min_type = sh.type; sk->min_offset = sh.offset; } sk->nr_items = 4096; sk->min_offset++; if (!sk->min_offset) sk->min_type++; else continue; if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) { sk->min_type = BTRFS_ROOT_ITEM_KEY; sk->min_objectid++; } else continue; if (sk->min_objectid >= sk->max_objectid) break; } close(fd); /* now actually remove them */ if (!do_remove_btrfs_children(tree, root_id, path)) { free_btrfs_tree(tree); ERROR("failed pruning\n"); return -1; } free_btrfs_tree(tree); /* All child subvols have been removed, now remove this one */ ignore_search: return btrfs_do_destroy_subvol(path); } bool btrfs_try_remove_subvol(const char *path) { if (!btrfs_detect(path)) return false; return btrfs_recursive_destroy(path) == 0; } int btrfs_destroy(struct lxc_storage *orig) { return btrfs_recursive_destroy(orig->src); } int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { bdev->src = strdup(dest); bdev->dest = strdup(dest); if (!bdev->src || !bdev->dest) return -1; return btrfs_subvolume_create(bdev->dest); } lxc-2.0.11/src/lxc/storage/lvm.c0000644061062106075000000002404213435013473013302 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "lvm.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" /* major()/minor() */ #ifdef MAJOR_IN_MKDEV # include #endif lxc_log_define(lvm, lxc); extern char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath); /* * LVM ops */ /* * path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not * yet exist. This function will attempt to create /dev/$vg/$lv of size * $size. If thinpool is specified, we'll check for it's existence and if * it's * a valid thin pool, and if so, we'll create the requested lv from that * thin * pool. */ static int do_lvm_create(const char *path, uint64_t size, const char *thinpool) { int ret, pid, len; char sz[24], *pathdup, *vg, *lv, *tp = NULL; if ((pid = fork()) < 0) { SYSERROR("failed fork"); return -1; } if (pid > 0) return wait_for_pid(pid); // specify bytes to lvcreate ret = snprintf(sz, 24, "%"PRIu64"b", size); if (ret < 0 || ret >= 24) exit(EXIT_FAILURE); pathdup = strdup(path); if (!pathdup) exit(EXIT_FAILURE); lv = strrchr(pathdup, '/'); if (!lv) exit(EXIT_FAILURE); *lv = '\0'; lv++; vg = strrchr(pathdup, '/'); if (!vg) exit(EXIT_FAILURE); vg++; if (thinpool) { len = strlen(pathdup) + strlen(thinpool) + 2; tp = alloca(len); ret = snprintf(tp, len, "%s/%s", pathdup, thinpool); if (ret < 0 || ret >= len) exit(EXIT_FAILURE); ret = lvm_is_thin_pool(tp); INFO("got %d for thin pool at path: %s", ret, tp); if (ret < 0) exit(EXIT_FAILURE); if (!ret) tp = NULL; } (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); if (!tp) execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); else execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL); SYSERROR("execlp"); exit(EXIT_FAILURE); } /* * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM * prefix "LVM-", then this is an lvm2 LV */ int lvm_detect(const char *path) { char devp[MAXPATHLEN], buf[4]; FILE *fout; int ret; struct stat statbuf; if (strncmp(path, "lvm:", 4) == 0) return 1; // take their word for it ret = stat(path, &statbuf); if (ret != 0) return 0; if (!S_ISBLK(statbuf.st_mode)) return 0; ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid", major(statbuf.st_rdev), minor(statbuf.st_rdev)); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("lvm uuid pathname too long"); return 0; } fout = fopen(devp, "r"); if (!fout) return 0; ret = fread(buf, 1, 4, fout); fclose(fout); if (ret != 4 || strncmp(buf, "LVM-", 4) != 0) return 0; return 1; } int lvm_mount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "lvm")) return -22; if (!bdev->src || !bdev->dest) return -22; /* if we might pass in data sometime, then we'll have to enrich * mount_unknown_fs */ return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts); } int lvm_umount(struct lxc_storage *bdev) { if (strcmp(bdev->type, "lvm")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } int lvm_compare_lv_attr(const char *path, int pos, const char expected) { struct lxc_popen_FILE *f; int ret, len, status, start=0; char *cmd, output[12]; const char *lvscmd = "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null"; len = strlen(lvscmd) + strlen(path) - 1; cmd = alloca(len); ret = snprintf(cmd, len, lvscmd, path); if (ret < 0 || ret >= len) return -1; f = lxc_popen(cmd); if (f == NULL) { SYSERROR("popen failed"); return -1; } ret = fgets(output, 12, f->f) == NULL; status = lxc_pclose(f); if (ret || WEXITSTATUS(status)) // Assume either vg or lvs do not exist, default // comparison to false. return 0; len = strlen(output); while(start < len && output[start] == ' ') start++; if (start + pos < len && output[start + pos] == expected) return 1; return 0; } int lvm_is_thin_volume(const char *path) { return lvm_compare_lv_attr(path, 6, 't'); } int lvm_is_thin_pool(const char *path) { return lvm_compare_lv_attr(path, 0, 't'); } int lvm_snapshot(const char *orig, const char *path, uint64_t size) { int ret, pid; char sz[24], *pathdup, *lv; if ((pid = fork()) < 0) { SYSERROR("failed fork"); return -1; } if (pid > 0) return wait_for_pid(pid); // specify bytes to lvcreate ret = snprintf(sz, 24, "%"PRIu64"b", size); if (ret < 0 || ret >= 24) exit(EXIT_FAILURE); pathdup = strdup(path); if (!pathdup) exit(EXIT_FAILURE); lv = strrchr(pathdup, '/'); if (!lv) { free(pathdup); exit(EXIT_FAILURE); } *lv = '\0'; lv++; // check if the original lv is backed by a thin pool, in which case we // cannot specify a size that's different from the original size. ret = lvm_is_thin_volume(orig); if (ret == -1) { free(pathdup); return -1; } (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); if (!ret) { ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL); } else { ret = execlp("lvcreate", "lvcreate", "-s", "-n", lv, orig, (char *)NULL); } free(pathdup); exit(EXIT_FAILURE); } int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { char fstype[100]; uint64_t size = newsize; int len, ret; const char *cmd_args[2]; char cmd_output[MAXPATHLEN]; if (!orig->src || !orig->dest) return -1; if (strcmp(orig->type, "lvm")) { const char *vg; if (snap) { ERROR("LVM snapshot from %s backing store is not supported", orig->type); return -1; } vg = lxc_global_config_value("lxc.bdev.lvm.vg"); len = strlen("/dev/") + strlen(vg) + strlen(cname) + 2; if ((new->src = malloc(len)) == NULL) return -1; ret = snprintf(new->src, len, "/dev/%s/%s", vg, cname); if (ret < 0 || ret >= len) return -1; } else { new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); if (!new->src) return -1; } if (orig->mntopts) { new->mntopts = strdup(orig->mntopts); if (!new->mntopts) return -1; } len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; new->dest = malloc(len); if (!new->dest) return -1; ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); if (ret < 0 || ret >= len) return -1; if (mkdir_p(new->dest, 0755) < 0) return -1; if (is_blktype(orig)) { if (!newsize && blk_getsize(orig, &size) < 0) { ERROR("Error getting size of %s", orig->src); return -1; } if (detect_fs(orig, fstype, 100) < 0) { INFO("could not find fstype for %s, using ext3", orig->src); return -1; } } else { sprintf(fstype, "ext3"); if (!newsize) size = DEFAULT_FS_SIZE; } if (snap) { if (lvm_snapshot(orig->src, new->src, size) < 0) { ERROR("could not create %s snapshot of %s", new->src, orig->src); return -1; } } else { if (do_lvm_create(new->src, size, lxc_global_config_value("lxc.bdev.lvm.thin_pool")) < 0) { ERROR("Error creating new lvm blockdev"); return -1; } cmd_args[0] = fstype; cmd_args[1] = new->src; // create an fs in the loopback file ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, (void *)cmd_args); if (ret < 0) return -1; } return 0; } int lvm_destroy(struct lxc_storage *orig) { pid_t pid; if ((pid = fork()) < 0) return -1; if (!pid) { (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); execlp("lvremove", "lvremove", "-f", orig->src, (char *)NULL); exit(EXIT_FAILURE); } return wait_for_pid(pid); } int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { const char *vg, *thinpool, *fstype, *lv = n; uint64_t sz; int ret, len; const char *cmd_args[2]; char cmd_output[MAXPATHLEN]; if (!specs) return -1; vg = specs->lvm.vg; if (!vg) vg = lxc_global_config_value("lxc.bdev.lvm.vg"); thinpool = specs->lvm.thinpool; if (!thinpool) thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool"); /* /dev/$vg/$lv */ if (specs->lvm.lv) lv = specs->lvm.lv; len = strlen(vg) + strlen(lv) + 7; bdev->src = malloc(len); if (!bdev->src) return -1; ret = snprintf(bdev->src, len, "/dev/%s/%s", vg, lv); if (ret < 0 || ret >= len) return -1; // fssize is in bytes. sz = specs->fssize; if (!sz) sz = DEFAULT_FS_SIZE; if (do_lvm_create(bdev->src, sz, thinpool) < 0) { ERROR("Error creating new lvm blockdev %s size %"PRIu64" bytes", bdev->src, sz); return -1; } fstype = specs->fstype; if (!fstype) fstype = DEFAULT_FSTYPE; cmd_args[0] = fstype; cmd_args[1] = bdev->src; ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, (void *)cmd_args); if (ret < 0) return -1; if (!(bdev->dest = strdup(dest))) return -1; if (mkdir_p(bdev->dest, 0755) < 0) { ERROR("Error creating %s", bdev->dest); return -1; } return 0; } lxc-2.0.11/src/lxc/storage/storage_utils.c0000644061062106075000000002423413435013473015373 00000000000000/* * lxc: linux Container library * * Copyright © 2017 Canonical Ltd. * * Authors: * Christian Brauner * * This program 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 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "nbd.h" #include "parse.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12, 114, size_t) #endif lxc_log_define(storage_utils, lxc); /* the bulk of this needs to become a common helper */ char *dir_new_path(char *src, const char *oldname, const char *name, const char *oldpath, const char *lxcpath) { char *ret, *p, *p2; int l1, l2, nlen; nlen = strlen(src) + 1; l1 = strlen(oldpath); p = src; /* if src starts with oldpath, look for oldname only after * that path */ if (strncmp(src, oldpath, l1) == 0) { p += l1; nlen += (strlen(lxcpath) - l1); } l2 = strlen(oldname); while ((p = strstr(p, oldname)) != NULL) { p += l2; nlen += strlen(name) - l2; } ret = malloc(nlen); if (!ret) return NULL; p = ret; if (strncmp(src, oldpath, l1) == 0) { p += sprintf(p, "%s", lxcpath); src += l1; } while ((p2 = strstr(src, oldname)) != NULL) { /* copy text up to oldname */ strncpy(p, src, p2 - src); /* move target pointer (p) */ p += p2 - src; /* print new name in place of oldname */ p += sprintf(p, "%s", name); /* move src to end of oldname */ src = p2 + l2; } /* copy the rest of src */ sprintf(p, "%s", src); return ret; } /* * attach_block_device returns true if all went well, * meaning either a block device was attached or was not * needed. It returns false if something went wrong and * container startup should be stopped. */ bool attach_block_device(struct lxc_conf *conf) { char *path; if (!conf->rootfs.path) return true; path = conf->rootfs.path; if (!requires_nbd(path)) return true; path = strchr(path, ':'); if (!path) return false; path++; if (!attach_nbd(path, conf)) return false; return true; } /* * return block size of dev->src in units of bytes */ int blk_getsize(struct lxc_storage *bdev, uint64_t *size) { int fd, ret; char *path = bdev->src; if (strcmp(bdev->type, "loop") == 0) path = bdev->src + 5; fd = open(path, O_RDONLY); if (fd < 0) return -1; ret = ioctl(fd, BLKGETSIZE64, size); // size of device in bytes close(fd); return ret; } void detach_block_device(struct lxc_conf *conf) { if (conf->nbd_idx != -1) detach_nbd_idx(conf->nbd_idx); } /* * Given a lxc_storage (presumably blockdev-based), detect the fstype * by trying mounting (in a private mntns) it. * @lxc_storage: bdev to investigate * @type: preallocated char* in which to write the fstype * @len: length of passed in char* * Returns length of fstype, of -1 on error */ int detect_fs(struct lxc_storage *bdev, char *type, int len) { int p[2], ret; size_t linelen; pid_t pid; FILE *f; char *sp1, *sp2, *sp3, *line = NULL; char *srcdev; if (!bdev || !bdev->src || !bdev->dest) return -1; srcdev = bdev->src; if (strcmp(bdev->type, "loop") == 0) srcdev = bdev->src + 5; ret = pipe(p); if (ret < 0) return -1; if ((pid = fork()) < 0) return -1; if (pid > 0) { int status; close(p[1]); memset(type, 0, len); ret = read(p[0], type, len - 1); close(p[0]); if (ret < 0) { SYSERROR("error reading from pipe"); wait(&status); return -1; } else if (ret == 0) { ERROR("child exited early - fstype not found"); wait(&status); return -1; } wait(&status); type[len - 1] = '\0'; INFO("detected fstype %s for %s", type, srcdev); return ret; } if (unshare(CLONE_NEWNS) < 0) exit(1); if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts); if (ret < 0) { ERROR("failed mounting %s onto %s to detect fstype", srcdev, bdev->dest); exit(1); } // if symlink, get the real dev name char devpath[MAXPATHLEN]; char *l = linkderef(srcdev, devpath); if (!l) exit(1); f = fopen("/proc/self/mounts", "r"); if (!f) exit(1); while (getline(&line, &linelen, f) != -1) { sp1 = strchr(line, ' '); if (!sp1) exit(1); *sp1 = '\0'; if (strcmp(line, l)) continue; sp2 = strchr(sp1 + 1, ' '); if (!sp2) exit(1); *sp2 = '\0'; sp3 = strchr(sp2 + 1, ' '); if (!sp3) exit(1); *sp3 = '\0'; sp2++; if (write(p[1], sp2, strlen(sp2)) != strlen(sp2)) exit(1); exit(0); } exit(1); } int do_mkfs_exec_wrapper(void *args) { int ret; char *mkfs; char **data = args; /* strlen("mkfs.") * + * strlen(data[0]) * + * \0 */ size_t len = 5 + strlen(data[0]) + 1; mkfs = malloc(len); if (!mkfs) return -1; ret = snprintf(mkfs, len, "mkfs.%s", data[0]); if (ret < 0 || (size_t)ret >= len) { free(mkfs); return -1; } TRACE("executing \"%s %s\"", mkfs, data[1]); execlp(mkfs, mkfs, data[1], (char *)NULL); SYSERROR("failed to run \"%s %s \"", mkfs, data[1]); free(mkfs); return -1; } /* * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm * is a block device. */ int is_blktype(struct lxc_storage *b) { if (strcmp(b->type, "lvm") == 0) return 1; return 0; } int mount_unknown_fs(const char *rootfs, const char *target, const char *options) { size_t i; int ret; struct cbarg { const char *rootfs; const char *target; const char *options; } cbarg = { .rootfs = rootfs, .target = target, .options = options, }; /* * find the filesystem type with brute force: * first we check with /etc/filesystems, in case the modules * are auto-loaded and fall back to the supported kernel fs */ char *fsfile[] = { "/etc/filesystems", "/proc/filesystems", }; for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) { if (access(fsfile[i], F_OK)) continue; ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg); if (ret < 0) { ERROR("failed to parse '%s'", fsfile[i]); return -1; } if (ret) return 0; } ERROR("failed to determine fs type for '%s'", rootfs); return -1; } /* * These are copied from conf.c. However as conf.c will be moved to using * the callback system, they can be pulled from there eventually, so we * don't need to pollute utils.c with these low level functions */ int find_fstype_cb(char *buffer, void *data) { struct cbarg { const char *rootfs; const char *target; const char *options; } *cbarg = data; unsigned long mntflags; char *mntdata; char *fstype; /* we don't try 'nodev' entries */ if (strstr(buffer, "nodev")) return 0; fstype = buffer; fstype += lxc_char_left_gc(fstype, strlen(fstype)); fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0'; DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs, cbarg->target, fstype); if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) { free(mntdata); return 0; } if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) { DEBUG("mount failed with error: %s", strerror(errno)); free(mntdata); return 0; } free(mntdata); INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs, cbarg->target, fstype); return 1; } char *linkderef(char *path, char *dest) { struct stat sbuf; ssize_t ret; ret = stat(path, &sbuf); if (ret < 0) return NULL; if (!S_ISLNK(sbuf.st_mode)) return path; ret = readlink(path, dest, MAXPATHLEN); if (ret < 0) { SYSERROR("error reading link %s", path); return NULL; } else if (ret >= MAXPATHLEN) { ERROR("link in %s too long", path); return NULL; } dest[ret] = '\0'; return dest; } /* * is an unprivileged user allowed to make this kind of snapshot */ bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, bool maybesnap) { if (!t) { // new type will be same as original // (unless snap && b->type == dir, in which case it will be // overlayfs -- which is also allowed) if (strcmp(b->type, "dir") == 0 || strcmp(b->type, "aufs") == 0 || strcmp(b->type, "overlayfs") == 0 || strcmp(b->type, "btrfs") == 0 || strcmp(b->type, "loop") == 0) return true; return false; } // unprivileged users can copy and snapshot dir, overlayfs, // and loop. In particular, not zfs, btrfs, or lvm. if (strcmp(t, "dir") == 0 || strcmp(t, "aufs") == 0 || strcmp(t, "overlayfs") == 0 || strcmp(t, "btrfs") == 0 || strcmp(t, "loop") == 0) return true; return false; } bool is_valid_storage_type(const char *type) { if (strcmp(type, "dir") == 0 || strcmp(type, "btrfs") == 0 || strcmp(type, "aufs") == 0 || strcmp(type, "loop") == 0 || strcmp(type, "lvm") == 0 || strcmp(type, "nbd") == 0 || strcmp(type, "overlayfs") == 0 || strcmp(type, "rbd") == 0 || strcmp(type, "zfs") == 0) return true; return false; } int storage_destroy_wrapper(void *data) { struct lxc_conf *conf = data; if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (!storage_destroy(conf)) return -1; return 0; } lxc-2.0.11/src/lxc/storage/loop.c0000644061062106075000000001370013435013473013454 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "log.h" #include "loop.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" lxc_log_define(loop, lxc); static int do_loop_create(const char *path, uint64_t size, const char *fstype); /* * No idea what the original blockdev will be called, but the copy will be * called $lxcpath/$lxcname/rootdev */ int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf) { char fstype[100]; uint64_t size = newsize; int len, ret; char *srcdev; if (snap) { ERROR("loop devices cannot be snapshotted."); return -1; } if (!orig->dest || !orig->src) return -1; len = strlen(lxcpath) + strlen(cname) + strlen("rootdev") + 3; srcdev = alloca(len); ret = snprintf(srcdev, len, "%s/%s/rootdev", lxcpath, cname); if (ret < 0 || ret >= len) return -1; new->src = malloc(len + 5); if (!new->src) return -1; ret = snprintf(new->src, len + 5, "loop:%s", srcdev); if (ret < 0 || ret >= len + 5) return -1; new->dest = malloc(len); if (!new->dest) return -1; ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); if (ret < 0 || ret >= len) return -1; // it's tempting to say: if orig->src == loopback and !newsize, then // copy the loopback file. However, we'd have to make sure to // correctly keep holes! So punt for now. if (is_blktype(orig)) { if (!newsize && blk_getsize(orig, &size) < 0) { ERROR("Error getting size of %s", orig->src); return -1; } if (detect_fs(orig, fstype, 100) < 0) { INFO("could not find fstype for %s, using %s", orig->src, DEFAULT_FSTYPE); return -1; } } else { sprintf(fstype, "%s", DEFAULT_FSTYPE); if (!newsize) size = DEFAULT_FS_SIZE; } return do_loop_create(srcdev, size, fstype); } int loop_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs) { const char *fstype; uint64_t sz; int ret, len; char *srcdev; if (!specs) return -1; // dest is passed in as $lxcpath / $lxcname / rootfs // srcdev will be: $lxcpath / $lxcname / rootdev // src will be 'loop:$srcdev' len = strlen(dest) + 2; srcdev = alloca(len); ret = snprintf(srcdev, len, "%s", dest); if (ret < 0 || ret >= len) return -1; sprintf(srcdev + len - 4, "dev"); bdev->src = malloc(len + 5); if (!bdev->src) return -1; ret = snprintf(bdev->src, len + 5, "loop:%s", srcdev); if (ret < 0 || ret >= len + 5) return -1; sz = specs->fssize; if (!sz) sz = DEFAULT_FS_SIZE; fstype = specs->fstype; if (!fstype) fstype = DEFAULT_FSTYPE; if (!(bdev->dest = strdup(dest))) return -1; if (mkdir_p(bdev->dest, 0755) < 0) { ERROR("Error creating %s", bdev->dest); return -1; } return do_loop_create(srcdev, sz, fstype); } int loop_destroy(struct lxc_storage *orig) { return unlink(orig->src + 5); } int loop_detect(const char *path) { int ret; struct stat s; if (strncmp(path, "loop:", 5) == 0) return 1; ret = stat(path, &s); if (ret < 0) return 0; if (__S_ISTYPE(s.st_mode, S_IFREG)) return 1; return 0; } int loop_mount(struct lxc_storage *bdev) { int ret, loopfd; char loname[MAXPATHLEN]; char *src = bdev->src; if (strcmp(bdev->type, "loop")) return -22; if (!bdev->src || !bdev->dest) return -22; /* skip prefix */ if (!strncmp(bdev->src, "loop:", 5)) src += 5; loopfd = lxc_prepare_loop_dev(src, loname, LO_FLAGS_AUTOCLEAR); if (loopfd < 0) { ERROR("failed to prepare loop device for loop file \"%s\"", src); return -1; } DEBUG("prepared loop device \"%s\"", loname); ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); if (ret < 0) ERROR("failed to mount rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); else bdev->lofd = loopfd; DEBUG("mounted rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); return ret; } int loop_umount(struct lxc_storage *bdev) { int ret; if (strcmp(bdev->type, "loop")) return -22; if (!bdev->src || !bdev->dest) return -22; ret = umount(bdev->dest); if (bdev->lofd >= 0) { close(bdev->lofd); bdev->lofd = -1; } return ret; } static int do_loop_create(const char *path, uint64_t size, const char *fstype) { int fd, ret; const char *cmd_args[2] = {fstype, path}; char cmd_output[MAXPATHLEN]; // create the new loopback file. fd = creat(path, S_IRUSR|S_IWUSR); if (fd < 0) return -1; if (lseek(fd, size, SEEK_SET) < 0) { SYSERROR("Error seeking to set new loop file size"); close(fd); return -1; } if (write(fd, "1", 1) != 1) { SYSERROR("Error creating new loop file"); close(fd); return -1; } ret = close(fd); if (ret < 0) { SYSERROR("Error closing new loop file"); return -1; } // create an fs in the loopback file ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, (void *)cmd_args); if (ret < 0) return -1; return 0; } lxc-2.0.11/src/lxc/storage/dir.h0000644061062106075000000000305513435013473013270 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_DIR_H #define __LXC_DIR_H #define _GNU_SOURCE #include struct lxc_storage; struct bdev_specs; struct lxc_conf; extern int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, int snap, uint64_t newsize, struct lxc_conf *conf); extern int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, struct bdev_specs *specs); extern int dir_destroy(struct lxc_storage *orig); extern int dir_detect(const char *path); extern int dir_mount(struct lxc_storage *bdev); extern int dir_umount(struct lxc_storage *bdev); #endif /* __LXC_DIR_H */ lxc-2.0.11/src/lxc/storage/rsync.h0000644061062106075000000000247313435013473013653 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_RSYNC_H #define __LXC_RSYNC_H #define _GNU_SOURCE #include struct rsync_data { struct lxc_storage *orig; struct lxc_storage *new; }; struct rsync_data_char { char *src; char *dest; }; int do_rsync(const char *src, const char *dest); int rsync_delta_wrapper(void *data); int rsync_delta(struct rsync_data_char *data); int rsync_rootfs(struct rsync_data *data); int rsync_rootfs_wrapper(void *data); #endif // __LXC_RSYNC_H lxc-2.0.11/src/lxc/sync.h0000644061062106075000000000332113435013473012016 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_SYNC_H #define __LXC_SYNC_H struct lxc_handler; enum { LXC_SYNC_STARTUP, LXC_SYNC_CONFIGURE, LXC_SYNC_POST_CONFIGURE, LXC_SYNC_CGROUP, LXC_SYNC_CGROUP_UNSHARE, LXC_SYNC_CGROUP_LIMITS, LXC_SYNC_POST_CGROUP, LXC_SYNC_RESTART, LXC_SYNC_POST_RESTART, LXC_SYNC_ERROR = -1 /* Used to report errors from another process */ }; int lxc_sync_init(struct lxc_handler *handler); void lxc_sync_fini(struct lxc_handler *); void lxc_sync_fini_parent(struct lxc_handler *); void lxc_sync_fini_child(struct lxc_handler *); int lxc_sync_wake_child(struct lxc_handler *, int); int lxc_sync_wait_child(struct lxc_handler *, int); int lxc_sync_wake_parent(struct lxc_handler *, int); int lxc_sync_wait_parent(struct lxc_handler *, int); int lxc_sync_barrier_parent(struct lxc_handler *, int); int lxc_sync_barrier_child(struct lxc_handler *, int); #endif lxc-2.0.11/src/lxc/parse.h0000644061062106075000000000261413435013473012160 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_PARSE_H #define __LXC_PARSE_H typedef int (*lxc_dir_cb)(const char *name, const char *directory, const char *file, void *data); typedef int (*lxc_file_cb)(char *buffer, void *data); extern int lxc_file_for_each_line(const char *file, lxc_file_cb callback, void* data); extern int lxc_char_left_gc(const char *buffer, size_t len); extern int lxc_char_right_gc(const char *buffer, size_t len); extern char *lxc_trim_whitespace_in_place(char *buffer); extern int lxc_is_line_empty(const char *line); #endif lxc-2.0.11/src/lxc/freezer.c0000644061062106075000000000510613435013473012502 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include "commands.h" #include "error.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "parse.h" #include "state.h" lxc_log_define(lxc_freezer, lxc); lxc_state_t freezer_state(const char *name, const char *lxcpath) { int ret; char v[100]; ret = lxc_cgroup_get("freezer.state", v, sizeof(v), name, lxcpath); if (ret < 0) return -1; v[99] = '\0'; v[lxc_char_right_gc(v, strlen(v))] = '\0'; return lxc_str2state(v); } static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath) { int ret; char v[100]; const char *state = freeze ? "FROZEN" : "THAWED"; size_t state_len = 6; lxc_state_t new_state = freeze ? FROZEN : THAWED; ret = lxc_cgroup_set("freezer.state", state, name, lxcpath); if (ret < 0) { ERROR("Failed to freeze %s", name); return -1; } for (;;) { ret = lxc_cgroup_get("freezer.state", v, sizeof(v), name, lxcpath); if (ret < 0) { ERROR("Failed to get freezer state of %s", name); return -1; } v[99] = '\0'; v[lxc_char_right_gc(v, strlen(v))] = '\0'; ret = strncmp(v, state, state_len); if (ret == 0) { lxc_cmd_serve_state_clients(name, lxcpath, new_state); lxc_monitor_send_state(name, new_state, lxcpath); return 0; } sleep(1); } } int lxc_freeze(const char *name, const char *lxcpath) { lxc_cmd_serve_state_clients(name, lxcpath, FREEZING); lxc_monitor_send_state(name, FREEZING, lxcpath); return do_freeze_thaw(true, name, lxcpath); } int lxc_unfreeze(const char *name, const char *lxcpath) { return do_freeze_thaw(false, name, lxcpath); } lxc-2.0.11/src/lxc/network.h0000644061062106075000000002256213435013473012543 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_NETWORK_H #define __LXC_NETWORK_H #include #include #include #include #include #include "list.h" struct lxc_conf; struct lxc_handler; struct lxc_netdev; enum { LXC_NET_EMPTY, LXC_NET_VETH, LXC_NET_MACVLAN, LXC_NET_PHYS, LXC_NET_VLAN, LXC_NET_NONE, LXC_NET_MAXCONFTYPE, }; /* * Defines the structure to configure an ipv4 address * @address : ipv4 address * @broadcast : ipv4 broadcast address * @mask : network mask */ struct lxc_inetdev { struct in_addr addr; struct in_addr bcast; unsigned int prefix; }; struct lxc_route { struct in_addr addr; }; /* * Defines the structure to configure an ipv6 address * @flags : set the address up * @address : ipv6 address * @broadcast : ipv6 broadcast address * @mask : network mask */ struct lxc_inet6dev { struct in6_addr addr; struct in6_addr mcast; struct in6_addr acast; unsigned int prefix; }; struct lxc_route6 { struct in6_addr addr; }; /* Contains information about the host side veth device. * @pair : Name of the host side veth device. * If the user requested that the host veth device be created with a * specific names this field will be set. If this field is set @veth1 * is not set. * @veth1 : Name of the host side veth device. * If the user did not request that the host veth device be created * with a specific name this field will be set. If this field is set * @pair is not set. * @ifindex : Ifindex of the network device. */ struct ifla_veth { char pair[IFNAMSIZ]; char veth1[IFNAMSIZ]; int ifindex; }; struct ifla_vlan { unsigned int flags; unsigned int fmask; unsigned short vid; unsigned short pad; }; struct ifla_macvlan { int mode; /* private, vepa, bridge, passthru */ }; /* Contains information about the physical network device as seen from the host. * @ifindex : The ifindex of the physical network device in the host's network * namespace. */ struct ifla_phys { int ifindex; }; union netdev_p { struct ifla_macvlan macvlan_attr; struct ifla_phys phys_attr; struct ifla_veth veth_attr; struct ifla_vlan vlan_attr; }; /* * Defines a structure to configure a network device * @idx : network counter * @ifindex : ifindex of the network device * Note that this is the ifindex of the network device in * the container's network namespace. If the network device * consists of a pair of network devices (e.g. veth pairs * attached to a network bridge) then this index cannot be * used to identify or modify the host veth device. See * struct ifla_veth for the host side information. * @type : network type (veth, macvlan, vlan, ...) * @flags : flag of the network device (IFF_UP, ... ) * @link : lxc.net.[i].link, name of bridge or host iface to attach * if any * @name : lxc.net.[i].name, name of iface on the container side * @hwaddr : mac address * @mtu : maximum transmission unit * @priv : information specific to the specificed network type * Note that this is a union so whether accessing a struct * is possible is dependent on the network type. * @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 * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered * from the associated @link * @ipv4_gateway : ipv4 gateway * @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered * from the associated @link * @ipv6_gateway : ipv6 gateway * @upscript : a script filename to be executed during interface * configuration * @downscript : a script filename to be executed during interface * destruction */ struct lxc_netdev { ssize_t idx; int ifindex; int type; int flags; char link[IFNAMSIZ]; char name[IFNAMSIZ]; char *hwaddr; char *mtu; union netdev_p priv; struct lxc_list ipv4; struct lxc_list ipv6; bool ipv4_gateway_auto; struct in_addr *ipv4_gateway; bool ipv6_gateway_auto; struct in6_addr *ipv6_gateway; char *upscript; char *downscript; }; /* Convert a string mac address to a socket structure. */ extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr); /* Move a device between namespaces. */ extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname); extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char *newname); /* Delete a network device. */ extern int lxc_netdev_delete_by_name(const char *name); extern int lxc_netdev_delete_by_index(int ifindex); /* * Change the device name */ extern int lxc_netdev_rename_by_name(const char *oldname, const char *newname); extern int lxc_netdev_rename_by_index(int ifindex, const char *newname); extern int netdev_set_flag(const char *name, int flag); /* * Set the device network up or down */ extern int lxc_netdev_isup(const char *name); extern int lxc_netdev_up(const char *name); extern int lxc_netdev_down(const char *name); /* * Change the mtu size for the specified device */ extern int lxc_netdev_set_mtu(const char *name, int mtu); /* * Create a virtual network devices */ extern int lxc_veth_create(const char *name1, const char *name2); extern int lxc_macvlan_create(const char *master, const char *name, int mode); extern int lxc_vlan_create(const char *master, const char *name, unsigned short vid); /* Set ip address. */ extern int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, struct in6_addr *mcast, struct in6_addr *acast, int prefix); extern int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, struct in_addr *bcast, int prefix); /* * Get ip address */ extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res); extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res); /* * Set a destination route to an interface */ extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest); extern int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest); /* * Set default route. */ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); /* * Attach an interface to the bridge */ extern int lxc_bridge_attach(const char *bridge, const char *ifname); extern int lxc_ovs_delete_port(const char *bridge, const char *nic); extern bool is_ovs_bridge(const char *bridge); /* * Create default gateway */ extern int lxc_route_create_default(const char *addr, const char *ifname, int gateway); /* * Delete default gateway */ extern int lxc_route_delete_default(const char *addr, const char *ifname, int gateway); /* * Activate neighbor proxying */ extern int lxc_neigh_proxy_on(const char *name, int family); /* * Disable neighbor proxying */ extern int lxc_neigh_proxy_off(const char *name, int family); /* Generate a new unique network interface name. * Allocated memory must be freed by caller. */ extern char *lxc_mkifname(char *template); extern const char *lxc_net_type_to_str(int type); extern int setup_private_host_hw_addr(char *veth1); extern int netdev_get_mtu(int ifindex); extern int lxc_create_network_priv(struct lxc_handler *handler); extern int lxc_network_move_created_netdev_priv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid); extern void lxc_delete_network(struct lxc_handler *handler); extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_create_network_unpriv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid); extern int lxc_requests_empty_network(struct lxc_handler *handler); extern int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler); extern int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, struct lxc_list *network); extern int lxc_network_send_veth_names_to_child(struct lxc_handler *handler); extern int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler); extern int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler); extern int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler); #endif /* __LXC_NETWORK_H */ lxc-2.0.11/src/lxc/monitor.c0000644061062106075000000002331213435013473012526 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "af_unix.h" #include "error.h" #include "log.h" #include "lxclock.h" #include "monitor.h" #include "state.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(lxc_monitor, lxc); /* routines used by monitor publishers (containers) */ int lxc_monitor_fifo_name(const char *lxcpath, char *fifo_path, size_t fifo_path_sz, int do_mkdirp) { int ret; char *rundir; rundir = get_rundir(); if (!rundir) return -1; if (do_mkdirp) { ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo.", rundir, lxcpath); free(rundir); return -1; } ret = mkdir_p(fifo_path, 0755); if (ret < 0) { ERROR("Unable to create monitor fifo directory %s.", fifo_path); free(rundir); return ret; } } ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s/monitor-fifo", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo.", rundir, lxcpath); free(rundir); return -1; } free(rundir); return 0; } static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) { int fd,ret; char fifo_path[PATH_MAX]; BUILD_BUG_ON(sizeof(*msg) > PIPE_BUF); /* write not guaranteed atomic */ ret = lxc_monitor_fifo_name(lxcpath, fifo_path, sizeof(fifo_path), 0); if (ret < 0) return; /* Open the fifo nonblock in case the monitor is dead, we don't want the * open to wait for a reader since it may never come. */ fd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (fd < 0) { /* It is normal for this open() to fail with ENXIO when there is * no monitor running, so we don't log it. */ if (errno == ENXIO || errno == ENOENT) return; WARN("%s - Failed to open fifo to send message", strerror(errno)); return; } if (fcntl(fd, F_SETFL, O_WRONLY) < 0) { close(fd); return; } ret = write(fd, msg, sizeof(*msg)); if (ret != sizeof(*msg)) { close(fd); SYSERROR("Failed to write to monitor fifo \"%s\".", fifo_path); return; } close(fd); } void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } void lxc_monitor_send_exit_code(const char *name, int exit_code, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_exit_code, .value = exit_code}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } /* routines used by monitor subscribers (lxc-monitor) */ int lxc_monitor_close(int fd) { return close(fd); } /* Enforces \0-termination for the abstract unix socket. This is not required * but allows us to print it out. * * Older version of liblxc only allowed for 105 bytes to be used for the * abstract unix domain socket name because the code for our abstract unix * socket handling performed invalid checks. Since we \0-terminate we could now * have a maximum of 106 chars. But to not break backwards compatibility we keep * the limit at 105. */ int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) { size_t len; int ret; char *path; uint64_t hash; /* addr.sun_path is only 108 bytes, so we hash the full name and * then append as much of the name as we can fit. */ memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; /* strlen("lxc/") + strlen("/monitor-sock") + 1 = 18 */ len = strlen(lxcpath) + 18; path = alloca(len); ret = snprintf(path, len, "lxc/%s/monitor-sock", lxcpath); if (ret < 0 || (size_t)ret >= len) { ERROR("failed to create name for monitor socket"); return -1; } /* Note: snprintf() will \0-terminate addr->sun_path on the 106th byte * and so the abstract socket name has 105 "meaningful" characters. This * is absolutely intentional. For further info read the comment for this * function above! */ len = sizeof(addr->sun_path) - 1; hash = fnv_64a_buf(path, ret, FNV1A_64_INIT); ret = snprintf(addr->sun_path, len, "@lxc/%016" PRIx64 "/%s", hash, lxcpath); if (ret < 0) { ERROR("failed to create hashed name for monitor socket"); return -1; } /* replace @ with \0 */ addr->sun_path[0] = '\0'; INFO("using monitor socket name \"%s\" (length of socket name %zu must be <= %zu)", &addr->sun_path[1], strlen(&addr->sun_path[1]), sizeof(addr->sun_path) - 3); return 0; } int lxc_monitor_open(const char *lxcpath) { struct sockaddr_un addr; int fd; size_t retry; size_t len; int backoff_ms[] = {10, 50, 100}; if (lxc_monitor_sock_name(lxcpath, &addr) < 0) return -1; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { ERROR("Failed to create socket: %s.", strerror(errno)); return -1; } len = strlen(&addr.sun_path[1]); DEBUG("opening monitor socket %s with len %zu", &addr.sun_path[1], len); if (len >= sizeof(addr.sun_path) - 1) { errno = ENAMETOOLONG; ERROR("name of monitor socket too long (%zu bytes): %s", len, strerror(errno)); close(fd); return -1; } for (retry = 0; retry < sizeof(backoff_ms) / sizeof(backoff_ms[0]); retry++) { fd = lxc_abstract_unix_connect(addr.sun_path); if (fd != -1 || errno != ECONNREFUSED) break; ERROR("Failed to connect to monitor socket. Retrying in %d ms: %s", backoff_ms[retry], strerror(errno)); usleep(backoff_ms[retry] * 1000); } if (fd < 0) { ERROR("Failed to connect to monitor socket: %s.", strerror(errno)); return -1; } return fd; } int lxc_monitor_read_fdset(struct pollfd *fds, nfds_t nfds, struct lxc_msg *msg, int timeout) { long i; int ret; ret = poll(fds, nfds, timeout * 1000); if (ret == -1) return -1; else if (ret == 0) return -2; // timed out /* Only read from the first ready fd, the others will remain ready for * when this routine is called again. */ for (i = 0; i < nfds; i++) { if (fds[i].revents != 0) { fds[i].revents = 0; ret = recv(fds[i].fd, msg, sizeof(*msg), 0); if (ret <= 0) { SYSERROR("Failed to receive message. Did monitord die?: %s.", strerror(errno)); return -1; } return ret; } } SYSERROR("No ready fd found."); return -1; } int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) { struct pollfd fds; fds.fd = fd; fds.events = POLLIN | POLLPRI; fds.revents = 0; return lxc_monitor_read_fdset(&fds, 1, msg, timeout); } int lxc_monitor_read(int fd, struct lxc_msg *msg) { return lxc_monitor_read_timeout(fd, msg, -1); } #define LXC_MONITORD_PATH LIBEXECDIR "/lxc/lxc-monitord" /* Used to spawn a monitord either on startup of a daemon container, or when * lxc-monitor starts. */ int lxc_monitord_spawn(const char *lxcpath) { int ret; int pipefd[2]; char pipefd_str[LXC_NUMSTRLEN64]; pid_t pid1, pid2; char *const args[] = { LXC_MONITORD_PATH, (char *)lxcpath, pipefd_str, NULL, }; /* double fork to avoid zombies when monitord exits */ pid1 = fork(); if (pid1 < 0) { SYSERROR("Failed to fork()."); return -1; } if (pid1) { DEBUG("Going to wait for pid %d.", pid1); if (waitpid(pid1, NULL, 0) != pid1) return -1; DEBUG("Finished waiting on pid %d.", pid1); return 0; } if (pipe(pipefd) < 0) { SYSERROR("Failed to create pipe."); _exit(EXIT_FAILURE); } pid2 = fork(); if (pid2 < 0) { SYSERROR("Failed to fork()."); _exit(EXIT_FAILURE); } if (pid2) { DEBUG("Trying to sync with child process."); char c; /* Wait for daemon to create socket. */ close(pipefd[1]); /* Sync with child, we're ignoring the return from read * because regardless if it works or not, either way we've * synced with the child process. the if-empty-statement * construct is to quiet the warn-unused-result warning. */ if (read(pipefd[0], &c, 1)) ; close(pipefd[0]); DEBUG("Successfully synced with child process."); _exit(EXIT_SUCCESS); } if (setsid() < 0) { SYSERROR("Failed to setsid()."); _exit(EXIT_FAILURE); } lxc_check_inherited(NULL, true, &pipefd[1], 1); if (null_stdfds() < 0) { SYSERROR("Failed to dup2() standard file descriptors to /dev/null."); _exit(EXIT_FAILURE); } close(pipefd[0]); ret = snprintf(pipefd_str, LXC_NUMSTRLEN64, "%d", pipefd[1]); if (ret < 0 || ret >= LXC_NUMSTRLEN64) { ERROR("Failed to create pid argument to pass to monitord."); _exit(EXIT_FAILURE); } DEBUG("Using pipe file descriptor %d for monitord.", pipefd[1]); execvp(args[0], args); SYSERROR("failed to exec lxc-monitord"); _exit(EXIT_FAILURE); } lxc-2.0.11/src/lxc/lxcseccomp.h0000644061062106075000000000262713435013473013212 00000000000000/* * lxc: linux Container library * * (C) Copyright Canonical, Inc. 2012 * * Authors: * Serge Hallyn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXCSECCOMP_H #define __LXC_LXCSECCOMP_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 lxc-2.0.11/src/lxc/nl.h0000644061062106075000000001731513435013473011463 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_NL_H #define __LXC_NL_H /* * Use this as a good size to allocate generic netlink messages */ #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif #define NLMSG_GOOD_SIZE (2*PAGE_SIZE) #define NLMSG_TAIL(nmsg) ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) #define NLA_NEXT_ATTR(attr) ((void *)((char *)attr) + NLA_ALIGN(attr->nla_len)) /* * struct nl_handler : the handler for netlink sockets, this structure * is used all along the netlink socket life cycle to specify the * netlink socket to be used. * * @fd: the file descriptor of the netlink socket * @seq: the sequence number of the netlink messages * @local: the bind address * @peer: the peer address */ struct nl_handler { int fd; int seq; struct sockaddr_nl local; struct sockaddr_nl peer; }; /* * struct nlmsg : the netlink message structure. This message is to be used to * be allocated with netlink_alloc. * * @nlmsghdr: a pointer to a netlink message header * @cap: capacity of the netlink message, this is the initially allocated size * and later operations (e.g. reserve and put) can not exceed this limit. */ struct nlmsg { struct nlmsghdr *nlmsghdr; ssize_t cap; }; /* * netlink_open : open a netlink socket, the function will * fill the handler with the right value * * @handler: a netlink handler to be used all along the netlink * socket life cycle * @protocol: specify the protocol to be used when opening the * netlink socket * * Return 0 on success, < 0 otherwise */ int netlink_open(struct nl_handler *handler, int protocol); /* * netlink_close : close a netlink socket, after this call, * the handler is no longer valid * * @handler: a handler to the netlink socket * * Returns 0 on success, < 0 otherwise */ int netlink_close(struct nl_handler *handler); /* * netlink_rcv : receive a netlink message from the kernel. * It is up to the caller to manage the allocation of the * netlink message * * @handler: a handler to the netlink socket * @nlmsg: a netlink message * * Returns 0 on success, < 0 otherwise */ int netlink_rcv(struct nl_handler *handler, struct nlmsg *nlmsg); /* * netlink_send: send a netlink message to the kernel. It is up * to the caller to manage the allocate of the netlink message * * @handler: a handler to the netlink socket * @nlmsg: a netlink message * * Returns 0 on success, < 0 otherwise */ int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg); /* * netlink_transaction: send a request to the kernel and read the response. * This is useful for transactional protocol. It is up to the caller * to manage the allocation of the netlink message. * * @handler: a handler to a opened netlink socket * @request: a netlink message pointer containing the request * @answer: a netlink message pointer to receive the result * * Returns 0 on success, < 0 otherwise */ int netlink_transaction(struct nl_handler *handler, struct nlmsg *request, struct nlmsg *anwser); /* * nla_put_string: copy a null terminated string to a netlink message * attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the string * @string: a null terminated string to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string); /* * nla_put_buffer: copy a buffer with a specified size to a netlink * message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the string * @data: a pointer to a buffer * @size: the size of the buffer * * Returns 0 on success, < 0 otherwise */ int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size); /* * nla_put_u32: copy an integer to a netlink message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the integer * @string: an integer to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_u32(struct nlmsg *nlmsg, int attr, int value); /* * nla_put_u16: copy an integer to a netlink message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the unsigned 16-bit value * @value: 16-bit attribute data value to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value); /* * nla_put_attr: add an attribute name to a netlink * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the integer * * Returns 0 on success, < 0 otherwise */ int nla_put_attr(struct nlmsg *nlmsg, int attr); /* * nla_begin_nested: begin the nesting attribute * * @nlmsg: the netlink message to be filled * @attr: the netsted attribute name * * Returns current nested pointer to be reused * to nla_end_nested. */ struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr); /* * nla_end_nested: end the nesting attribute * * @nlmsg: the netlink message * @nested: the nested pointer * * Returns the current */ void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr); /* * nlmsg_allocate : allocate a netlink message. The netlink format message * is a header, a padding, a payload and a padding again. * When a netlink message is allocated, the size specify the * payload we want. So the real size of the allocated message * is sizeof(header) + sizeof(padding) + payloadsize + sizeof(padding), * in other words, the function will allocate more than specified. When * the buffer is allocated, the content is zeroed. * The function will also fill the field nlmsg_len with NLMSG_HDRLEN. * If the allocation must be for the specified size, just use malloc. * * @size: the capacity of the payload to be allocated * * Returns a pointer to the newly allocated netlink message, NULL otherwise */ struct nlmsg *nlmsg_alloc(size_t size); /* * nlmsg_alloc_reserve: like nlmsg_alloc(), but reserve the whole payload * after allocated, that is, the field nlmsg_len be set to the capacity * of nlmsg. Often used to allocate a message for the reply. * * @size: the capacity of the payload to be allocated. */ struct nlmsg *nlmsg_alloc_reserve(size_t size); /* * Reserve room for additional data at the tail of a netlink message * * @nlmsg: the netlink message * @len: length of additional data to reserve room for * * Returns a pointer to newly reserved room or NULL */ void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len); /* * nlmsg_free : free a previously allocate message * * @nlmsg: the netlink message to be freed */ void nlmsg_free(struct nlmsg *nlmsg); /* * nlmsg_data : returns a pointer to the data contained in the netlink message * * @nlmsg : the netlink message to get the data * * Returns a pointer to the netlink data or NULL if there is no data */ void *nlmsg_data(struct nlmsg *nlmsg); #endif lxc-2.0.11/src/lxc/start.h0000644061062106075000000001143113435013473012200 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_START_H #define __LXC_START_H #include #include #include #include #include #include "conf.h" #include "config.h" #include "namespace.h" #include "state.h" struct lxc_handler { /* The clone flags that were requested. */ int clone_flags; /* File descriptors referring to the network namespace of the container. */ int netnsfd; /* File descriptor to pin the rootfs for privileged containers. */ int pinfd; /* Signal file descriptor. */ int sigfd; /* List of file descriptors referring to the namespaces of the * container. Note that these are not necessarily identical to * the "clone_flags" handler field in case namespace inheritance is * requested. */ int nsfd[LXC_NS_MAX]; /* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data * between child and parent. */ int data_sock[2]; /* The socketpair() fds used to wait on successful daemonized startup. */ int state_socket_pair[2]; /* Socketpair to synchronize processes during container creation. */ int sync_sock[2]; /* Pointer to the name of the container. Do not free! */ const char *name; /* Pointer to the path the container. Do not free! */ const char *lxcpath; /* Whether the container's startup process euid is 0. */ bool am_root; /* Indicates whether should we close std{in,out,err} on start. */ bool backgrounded; /* The child's pid. */ pid_t pid; /* Whether the child has already exited. */ bool init_died; /* The signal mask prior to setting up the signal file descriptor. */ sigset_t oldmask; /* The container's in-memory configuration. */ struct lxc_conf *conf; /* A list of clients registered to be informed about a container state. */ struct lxc_list state_clients; /* A set of operations to be performed at various stages of the * container's life. */ struct lxc_operations *ops; /* This holds the cgroup information. Note that the data here is * specific to the cgroup driver used. */ void *cgroup_data; /* Data to be passed to handler ops. */ void *data; /* Current state of the container. */ lxc_state_t state; /* The exit status of the container; not defined unless ->init_died == * true. */ int exit_status; }; struct lxc_operations { int (*start)(struct lxc_handler *, void *); int (*post_start)(struct lxc_handler *, void *); }; struct state_client { int clientfd; lxc_state_t states[MAX_STATE]; }; extern int lxc_poll(const char *name, struct lxc_handler *handler); extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state); extern int lxc_serve_state_clients(const char *name, struct lxc_handler *handler, lxc_state_t state); extern void lxc_abort(const char *name, struct lxc_handler *handler); extern struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, const char *lxcpath, bool daemonize); extern void lxc_zero_handler(struct lxc_handler *handler); extern void lxc_free_handler(struct lxc_handler *handler); extern int lxc_init(const char *name, struct lxc_handler *handler); extern void lxc_fini(const char *name, struct lxc_handler *handler); /* lxc_check_inherited: Check for any open file descriptors and close them if * requested. * @param[in] conf The container's configuration. * @param[in] closeall Whether we should close all open file descriptors. * @param[in] fds_to_ignore Array of file descriptors to ignore. * @param[in] len_fds Length of fds_to_ignore array. */ extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int *fds_to_ignore, size_t len_fds); extern int __lxc_start(const char *, struct lxc_handler *, struct lxc_operations *, void *, const char *, bool, int *); extern int resolve_clone_flags(struct lxc_handler *handler); #endif lxc-2.0.11/src/lxc/commands.h0000644061062106075000000001026313435013473012646 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_COMMANDS_H #define __LXC_COMMANDS_H #include #include #include #include "lxccontainer.h" #include "macro.h" #include "state.h" /* Length of abstract unix domain socket socket address. */ #define LXC_AUDS_ADDR_LEN sizeof(((struct sockaddr_un *)0)->sun_path) /* pointer conversion macros */ #define PTR_TO_INT(p) ((int)((intptr_t)(p))) #define INT_TO_PTR(u) ((void *)((intptr_t)(u))) #define PTR_TO_INTMAX(p) ((intmax_t)((intptr_t)(p))) #define INTMAX_TO_PTR(u) ((void *)((intptr_t)(u))) typedef enum { LXC_CMD_CONSOLE, LXC_CMD_CONSOLE_WINCH, LXC_CMD_STOP, LXC_CMD_GET_STATE, LXC_CMD_GET_INIT_PID, LXC_CMD_GET_CLONE_FLAGS, LXC_CMD_GET_CGROUP, LXC_CMD_GET_CONFIG_ITEM, LXC_CMD_GET_NAME, LXC_CMD_GET_LXCPATH, LXC_CMD_ADD_STATE_CLIENT, LXC_CMD_CONSOLE_LOG, LXC_CMD_SERVE_STATE_CLIENTS, LXC_CMD_MAX, } lxc_cmd_t; struct lxc_cmd_req { lxc_cmd_t cmd; int datalen; const void *data; }; struct lxc_cmd_rsp { int ret; /* 0 on success, -errno on failure */ int datalen; void *data; }; struct lxc_cmd_rr { struct lxc_cmd_req req; struct lxc_cmd_rsp rsp; }; struct lxc_cmd_console_rsp_data { int masterfd; int ttynum; }; extern int lxc_cmd_console_winch(const char *name, const char *lxcpath); extern int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath); /* * Get the 'real' cgroup path (as seen in /proc/self/cgroup) for a container * for a particular subsystem */ extern char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, const char *subsystem); extern int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath); extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath); extern char *lxc_cmd_get_name(const char *hashed_sock); extern char *lxc_cmd_get_lxcpath(const char *hashed_sock); extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath); extern int lxc_cmd_get_state(const char *name, const char *lxcpath); extern int lxc_cmd_stop(const char *name, const char *lxcpath); /* lxc_cmd_add_state_client Register a new state client fd in the container's * in-memory handler. * * @param[in] name Name of container to connect to. * @param[in] lxcpath The lxcpath in which the container is running. * @param[in] states The states to wait for. * @param[out] state_client_fd The state client fd from which the state can be * received. * @return Return < 0 on error * == MAX_STATE when state needs to retrieved * via socket fd * < MAX_STATE current container state */ extern int lxc_cmd_add_state_client(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int *state_client_fd); extern int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, lxc_state_t state); struct lxc_epoll_descr; struct lxc_handler; extern int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix); extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler); extern int lxc_try_cmd(const char *name, const char *lxcpath); #endif /* __commands_h */ lxc-2.0.11/src/lxc/criu.h0000644061062106075000000000227213435013473012010 00000000000000/* * lxc: linux Container library * * Copyright © 2014-2015 Canonical Ltd. * * Authors: * Tycho Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CRIU_H #define __LXC_CRIU_H #include #include bool __criu_pre_dump(struct lxc_container *c, struct migrate_opts *opts); bool __criu_dump(struct lxc_container *c, struct migrate_opts *opts); bool __criu_restore(struct lxc_container *c, struct migrate_opts *opts); #endif lxc-2.0.11/src/lxc/memory_utils.h0000644061062106075000000000346113435013473013577 00000000000000/* liblxcapi * * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_MEMORY_UTILS_H #define __LXC_MEMORY_UTILS_H #include #include #include #include #include #include #include "macro.h" static inline void __auto_free__(void *p) { free(*(void **)p); } static inline void __auto_fclose__(FILE **f) { if (*f) fclose(*f); } static inline void __auto_closedir__(DIR **d) { if (*d) closedir(*d); } #define close_prot_errno_disarm(fd) \ if (fd >= 0) { \ int _e_ = errno; \ close(fd); \ errno = _e_; \ fd = -EBADF; \ } static inline void __auto_close__(int *fd) { close_prot_errno_disarm(*fd); } #define __do_close_prot_errno __attribute__((__cleanup__(__auto_close__))) #define __do_free __attribute__((__cleanup__(__auto_free__))) #define __do_fclose __attribute__((__cleanup__(__auto_fclose__))) #define __do_closedir __attribute__((__cleanup__(__auto_closedir__))) #endif /* __LXC_MEMORY_UTILS_H */ lxc-2.0.11/src/lxc/confile.h0000644061062106075000000000551013435013473012463 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONFILE_H #define __LXC_CONFILE_H #include #include #include struct lxc_conf; struct lxc_list; typedef int (*config_set_cb)(const char *key, const char *value, struct lxc_conf *conf, void *data); typedef int (*config_get_cb)(const char *key, char *value, int inlen, struct lxc_conf *conf); typedef int (*config_clr_cb)(const char *key, struct lxc_conf *conf, void *data); struct lxc_config_t { char *name; config_set_cb set; config_get_cb get; config_clr_cb clr; }; 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_list_config_items(char *retv, int inlen); extern int lxc_config_read(const char *file, struct lxc_conf *conf, bool from_include); extern int append_unexp_config_line(const char *line, struct lxc_conf *conf); extern int lxc_config_define_add(struct lxc_list *defines, char* arg); extern int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf); /* needed for lxc-attach */ extern signed long lxc_config_parse_arch(const char *arch); extern int lxc_fill_elevated_privileges(char *flaglist, int *flags); extern int lxc_clear_config_item(struct lxc_conf *c, const char *key); extern void write_config(FILE *fout, struct lxc_conf *c); extern bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, const char *v); /* These are used when cloning a container */ extern void clear_unexp_config_line(struct lxc_conf *conf, const char *key, bool rm_subkeys); extern bool clone_update_unexp_hooks(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newmame); bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname, const char *ovldir); extern bool network_new_hwaddrs(struct lxc_conf *conf); #endif lxc-2.0.11/src/lxc/lsm/0000755061062106075000000000000013435013523011541 500000000000000lxc-2.0.11/src/lxc/lsm/apparmor.c0000644061062106075000000001410213435013473013450 00000000000000/* apparmor * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "log.h" #include "lsm.h" #include "conf.h" #include "utils.h" lxc_log_define(lxc_apparmor, lxc); /* set by lsm_apparmor_drv_init if true */ static int aa_enabled = 0; static int mount_features_enabled = 0; #define AA_DEF_PROFILE "lxc-container-default" #define AA_DEF_PROFILE_CGNS "lxc-container-default-cgns" #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" #define AA_UNCHANGED "unchanged" static bool check_mount_feature_enabled(void) { return mount_features_enabled == 1; } static void load_mount_features_enabled(void) { struct stat statbuf; int ret; ret = stat(AA_MOUNT_RESTR, &statbuf); if (ret == 0) mount_features_enabled = 1; } /* aa_getcon is not working right now. Use our hand-rolled version below */ static int apparmor_enabled(void) { FILE *fin; char e; int ret; fin = fopen(AA_ENABLED_FILE, "r"); if (!fin) return 0; ret = fscanf(fin, "%c", &e); fclose(fin); if (ret == 1 && e == 'Y') { load_mount_features_enabled(); return 1; } return 0; } static char *apparmor_process_label_get(pid_t pid) { char path[100], *space; int ret; char *buf = NULL, *newbuf; int sz = 0; FILE *f; ret = snprintf(path, 100, "/proc/%d/attr/current", pid); if (ret < 0 || ret >= 100) { ERROR("path name too long"); return NULL; } again: f = fopen(path, "r"); if (!f) { SYSERROR("opening %s", path); free(buf); return NULL; } sz += 1024; newbuf = realloc(buf, sz); if (!newbuf) { free(buf); ERROR("out of memory"); fclose(f); return NULL; } buf = newbuf; memset(buf, 0, sz); ret = fread(buf, 1, sz - 1, f); fclose(f); if (ret < 0) { ERROR("reading %s", path); free(buf); return NULL; } if (ret >= sz) goto again; space = strchr(buf, '\n'); if (space) *space = '\0'; space = strchr(buf, ' '); if (space) *space = '\0'; return buf; } /* * Probably makes sense to reorganize these to only read * the label once */ static bool apparmor_am_unconfined(void) { char *p = apparmor_process_label_get(lxc_raw_getpid()); bool ret = false; if (!p || strcmp(p, "unconfined") == 0) ret = true; free(p); return ret; } /* aa stacking is not yet supported */ static bool aa_stacking_supported(void) { return false; } static bool aa_needs_transition(char *curlabel) { if (!curlabel) return false; if (strcmp(curlabel, "unconfined") == 0) return false; if (strcmp(curlabel, "/usr/bin/lxc-start") == 0) return false; return true; } /* * apparmor_process_label_set: Set AppArmor process profile * * @label : the profile to set * @conf : the container configuration to use if @label is NULL * @default : use the default profile if @label is NULL * @on_exec : this is ignored. Apparmor profile will be changed immediately * * Returns 0 on success, < 0 on failure * * Notes: This relies on /proc being available. */ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf, bool use_default, bool on_exec) { int label_fd, ret; pid_t tid; const char *label = inlabel ? inlabel : conf->lsm_aa_profile; char *curlabel; if (!aa_enabled) return 0; /* user may request that we just ignore apparmor */ if (label && strcmp(label, AA_UNCHANGED) == 0) { INFO("apparmor profile unchanged per user request"); return 0; } curlabel = apparmor_process_label_get(lxc_raw_getpid()); if (!aa_stacking_supported() && aa_needs_transition(curlabel)) { // we're already confined, and stacking isn't supported if (!label || strcmp(curlabel, label) == 0) { // no change requested free(curlabel); return 0; } ERROR("already apparmor confined, but new label requested."); free(curlabel); return -1; } free(curlabel); if (!label) { if (use_default) { if (cgns_supported()) label = AA_DEF_PROFILE_CGNS; else label = AA_DEF_PROFILE; } else label = "unconfined"; } if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) { WARN("Incomplete AppArmor support in your kernel"); if (!conf->lsm_aa_allow_incomplete) { ERROR("If you really want to start this container, set"); ERROR("lxc.aa_allow_incomplete = 1"); ERROR("in your container configuration file"); return -1; } } if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) { INFO("apparmor profile unchanged"); return 0; } tid = lxc_raw_gettid(); label_fd = lsm_process_label_fd_get(tid, on_exec); if (label_fd < 0) { SYSERROR("Failed to change apparmor profile to %s", label); return -1; } ret = lsm_process_label_set_at(label_fd, label, on_exec); close(label_fd); if (ret < 0) { SYSERROR("Failed to change apparmor profile to %s", label); return -1; } INFO("Changed apparmor profile to %s", label); return 0; } static struct lsm_drv apparmor_drv = { .name = "AppArmor", .enabled = apparmor_enabled, .process_label_get = apparmor_process_label_get, .process_label_set = apparmor_process_label_set, }; struct lsm_drv *lsm_apparmor_drv_init(void) { if (!apparmor_enabled()) return NULL; aa_enabled = 1; return &apparmor_drv; } lxc-2.0.11/src/lxc/lsm/selinux.c0000644061062106075000000000577213435013473013333 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "conf.h" #include "log.h" #include "lsm.h" #define DEFAULT_LABEL "unconfined_t" lxc_log_define(lxc_lsm_selinux, lxc); /* * selinux_process_label_get: Get SELinux context of a process * * @pid : the pid to get, or 0 for self * * Returns the context of the given pid. The caller must free() * the returned string. * * Note that this relies on /proc being available. */ static char *selinux_process_label_get(pid_t pid) { security_context_t ctx; char *label; if (getpidcon_raw(pid, &ctx) < 0) { SYSERROR("failed to get SELinux context for pid %d", pid); return NULL; } label = strdup((char *)ctx); freecon(ctx); return label; } /* * selinux_process_label_set: Set SELinux context of a process * * @label : label string * @conf : the container configuration to use if @label is NULL * @default : use the default context if @label is NULL * @on_exec : the new context will take effect on exec(2) not immediately * * Returns 0 on success, < 0 on failure * * Notes: This relies on /proc being available. */ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf, bool use_default, bool on_exec) { int ret; const char *label; label = inlabel ? inlabel : conf->lsm_se_context; if (!label) { if (!use_default) return -EINVAL; label = DEFAULT_LABEL; } if (strcmp(label, "unconfined_t") == 0) return 0; if (on_exec) ret = setexeccon_raw((char *)label); else ret = setcon_raw((char *)label); if (ret < 0) { SYSERROR("Failed to set SELinux%s context to \"%s\"", on_exec ? " exec" : "", label); return -1; } INFO("Changed SELinux%s context to \"%s\"", on_exec ? " exec" : "", label); return 0; } static struct lsm_drv selinux_drv = { .name = "SELinux", .enabled = is_selinux_enabled, .process_label_get = selinux_process_label_get, .process_label_set = selinux_process_label_set, }; struct lsm_drv *lsm_selinux_drv_init(void) { if (!is_selinux_enabled()) return NULL; return &selinux_drv; } lxc-2.0.11/src/lxc/lsm/lsm.h0000644061062106075000000000314413435013473012433 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LSM_H #define __LXC_LSM_H struct lxc_conf; #include #include "macro.h" #include "utils.h" struct lsm_drv { const char *name; int (*enabled)(void); char *(*process_label_get)(pid_t pid); int (*process_label_set)(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec); }; extern void lsm_init(void); extern int lsm_enabled(void); extern const char *lsm_name(void); extern char *lsm_process_label_get(pid_t pid); extern int lsm_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec); extern int lsm_process_label_fd_get(pid_t pid, bool on_exec); extern int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec); #endif /* __LXC_LSM_H */ lxc-2.0.11/src/lxc/lsm/lsm.c0000644061062106075000000000771313435013473012434 00000000000000/* * lxc: linux Container library * * Authors: * Copyright © 2012 Serge Hallyn * Copyright © 2012 Canonical Ltd. * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include "conf.h" #include "log.h" #include "lsm.h" lxc_log_define(lxc_lsm, lxc); static struct lsm_drv *drv = NULL; extern struct lsm_drv *lsm_apparmor_drv_init(void); extern struct lsm_drv *lsm_selinux_drv_init(void); extern struct lsm_drv *lsm_nop_drv_init(void); __attribute__((constructor)) void lsm_init(void) { if (drv) { INFO("LSM security driver %s", drv->name); return; } #if HAVE_APPARMOR drv = lsm_apparmor_drv_init(); #endif #if HAVE_SELINUX if (!drv) drv = lsm_selinux_drv_init(); #endif if (!drv) drv = lsm_nop_drv_init(); INFO("Initialized LSM security driver %s", drv->name); } int lsm_enabled(void) { if (drv) return drv->enabled(); return 0; } const char *lsm_name(void) { if (drv) return drv->name; return "none"; } char *lsm_process_label_get(pid_t pid) { if (!drv) { ERROR("LSM driver not inited"); return NULL; } return drv->process_label_get(pid); } int lsm_process_label_fd_get(pid_t pid, bool on_exec) { int ret = -1; int labelfd = -1; const char *name; char path[LXC_LSMATTRLEN]; name = lsm_name(); if (strcmp(name, "nop") == 0) return 0; if (strcmp(name, "none") == 0) return 0; /* We don't support on-exec with AppArmor */ if (strcmp(name, "AppArmor") == 0) on_exec = 0; if (on_exec) ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/exec", pid); else ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/current", pid); if (ret < 0 || ret >= LXC_LSMATTRLEN) return -1; labelfd = open(path, O_RDWR); if (labelfd < 0) { SYSERROR("%s - Unable to %s LSM label file descriptor", name, strerror(errno)); return -1; } return labelfd; } int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec) { int ret = -1; const char *name; name = lsm_name(); if (strcmp(name, "nop") == 0) return 0; if (strcmp(name, "none") == 0) return 0; /* We don't support on-exec with AppArmor */ if (strcmp(name, "AppArmor") == 0) on_exec = false; if (strcmp(name, "AppArmor") == 0) { size_t len; char *command; if (on_exec) { ERROR("Changing AppArmor profile on exec not supported"); return -EINVAL; } len = strlen(label) + strlen("changeprofile ") + 1; command = malloc(len); if (!command) return -1; ret = snprintf(command, len, "changeprofile %s", label); if (ret < 0 || (size_t)ret >= len) { free(command); return -1; } ret = lxc_write_nointr(label_fd, command, len - 1); free(command); } else if (strcmp(name, "SELinux") == 0) { ret = lxc_write_nointr(label_fd, label, strlen(label)); } else { ret = -EINVAL; } if (ret < 0) { SYSERROR("Failed to set %s label \"%s\"", name, label); return -1; } INFO("Set %s label to \"%s\"", name, label); return 0; } int lsm_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec) { if (!drv) { ERROR("LSM driver not inited"); return -1; } return drv->process_label_set(label, conf, use_default, on_exec); } lxc-2.0.11/src/lxc/lsm/nop.c0000644061062106075000000000255713435013473012436 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "lsm/lsm.h" static char *nop_process_label_get(pid_t pid) { return NULL; } static int nop_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec) { return 0; } static int nop_enabled(void) { return 0; } static struct lsm_drv nop_drv = { .name = "nop", .enabled = nop_enabled, .process_label_get = nop_process_label_get, .process_label_set = nop_process_label_set, }; struct lsm_drv *lsm_nop_drv_init(void) { return &nop_drv; } lxc-2.0.11/src/lxc/seccomp.c0000644061062106075000000005401313435013473012472 00000000000000/* * lxc: linux Container library * * (C) Copyright Canonical, Inc. 2012 * * Authors: * Serge Hallyn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include "config.h" #include "log.h" #include "lxcseccomp.h" lxc_log_define(lxc_seccomp, lxc); static int parse_config_v1(FILE *f, struct lxc_conf *conf) { char line[1024]; int ret; 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.", nr); return ret; } } return 0; } #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH static void remove_trailing_newlines(char *l) { char *p = l; while (*p) p++; while (--p >= l && *p == '\n') *p = '\0'; } static uint32_t get_v2_default_action(char *line) { uint32_t ret_action = -1; while (*line == ' ') line++; // after 'whitelist' or 'blacklist' comes default behavior if (strncmp(line, "kill", 4) == 0) ret_action = SCMP_ACT_KILL; else if (strncmp(line, "errno", 5) == 0) { int e; if (sscanf(line + 5, "%d", &e) != 1) { ERROR("Bad errno value in %s.", line); return -2; } ret_action = SCMP_ACT_ERRNO(e); } else if (strncmp(line, "allow", 5) == 0) ret_action = SCMP_ACT_ALLOW; else if (strncmp(line, "trap", 4) == 0) ret_action = SCMP_ACT_TRAP; return ret_action; } static const char *get_action_name(uint32_t action) { // The upper 16 bits indicate the type of the seccomp action switch(action & 0xffff0000){ case SCMP_ACT_KILL: return "kill"; case SCMP_ACT_ALLOW: return "allow"; case SCMP_ACT_TRAP: return "trap"; case SCMP_ACT_ERRNO(0): return "errno"; default: return "invalid action"; } } static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action) { char *p = strchr(line, ' '); uint32_t ret; if (!p) return def_action; *p = '\0'; p++; while (*p == ' ') p++; if (!*p || *p == '#') return def_action; ret = get_v2_default_action(p); switch(ret) { case -2: return -1; case -1: return def_action; default: return ret; } return ret; } #endif #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH enum lxc_hostarch_t { lxc_seccomp_arch_all = 0, lxc_seccomp_arch_native, lxc_seccomp_arch_i386, lxc_seccomp_arch_x32, lxc_seccomp_arch_amd64, lxc_seccomp_arch_arm, lxc_seccomp_arch_arm64, lxc_seccomp_arch_ppc64, lxc_seccomp_arch_ppc64le, lxc_seccomp_arch_ppc, lxc_seccomp_arch_mips, lxc_seccomp_arch_mips64, lxc_seccomp_arch_mips64n32, lxc_seccomp_arch_mipsel, lxc_seccomp_arch_mipsel64, lxc_seccomp_arch_mipsel64n32, lxc_seccomp_arch_s390x, lxc_seccomp_arch_unknown = 999, }; #ifdef __MIPSEL__ # define MIPS_ARCH_O32 lxc_seccomp_arch_mipsel # define MIPS_ARCH_N64 lxc_seccomp_arch_mipsel64 #else # define MIPS_ARCH_O32 lxc_seccomp_arch_mips # define MIPS_ARCH_N64 lxc_seccomp_arch_mips64 #endif int get_hostarch(void) { struct utsname uts; if (uname(&uts) < 0) { SYSERROR("Failed to read host arch."); return -1; } if (strcmp(uts.machine, "i686") == 0) return lxc_seccomp_arch_i386; // no x32 kernels else if (strcmp(uts.machine, "x86_64") == 0) return lxc_seccomp_arch_amd64; else if (strncmp(uts.machine, "armv7", 5) == 0) return lxc_seccomp_arch_arm; else if (strncmp(uts.machine, "aarch64", 7) == 0) return lxc_seccomp_arch_arm64; else if (strncmp(uts.machine, "ppc64le", 7) == 0) return lxc_seccomp_arch_ppc64le; else if (strncmp(uts.machine, "ppc64", 5) == 0) return lxc_seccomp_arch_ppc64; else if (strncmp(uts.machine, "ppc", 3) == 0) return lxc_seccomp_arch_ppc; else if (strncmp(uts.machine, "mips64", 6) == 0) return MIPS_ARCH_N64; else if (strncmp(uts.machine, "mips", 4) == 0) return MIPS_ARCH_O32; else if (strncmp(uts.machine, "s390x", 5) == 0) return lxc_seccomp_arch_s390x; return lxc_seccomp_arch_unknown; } scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action) { scmp_filter_ctx ctx; int ret; uint32_t arch; switch(n_arch) { case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break; case lxc_seccomp_arch_x32: arch = SCMP_ARCH_X32; break; case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break; case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break; #ifdef SCMP_ARCH_AARCH64 case lxc_seccomp_arch_arm64: arch = SCMP_ARCH_AARCH64; break; #endif #ifdef SCMP_ARCH_PPC64LE case lxc_seccomp_arch_ppc64le: arch = SCMP_ARCH_PPC64LE; break; #endif #ifdef SCMP_ARCH_PPC64 case lxc_seccomp_arch_ppc64: arch = SCMP_ARCH_PPC64; break; #endif #ifdef SCMP_ARCH_PPC case lxc_seccomp_arch_ppc: arch = SCMP_ARCH_PPC; break; #endif #ifdef SCMP_ARCH_MIPS case lxc_seccomp_arch_mips: arch = SCMP_ARCH_MIPS; break; case lxc_seccomp_arch_mips64: arch = SCMP_ARCH_MIPS64; break; case lxc_seccomp_arch_mips64n32: arch = SCMP_ARCH_MIPS64N32; break; case lxc_seccomp_arch_mipsel: arch = SCMP_ARCH_MIPSEL; break; case lxc_seccomp_arch_mipsel64: arch = SCMP_ARCH_MIPSEL64; break; case lxc_seccomp_arch_mipsel64n32: arch = SCMP_ARCH_MIPSEL64N32; break; #endif #ifdef SCMP_ARCH_S390X case lxc_seccomp_arch_s390x: arch = SCMP_ARCH_S390X; break; #endif default: return NULL; } if ((ctx = seccomp_init(default_policy_action)) == NULL) { ERROR("Error initializing seccomp context."); return NULL; } if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("Failed to turn off no-new-privs."); seccomp_release(ctx); return NULL; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif ret = seccomp_arch_add(ctx, arch); if (ret != 0) { ERROR("Seccomp error %d (%s) adding arch: %d", ret, strerror(-ret), (int)n_arch); seccomp_release(ctx); return NULL; } if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) { ERROR("Seccomp error removing native arch"); seccomp_release(ctx); return NULL; } return ctx; } bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, uint32_t action) { int nr, ret; ret = seccomp_arch_exist(ctx, arch); if (arch && ret != 0) { ERROR("BUG: Seccomp: rule and context arch do not match (arch " "%d): %s.", arch, strerror(-ret)); return false; } if (strncmp(line, "reject_force_umount", 19) == 0) { INFO("Setting Seccomp rule to reject force umounts."); ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(umount2), 1, SCMP_A1(SCMP_CMP_MASKED_EQ , MNT_FORCE , MNT_FORCE )); if (ret < 0) { ERROR("Failed (%d) loading rule to reject force " "umount: %s.", ret, strerror(-ret)); return false; } return true; } nr = seccomp_syscall_resolve_name(line); if (nr == __NR_SCMP_ERROR) { WARN("Seccomp: failed to resolve syscall: %s.", line); WARN("This syscall will NOT be blacklisted."); return true; } if (nr < 0) { WARN("Seccomp: got negative for syscall: %d: %s.", nr, line); WARN("This syscall will NOT be blacklisted."); return true; } ret = seccomp_rule_add_exact(ctx, action, nr, 0); if (ret < 0) { ERROR("Failed (%d) loading rule for %s (nr %d action %d(%s)): %s.", ret, line, nr, action, get_action_name(action), strerror(-ret)); return false; } return true; } /* * v2 consists of * [x86] * open * read * write * close * # a comment * [x86_64] * open * read * write * close */ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) { char *p; int ret; scmp_filter_ctx compat_ctx[2] = {NULL, NULL}; bool blacklist = false; uint32_t default_policy_action = -1, default_rule_action = -1, action; enum lxc_hostarch_t native_arch = get_hostarch(), cur_rule_arch = native_arch; uint32_t compat_arch[2] = {SCMP_ARCH_NATIVE, SCMP_ARCH_NATIVE}; if (strncmp(line, "blacklist", 9) == 0) blacklist = true; else if (strncmp(line, "whitelist", 9) != 0) { ERROR("Bad seccomp policy style: %s.", line); return -1; } if ((p = strchr(line, ' '))) { default_policy_action = get_v2_default_action(p + 1); if (default_policy_action == -2) return -1; } /* for blacklist, allow any syscall which has no rule */ if (blacklist) { if (default_policy_action == -1) default_policy_action = SCMP_ACT_ALLOW; if (default_rule_action == -1) default_rule_action = SCMP_ACT_KILL; } else { if (default_policy_action == -1) default_policy_action = SCMP_ACT_KILL; if (default_rule_action == -1) default_rule_action = SCMP_ACT_ALLOW; } if (native_arch == lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_X86; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_i386, default_policy_action); compat_arch[1] = SCMP_ARCH_X32; compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_x32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; #ifdef SCMP_ARCH_PPC } else if (native_arch == lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_PPC; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_ppc, default_policy_action); if (!compat_ctx[0]) goto bad; #endif #ifdef SCMP_ARCH_ARM } else if (native_arch == lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_ARM; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_arm, default_policy_action); if (!compat_ctx[0]) goto bad; #endif #ifdef SCMP_ARCH_MIPS } else if (native_arch == lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_MIPS; compat_arch[1] = SCMP_ARCH_MIPS64N32; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_mips, default_policy_action); compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_mips64n32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; } else if (native_arch == lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_MIPSEL; compat_arch[1] = SCMP_ARCH_MIPSEL64N32; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_mipsel, default_policy_action); compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_mipsel64n32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; #endif } if (default_policy_action != SCMP_ACT_KILL) { ret = seccomp_reset(conf->seccomp_ctx, default_policy_action); if (ret != 0) { ERROR("Error re-initializing Seccomp."); return -1; } if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("Failed to turn off no-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif } while (fgets(line, 1024, f)) { if (line[0] == '#') continue; if (strlen(line) == 0) continue; remove_trailing_newlines(line); INFO("processing: .%s.", line); if (line[0] == '[') { // read the architecture for next set of rules if (strcmp(line, "[x86]") == 0 || strcmp(line, "[X86]") == 0) { if (native_arch != lxc_seccomp_arch_i386 && native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_i386; } else if (strcmp(line, "[x32]") == 0 || strcmp(line, "[X32]") == 0) { if (native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_x32; } else if (strcmp(line, "[X86_64]") == 0 || strcmp(line, "[x86_64]") == 0) { if (native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_amd64; } else if (strcmp(line, "[all]") == 0 || strcmp(line, "[ALL]") == 0) { cur_rule_arch = lxc_seccomp_arch_all; } #ifdef SCMP_ARCH_ARM else if (strcmp(line, "[arm]") == 0 || strcmp(line, "[ARM]") == 0) { if (native_arch != lxc_seccomp_arch_arm && native_arch != lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_arm; } #endif #ifdef SCMP_ARCH_AARCH64 else if (strcmp(line, "[arm64]") == 0 || strcmp(line, "[ARM64]") == 0) { if (native_arch != lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_arm64; } #endif #ifdef SCMP_ARCH_PPC64LE else if (strcmp(line, "[ppc64le]") == 0 || strcmp(line, "[PPC64LE]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64le) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64le; } #endif #ifdef SCMP_ARCH_PPC64 else if (strcmp(line, "[ppc64]") == 0 || strcmp(line, "[PPC64]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64; } #endif #ifdef SCMP_ARCH_PPC else if (strcmp(line, "[ppc]") == 0 || strcmp(line, "[PPC]") == 0) { if (native_arch != lxc_seccomp_arch_ppc && native_arch != lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc; } #endif #ifdef SCMP_ARCH_MIPS else if (strcmp(line, "[mips64]") == 0 || strcmp(line, "[MIPS64]") == 0) { if (native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips64; } else if (strcmp(line, "[mips64n32]") == 0 || strcmp(line, "[MIPS64N32]") == 0) { if (native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips64n32; } else if (strcmp(line, "[mips]") == 0 || strcmp(line, "[MIPS]") == 0) { if (native_arch != lxc_seccomp_arch_mips && native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips; } else if (strcmp(line, "[mipsel64]") == 0 || strcmp(line, "[MIPSEL64]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel64; } else if (strcmp(line, "[mipsel64n32]") == 0 || strcmp(line, "[MIPSEL64N32]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel64n32; } else if (strcmp(line, "[mipsel]") == 0 || strcmp(line, "[MIPSEL]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel && native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel; } #endif #ifdef SCMP_ARCH_S390X else if (strcmp(line, "[s390x]") == 0 || strcmp(line, "[S390X]") == 0) { if (native_arch != lxc_seccomp_arch_s390x) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_s390x; } #endif else goto bad_arch; continue; } /* irrelevant arch - i.e. arm on i386 */ if (cur_rule_arch == lxc_seccomp_arch_unknown) continue; /* read optional action which follows the syscall */ action = get_and_clear_v2_action(line, default_rule_action); if (action == -1) { ERROR("Failed to interpret action."); goto bad_rule; } if (cur_rule_arch == native_arch || cur_rule_arch == lxc_seccomp_arch_native || compat_arch[0] == SCMP_ARCH_NATIVE) { INFO("Adding native rule for %s action %d(%s).", line, action, get_action_name(action)); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; } else if (cur_rule_arch != lxc_seccomp_arch_all) { int arch_index = cur_rule_arch == lxc_seccomp_arch_mips64n32 || cur_rule_arch == lxc_seccomp_arch_mipsel64n32 ? 1 : 0; INFO("Adding compat-only rule for %s action %d(%s).", line, action, get_action_name(action)); if (!do_resolve_add_rule(compat_arch[arch_index], line, compat_ctx[arch_index], action)) goto bad_rule; } else { INFO("Adding native rule for %s action %d(%s).", line, action, get_action_name(action)); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; INFO("Adding compat rule for %s action %d(%s).", line, action, get_action_name(action)); if (!do_resolve_add_rule(compat_arch[0], line, compat_ctx[0], action)) goto bad_rule; if (compat_arch[1] != SCMP_ARCH_NATIVE && !do_resolve_add_rule(compat_arch[1], line, compat_ctx[1], action)) goto bad_rule; } } if (compat_ctx[0]) { INFO("Merging in the compat Seccomp ctx into the main one."); if (seccomp_merge(conf->seccomp_ctx, compat_ctx[0]) != 0 || (compat_ctx[1] != NULL && seccomp_merge(conf->seccomp_ctx, compat_ctx[1]) != 0)) { ERROR("Error merging compat Seccomp contexts."); goto bad; } } return 0; bad_arch: ERROR("Unsupported arch: %s.", line); bad_rule: bad: if (compat_ctx[0]) seccomp_release(compat_ctx[0]); if (compat_ctx[1]) seccomp_release(compat_ctx[1]); return -1; } #else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) { return -1; } #endif /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */ /* * 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' or '2' * the directives must include 'whitelist'(version == 1 or 2) or 'blacklist' * (version == 2) 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 && version != 2)) { ERROR("Invalid version."); return -1; } if (!fgets(line, 1024, f)) { ERROR("Invalid config file."); return -1; } if (version == 1 && !strstr(line, "whitelist")) { ERROR("Only whitelist policy is supported."); return -1; } if (strstr(line, "debug")) { ERROR("Debug not yet implemented."); return -1; } if (version == 1) return parse_config_v1(f, conf); return parse_config_v2(f, line, conf); } /* * use_seccomp: return true if we should try and apply a seccomp policy * if defined for the container. * This will return false if * 1. seccomp is not enabled in the kernel * 2. a seccomp policy is already enabled for this task */ static bool use_seccomp(void) { FILE *f = fopen("/proc/self/status", "r"); char line[1024]; bool already_enabled = false; bool found = false; int ret, v; if (!f) return true; while (fgets(line, 1024, f)) { if (strncmp(line, "Seccomp:", 8) == 0) { found = true; ret = sscanf(line + 8, "%d", &v); if (ret == 1 && v != 0) already_enabled = true; break; } } fclose(f); if (!found) { /* no Seccomp line, no seccomp in kernel */ INFO("Seccomp is not enabled in the kernel."); return false; } if (already_enabled) { /* already seccomp-confined */ INFO("Already seccomp-confined, not loading new policy."); return false; } return true; } int lxc_read_seccomp_config(struct lxc_conf *conf) { FILE *f; int ret; int check_seccomp_attr_set; if (!conf->seccomp) return 0; if (!use_seccomp()) return 0; #if HAVE_SCMP_FILTER_CTX /* XXX for debug, pass in SCMP_ACT_TRAP */ conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL); ret = !conf->seccomp_ctx; #else ret = seccomp_init(SCMP_ACT_KILL) < 0; #endif if (ret) { ERROR("Failed initializing seccomp."); return -1; } /* turn off no-new-privs. We don't want it in lxc, and it breaks * with apparmor */ #if HAVE_SCMP_FILTER_CTX check_seccomp_attr_set = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0); #else check_seccomp_attr_set = seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0); #endif if (check_seccomp_attr_set) { ERROR("Failed to turn off no-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif f = fopen(conf->seccomp, "r"); if (!f) { SYSERROR("Failed to open seccomp policy file %s.", 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; if (!use_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; } /* After load seccomp filter into the kernel successfully, export the current seccomp * filter to log file */ #if HAVE_SCMP_FILTER_CTX if ((lxc_log_get_level() <= LXC_LOG_LEVEL_TRACE || conf->loglevel <= LXC_LOG_LEVEL_TRACE) && lxc_log_fd >= 0) { ret = seccomp_export_pfc(conf->seccomp_ctx, lxc_log_fd); /* Just give an warning when export error */ if (ret < 0) WARN("Failed to export seccomp filter to log file: %s.", strerror(-ret)); } #endif return 0; } void lxc_seccomp_free(struct lxc_conf *conf) { 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 } lxc-2.0.11/src/lxc/rtnl.c0000644061062106075000000000415413435013473012021 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "nl.h" #include "rtnl.h" extern int rtnetlink_open(struct rtnl_handler *handler) { return netlink_open(&handler->nlh, NETLINK_ROUTE); } extern int rtnetlink_close(struct rtnl_handler *handler) { return netlink_close(&handler->nlh); } extern int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg) { return netlink_rcv(&handler->nlh, (struct nlmsg *)&rtnlmsg->nlmsghdr); } extern int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg) { return netlink_send(&handler->nlh, (struct nlmsg *)&rtnlmsg->nlmsghdr); } extern int rtnetlink_transaction(struct rtnl_handler *handler, struct rtnlmsg *request, struct rtnlmsg *answer) { return netlink_transaction(&handler->nlh, (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&answer->nlmsghdr); } extern struct rtnlmsg *rtnlmsg_alloc(size_t size) { /* size_t len; len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtnlmsghdr))) + size; return (struct rtnlmsg *)nlmsg_alloc(len); */ return NULL; } extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg) { free(rtnlmsg); } lxc-2.0.11/src/lxc/state.c0000644061062106075000000000636613435013473012171 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "start.h" lxc_log_define(lxc_state, lxc); static const char * const strstate[] = { "STOPPED", "STARTING", "RUNNING", "STOPPING", "ABORTING", "FREEZING", "FROZEN", "THAWED", }; const char *lxc_state2str(lxc_state_t state) { if (state < STOPPED || state > MAX_STATE - 1) return NULL; return strstate[state]; } lxc_state_t lxc_str2state(const char *state) { size_t len; lxc_state_t i; len = sizeof(strstate)/sizeof(strstate[0]); for (i = 0; i < len; i++) if (!strcmp(strstate[i], state)) return i; ERROR("invalid state '%s'", state); return -1; } lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { extern lxc_state_t freezer_state(const char *name, const char *lxcpath); lxc_state_t state = freezer_state(name, lxcpath); if (state != FROZEN && state != FREEZING) state = lxc_cmd_get_state(name, lxcpath); return state; } static int fillwaitedstates(const char *strstates, lxc_state_t *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, const char *lxcpath) { int state = -1; lxc_state_t s[MAX_STATE] = {0}; if (fillwaitedstates(states, s)) return -1; for (;;) { state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout); if (state >= 0) break; if (errno != ECONNREFUSED) { SYSERROR("Failed to receive state from monitor"); return -1; } if (timeout > 0) timeout--; if (timeout == 0) return -1; sleep(1); } if (state < 0) { ERROR("Failed to retrieve state from monitor"); return -1; } TRACE("Retrieved state of container %s", lxc_state2str(state)); if (!s[state]) return -1; return 0; } lxc-2.0.11/src/lxc/conf.c0000644061062106075000000033032213435013473011766 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* makedev() */ #ifdef MAJOR_IN_MKDEV # include #endif #ifdef HAVE_STATVFS #include #endif #if HAVE_PTY_H #include #else #include <../include/openpty.h> #endif #include "af_unix.h" #include "caps.h" /* for lxc_caps_last_cap() */ #include "cgroup.h" #include "conf.h" #include "confile_utils.h" #include "console.h" #include "error.h" #include "log.h" #include "lxclock.h" #include "lxcseccomp.h" #include "namespace.h" #include "network.h" #include "parse.h" #include "storage.h" #include "storage/aufs.h" #include "storage/overlay.h" #include "utils.h" #include "lsm/lsm.h" #if !HAVE_GETGRGID_R #include "../include/getgrgid_r.h" #endif #if HAVE_LIBCAP #include #endif #if HAVE_SYS_PERSONALITY_H #include #endif #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif lxc_log_define(lxc_conf, lxc); /* Define pivot_root() if missing from the C library */ #ifndef HAVE_PIVOT_ROOT static int pivot_root(const char * new_root, const char * put_old) { #ifdef __NR_pivot_root return syscall(__NR_pivot_root, new_root, put_old); #else errno = ENOSYS; return -1; #endif } #else extern int pivot_root(const char * new_root, const char * put_old); #endif #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif #ifndef MS_LAZYTIME #define MS_LAZYTIME (1<<25) #endif char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount", "autodev", "start", "stop", "post-stop", "clone", "destroy"}; struct mount_opt { char *name; int clear; int flag; }; struct caps_opt { char *name; int value; }; /* * The lxc_conf of the container currently being worked on in an * API call * This is used in the error calls */ #ifdef HAVE_TLS __thread struct lxc_conf *current_config; #else struct lxc_conf *current_config; #endif static struct mount_opt mount_opt[] = { { "async", 1, MS_SYNCHRONOUS }, { "atime", 1, MS_NOATIME }, { "bind", 0, MS_BIND }, { "defaults", 0, 0 }, { "dev", 1, MS_NODEV }, { "diratime", 1, MS_NODIRATIME }, { "dirsync", 0, MS_DIRSYNC }, { "exec", 1, MS_NOEXEC }, { "lazytime", 0, MS_LAZYTIME }, { "mand", 0, MS_MANDLOCK }, { "noatime", 0, MS_NOATIME }, { "nodev", 0, MS_NODEV }, { "nodiratime", 0, MS_NODIRATIME }, { "noexec", 0, MS_NOEXEC }, { "nomand", 1, MS_MANDLOCK }, { "norelatime", 1, MS_RELATIME }, { "nostrictatime", 1, MS_STRICTATIME }, { "nosuid", 0, MS_NOSUID }, { "rbind", 0, MS_BIND|MS_REC }, { "relatime", 0, MS_RELATIME }, { "remount", 0, MS_REMOUNT }, { "ro", 0, MS_RDONLY }, { "rw", 1, MS_RDONLY }, { "strictatime", 0, MS_STRICTATIME }, { "suid", 1, MS_NOSUID }, { "sync", 0, MS_SYNCHRONOUS }, { NULL, 0, 0 }, }; static struct mount_opt propagation_opt[] = { { "private", 0, MS_PRIVATE }, { "shared", 0, MS_SHARED }, { "slave", 0, MS_SLAVE }, { "unbindable", 0, MS_UNBINDABLE }, { "rprivate", 0, MS_PRIVATE|MS_REC }, { "rshared", 0, MS_SHARED|MS_REC }, { "rslave", 0, MS_SLAVE|MS_REC }, { "runbindable", 0, MS_UNBINDABLE|MS_REC }, { NULL, 0, 0 }, }; static struct caps_opt caps_opt[] = { #if HAVE_LIBCAP { "chown", CAP_CHOWN }, { "dac_override", CAP_DAC_OVERRIDE }, { "dac_read_search", CAP_DAC_READ_SEARCH }, { "fowner", CAP_FOWNER }, { "fsetid", CAP_FSETID }, { "kill", CAP_KILL }, { "setgid", CAP_SETGID }, { "setuid", CAP_SETUID }, { "setpcap", CAP_SETPCAP }, { "linux_immutable", CAP_LINUX_IMMUTABLE }, { "net_bind_service", CAP_NET_BIND_SERVICE }, { "net_broadcast", CAP_NET_BROADCAST }, { "net_admin", CAP_NET_ADMIN }, { "net_raw", CAP_NET_RAW }, { "ipc_lock", CAP_IPC_LOCK }, { "ipc_owner", CAP_IPC_OWNER }, { "sys_module", CAP_SYS_MODULE }, { "sys_rawio", CAP_SYS_RAWIO }, { "sys_chroot", CAP_SYS_CHROOT }, { "sys_ptrace", CAP_SYS_PTRACE }, { "sys_pacct", CAP_SYS_PACCT }, { "sys_admin", CAP_SYS_ADMIN }, { "sys_boot", CAP_SYS_BOOT }, { "sys_nice", CAP_SYS_NICE }, { "sys_resource", CAP_SYS_RESOURCE }, { "sys_time", CAP_SYS_TIME }, { "sys_tty_config", CAP_SYS_TTY_CONFIG }, { "mknod", CAP_MKNOD }, { "lease", CAP_LEASE }, #ifdef CAP_AUDIT_READ { "audit_read", CAP_AUDIT_READ }, #endif #ifdef CAP_AUDIT_WRITE { "audit_write", CAP_AUDIT_WRITE }, #endif #ifdef CAP_AUDIT_CONTROL { "audit_control", CAP_AUDIT_CONTROL }, #endif { "setfcap", CAP_SETFCAP }, { "mac_override", CAP_MAC_OVERRIDE }, { "mac_admin", CAP_MAC_ADMIN }, #ifdef CAP_SYSLOG { "syslog", CAP_SYSLOG }, #endif #ifdef CAP_WAKE_ALARM { "wake_alarm", CAP_WAKE_ALARM }, #endif #ifdef CAP_BLOCK_SUSPEND { "block_suspend", CAP_BLOCK_SUSPEND }, #endif #endif }; static int run_buffer(char *buffer) { struct lxc_popen_FILE *f; char *output; int ret; f = lxc_popen(buffer); if (!f) { SYSERROR("Failed to popen() %s.", buffer); return -1; } output = malloc(LXC_LOG_BUFFER_SIZE); if (!output) { ERROR("Failed to allocate memory for %s.", buffer); lxc_pclose(f); return -1; } while (fgets(output, LXC_LOG_BUFFER_SIZE, f->f)) DEBUG("Script %s with output: %s.", buffer, output); free(output); ret = lxc_pclose(f); if (ret == -1) { SYSERROR("Script exited with error."); return -1; } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { ERROR("Script exited with status %d.", WEXITSTATUS(ret)); return -1; } else if (WIFSIGNALED(ret)) { ERROR("Script terminated by signal %d.", WTERMSIG(ret)); return -1; } return 0; } static int run_script_argv(const char *name, const char *section, const char *script, const char *hook, const char *lxcpath, char **argsin) { int ret, i; char *buffer; size_t size = 0; INFO("Executing script \"%s\" for container \"%s\", config section \"%s\".", script, name, section); for (i = 0; argsin && argsin[i]; i++) size += strlen(argsin[i]) + 1; size += strlen(hook) + 1; size += strlen("exec"); size += strlen(script); size += strlen(name); size += strlen(section); size += 4; if (size > INT_MAX) return -1; buffer = alloca(size); if (!buffer) { ERROR("Failed to allocate memory."); return -1; } ret = snprintf(buffer, size, "exec %s %s %s %s", script, name, section, hook); if (ret < 0 || (size_t)ret >= size) { ERROR("Script name too long."); return -1; } for (i = 0; argsin && argsin[i]; i++) { int len = size - ret; int rc; rc = snprintf(buffer + ret, len, " %s", argsin[i]); if (rc < 0 || rc >= len) { ERROR("Script args too long."); return -1; } ret += rc; } return run_buffer(buffer); } int run_script(const char *name, const char *section, const char *script, ...) { int ret; char *buffer, *p; size_t size = 0; va_list ap; INFO("Executing script \"%s\" for container \"%s\", config section \"%s\".", script, name, section); va_start(ap, script); while ((p = va_arg(ap, char *))) size += strlen(p) + 1; va_end(ap); size += strlen("exec"); size += strlen(script); size += strlen(name); size += strlen(section); size += 4; if (size > INT_MAX) return -1; buffer = alloca(size); if (!buffer) { ERROR("Failed to allocate memory."); return -1; } ret = snprintf(buffer, size, "exec %s %s %s", script, name, section); if (ret < 0 || ret >= size) { ERROR("Script name too long."); return -1; } va_start(ap, script); while ((p = va_arg(ap, char *))) { int len = size - ret; int rc; rc = snprintf(buffer + ret, len, " %s", p); if (rc < 0 || rc >= len) { ERROR("Script args too long."); va_end(ap); return -1; } ret += rc; } va_end(ap); return run_buffer(buffer); } /* * pin_rootfs * if rootfs is a directory, then open ${rootfs}/lxc.hold for writing for * the duration of the container run, to prevent the container from marking * the underlying fs readonly on shutdown. unlink the file immediately so * no name pollution is happens * return -1 on error. * return -2 if nothing needed to be pinned. * return an open fd (>=0) if we pinned it. */ int pin_rootfs(const char *rootfs) { char absrootfs[MAXPATHLEN]; char absrootfspin[MAXPATHLEN]; struct stat s; int ret, fd; if (rootfs == NULL || strlen(rootfs) == 0) return -2; if (!realpath(rootfs, absrootfs)) return -2; if (access(absrootfs, F_OK)) return -1; if (stat(absrootfs, &s)) return -1; if (!S_ISDIR(s.st_mode)) return -2; ret = snprintf(absrootfspin, MAXPATHLEN, "%s/lxc.hold", absrootfs); if (ret >= MAXPATHLEN) return -1; fd = open(absrootfspin, O_CREAT | O_RDWR, S_IWUSR|S_IRUSR); if (fd < 0) return fd; (void)unlink(absrootfspin); return fd; } /* * If we are asking to remount something, make sure that any * NOEXEC etc are honored. */ unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags) { #ifdef HAVE_STATVFS struct statvfs sb; unsigned long required_flags = 0; if (!s) s = d; if (!s) return flags; if (statvfs(s, &sb) < 0) return flags; if (flags & MS_REMOUNT) { if (sb.f_flag & MS_NOSUID) required_flags |= MS_NOSUID; if (sb.f_flag & MS_NODEV) required_flags |= MS_NODEV; if (sb.f_flag & MS_RDONLY) required_flags |= MS_RDONLY; if (sb.f_flag & MS_NOEXEC) required_flags |= MS_NOEXEC; } if (sb.f_flag & MS_NOATIME) required_flags |= MS_NOATIME; if (sb.f_flag & MS_NODIRATIME) required_flags |= MS_NODIRATIME; if (sb.f_flag & MS_LAZYTIME) required_flags |= MS_LAZYTIME; if (sb.f_flag & MS_RELATIME) required_flags |= MS_RELATIME; if (sb.f_flag & MS_STRICTATIME) required_flags |= MS_STRICTATIME; return flags | required_flags; #else return flags; #endif } static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler) { int r; int i; static struct { int match_mask; int match_flag; const char *source; const char *destination; const char *fstype; unsigned long flags; const char *options; } default_mounts[] = { /* Read-only bind-mounting... In older kernels, doing that required * to do one MS_BIND mount and then MS_REMOUNT|MS_RDONLY the same * one. According to mount(2) manpage, MS_BIND honors MS_RDONLY from * kernel 2.6.26 onwards. However, this apparently does not work on * kernel 3.8. Unfortunately, on that very same kernel, doing the * same trick as above doesn't seem to work either, there one needs * to ALSO specify MS_BIND for the remount, otherwise the entire * fs is remounted read-only or the mount fails because it's busy... * MS_REMOUNT|MS_BIND|MS_RDONLY seems to work for kernels as low as * 2.6.32... */ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, /* proc/tty is used as a temporary placeholder for proc/sys/net which we'll move back in a few steps */ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys/net", "%r/proc/tty", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys", "%r/proc/sys", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sys", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/tty", "%r/proc/sys/net", NULL, MS_MOVE, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sysrq-trigger", "%r/proc/sysrq-trigger", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sysrq-trigger", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW, "sysfs", "%r/sys", "sysfs", 0, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO, "sysfs", "%r/sys", "sysfs", MS_RDONLY, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/sys", "sysfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/sys", "%r/sys", NULL, MS_BIND, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, NULL, "%r/sys", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/sys/devices/virtual/net", "sysfs", 0, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/sys/devices/virtual/net/devices/virtual/net", "%r/sys/devices/virtual/net", NULL, MS_BIND, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, NULL, "%r/sys/devices/virtual/net", NULL, MS_REMOUNT|MS_BIND|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL }, { 0, 0, NULL, NULL, NULL, 0, NULL } }; for (i = 0; default_mounts[i].match_mask; i++) { if ((flags & default_mounts[i].match_mask) == default_mounts[i].match_flag) { char *source = NULL; char *destination = NULL; int saved_errno; unsigned long mflags; if (default_mounts[i].source) { /* will act like strdup if %r is not present */ source = lxc_string_replace("%r", conf->rootfs.path ? conf->rootfs.mount : "", default_mounts[i].source); if (!source) { SYSERROR("memory allocation error"); return -1; } } if (!default_mounts[i].destination) { ERROR("BUG: auto mounts destination %d was NULL", i); free(source); return -1; } /* will act like strdup if %r is not present */ destination = lxc_string_replace("%r", conf->rootfs.path ? conf->rootfs.mount : "", default_mounts[i].destination); if (!destination) { saved_errno = errno; SYSERROR("memory allocation error"); free(source); errno = saved_errno; return -1; } mflags = add_required_remount_flags(source, destination, default_mounts[i].flags); r = safe_mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options, conf->rootfs.path ? conf->rootfs.mount : NULL); saved_errno = errno; if (r < 0 && errno == ENOENT) { INFO("Mount source or target for %s on %s doesn't exist. Skipping.", source, destination); r = 0; } else if (r < 0) SYSERROR("error mounting %s on %s flags %lu", source, destination, mflags); free(source); free(destination); if (r < 0) { errno = saved_errno; return -1; } } } if (flags & LXC_AUTO_CGROUP_MASK) { int cg_flags; cg_flags = flags & (LXC_AUTO_CGROUP_MASK & ~LXC_AUTO_CGROUP_FORCE); /* If the type of cgroup mount was not specified, it depends on the * container's capabilities as to what makes sense: if we have * CAP_SYS_ADMIN, the read-only part can be remounted read-write * anyway, so we may as well default to read-write; then the admin * will not be given a false sense of security. (And if they really * want mixed r/o r/w, then they can explicitly specify :mixed.) * OTOH, if the container lacks CAP_SYS_ADMIN, do only default to * :mixed, because then the container can't remount it read-write. */ if (cg_flags == LXC_AUTO_CGROUP_NOSPEC || cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC) { int has_sys_admin = 0; if (!lxc_list_empty(&conf->keepcaps)) has_sys_admin = in_caplist(CAP_SYS_ADMIN, &conf->keepcaps); else has_sys_admin = !in_caplist(CAP_SYS_ADMIN, &conf->caps); if (cg_flags == LXC_AUTO_CGROUP_NOSPEC) cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_RW : LXC_AUTO_CGROUP_MIXED; else cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_FULL_RW : LXC_AUTO_CGROUP_FULL_MIXED; } if (flags & LXC_AUTO_CGROUP_FORCE) cg_flags |= LXC_AUTO_CGROUP_FORCE; if (!cgroup_mount(conf->rootfs.path ? conf->rootfs.mount : "", handler, cg_flags)) { SYSERROR("error mounting /sys/fs/cgroup"); return -1; } } return 0; } static int setup_utsname(struct utsname *utsname) { if (!utsname) return 0; if (sethostname(utsname->nodename, strlen(utsname->nodename))) { SYSERROR("failed to set the hostname to '%s'", utsname->nodename); return -1; } INFO("'%s' hostname has been setup", utsname->nodename); return 0; } struct dev_symlinks { const char *oldpath; const char *name; }; static const struct dev_symlinks dev_symlinks[] = { {"/proc/self/fd", "fd"}, {"/proc/self/fd/0", "stdin"}, {"/proc/self/fd/1", "stdout"}, {"/proc/self/fd/2", "stderr"}, }; static int lxc_setup_dev_symlinks(const struct lxc_rootfs *rootfs) { char path[MAXPATHLEN]; int ret,i; struct stat s; for (i = 0; i < sizeof(dev_symlinks) / sizeof(dev_symlinks[0]); i++) { const struct dev_symlinks *d = &dev_symlinks[i]; ret = snprintf(path, sizeof(path), "%s/dev/%s", rootfs->path ? rootfs->mount : "", d->name); if (ret < 0 || ret >= MAXPATHLEN) return -1; /* * Stat the path first. If we don't get an error * accept it as is and don't try to create it */ if (!stat(path, &s)) { continue; } ret = symlink(d->oldpath, path); if (ret && errno != EEXIST) { if ( errno == EROFS ) { WARN("Warning: Read Only file system while creating %s", path); } else { SYSERROR("Error creating %s", path); return -1; } } } return 0; } /* Build a space-separate list of ptys to pass to systemd. */ static bool append_ptyname(char **pp, char *name) { char *p; if (!*pp) { *pp = malloc(strlen(name) + strlen("container_ttys=") + 1); if (!*pp) return false; sprintf(*pp, "container_ttys=%s", name); return true; } p = realloc(*pp, strlen(*pp) + strlen(name) + 2); if (!p) return false; *pp = p; strcat(p, " "); strcat(p, name); return true; } static int lxc_setup_ttys(struct lxc_conf *conf) { int i, ret; const struct lxc_tty_info *tty_info = &conf->tty_info; char *ttydir = conf->ttydir; char path[MAXPATHLEN], lxcpath[MAXPATHLEN]; if (!conf->rootfs.path) return 0; for (i = 0; i < tty_info->nbtty; i++) { struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; ret = snprintf(path, sizeof(path), "/dev/tty%d", i + 1); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; if (ttydir) { /* create dev/lxc/tty%d" */ ret = snprintf(lxcpath, sizeof(lxcpath), "/dev/%s/tty%d", ttydir, i + 1); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = creat(lxcpath, 0660); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"%s\"", lxcpath); return -1; } if (ret >= 0) close(ret); ret = unlink(path); if (ret < 0 && errno != ENOENT) { SYSERROR("Failed to unlink \"%s\"", path); return -1; } ret = mount(pty_info->name, lxcpath, "none", MS_BIND, 0); if (ret < 0) { WARN("Failed to bind mount \"%s\" onto \"%s\"", pty_info->name, path); continue; } DEBUG("bind mounted \"%s\" onto \"%s\"", pty_info->name, path); ret = snprintf(lxcpath, sizeof(lxcpath), "%s/tty%d", ttydir, i + 1); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = symlink(lxcpath, path); if (ret < 0) { SYSERROR("Failed to create symlink \"%s\" -> \"%s\"", path, lxcpath); return -1; } } else { /* If we populated /dev, then we need to create * /dev/ttyN */ ret = access(path, F_OK); if (ret < 0) { ret = creat(path, 0660); if (ret < 0) { SYSERROR("Failed to create \"%s\"", path); /* this isn't fatal, continue */ } else { close(ret); } } ret = mount(pty_info->name, path, "none", MS_BIND, 0); if (ret < 0) { SYSERROR("Failed to mount '%s'->'%s'", pty_info->name, path); continue; } DEBUG("Bind mounted \"%s\" onto \"%s\"", pty_info->name, path); } if (!append_ptyname(&conf->pty_names, pty_info->name)) { ERROR("Error setting up container_ttys string"); return -1; } } INFO("Finished setting up %d /dev/tty device(s)", tty_info->nbtty); return 0; } int lxc_allocate_ttys(const char *name, struct lxc_conf *conf) { struct lxc_tty_info *tty_info = &conf->tty_info; int i, ret; /* no tty in the configuration */ if (!conf->tty) return 0; tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty); if (!tty_info->pty_info) { SYSERROR("failed to allocate struct *pty_info"); return -ENOMEM; } for (i = 0; i < conf->tty; i++) { struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; ret = openpty(&pty_info->master, &pty_info->slave, pty_info->name, NULL, NULL); if (ret) { SYSERROR("failed to create pty device number %d", i); tty_info->nbtty = i; lxc_delete_tty(tty_info); return -ENOTTY; } DEBUG("allocated pty \"%s\" with master fd %d and slave fd %d", pty_info->name, pty_info->master, pty_info->slave); /* Prevent leaking the file descriptors to the container */ ret = fcntl(pty_info->master, F_SETFD, FD_CLOEXEC); if (ret < 0) WARN("failed to set FD_CLOEXEC flag on master fd %d of " "pty device \"%s\": %s", pty_info->master, pty_info->name, strerror(errno)); ret = fcntl(pty_info->slave, F_SETFD, FD_CLOEXEC); if (ret < 0) WARN("failed to set FD_CLOEXEC flag on slave fd %d of " "pty device \"%s\": %s", pty_info->slave, pty_info->name, strerror(errno)); pty_info->busy = 0; } tty_info->nbtty = conf->tty; INFO("finished allocating %d pts devices", conf->tty); return 0; } void lxc_delete_tty(struct lxc_tty_info *tty_info) { int i; for (i = 0; i < tty_info->nbtty; i++) { struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; close(pty_info->master); close(pty_info->slave); } free(tty_info->pty_info); tty_info->pty_info = NULL; tty_info->nbtty = 0; } static int lxc_send_ttys_to_parent(struct lxc_handler *handler) { int i; struct lxc_conf *conf = handler->conf; struct lxc_tty_info *tty_info = &conf->tty_info; int sock = handler->data_sock[0]; int ret = -1; if (!conf->tty) return 0; for (i = 0; i < conf->tty; i++) { int ttyfds[2]; struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; ttyfds[0] = pty_info->master; ttyfds[1] = pty_info->slave; ret = lxc_abstract_unix_send_fds(sock, ttyfds, 2, NULL, 0); if (ret < 0) break; TRACE("Send pty \"%s\" with master fd %d and slave fd %d to " "parent", pty_info->name, pty_info->master, pty_info->slave); } if (ret < 0) ERROR("Failed to send %d ttys to parent: %s", conf->tty, strerror(errno)); else TRACE("Sent %d ttys to parent", conf->tty); return ret; } static int lxc_create_ttys(struct lxc_handler *handler) { int ret = -1; struct lxc_conf *conf = handler->conf; ret = lxc_allocate_ttys(handler->name, conf); if (ret < 0) { ERROR("Failed to allocate ttys"); goto on_error; } ret = lxc_send_ttys_to_parent(handler); if (ret < 0) { ERROR("Failed to send ttys to parent"); goto on_error; } if (!conf->is_execute) { ret = lxc_setup_ttys(conf); if (ret < 0) { ERROR("Failed to setup ttys"); goto on_error; } } if (conf->pty_names) { ret = setenv("container_ttys", conf->pty_names, 1); if (ret < 0) SYSERROR("Failed to set \"container_ttys=%s\"", conf->pty_names); } ret = 0; on_error: lxc_delete_tty(&conf->tty_info); return ret; } static int setup_rootfs_pivot_root(const char *rootfs) { int oldroot = -1, newroot = -1; oldroot = open("/", O_DIRECTORY | O_RDONLY); if (oldroot < 0) { SYSERROR("Error opening old-/ for fchdir"); return -1; } newroot = open(rootfs, O_DIRECTORY | O_RDONLY); if (newroot < 0) { SYSERROR("Error opening new-/ for fchdir"); goto fail; } /* change into new root fs */ if (fchdir(newroot)) { SYSERROR("can't chdir to new rootfs '%s'", rootfs); goto fail; } /* pivot_root into our new root fs */ if (pivot_root(".", ".")) { SYSERROR("pivot_root syscall failed"); goto fail; } /* * at this point the old-root is mounted on top of our new-root * To unmounted it we must not be chdir'd into it, so escape back * to old-root */ if (fchdir(oldroot) < 0) { SYSERROR("Error entering oldroot"); goto fail; } if (umount2(".", MNT_DETACH) < 0) { SYSERROR("Error detaching old root"); goto fail; } if (fchdir(newroot) < 0) { SYSERROR("Error re-entering newroot"); goto fail; } close(oldroot); close(newroot); DEBUG("pivot_root syscall to '%s' successful", rootfs); return 0; fail: if (oldroot != -1) close(oldroot); if (newroot != -1) close(newroot); return -1; } /* Just create a path for /dev under $lxcpath/$name and in rootfs If we hit an * error, log it but don't fail yet. */ static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs, const char *lxcpath) { int ret; size_t clen; char *path; mode_t cur_mask; INFO("Preparing \"/dev\""); /* $(rootfs->mount) + "/dev/pts" + '\0' */ clen = (rootfs->path ? strlen(rootfs->mount) : 0) + 9; path = alloca(clen); ret = snprintf(path, clen, "%s/dev", rootfs->path ? rootfs->mount : ""); if (ret < 0 || (size_t)ret >= clen) return -1; cur_mask = umask(S_IXUSR | S_IXGRP | S_IXOTH); ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"/dev\" directory"); ret = -errno; goto reset_umask; } ret = safe_mount("none", path, "tmpfs", 0, "size=500000,mode=755", rootfs->path ? rootfs->mount : NULL); if (ret < 0) { SYSERROR("Failed to mount tmpfs on \"%s\"", path); goto reset_umask; } TRACE("Mounted tmpfs on \"%s\"", path); ret = snprintf(path, clen, "%s/dev/pts", rootfs->path ? rootfs->mount : ""); if (ret < 0 || (size_t)ret >= clen) { ret = -1; goto reset_umask; } /* If we are running on a devtmpfs mapping, dev/pts may already exist. * If not, then create it and exit if that fails... */ ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", path); ret = -errno; goto reset_umask; } ret = 0; reset_umask: (void)umask(cur_mask); INFO("Prepared \"/dev\""); return ret; } struct lxc_device_node { const char *name; const mode_t mode; const int maj; const int min; }; static const struct lxc_device_node lxc_devices[] = { { "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 }, { "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 }, { "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 }, { "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 }, { "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 }, { "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 }, }; enum { LXC_DEVNODE_BIND, LXC_DEVNODE_MKNOD, LXC_DEVNODE_PARTIAL, LXC_DEVNODE_OPEN, }; static int lxc_fill_autodev(const struct lxc_rootfs *rootfs) { int i, ret; char path[PATH_MAX]; mode_t cmask; int use_mknod = LXC_DEVNODE_MKNOD; ret = snprintf(path, PATH_MAX, "%s/dev", rootfs->path ? rootfs->mount : ""); if (ret < 0 || ret >= PATH_MAX) return -1; /* ignore, just don't try to fill in */ if (!dir_exists(path)) return 0; INFO("Populating \"/dev\""); cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH); for (i = 0; i < sizeof(lxc_devices) / sizeof(lxc_devices[0]); i++) { char hostpath[PATH_MAX]; const struct lxc_device_node *device = &lxc_devices[i]; ret = snprintf(path, PATH_MAX, "%s/dev/%s", rootfs->path ? rootfs->mount : "", device->name); if (ret < 0 || ret >= PATH_MAX) return -1; if (use_mknod >= LXC_DEVNODE_MKNOD) { ret = mknod(path, device->mode, makedev(device->maj, device->min)); if (ret == 0 || (ret < 0 && errno == EEXIST)) { DEBUG("Created device node \"%s\"", path); } else if (ret < 0) { if (errno != EPERM) { SYSERROR("Failed to create device node \"%s\"", path); return -1; } use_mknod = LXC_DEVNODE_BIND; } /* Device nodes are fully useable. */ if (use_mknod == LXC_DEVNODE_OPEN) continue; if (use_mknod == LXC_DEVNODE_MKNOD) { /* See * - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=55956b59df336f6738da916dbb520b6e37df9fbd * - https://lists.linuxfoundation.org/pipermail/containers/2018-June/039176.html */ ret = open(path, O_RDONLY | O_CLOEXEC); if (ret >= 0) { close(ret); /* Device nodes are fully useable. */ use_mknod = LXC_DEVNODE_OPEN; continue; } TRACE("Failed to open \"%s\" device", path); /* Device nodes are only partially useable. */ use_mknod = LXC_DEVNODE_PARTIAL; } } if (use_mknod != LXC_DEVNODE_PARTIAL) { /* If we are dealing with partially functional device * nodes the prio mknod() call will have created the * device node so we can use it as a bind-mount target. */ ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create file \"%s\"", path); return -1; } } /* Fallback to bind-mounting the device from the host. */ ret = snprintf(hostpath, PATH_MAX, "/dev/%s", device->name); if (ret < 0 || ret >= PATH_MAX) return -1; ret = safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL); if (ret < 0) { SYSERROR("Failed to bind mount host device node \"%s\" " "onto \"%s\"", hostpath, path); return -1; } DEBUG("Bind mounted host device node \"%s\" onto \"%s\"", hostpath, path); } (void)umask(cmask); INFO("Populated \"/dev\""); return 0; } static int lxc_setup_rootfs(struct lxc_conf *conf) { int ret; struct lxc_storage *bdev; const struct lxc_rootfs *rootfs; rootfs = &conf->rootfs; if (!rootfs->path) { if (mount("", "/", NULL, MS_SLAVE | MS_REC, 0)) { SYSERROR("Failed to make / rslave."); return -1; } return 0; } if (access(rootfs->mount, F_OK)) { SYSERROR("Failed to access to \"%s\". Check it is present.", rootfs->mount); return -1; } bdev = storage_init(conf, rootfs->path, rootfs->mount, rootfs->options); if (!bdev) { ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return -1; } ret = bdev->ops->mount(bdev); storage_put(bdev); if (ret < 0) { ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return -1; } DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return 0; } int prepare_ramfs_root(char *root) { char buf[LXC_LINELEN], *p; char nroot[PATH_MAX]; FILE *f; int i; char *p2; if (realpath(root, nroot) == NULL) return -errno; if (chdir("/") == -1) return -errno; /* * We could use here MS_MOVE, but in userns this mount is * locked and can't be moved. */ if (mount(root, "/", NULL, MS_REC | MS_BIND, NULL) < 0) { SYSERROR("Failed to move %s into /", root); return -errno; } if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) { SYSERROR("Failed to make . rprivate"); return -errno; } /* * The following code cleans up inhereted mounts which are not * required for CT. * * The mountinfo file shows not all mounts, if a few points have been * unmounted between read operations from the mountinfo. So we need to * read mountinfo a few times. * * This loop can be skipped if a container uses unserns, because all * inherited mounts are locked and we should live with all this trash. */ while (1) { int progress = 0; f = fopen("./proc/self/mountinfo", "r"); if (!f) { SYSERROR("Unable to open /proc/self/mountinfo"); return -1; } while (fgets(buf, LXC_LINELEN, f)) { for (p = buf, i=0; p && i < 4; i++) p = strchr(p+1, ' '); if (!p) continue; p2 = strchr(p+1, ' '); if (!p2) continue; *p2 = '\0'; *p = '.'; if (strcmp(p + 1, "/") == 0) continue; if (strcmp(p + 1, "/proc") == 0) continue; if (umount2(p, MNT_DETACH) == 0) progress++; } fclose(f); if (!progress) break; } /* This also can be skipped if a container uses unserns */ umount2("./proc", MNT_DETACH); /* It is weird, but chdir("..") moves us in a new root */ if (chdir("..") == -1) { SYSERROR("Unable to change working directory"); return -1; } if (chroot(".") == -1) { SYSERROR("Unable to chroot"); return -1; } return 0; } static int setup_pivot_root(const struct lxc_rootfs *rootfs) { if (!rootfs->path) { DEBUG("container does not have a rootfs, so not doing pivot root"); return 0; } if (detect_ramfs_rootfs()) { DEBUG("detected that container is on ramfs"); if (prepare_ramfs_root(rootfs->mount)) { ERROR("failed to prepare minimal ramfs root"); return -1; } DEBUG("prepared ramfs root for container"); return 0; } if (setup_rootfs_pivot_root(rootfs->mount) < 0) { ERROR("failed to pivot root"); return -1; } DEBUG("finished pivot root"); return 0; } static struct id_map *find_mapped_nsid_entry(struct lxc_conf *conf, unsigned id, enum idtype idtype) { struct lxc_list *it; struct id_map *map; struct id_map *retmap = NULL; /* Shortcut for container's root mappings. */ if (id == 0) { if (idtype == ID_TYPE_UID) return conf->root_nsuid_map; if (idtype == ID_TYPE_GID) return conf->root_nsgid_map; } lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->nsid && id < map->nsid + map->range) { retmap = map; break; } } return retmap; } static int lxc_setup_devpts(struct lxc_conf *conf) { int ret; const char *default_devpts_mntopts = "gid=5,newinstance,ptmxmode=0666,mode=0620"; char devpts_mntopts[256]; if (conf->pts <= 0) { DEBUG("no new devpts instance will be mounted since no pts " "devices are requested"); return 0; } ret = snprintf(devpts_mntopts, sizeof(devpts_mntopts), "%s,max=%d", default_devpts_mntopts, conf->pts); if (ret < 0 || (size_t)ret >= sizeof(devpts_mntopts)) return -1; /* Unmount old devpts instance. */ ret = access("/dev/pts/ptmx", F_OK); if (!ret) { ret = umount("/dev/pts"); if (ret < 0) { SYSERROR("failed to unmount old devpts instance"); return -1; } DEBUG("unmounted old /dev/pts instance"); } /* Create mountpoint for devpts instance. */ ret = mkdir("/dev/pts", 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("failed to create the \"/dev/pts\" directory"); return -1; } /* mount new devpts instance */ ret = mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, devpts_mntopts); if (ret < 0) { /* try mounting without gid=5 */ ret = mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, devpts_mntopts + sizeof("gid=5")); if (ret < 0) { SYSERROR("Failed to mount new devpts instance"); return -1; } } DEBUG("mount new devpts instance with options \"%s\"", devpts_mntopts); /* Remove any pre-existing /dev/ptmx file. */ ret = access("/dev/ptmx", F_OK); if (!ret) { ret = remove("/dev/ptmx"); if (ret < 0) { SYSERROR("failed to remove existing \"/dev/ptmx\""); return -1; } DEBUG("removed existing \"/dev/ptmx\""); } /* Create dummy /dev/ptmx file as bind mountpoint for /dev/pts/ptmx. */ ret = open("/dev/ptmx", O_CREAT, 0666); if (ret < 0) { SYSERROR("failed to create dummy \"/dev/ptmx\" file as bind mount target"); return -1; } close(ret); DEBUG("created dummy \"/dev/ptmx\" file as bind mount target"); /* Fallback option: create symlink /dev/ptmx -> /dev/pts/ptmx */ ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL); if (!ret) { DEBUG("bind mounted \"/dev/pts/ptmx\" to \"/dev/ptmx\""); return 0; } else { /* Fallthrough and try to create a symlink. */ ERROR("failed to bind mount \"/dev/pts/ptmx\" to \"/dev/ptmx\""); } /* Remove the dummy /dev/ptmx file we created above. */ ret = remove("/dev/ptmx"); if (ret < 0) { SYSERROR("failed to remove existing \"/dev/ptmx\""); return -1; } /* Fallback option: Create symlink /dev/ptmx -> /dev/pts/ptmx. */ ret = symlink("/dev/pts/ptmx", "/dev/ptmx"); if (ret < 0) { SYSERROR("failed to create symlink \"/dev/ptmx\" -> \"/dev/pts/ptmx\""); return -1; } DEBUG("created symlink \"/dev/ptmx\" -> \"/dev/pts/ptmx\""); return 0; } static int setup_personality(int persona) { #if HAVE_SYS_PERSONALITY_H if (persona == -1) return 0; if (personality(persona) < 0) { SYSERROR("failed to set personality to '0x%x'", persona); return -1; } INFO("set personality to '0x%x'", persona); #endif return 0; } static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs, const struct lxc_console *console) { char path[MAXPATHLEN]; int ret, fd; if (console->path && !strcmp(console->path, "none")) return 0; ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs->mount); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; /* When we are asked to setup a console we remove any previous * /dev/console bind-mounts. */ if (file_exists(path)) { ret = lxc_unstack_mountpoint(path, false); if (ret < 0) { ERROR("failed to unmount \"%s\": %s", path, strerror(errno)); return -ret; } else { DEBUG("cleared all (%d) mounts from \"%s\"", ret, path); } ret = unlink(path); if (ret < 0) { SYSERROR("error unlinking %s", path); return -errno; } } /* For unprivileged containers autodev or automounts will already have * taken care of creating /dev/console. */ fd = open(path, O_CREAT | O_EXCL, S_IXUSR | S_IXGRP | S_IXOTH); if (fd < 0) { if (errno != EEXIST) { SYSERROR("failed to create console"); return -errno; } } else { close(fd); } if (chmod(console->name, S_IXUSR | S_IXGRP | S_IXOTH)) { SYSERROR("failed to set mode '0%o' to '%s'", S_IXUSR | S_IXGRP | S_IXOTH, console->name); return -errno; } if (safe_mount(console->name, path, "none", MS_BIND, 0, rootfs->mount) < 0) { ERROR("failed to mount '%s' on '%s'", console->name, path); return -1; } DEBUG("mounted pts device \"%s\" onto \"%s\"", console->name, path); return 0; } static int lxc_setup_ttydir_console(const struct lxc_rootfs *rootfs, const struct lxc_console *console, char *ttydir) { int ret; char path[MAXPATHLEN], lxcpath[MAXPATHLEN]; /* create rootfs/dev/ directory */ ret = snprintf(path, sizeof(path), "%s/dev/%s", rootfs->mount, ttydir); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; ret = mkdir(path, 0755); if (ret && errno != EEXIST) { SYSERROR("failed with errno %d to create %s", errno, path); return -errno; } DEBUG("Created directory for console and tty devices at \"%s\"", path); ret = snprintf(lxcpath, sizeof(lxcpath), "%s/dev/%s/console", rootfs->mount, ttydir); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = creat(lxcpath, 0660); if (ret == -1 && errno != EEXIST) { SYSERROR("error %d creating %s", errno, lxcpath); return -errno; } if (ret >= 0) close(ret); ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs->mount); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; /* When we are asked to setup a console we remove any previous * /dev/console bind-mounts. */ if (console->path && !strcmp(console->path, "none")) { struct stat st; ret = stat(path, &st); if (ret < 0) { if (errno == ENOENT) return 0; SYSERROR("failed stat() \"%s\"", path); return -errno; } /* /dev/console must be character device with major number 5 and * minor number 1. If not, give benefit of the doubt and assume * the user has mounted something else right there on purpose. */ if (((st.st_mode & S_IFMT) != S_IFCHR) || major(st.st_rdev) != 5 || minor(st.st_rdev) != 1) return 0; /* In case the user requested a bind-mount for /dev/console and * requests a ttydir we move the mount to the * /dev/mount); if (ret < 0) { if (errno != EINVAL) { ERROR("failed to MS_MOVE \"%s\" to \"%s\": %s", path, lxcpath, strerror(errno)); return -errno; } /* path was not a mountpoint */ ret = rename(path, lxcpath); if (ret < 0) { ERROR("failed to rename \"%s\" to \"%s\": %s", path, lxcpath, strerror(errno)); return -errno; } DEBUG("renamed \"%s\" to \"%s\"", path, lxcpath); } else { DEBUG("moved mount \"%s\" to \"%s\"", path, lxcpath); } /* Clear all remaining bind-mounts. */ ret = lxc_unstack_mountpoint(path, false); if (ret < 0) { ERROR("failed to unmount \"%s\": %s", path, strerror(errno)); return -ret; } else { DEBUG("cleared all (%d) mounts from \"%s\"", ret, path); } } else { if (file_exists(path)) { ret = lxc_unstack_mountpoint(path, false); if (ret < 0) { ERROR("failed to unmount \"%s\": %s", path, strerror(errno)); return -ret; } else { DEBUG("cleared all (%d) mounts from \"%s\"", ret, path); } } if (safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs->mount) < 0) { ERROR("failed to mount '%s' on '%s'", console->name, lxcpath); return -1; } DEBUG("mounted \"%s\" onto \"%s\"", console->name, lxcpath); } /* create symlink from rootfs /dev/console to '/console' */ ret = snprintf(lxcpath, sizeof(lxcpath), "%s/console", ttydir); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = unlink(path); if (ret && errno != ENOENT) { SYSERROR("error unlinking %s", path); return -errno; } ret = symlink(lxcpath, path); if (ret < 0) { SYSERROR("failed to create symlink for console from \"%s\" to \"%s\"", lxcpath, path); return -1; } DEBUG("console has been setup under \"%s\" and symlinked to \"%s\"", lxcpath, path); return 0; } static int lxc_setup_console(const struct lxc_rootfs *rootfs, const struct lxc_console *console, char *ttydir) { /* We don't have a rootfs, /dev/console will be shared. */ if (!rootfs->path) { DEBUG("/dev/console will be shared with the host"); return 0; } if (!ttydir) return lxc_setup_dev_console(rootfs, console); return lxc_setup_ttydir_console(rootfs, console, ttydir); } static int setup_kmsg(const struct lxc_rootfs *rootfs, const struct lxc_console *console) { 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; ret = unlink(kpath); if (ret && errno != ENOENT) { SYSERROR("error unlinking %s", kpath); return -1; } ret = symlink("console", kpath); if (ret) { SYSERROR("failed to create symlink for kmsg"); return -1; } return 0; } static void parse_mntopt(char *opt, unsigned long *flags, char **data) { struct mount_opt *mo; /* If opt is found in mount_opt, set or clear flags. * Otherwise append it to data. */ for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (!strncmp(opt, mo->name, strlen(mo->name))) { if (mo->clear) *flags &= ~mo->flag; else *flags |= mo->flag; return; } } if (strlen(*data)) strcat(*data, ","); strcat(*data, opt); } int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata) { char *s, *data; char *p, *saveptr = NULL; *mntdata = NULL; *mntflags = 0L; if (!mntopts) return 0; s = strdup(mntopts); if (!s) { SYSERROR("failed to allocate memory"); return -1; } data = malloc(strlen(s) + 1); if (!data) { SYSERROR("failed to allocate memory"); free(s); return -1; } *data = 0; for (p = strtok_r(s, ",", &saveptr); p != NULL; p = strtok_r(NULL, ",", &saveptr)) parse_mntopt(p, mntflags, &data); if (*data) *mntdata = data; else free(data); free(s); return 0; } static void parse_propagationopt(char *opt, unsigned long *flags) { struct mount_opt *mo; /* If opt is found in propagation_opt, set or clear flags. */ for (mo = &propagation_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) *flags &= ~mo->flag; else *flags |= mo->flag; return; } } } static int parse_propagationopts(const char *mntopts, unsigned long *pflags) { char *s; char *p, *saveptr = NULL; *pflags = 0L; if (!mntopts) return 0; s = strdup(mntopts); if (!s) { SYSERROR("Failed to allocate memory"); return -ENOMEM; } for (p = strtok_r(s, ",", &saveptr); p != NULL; p = strtok_r(NULL, ",", &saveptr)) parse_propagationopt(p, pflags); free(s); return 0; } static void null_endofword(char *word) { while (*word && *word != ' ' && *word != '\t') word++; *word = '\0'; } /* * skip @nfields spaces in @src */ static char *get_field(char *src, int nfields) { char *p = src; int i; for (i = 0; i < nfields; i++) { while (*p && *p != ' ' && *p != '\t') p++; if (!*p) break; p++; } return p; } static int mount_entry(const char *fsname, const char *target, const char *fstype, unsigned long mountflags, unsigned long pflags, const char *data, bool optional, bool dev, const char *rootfs) { int ret; #ifdef HAVE_STATVFS struct statvfs sb; #endif ret = safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs); if (ret < 0) { if (optional) { INFO("Failed to mount \"%s\" on \"%s\" (optional): %s", fsname ? fsname : "(null)", target, strerror(errno)); return 0; } SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname ? fsname : "(null)", target); return -1; } if ((mountflags & MS_REMOUNT) || (mountflags & MS_BIND)) { unsigned long rqd_flags = 0; DEBUG("Remounting \"%s\" on \"%s\" to respect bind or remount " "options", fsname ? fsname : "(none)", target ? target : "(none)"); if (mountflags & MS_RDONLY) rqd_flags |= MS_RDONLY; #ifdef HAVE_STATVFS if (fsname && statvfs(fsname, &sb) == 0) { unsigned long required_flags = rqd_flags; if (sb.f_flag & MS_NOSUID) required_flags |= MS_NOSUID; if (sb.f_flag & MS_NODEV && !dev) required_flags |= MS_NODEV; if (sb.f_flag & MS_RDONLY) required_flags |= MS_RDONLY; if (sb.f_flag & MS_NOEXEC) required_flags |= MS_NOEXEC; DEBUG("Flags for \"%s\" were %lu, required extra flags " "are %lu", fsname, sb.f_flag, required_flags); /* If this was a bind mount request, and required_flags * does not have any flags which are not already in * mountflags, then skip the remount. */ if (!(mountflags & MS_REMOUNT)) { if (!(required_flags & ~mountflags) && rqd_flags == 0) { DEBUG("Mountflags already were %lu, " "skipping remount", mountflags); goto skipremount; } } mountflags |= required_flags; } #endif ret = mount(fsname, target, fstype, mountflags | MS_REMOUNT, data); if (ret < 0) { if (optional) { INFO("Failed to mount \"%s\" on \"%s\" " "(optional): %s", fsname ? fsname : "(null)", target, strerror(errno)); return 0; } SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname ? fsname : "(null)", target); return -1; } } if (pflags) { ret = mount(NULL, target, NULL, pflags, NULL); if (ret < 0) { if (optional) { INFO("%s - Failed to change mount propagation " "for \"%s\" (optional)", strerror(errno), target); return 0; } else { SYSERROR("Failed to change mount propagation " "for \"%s\" (optional)", target); return -1; } } DEBUG("Changed mount propagation for \"%s\"", target); } #ifdef HAVE_STATVFS skipremount: #endif DEBUG("Mounted \"%s\" on \"%s\" with filesystem type \"%s\"", fsname ? fsname : "(null)", target, fstype); return 0; } /* Remove "optional", "create=dir", and "create=file" from mntopt */ static void cull_mntent_opt(struct mntent *mntent) { int i; char *list[] = {"create=dir", "create=file", "optional", NULL}; for (i = 0; list[i]; i++) { char *p, *p2; p = strstr(mntent->mnt_opts, list[i]); if (!p) continue; p2 = strchr(p, ','); if (!p2) { /* no more mntopts, so just chop it here */ *p = '\0'; continue; } memmove(p, p2 + 1, strlen(p2 + 1) + 1); } } static int mount_entry_create_dir_file(const struct mntent *mntent, const char *path, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int ret = 0; if (!strncmp(mntent->mnt_type, "overlay", 7)) ret = ovl_mkdir(mntent, rootfs, lxc_name, lxc_path); else if (!strncmp(mntent->mnt_type, "aufs", 4)) ret = aufs_mkdir(mntent, rootfs, lxc_name, lxc_path); if (ret < 0) return -1; if (hasmntopt(mntent, "create=dir")) { ret = mkdir_p(path, 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", path); return -1; } } if (hasmntopt(mntent, "create=file") && access(path, F_OK)) { int fd; char *p1, *p2; p1 = strdup(path); if (!p1) return -1; p2 = dirname(p1); ret = mkdir_p(p2, 0755); free(p1); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", path); return -1; } fd = open(path, O_CREAT, 0644); if (fd < 0) return -1; close(fd); } return 0; } /* rootfs, lxc_name, and lxc_path can be NULL when the container is created * without a rootfs. */ static inline int mount_entry_on_generic(struct mntent *mntent, const char *path, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int ret; unsigned long mntflags, pflags; char *mntdata; bool dev, optional; char *rootfs_path = NULL; optional = hasmntopt(mntent, "optional") != NULL; dev = hasmntopt(mntent, "dev") != NULL; if (rootfs && rootfs->path) rootfs_path = rootfs->mount; ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name, lxc_path); if (ret < 0) { if (optional) return 0; return -1; } cull_mntent_opt(mntent); ret = parse_propagationopts(mntent->mnt_opts, &pflags); if (ret < 0) return -1; ret = parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata); if (ret < 0) return -1; ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags, pflags, mntdata, optional, dev, rootfs_path); free(mntdata); return ret; } static inline int mount_entry_on_systemfs(struct mntent *mntent) { int ret; char path[MAXPATHLEN]; /* For containers created without a rootfs all mounts are treated as * absolute paths starting at / on the host. */ if (mntent->mnt_dir[0] != '/') ret = snprintf(path, sizeof(path), "/%s", mntent->mnt_dir); else ret = snprintf(path, sizeof(path), "%s", mntent->mnt_dir); if (ret < 0 || ret >= sizeof(path)) return -1; return mount_entry_on_generic(mntent, path, NULL, NULL, NULL); } static int mount_entry_on_absolute_rootfs(struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int offset; char *aux; const char *lxcpath; char path[MAXPATHLEN]; int ret = 0; lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) return -1; /* If rootfs->path is a blockdev path, allow container fstab to use * //rootfs" as the target prefix. */ ret = snprintf(path, MAXPATHLEN, "%s/%s/rootfs", lxcpath, lxc_name); if (ret < 0 || ret >= MAXPATHLEN) goto skipvarlib; aux = strstr(mntent->mnt_dir, path); if (aux) { offset = strlen(path); goto skipabs; } skipvarlib: aux = strstr(mntent->mnt_dir, rootfs->path); if (!aux) { WARN("Ignoring mount point \"%s\"", mntent->mnt_dir); return ret; } offset = strlen(rootfs->path); skipabs: ret = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->mount, aux + offset); if (ret < 0 || ret >= MAXPATHLEN) return -1; return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } static int mount_entry_on_relative_rootfs(struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { char path[MAXPATHLEN]; int ret; /* relative to root mount point */ ret = snprintf(path, sizeof(path), "%s/%s", rootfs->mount, mntent->mnt_dir); if (ret < 0 || ret >= sizeof(path)) { ERROR("path name too long"); return -1; } return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } /* This logs a NOTICE() when a user specifies mounts that would conflict with * devices liblxc sets up automatically. */ static void log_notice_on_conflict(const struct lxc_conf *conf, const char *src, const char *dest) { char *clean_mnt_fsname, *clean_mnt_dir, *tmp; bool needs_warning = false; clean_mnt_fsname = lxc_deslashify(src); if (!clean_mnt_fsname) return; clean_mnt_dir = lxc_deslashify(dest); if (!clean_mnt_dir) { free(clean_mnt_fsname); return; } tmp = clean_mnt_dir; if (*tmp == '/') tmp++; if (strncmp(src, "/dev", 4) || strncmp(tmp, "dev", 3)) { free(clean_mnt_dir); free(clean_mnt_fsname); return; } if (!conf->autodev && !conf->pts && !conf->tty && (!conf->console.path || !strcmp(conf->console.path, "none"))) { free(clean_mnt_dir); free(clean_mnt_fsname); return; } if (!strcmp(tmp, "dev") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/pts") && (conf->autodev > 0 || conf->pts > 0)) needs_warning = true; else if (!strcmp(tmp, "dev/ptmx") && (conf->autodev > 0 || conf->pts > 0)) needs_warning = true; else if (!strcmp(tmp, "dev/pts/ptmx") && (conf->autodev > 0 || conf->pts > 0)) needs_warning = true; else if (!strcmp(tmp, "dev/null") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/zero") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/full") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/urandom") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/random") && conf->autodev > 0) needs_warning = true; else if (!strcmp(tmp, "dev/tty") && conf->autodev > 0) needs_warning = true; else if (!strncmp(tmp, "dev/tty", 7) && (conf->autodev > 0 || conf->tty > 0)) needs_warning = true; if (needs_warning) NOTICE("Requesting to mount \"%s\" on \"%s\" while requesting " "automatic device setup under \"/dev\"", clean_mnt_fsname, clean_mnt_dir); free(clean_mnt_dir); free(clean_mnt_fsname); } static int mount_file_entries(const struct lxc_conf *conf, const struct lxc_rootfs *rootfs, FILE *file, const char *lxc_name, const char *lxc_path) { struct mntent mntent; char buf[4096]; int ret = -1; while (getmntent_r(file, &mntent, buf, sizeof(buf))) { log_notice_on_conflict(conf, mntent.mnt_fsname, mntent.mnt_dir); if (!rootfs->path) ret = mount_entry_on_systemfs(&mntent); else if (mntent.mnt_dir[0] != '/') ret = mount_entry_on_relative_rootfs(&mntent, rootfs, lxc_name, lxc_path); else ret = mount_entry_on_absolute_rootfs(&mntent, rootfs, lxc_name, lxc_path); if (ret < 0) return -1; } ret = 0; INFO("Set up mount entries"); return ret; } static int setup_mount(const struct lxc_conf *conf, const struct lxc_rootfs *rootfs, const char *fstab, const char *lxc_name, const char *lxc_path) { FILE *f; int ret; if (!fstab) return 0; f = setmntent(fstab, "r"); if (!f) { SYSERROR("Failed to open \"%s\"", fstab); return -1; } ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); if (ret < 0) ERROR("Failed to set up mount entries"); endmntent(f); return ret; } FILE *make_anonymous_mount_file(struct lxc_list *mount) { int ret; char *mount_entry; struct lxc_list *iterator; int fd = -1; fd = memfd_create(".lxc_mount_file", MFD_CLOEXEC); if (fd < 0) { char template[] = P_tmpdir "/.lxc_mount_file_XXXXXX"; if (errno != ENOSYS) return NULL; fd = lxc_make_tmpfile(template, true); if (fd < 0) { SYSERROR("Could not create temporary mount file"); return NULL; } TRACE("Created temporary mount file"); } lxc_list_for_each (iterator, mount) { size_t len; mount_entry = iterator->elem; len = strlen(mount_entry); ret = lxc_write_nointr(fd, mount_entry, len); if (ret != len) goto on_error; ret = lxc_write_nointr(fd, "\n", 1); if (ret != 1) goto on_error; } ret = lseek(fd, 0, SEEK_SET); if (ret < 0) goto on_error; return fdopen(fd, "r+"); on_error: SYSERROR("Failed to write mount entry to temporary mount file"); close(fd); return NULL; } static int setup_mount_entries(const struct lxc_conf *conf, const struct lxc_rootfs *rootfs, struct lxc_list *mount, const char *lxc_name, const char *lxc_path) { FILE *f; int ret; f = make_anonymous_mount_file(mount); if (!f) return -1; ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); fclose(f); return ret; } static int parse_cap(const char *cap) { char *ptr = NULL; size_t i; int capid = -1; size_t end = sizeof(caps_opt)/sizeof(caps_opt[0]); if (!strcmp(cap, "none")) return -2; for (i = 0; i < end; i++) { if (strcmp(cap, caps_opt[i].name)) continue; capid = caps_opt[i].value; break; } if (capid < 0) { /* try to see if it's numeric, so the user may specify * capabilities that the running kernel knows about but * we don't */ errno = 0; capid = strtol(cap, &ptr, 10); if (!ptr || *ptr != '\0' || errno != 0) /* not a valid number */ capid = -1; else if (capid > lxc_caps_last_cap()) /* we have a number but it's not a valid * capability */ capid = -1; } return capid; } int in_caplist(int cap, struct lxc_list *caps) { struct lxc_list *iterator; int capid; lxc_list_for_each(iterator, caps) { capid = parse_cap(iterator->elem); if (capid == cap) return 1; } return 0; } static int setup_caps(struct lxc_list *caps) { struct lxc_list *iterator; char *drop_entry; int capid; lxc_list_for_each(iterator, caps) { drop_entry = iterator->elem; capid = parse_cap(drop_entry); if (capid < 0) { ERROR("unknown capability %s", drop_entry); return -1; } DEBUG("drop capability '%s' (%d)", drop_entry, capid); if (prctl(PR_CAPBSET_DROP, capid, 0, 0, 0)) { SYSERROR("failed to remove %s capability", drop_entry); return -1; } } DEBUG("capabilities have been setup"); return 0; } static int dropcaps_except(struct lxc_list *caps) { struct lxc_list *iterator; char *keep_entry; int i, capid; int numcaps = lxc_caps_last_cap() + 1; INFO("found %d capabilities", numcaps); if (numcaps <= 0 || numcaps > 200) return -1; // caplist[i] is 1 if we keep capability i int *caplist = alloca(numcaps * sizeof(int)); memset(caplist, 0, numcaps * sizeof(int)); lxc_list_for_each(iterator, caps) { keep_entry = iterator->elem; capid = parse_cap(keep_entry); if (capid == -2) continue; if (capid < 0) { ERROR("unknown capability %s", keep_entry); return -1; } DEBUG("keep capability '%s' (%d)", keep_entry, capid); caplist[capid] = 1; } for (i=0; iloglevel = LXC_LOG_LEVEL_NOTSET; new->personality = -1; new->autodev = 1; new->console.log_path = NULL; new->console.log_fd = -1; new->console.path = NULL; new->console.peer = -1; new->console.peerpty.busy = -1; new->console.peerpty.master = -1; new->console.peerpty.slave = -1; new->console.master = -1; new->console.slave = -1; new->console.name[0] = '\0'; new->maincmd_fd = -1; new->nbd_idx = -1; new->rootfs.mount = strdup(default_rootfs_mount); if (!new->rootfs.mount) { ERROR("lxc_conf_init : %s", strerror(errno)); free(new); return NULL; } new->kmsg = 0; new->logfd = -1; lxc_list_init(&new->cgroup); lxc_list_init(&new->network); lxc_list_init(&new->mount_list); lxc_list_init(&new->caps); lxc_list_init(&new->keepcaps); lxc_list_init(&new->id_map); new->root_nsuid_map = NULL; new->root_nsgid_map = NULL; lxc_list_init(&new->includes); lxc_list_init(&new->aliens); lxc_list_init(&new->environment); for (i=0; ihooks[i]); lxc_list_init(&new->groups); new->lsm_aa_profile = NULL; new->lsm_se_context = NULL; new->tmp_umount_proc = 0; for (i = 0; i < LXC_NS_MAX; i++) new->inherit_ns_fd[i] = -1; /* if running in a new user namespace, init and COMMAND * default to running as UID/GID 0 when using lxc-execute */ new->init_uid = 0; new->init_gid = 0; return new; } int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size) { char path[MAXPATHLEN]; int fd, ret; if (geteuid() != 0 && idtype == ID_TYPE_GID) { size_t buflen; ret = snprintf(path, MAXPATHLEN, "/proc/%d/setgroups", pid); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("Failed to create string"); return -E2BIG; } fd = open(path, O_WRONLY); if (fd < 0 && errno != ENOENT) { SYSERROR("Failed to open \"%s\"", path); return -1; } if (fd >= 0) { buflen = sizeof("deny\n") - 1; errno = 0; ret = lxc_write_nointr(fd, "deny\n", buflen); if (ret != buflen) { SYSERROR("Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid); close(fd); return -1; } close(fd); } } ret = snprintf(path, MAXPATHLEN, "/proc/%d/%cid_map", pid, idtype == ID_TYPE_UID ? 'u' : 'g'); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("Failed to create string"); return -E2BIG; } fd = open(path, O_WRONLY); if (fd < 0) { SYSERROR("Failed to open \"%s\"", path); return -1; } errno = 0; ret = lxc_write_nointr(fd, buf, buf_size); if (ret != buf_size) { SYSERROR("Failed to write %cid mapping to \"%s\"", idtype == ID_TYPE_UID ? 'u' : 'g', path); close(fd); return -1; } close(fd); return 0; } /* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. * * @return 1 if functional binary was found * @return 0 if binary exists but is lacking privilege * @return -ENOENT if binary does not exist * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID * */ static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) { char *path; int ret; struct stat st; int fret = 0; if (cap != CAP_SETUID && cap != CAP_SETGID) return -EINVAL; path = on_path(binary, NULL); if (!path) return -ENOENT; ret = stat(path, &st); if (ret < 0) { fret = -errno; goto cleanup; } /* Check if the binary is setuid. */ if (st.st_mode & S_ISUID) { DEBUG("The binary \"%s\" does have the setuid bit set.", path); fret = 1; goto cleanup; } #if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES /* Check if it has the CAP_SETUID capability. */ if ((cap & CAP_SETUID) && lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) && lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) { DEBUG("The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE " "and CAP_PERMITTED sets.", path); fret = 1; goto cleanup; } /* Check if it has the CAP_SETGID capability. */ if ((cap & CAP_SETGID) && lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) && lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) { DEBUG("The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE " "and CAP_PERMITTED sets.", path); fret = 1; goto cleanup; } #else /* If we cannot check for file capabilities we need to give the benefit * of the doubt. Otherwise we might fail even though all the necessary * file capabilities are set. */ DEBUG("Cannot check for file capabilites as full capability support is " "missing. Manual intervention needed."); fret = 1; #endif cleanup: free(path); return fret; } int lxc_map_ids_exec_wrapper(void *args) { execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL); return -1; } /* Calculate the number of chars needed to represent a given integer as a C * string. Include room for '-' to indicate negative numbers and the \0 byte. * This is based on systemd. */ #define INTTYPE_TO_STRLEN(type) \ (2 + (sizeof(type) <= 1 \ ? 3 \ : sizeof(type) <= 2 \ ? 5 \ : sizeof(type) <= 4 \ ? 10 \ : sizeof(type) <= 8 \ ? 20 \ : sizeof(int[-2 * (sizeof(type) > 8)]))) int lxc_map_ids(struct lxc_list *idmap, pid_t pid) { int fill, left; char u_or_g; char *pos; char cmd_output[PATH_MAX]; struct id_map *map; struct lxc_list *iterator; enum idtype type; /* strlen("new@idmap") = 9 * + * strlen(" ") = 1 * + * INTTYPE_TO_STRLEN(uint32_t) * + * strlen(" ") = 1 * * We add some additional space to make sure that we really have * LXC_IDMAPLEN bytes available for our the {g,u]id mapping. */ int ret = 0, gidmap = 0, uidmap = 0; char mapbuf[9 + 1 + INTTYPE_TO_STRLEN(uint32_t) + 1 + LXC_IDMAPLEN] = {0}; bool had_entry = false, use_shadow = false; int hostuid, hostgid; hostuid = geteuid(); hostgid = getegid(); /* If new{g,u}idmap exists, that is, if shadow is handing out subuid * ranges, then insist that root also reserve ranges in subuid. This * will protected it by preventing another user from being handed the * range by shadow. */ uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID); if (uidmap == -ENOENT) WARN("newuidmap binary is missing"); else if (!uidmap) WARN("newuidmap is lacking necessary privileges"); gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID); if (gidmap == -ENOENT) WARN("newgidmap binary is missing"); else if (!gidmap) WARN("newgidmap is lacking necessary privileges"); if (uidmap > 0 && gidmap > 0) { DEBUG("Functional newuidmap and newgidmap binary found"); use_shadow = true; } else { /* In case unprivileged users run application containers via * execute() or a start*() there are valid cases where they may * only want to map their own {g,u}id. Let's not block them from * doing so by requiring geteuid() == 0. */ DEBUG("No newuidmap and newgidmap binary found. Trying to " "write directly with euid %d", hostuid); } /* Check if we really need to use newuidmap and newgidmap. * If the user is only remapping his own {g,u}id, we don't need it. */ if (use_shadow && lxc_list_len(idmap) == 2) { use_shadow = false; lxc_list_for_each(iterator, idmap) { map = iterator->elem; if (map->idtype == ID_TYPE_UID && map->range == 1 && map->nsid == hostuid && map->hostid == hostuid) continue; if (map->idtype == ID_TYPE_GID && map->range == 1 && map->nsid == hostgid && map->hostid == hostgid) continue; use_shadow = true; break; } } for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID; type++, u_or_g = 'g') { pos = mapbuf; if (use_shadow) pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid); lxc_list_for_each(iterator, idmap) { map = iterator->elem; if (map->idtype != type) continue; had_entry = true; left = LXC_IDMAPLEN - (pos - mapbuf); fill = snprintf(pos, left, "%s%lu %lu %lu%s", use_shadow ? " " : "", map->nsid, map->hostid, map->range, use_shadow ? "" : "\n"); if (fill <= 0 || fill >= left) { /* The kernel only takes <= 4k for writes to * /proc//{g,u}id_map */ SYSERROR("Too many %cid mappings defined", u_or_g); return -1; } pos += fill; } if (!had_entry) continue; /* Try to catch the output of new{g,u}idmap to make debugging * easier. */ if (use_shadow) { ret = run_command(cmd_output, sizeof(cmd_output), lxc_map_ids_exec_wrapper, (void *)mapbuf); if (ret < 0) { ERROR("new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf); return -1; } TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf); } else { ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf); if (ret < 0) { ERROR("Failed to write mapping: %s", mapbuf); return -1; } TRACE("Wrote mapping \"%s\"", mapbuf); } memset(mapbuf, 0, sizeof(mapbuf)); } return 0; } /* * return the host uid/gid to which the container root is mapped in * *val. * Return true if id was found, false otherwise. */ bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype, unsigned long *val) { struct lxc_list *it; struct id_map *map; unsigned nsid; if (idtype == ID_TYPE_UID) nsid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid; else nsid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid; lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (map->nsid != nsid) continue; *val = map->hostid; return true; } return false; } int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype) { struct lxc_list *it; struct id_map *map; lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->hostid && id < map->hostid + map->range) return (id - map->hostid) + map->nsid; } return -1; } int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype) { struct lxc_list *it; struct id_map *map; unsigned int freeid = 0; again: lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (freeid >= map->nsid && freeid < map->nsid + map->range) { freeid = map->nsid + map->range; goto again; } } return freeid; } int chown_mapped_root_exec_wrapper(void *args) { execvp("lxc-usernsexec", args); return -1; } /* * chown_mapped_root: for an unprivileged user with uid/gid X to * chown a dir to subuid/subgid Y, he needs to run chown as root * in a userns where nsid 0 is mapped to hostuid/hostgid Y, and * nsid Y is mapped to hostuid/hostgid X. That way, the container * root is privileged with respect to hostuid/hostgid X, allowing * him to do the chown. */ int chown_mapped_root(char *path, struct lxc_conf *conf) { uid_t rootuid, rootgid; unsigned long val; char *chownpath = path; int hostuid, hostgid, ret; struct stat sb; char map1[100], map2[100], map3[100], map4[100], map5[100]; char ugid[100]; char *args1[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "-m", map3, "-m", map5, "--", "chown", ugid, path, NULL}; char *args2[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "-m", map3, "-m", map4, "-m", map5, "--", "chown", ugid, path, NULL}; char cmd_output[MAXPATHLEN]; hostuid = geteuid(); hostgid = getegid(); if (!get_mapped_rootid(conf, ID_TYPE_UID, &val)) { ERROR("No uid mapping for container root"); return -1; } rootuid = (uid_t)val; if (!get_mapped_rootid(conf, ID_TYPE_GID, &val)) { ERROR("No gid mapping for container root"); return -1; } rootgid = (gid_t)val; /* * In case of overlay, we want only the writeable layer to be chowned */ if (strncmp(path, "overlayfs:", 10) == 0 || strncmp(path, "aufs:", 5) == 0) { chownpath = strchr(path, ':'); if (!chownpath) { ERROR("Bad overlay path: %s", path); return -1; } chownpath = strchr(chownpath + 1, ':'); if (!chownpath) { ERROR("Bad overlay path: %s", path); return -1; } chownpath++; } path = chownpath; if (hostuid == 0) { if (chown(path, rootuid, rootgid) < 0) { ERROR("Error chowning %s", path); return -1; } return 0; } if (rootuid == hostuid) { // nothing to do INFO("Container root is our uid; no need to chown"); return 0; } /* save the current gid of "path" */ if (stat(path, &sb) < 0) { ERROR("Error stat %s", path); return -1; } /* Update the path argument in case this was overlayfs. */ args1[sizeof(args1) / sizeof(args1[0]) - 2] = path; args2[sizeof(args2) / sizeof(args2[0]) - 2] = path; /* * A file has to be group-owned by a gid mapped into the * container, or the container won't be privileged over it. */ DEBUG("trying to chown \"%s\" to %d", path, hostgid); if (sb.st_uid == hostuid && mapped_hostid(sb.st_gid, conf, ID_TYPE_GID) < 0 && chown(path, -1, hostgid) < 0) { ERROR("Failed chgrping %s", path); return -1; } // "u:0:rootuid:1" ret = snprintf(map1, 100, "u:0:%d:1", rootuid); if (ret < 0 || ret >= 100) { ERROR("Error uid printing map string"); return -1; } // "u:hostuid:hostuid:1" ret = snprintf(map2, 100, "u:%d:%d:1", hostuid, hostuid); if (ret < 0 || ret >= 100) { ERROR("Error uid printing map string"); return -1; } // "g:0:rootgid:1" ret = snprintf(map3, 100, "g:0:%d:1", rootgid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } // "g:pathgid:rootgid+pathgid:1" ret = snprintf(map4, 100, "g:%d:%d:1", (gid_t)sb.st_gid, rootgid + (gid_t)sb.st_gid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } // "g:hostgid:hostgid:1" ret = snprintf(map5, 100, "g:%d:%d:1", hostgid, hostgid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } // "0:pathgid" (chown) ret = snprintf(ugid, 100, "0:%d", (gid_t)sb.st_gid); if (ret < 0 || ret >= 100) { ERROR("Error owner printing format string for chown"); return -1; } if (hostgid == sb.st_gid) ret = run_command(cmd_output, sizeof(cmd_output), chown_mapped_root_exec_wrapper, (void *)args1); else ret = run_command(cmd_output, sizeof(cmd_output), chown_mapped_root_exec_wrapper, (void *)args2); if (ret < 0) ERROR("lxc-usernsexec failed: %s", cmd_output); return ret; } /* NOTE: Must not be called from inside the container namespace! */ int lxc_create_tmp_proc_mount(struct lxc_conf *conf) { int mounted; mounted = lxc_mount_proc_if_needed(conf->rootfs.path ? conf->rootfs.mount : ""); if (mounted == -1) { SYSERROR("failed to mount /proc in the container"); /* continue only if there is no rootfs */ if (conf->rootfs.path) return -1; } else if (mounted == 1) { conf->tmp_umount_proc = 1; } return 0; } void tmp_proc_unmount(struct lxc_conf *lxc_conf) { if (lxc_conf->tmp_umount_proc == 1) { umount("/proc"); lxc_conf->tmp_umount_proc = 0; } } #define LXC_SENDFILE_MAX 0x7ffff000 static ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count) { ssize_t ret; again: ret = sendfile(out_fd, in_fd, offset, count); if (ret < 0) { if (errno == EINTR) goto again; return -1; } return ret; } /* Walk /proc/mounts and change any shared entries to slave. */ void remount_all_slave(void) { int memfd, mntinfo_fd, ret; ssize_t copied; FILE *f; size_t len = 0; char *line = NULL; mntinfo_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); if (mntinfo_fd < 0) { SYSERROR("Failed to open \"/proc/self/mountinfo\""); return; } memfd = memfd_create(".lxc_mountinfo", MFD_CLOEXEC); if (memfd < 0) { char template[] = P_tmpdir "/.lxc_mountinfo_XXXXXX"; if (errno != ENOSYS) { SYSERROR("Failed to create temporary in-memory file"); close(mntinfo_fd); return; } memfd = lxc_make_tmpfile(template, true); if (memfd < 0) { close(mntinfo_fd); WARN("Failed to create temporary file"); return; } } again: copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX); if (copied < 0) { if (errno == EINTR) goto again; SYSERROR("Failed to copy \"/proc/self/mountinfo\""); close(mntinfo_fd); close(memfd); return; } close(mntinfo_fd); /* After a successful fdopen() memfd will be closed when calling * fclose(f). Calling close(memfd) afterwards is undefined. */ ret = lseek(memfd, 0, SEEK_SET); if (ret < 0) { SYSERROR("Failed to reset file descriptor offset"); close(memfd); return; } f = fdopen(memfd, "r"); if (!f) { SYSERROR("Failed to open copy of \"/proc/self/mountinfo\" to mark " "all shared. Continuing"); close(memfd); return; } while (getline(&line, &len, f) != -1) { int ret; char *opts, *target; target = get_field(line, 4); if (!target) continue; opts = get_field(target, 2); if (!opts) continue; null_endofword(opts); if (!strstr(opts, "shared")) continue; null_endofword(target); ret = mount(NULL, target, NULL, MS_SLAVE, NULL); if (ret < 0) { SYSERROR("Failed to make \"%s\" MS_SLAVE", target); ERROR("Continuing..."); continue; } TRACE("Remounted \"%s\" as MS_SLAVE", target); } fclose(f); free(line); TRACE("Remounted all mount table entries as MS_SLAVE"); } void lxc_execute_bind_init(struct lxc_conf *conf) { int ret; char path[PATH_MAX], destpath[PATH_MAX], *p; /* If init exists in the container, don't bind mount a static one */ p = choose_init(conf->rootfs.mount); if (p) { free(p); return; } ret = snprintf(path, PATH_MAX, SBINDIR "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) { WARN("Path name too long searching for lxc.init.static"); return; } if (!file_exists(path)) { INFO("%s does not exist on host", path); return; } ret = snprintf(destpath, PATH_MAX, "%s%s", conf->rootfs.mount, "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) { WARN("Path name too long for container's lxc.init.static"); return; } if (!file_exists(destpath)) { FILE * pathfile = fopen(destpath, "wb"); if (!pathfile) { SYSERROR("Failed to create mount target '%s'", destpath); return; } fclose(pathfile); } ret = safe_mount(path, destpath, "none", MS_BIND, NULL, conf->rootfs.mount); if (ret < 0) SYSERROR("Failed to bind lxc.init.static into container"); INFO("lxc.init.static bound into container at %s", path); } /* * This does the work of remounting / if it is shared, calling the * container pre-mount hooks, and mounting the rootfs. */ int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath) { if (conf->rootfs_setup) { /* * rootfs was set up in another namespace. bind-mount it * to give us a mount in our own ns so we can pivot_root to it */ const char *path = conf->rootfs.mount; if (mount(path, path, "rootfs", MS_BIND, NULL) < 0) { ERROR("Failed to bind-mount container / onto itself"); return -1; } return 0; } remount_all_slave(); if (run_lxc_hooks(name, "pre-mount", conf, lxcpath, NULL)) { ERROR("failed to run pre-mount hooks for container '%s'.", name); return -1; } if (lxc_setup_rootfs(conf)) { ERROR("failed to setup rootfs for '%s'", name); return -1; } conf->rootfs_setup = true; return 0; } static bool verify_start_hooks(struct lxc_conf *conf) { struct lxc_list *it; char path[MAXPATHLEN]; lxc_list_for_each(it, &conf->hooks[LXCHOOK_START]) { char *hookname = it->elem; struct stat st; int ret; ret = snprintf(path, MAXPATHLEN, "%s%s", conf->rootfs.path ? conf->rootfs.mount : "", hookname); if (ret < 0 || ret >= MAXPATHLEN) return false; ret = stat(path, &st); if (ret) { SYSERROR("Start hook %s not found in container", hookname); return false; } return true; } return true; } int lxc_setup(struct lxc_handler *handler) { int ret; const char *name = handler->name; struct lxc_conf *lxc_conf = handler->conf; const char *lxcpath = handler->lxcpath; if (do_rootfs_setup(lxc_conf, name, lxcpath) < 0) { ERROR("Error setting up rootfs mount after spawn"); return -1; } if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) { if (setup_utsname(lxc_conf->utsname)) { ERROR("failed to setup the utsname for '%s'", name); return -1; } } if (lxc_setup_network_in_child_namespaces(lxc_conf, &lxc_conf->network)) { ERROR("failed to setup the network for '%s'", name); return -1; } if (lxc_network_send_name_and_ifindex_to_parent(handler) < 0) { ERROR("Failed to network device names and ifindices to parent"); return -1; } if (lxc_conf->autodev > 0) { if (mount_autodev(name, &lxc_conf->rootfs, lxcpath)) { ERROR("failed to mount /dev in the container"); return -1; } } /* do automatic mounts (mainly /proc and /sys), but exclude * those that need to wait until other stuff has finished */ if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler) < 0) { ERROR("failed to setup the automatic mounts for '%s'", name); return -1; } if (setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) { ERROR("failed to setup the mounts for '%s'", name); return -1; } if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) { ERROR("failed to setup the mount entries for '%s'", name); return -1; } /* Make sure any start hooks are in the container */ if (!verify_start_hooks(lxc_conf)) return -1; if (lxc_conf->is_execute) lxc_execute_bind_init(lxc_conf); /* now mount only cgroup, if wanted; * before, /sys could not have been mounted * (is either mounted automatically or via fstab entries) */ if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & (LXC_AUTO_CGROUP_MASK), handler) < 0) { ERROR("failed to setup the automatic mounts for '%s'", name); return -1; } if (run_lxc_hooks(name, "mount", lxc_conf, lxcpath, NULL)) { ERROR("failed to run mount hooks for container '%s'.", name); return -1; } if (lxc_conf->autodev > 0) { if (run_lxc_hooks(name, "autodev", lxc_conf, lxcpath, NULL)) { ERROR("failed to run autodev hooks for container '%s'.", name); return -1; } if (lxc_fill_autodev(&lxc_conf->rootfs)) { ERROR("failed to populate /dev in the container"); return -1; } } ret = lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console, lxc_conf->ttydir); if (ret < 0) { ERROR("Failed to setup console"); return -1; } if (lxc_conf->kmsg) { if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) // don't fail ERROR("failed to setup kmsg for '%s'", name); } ret = lxc_setup_dev_symlinks(&lxc_conf->rootfs); if (ret < 0) { ERROR("Failed to setup /dev symlinks"); return -1; } /* mount /proc if it's not already there */ if (lxc_create_tmp_proc_mount(lxc_conf) < 0) { ERROR("failed to LSM mount proc for '%s'", name); return -1; } if (setup_pivot_root(&lxc_conf->rootfs)) { ERROR("failed to set rootfs for '%s'", name); return -1; } if (lxc_setup_devpts(lxc_conf)) { ERROR("failed to setup the new pts instance"); return -1; } ret = lxc_create_ttys(handler); if (ret < 0) return -1; if (setup_personality(lxc_conf->personality)) { ERROR("failed to setup personality"); return -1; } if (!lxc_list_empty(&lxc_conf->keepcaps)) { if (!lxc_list_empty(&lxc_conf->caps)) { ERROR("Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both."); return -1; } if (dropcaps_except(&lxc_conf->keepcaps)) { ERROR("failed to keep requested caps"); return -1; } } else if (setup_caps(&lxc_conf->caps)) { ERROR("failed to drop capabilities"); return -1; } NOTICE("Container \"%s\" is set up", name); return 0; } int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, const char *lxcpath, char *argv[]) { int which = -1; struct lxc_list *it; 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, "autodev") == 0) which = LXCHOOK_AUTODEV; else if (strcmp(hook, "start") == 0) which = LXCHOOK_START; else if (strcmp(hook, "stop") == 0) which = LXCHOOK_STOP; else if (strcmp(hook, "post-stop") == 0) which = LXCHOOK_POSTSTOP; else if (strcmp(hook, "clone") == 0) which = LXCHOOK_CLONE; else if (strcmp(hook, "destroy") == 0) which = LXCHOOK_DESTROY; else return -1; lxc_list_for_each(it, &conf->hooks[which]) { int ret; char *hookname = it->elem; ret = run_script_argv(name, "lxc", hookname, hook, lxcpath, argv); if (ret) return ret; } 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); free(netdev->upscript); free(netdev->downscript); free(netdev->hwaddr); free(netdev->mtu); free(netdev->ipv4_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 = strchr(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 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; } static int lxc_free_idmap(struct lxc_list *id_map) { struct lxc_list *it, *next; lxc_list_for_each_safe(it, id_map, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_idmaps(struct lxc_conf *c) { return lxc_free_idmap(&c->id_map); } int lxc_clear_config_keepcaps(struct lxc_conf *c) { struct lxc_list *it,*next; lxc_list_for_each_safe(it, &c->keepcaps, 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 = NULL; if (strcmp(key, "lxc.cgroup") == 0) all = true; else if (strncmp(key, "lxc.cgroup.", sizeof("lxc.cgroup.")-1) == 0) k = key + sizeof("lxc.cgroup.")-1; else return -1; 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_groups(struct lxc_conf *c) { struct lxc_list *it,*next; lxc_list_for_each_safe(it, &c->groups, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_environment(struct lxc_conf *c) { struct lxc_list *it,*next; lxc_list_for_each_safe(it, &c->environment, next) { lxc_list_del(it); free(it->elem); 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_automounts(struct lxc_conf *c) { c->auto_mounts = 0; 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 = NULL; int i; if (strcmp(key, "lxc.hook") == 0) all = true; else if (strncmp(key, "lxc.hook.", sizeof("lxc.hook.")-1) == 0) k = key + sizeof("lxc.hook.")-1; else return -1; for (i=0; ihooks[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; } static inline void lxc_clear_aliens(struct lxc_conf *conf) { struct lxc_list *it,*next; lxc_list_for_each_safe(it, &conf->aliens, next) { lxc_list_del(it); free(it->elem); free(it); } } void lxc_clear_includes(struct lxc_conf *conf) { struct lxc_list *it,*next; lxc_list_for_each_safe(it, &conf->includes, next) { lxc_list_del(it); free(it->elem); free(it); } } void lxc_conf_free(struct lxc_conf *conf) { if (!conf) return; if (current_config == conf) current_config = NULL; lxc_pty_conf_free(&conf->console); free(conf->rootfs.mount); free(conf->rootfs.bdev_type); free(conf->rootfs.options); free(conf->rootfs.path); free(conf->logfile); if (conf->logfd != -1) close(conf->logfd); free(conf->utsname); free(conf->ttydir); free(conf->fstab); free(conf->rcfile); free(conf->init_cmd); free(conf->unexpanded_config); free(conf->pty_names); lxc_clear_config_network(conf); free(conf->lsm_aa_profile); free(conf->lsm_se_context); lxc_seccomp_free(conf); lxc_clear_config_caps(conf); lxc_clear_config_keepcaps(conf); lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf, "lxc.hook"); lxc_clear_mount_entries(conf); lxc_clear_idmaps(conf); lxc_clear_groups(conf); lxc_clear_includes(conf); lxc_clear_aliens(conf); lxc_clear_environment(conf); free(conf); } struct userns_fn_data { int (*fn)(void *); const char *fn_name; void *arg; int p[2]; }; static int run_userns_fn(void *data) { int ret; char c; struct userns_fn_data *d = data; /* Close write end of the pipe. */ close(d->p[1]); /* Wait for parent to finish establishing a new mapping in the user * namespace we are executing in. */ ret = lxc_read_nointr(d->p[0], &c, 1); /* Close read end of the pipe. */ close(d->p[0]); if (ret != 1) return -1; if (d->fn_name) TRACE("Calling function \"%s\"", d->fn_name); /* Call function to run. */ return d->fn(d->arg); } static struct id_map *mapped_nsid_add(struct lxc_conf *conf, unsigned id, enum idtype idtype) { struct id_map *map, *retmap; map = find_mapped_nsid_entry(conf, id, idtype); if (!map) return NULL; retmap = malloc(sizeof(*retmap)); if (!retmap) return NULL; memcpy(retmap, map, sizeof(*retmap)); return retmap; } static struct id_map *find_mapped_hostid_entry(struct lxc_conf *conf, unsigned id, enum idtype idtype) { struct lxc_list *it; struct id_map *map; struct id_map *retmap = NULL; lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->hostid && id < map->hostid + map->range) { retmap = map; break; } } return retmap; } /* * Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already * existing one or establish a new one. */ static struct id_map *mapped_hostid_add(struct lxc_conf *conf, uid_t id, enum idtype type) { int hostid_mapped; struct id_map *entry = NULL, *tmp = NULL; entry = malloc(sizeof(*entry)); if (!entry) return NULL; /* Reuse existing mapping. */ tmp = find_mapped_hostid_entry(conf, id, type); if (tmp) return memcpy(entry, tmp, sizeof(*entry)); /* Find new mapping. */ hostid_mapped = find_unmapped_nsid(conf, type); if (hostid_mapped < 0) { DEBUG("Failed to find free mapping for id %d", id); free(entry); return NULL; } entry->idtype = type; entry->nsid = hostid_mapped; entry->hostid = (unsigned long)id; entry->range = 1; return entry; } struct lxc_list *get_minimal_idmap(struct lxc_conf *conf) { uid_t euid, egid; uid_t nsuid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid; gid_t nsgid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid; struct lxc_list *idmap = NULL, *tmplist = NULL; struct id_map *container_root_uid = NULL, *container_root_gid = NULL, *host_uid_map = NULL, *host_gid_map = NULL; /* Find container root mappings. */ container_root_uid = mapped_nsid_add(conf, nsuid, ID_TYPE_UID); if (!container_root_uid) { DEBUG("Failed to find mapping for namespace uid %d", 0); goto on_error; } euid = geteuid(); if (euid >= container_root_uid->hostid && euid < (container_root_uid->hostid + container_root_uid->range)) host_uid_map = container_root_uid; container_root_gid = mapped_nsid_add(conf, nsgid, ID_TYPE_GID); if (!container_root_gid) { DEBUG("Failed to find mapping for namespace gid %d", 0); goto on_error; } egid = getegid(); if (egid >= container_root_gid->hostid && egid < (container_root_gid->hostid + container_root_gid->range)) host_gid_map = container_root_gid; /* Check whether the {g,u}id of the user has a mapping. */ if (!host_uid_map) host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID); if (!host_uid_map) { DEBUG("Failed to find mapping for uid %d", euid); goto on_error; } if (!host_gid_map) host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID); if (!host_gid_map) { DEBUG("Failed to find mapping for gid %d", egid); goto on_error; } /* Allocate new {g,u}id map list. */ idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; lxc_list_init(idmap); /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, container_root_uid); lxc_list_add_tail(idmap, tmplist); if (host_uid_map && (host_uid_map != container_root_uid)) { /* idmap will now keep track of that memory. */ container_root_uid = NULL; /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_uid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ container_root_uid = NULL; /* idmap will now keep track of that memory. */ host_uid_map = NULL; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, container_root_gid); lxc_list_add_tail(idmap, tmplist); if (host_gid_map && (host_gid_map != container_root_gid)) { /* idmap will now keep track of that memory. */ container_root_gid = NULL; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_gid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ container_root_gid = NULL; /* idmap will now keep track of that memory. */ host_gid_map = NULL; TRACE("Allocated minimal idmapping"); return idmap; on_error: if (idmap) { lxc_free_idmap(idmap); free(idmap); } if (container_root_uid) free(container_root_uid); if (container_root_gid) free(container_root_gid); if (host_uid_map && (host_uid_map != container_root_uid)) free(host_uid_map); if (host_gid_map && (host_gid_map != container_root_gid)) free(host_gid_map); return NULL; } /* Run a function in a new user namespace. * The caller's euid/egid will be mapped if it is not already. * Afaict, userns_exec_1() is only used to operate based on privileges for the * user's own {g,u}id on the host and for the container root's unmapped {g,u}id. * This means we require only to establish a mapping from: * - the container root {g,u}id as seen from the host > user's host {g,u}id * - the container root -> some sub{g,u}id * The former we add, if the user did not specifiy a mapping. The latter we * retrieve from the ontainer's configured {g,u}id mappings as it must have been * there to start the container in the first place. */ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name) { pid_t pid; struct userns_fn_data d; int p[2]; char c = '1'; int ret = -1, status = -1; struct lxc_list *idmap; if (!conf) return -EINVAL; idmap = get_minimal_idmap(conf); if (!idmap) return -1; ret = pipe2(p, O_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create pipe"); return -1; } d.fn = fn; d.fn_name = fn_name; d.arg = data; d.p[0] = p[0]; d.p[1] = p[1]; /* Clone child in new user namespace. */ pid = lxc_raw_clone_cb(run_userns_fn, &d, CLONE_NEWUSER); if (pid < 0) { ERROR("failed to clone child process in new user namespace"); goto on_error; } close(p[0]); p[0] = -1; if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE || conf->loglevel == LXC_LOG_LEVEL_TRACE) { struct lxc_list *it; struct id_map *map; lxc_list_for_each(it, idmap) { map = it->elem; TRACE("Establishing %cid mapping for \"%d\" in new " "user namespace: nsuid %lu - hostid %lu - range " "%lu", (map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range); } } /* Set up {g,u}id mapping for user namespace of child process. */ ret = lxc_map_ids(idmap, pid); if (ret < 0) { ERROR("Error setting up {g,u}id mappings for child process " "\"%d\"", pid); goto on_error; } /* Tell child to proceed. */ if (lxc_write_nointr(p[1], &c, 1) != 1) { SYSERROR("Failed telling child process \"%d\" to proceed", pid); goto on_error; } on_error: if (p[0] != -1) close(p[0]); close(p[1]); /* Wait for child to finish. */ if (pid > 0) status = wait_for_pid(pid); if (status < 0) ret = -1; return ret; } int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name) { pid_t pid; uid_t euid, egid; struct userns_fn_data d; int p[2]; struct id_map *map; struct lxc_list *cur; char c = '1'; int ret = -1; struct lxc_list *idmap = NULL, *tmplist = NULL; struct id_map *container_root_uid = NULL, *container_root_gid = NULL, *host_uid_map = NULL, *host_gid_map = NULL; if (!conf) return -EINVAL; ret = pipe2(p, O_CLOEXEC); if (ret < 0) { SYSERROR("opening pipe"); return -1; } d.fn = fn; d.fn_name = fn_name; d.arg = data; d.p[0] = p[0]; d.p[1] = p[1]; /* Clone child in new user namespace. */ pid = lxc_clone(run_userns_fn, &d, CLONE_NEWUSER); if (pid < 0) { ERROR("failed to clone child process in new user namespace"); goto on_error; } close(p[0]); p[0] = -1; euid = geteuid(); egid = getegid(); /* Allocate new {g,u}id map list. */ idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; lxc_list_init(idmap); /* Find container root. */ lxc_list_for_each(cur, &conf->id_map) { struct id_map *tmpmap; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; tmpmap = malloc(sizeof(*tmpmap)); if (!tmpmap) { free(tmplist); goto on_error; } memset(tmpmap, 0, sizeof(*tmpmap)); memcpy(tmpmap, cur->elem, sizeof(*tmpmap)); tmplist->elem = tmpmap; lxc_list_add_tail(idmap, tmplist); map = cur->elem; if (map->idtype == ID_TYPE_UID) if (euid >= map->hostid && euid < map->hostid + map->range) host_uid_map = map; if (map->idtype == ID_TYPE_GID) if (egid >= map->hostid && egid < map->hostid + map->range) host_gid_map = map; if (map->nsid != 0) continue; if (map->idtype == ID_TYPE_UID) if (container_root_uid == NULL) container_root_uid = map; if (map->idtype == ID_TYPE_GID) if (container_root_gid == NULL) container_root_gid = map; } if (!container_root_uid || !container_root_gid) { ERROR("No mapping for container root found"); goto on_error; } /* Check whether the {g,u}id of the user has a mapping. */ if (!host_uid_map) host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID); else host_uid_map = container_root_uid; if (!host_gid_map) host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID); else host_gid_map = container_root_gid; if (!host_uid_map) { DEBUG("Failed to find mapping for uid %d", euid); goto on_error; } if (!host_gid_map) { DEBUG("Failed to find mapping for gid %d", egid); goto on_error; } if (host_uid_map && (host_uid_map != container_root_uid)) { /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_uid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ host_uid_map = NULL; if (host_gid_map && (host_gid_map != container_root_gid)) { tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_gid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ host_gid_map = NULL; if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE || conf->loglevel == LXC_LOG_LEVEL_TRACE) { lxc_list_for_each(cur, idmap) { map = cur->elem; TRACE("establishing %cid mapping for \"%d\" in new " "user namespace: nsuid %lu - hostid %lu - range " "%lu", (map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range); } } /* Set up {g,u}id mapping for user namespace of child process. */ ret = lxc_map_ids(idmap, pid); if (ret < 0) { ERROR("error setting up {g,u}id mappings for child process " "\"%d\"", pid); goto on_error; } /* Tell child to proceed. */ if (lxc_write_nointr(p[1], &c, 1) != 1) { SYSERROR("Failed telling child process \"%d\" to proceed", pid); goto on_error; } on_error: if (p[0] != -1) close(p[0]); close(p[1]); /* Wait for child to finish. */ if (pid > 0) ret = wait_for_pid(pid); if (idmap) { lxc_free_idmap(idmap); free(idmap); } if (host_uid_map && (host_uid_map != container_root_uid)) free(host_uid_map); if (host_gid_map && (host_gid_map != container_root_gid)) free(host_gid_map); return ret; } /* not thread-safe, do not use from api without first forking */ static char* getuname(void) { struct passwd pwent; struct passwd *pwentp = NULL; char *buf; char *username; size_t bufsize; int ret; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getpwuid_r(geteuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) WARN("Could not find matched password record."); ERROR("Failed to get password record - %u", geteuid()); free(buf); return NULL; } username = strdup(pwent.pw_name); free(buf); return username; } /* not thread-safe, do not use from api without first forking */ static char *getgname(void) { struct group grent; struct group *grentp = NULL; char *buf; char *grname; size_t bufsize; int ret; bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getgrgid_r(getegid(), &grent, buf, bufsize, &grentp); if (!grentp) { if (ret == 0) WARN("Could not find matched group record"); ERROR("Failed to get group record - %u", getegid()); free(buf); return NULL; } grname = strdup(grent.gr_name); free(buf); return grname; } /* not thread-safe, do not use from api without first forking */ void suggest_default_idmap(void) { FILE *f; unsigned int uid = 0, urange = 0, gid = 0, grange = 0; char *line = NULL; char *uname, *gname; size_t len = 0; if (!(uname = getuname())) return; if (!(gname = getgname())) { free(uname); return; } f = fopen(subuidfile, "r"); if (!f) { ERROR("Your system is not configured with subuids"); free(gname); free(uname); return; } while (getline(&line, &len, f) != -1) { size_t no_newline = 0; char *p = strchr(line, ':'), *p2; if (*line == '#') continue; if (!p) continue; *p = '\0'; p++; if (strcmp(line, uname)) continue; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; p2++; if (!*p2) continue; no_newline = strcspn(p2, "\n"); p2[no_newline] = '\0'; if (lxc_safe_uint(p, &uid) < 0) WARN("Could not parse UID."); if (lxc_safe_uint(p2, &urange) < 0) WARN("Could not parse UID range."); } fclose(f); f = fopen(subgidfile, "r"); if (!f) { ERROR("Your system is not configured with subgids"); free(gname); free(uname); return; } while (getline(&line, &len, f) != -1) { size_t no_newline = 0; char *p = strchr(line, ':'), *p2; if (*line == '#') continue; if (!p) continue; *p = '\0'; p++; if (strcmp(line, uname)) continue; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; p2++; if (!*p2) continue; no_newline = strcspn(p2, "\n"); p2[no_newline] = '\0'; if (lxc_safe_uint(p, &gid) < 0) WARN("Could not parse GID."); if (lxc_safe_uint(p2, &grange) < 0) WARN("Could not parse GID range."); } fclose(f); free(line); if (!urange || !grange) { ERROR("You do not have subuids or subgids allocated"); ERROR("Unprivileged containers require subuids and subgids"); free(uname); free(gname); return; } ERROR("You must either run as root, or define uid mappings"); ERROR("To pass uid mappings to lxc-create, you could create"); ERROR("~/.config/lxc/default.conf:"); ERROR("lxc.include = %s", LXC_DEFAULT_CONFIG); ERROR("lxc.id_map = u 0 %u %u", uid, urange); ERROR("lxc.id_map = g 0 %u %u", gid, grange); free(gname); free(uname); } static void free_cgroup_settings(struct lxc_list *result) { struct lxc_list *iterator, *next; lxc_list_for_each_safe(iterator, result, next) { lxc_list_del(iterator); free(iterator); } free(result); } /* * Return the list of cgroup_settings sorted according to the following rules * 1. Put memory.limit_in_bytes before memory.memsw.limit_in_bytes */ struct lxc_list *sort_cgroup_settings(struct lxc_list* cgroup_settings) { struct lxc_list *result; struct lxc_list *memsw_limit = NULL; struct lxc_list *it = NULL; struct lxc_cgroup *cg = NULL; struct lxc_list *item = NULL; result = malloc(sizeof(*result)); if (!result) { ERROR("failed to allocate memory to sort cgroup settings"); return NULL; } lxc_list_init(result); /*Iterate over the cgroup settings and copy them to the output list*/ lxc_list_for_each(it, cgroup_settings) { item = malloc(sizeof(*item)); if (!item) { ERROR("failed to allocate memory to sort cgroup settings"); free_cgroup_settings(result); return NULL; } item->elem = it->elem; cg = it->elem; if (strcmp(cg->subsystem, "memory.memsw.limit_in_bytes") == 0) { /* Store the memsw_limit location */ memsw_limit = item; } else if (strcmp(cg->subsystem, "memory.limit_in_bytes") == 0 && memsw_limit != NULL) { /* lxc.cgroup.memory.memsw.limit_in_bytes is found before * lxc.cgroup.memory.limit_in_bytes, swap these two items */ item->elem = memsw_limit->elem; memsw_limit->elem = it->elem; } lxc_list_add_tail(result, item); } return result; } lxc-2.0.11/src/lxc/log.h0000644061062106075000000002737313435013473011640 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Cedric Le Goater * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LOG_H #define __LXC_LOG_H #include "config.h" #include #include #include #include #include #include #include #include "conf.h" #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 #endif #ifndef F_DUPFD_CLOEXEC #define F_DUPFD_CLOEXEC 1030 #endif #define LXC_LOG_PREFIX_SIZE 32 #define LXC_LOG_BUFFER_SIZE 4096 /* This attribute is required to silence clang warnings */ #if defined(__GNUC__) #define ATTR_UNUSED __attribute__ ((unused)) #else #define ATTR_UNUSED #endif struct lxc_log { const char *name; const char *lxcpath; const char *file; const char *level; const char *prefix; bool quiet; }; /*! *\brief Initialize the log * *\param log lxc log configuration. */ int lxc_log_init(struct lxc_log *log); /* predefined priorities. */ enum lxc_loglevel { LXC_LOG_LEVEL_TRACE, LXC_LOG_LEVEL_DEBUG, LXC_LOG_LEVEL_INFO, LXC_LOG_LEVEL_NOTICE, LXC_LOG_LEVEL_WARN, LXC_LOG_LEVEL_ERROR, LXC_LOG_LEVEL_CRIT, LXC_LOG_LEVEL_ALERT, LXC_LOG_LEVEL_FATAL, LXC_LOG_LEVEL_NOTSET, }; /* location information of the logging event */ struct lxc_log_locinfo { const char *file; const char *func; int line; }; #define LXC_LOG_LOCINFO_INIT \ { .file = __FILE__, .func = __func__, .line = __LINE__ } /* brief logging event object */ struct lxc_log_event { const char* category; int priority; struct timespec timestamp; struct lxc_log_locinfo *locinfo; const char *fmt; va_list *vap; }; /* log appender object */ struct lxc_log_appender { const char* name; int (*append)(const struct lxc_log_appender *, struct lxc_log_event *); /* * appenders can be stacked */ struct lxc_log_appender *next; }; /* log category object */ struct lxc_log_category { const char *name; int priority; struct lxc_log_appender *appender; const struct lxc_log_category *parent; }; #ifndef NO_LXC_CONF extern int lxc_log_use_global_fd; #endif /* * Returns true if the chained priority is equal to or higher than * given priority. */ static inline int lxc_log_priority_is_enabled(const struct lxc_log_category* category, int priority) { while (category->priority == LXC_LOG_LEVEL_NOTSET && category->parent) category = category->parent; int cmp_prio = category->priority; #ifndef NO_LXC_CONF if (!lxc_log_use_global_fd && current_config && current_config->loglevel != LXC_LOG_LEVEL_NOTSET) cmp_prio = current_config->loglevel; #endif return priority >= cmp_prio; } /* * converts a priority to a literal string */ static inline const char* lxc_log_priority_to_string(int priority) { switch (priority) { case LXC_LOG_LEVEL_TRACE: return "TRACE"; case LXC_LOG_LEVEL_DEBUG: return "DEBUG"; case LXC_LOG_LEVEL_INFO: return "INFO"; case LXC_LOG_LEVEL_NOTICE: return "NOTICE"; case LXC_LOG_LEVEL_WARN: return "WARN"; case LXC_LOG_LEVEL_ERROR: return "ERROR"; case LXC_LOG_LEVEL_CRIT: return "CRIT"; case LXC_LOG_LEVEL_ALERT: return "ALERT"; case LXC_LOG_LEVEL_FATAL: return "FATAL"; default: return "NOTSET"; } } /* * converts a literal priority to an int */ static inline int lxc_log_priority_to_int(const char* name) { if (!strcasecmp("TRACE", name)) return LXC_LOG_LEVEL_TRACE; if (!strcasecmp("DEBUG", name)) return LXC_LOG_LEVEL_DEBUG; if (!strcasecmp("INFO", name)) return LXC_LOG_LEVEL_INFO; if (!strcasecmp("NOTICE", name)) return LXC_LOG_LEVEL_NOTICE; if (!strcasecmp("WARN", name)) return LXC_LOG_LEVEL_WARN; if (!strcasecmp("ERROR", name)) return LXC_LOG_LEVEL_ERROR; if (!strcasecmp("CRIT", name)) return LXC_LOG_LEVEL_CRIT; if (!strcasecmp("ALERT", name)) return LXC_LOG_LEVEL_ALERT; if (!strcasecmp("FATAL", name)) return LXC_LOG_LEVEL_FATAL; return LXC_LOG_LEVEL_NOTSET; } static inline void __lxc_log_append(const struct lxc_log_appender *appender, struct lxc_log_event* event) { va_list va, *va_keep; va_keep = event->vap; while (appender) { va_copy(va, *va_keep); event->vap = &va; appender->append(appender, event); appender = appender->next; va_end(va); } } static inline void __lxc_log(const struct lxc_log_category* category, struct lxc_log_event* event) { while (category) { __lxc_log_append(category->appender, event); category = category->parent; } } /* * Helper macro to define log functions. */ #define lxc_log_priority_define(acategory, LEVEL) \ \ ATTR_UNUSED static inline void LXC_##LEVEL(struct lxc_log_locinfo *, \ const char *, ...) __attribute__ ((format (printf, 2, 3))); \ \ ATTR_UNUSED static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \ const char* format, ...) \ { \ if (lxc_log_priority_is_enabled(acategory, \ LXC_LOG_LEVEL_##LEVEL)) { \ struct lxc_log_event evt = { \ .category = (acategory)->name, \ .priority = LXC_LOG_LEVEL_##LEVEL, \ .fmt = format, \ .locinfo = locinfo \ }; \ va_list va_ref; \ \ /* clock_gettime() is explicitly marked as MT-Safe \ * without restrictions. So let's use it for our \ * logging stamps. */ \ clock_gettime(CLOCK_REALTIME, &evt.timestamp); \ \ va_start(va_ref, format); \ evt.vap = &va_ref; \ __lxc_log(acategory, &evt); \ va_end(va_ref); \ } \ } /* * Helper macro to define and use static categories. */ #define lxc_log_category_define(name, parent) \ extern struct lxc_log_category lxc_log_category_##parent; \ struct lxc_log_category lxc_log_category_##name = { \ #name, \ LXC_LOG_LEVEL_NOTSET, \ NULL, \ &lxc_log_category_##parent \ }; #define lxc_log_define(name, parent) \ lxc_log_category_define(name, parent) \ \ lxc_log_priority_define(&lxc_log_category_##name, TRACE) \ lxc_log_priority_define(&lxc_log_category_##name, DEBUG) \ lxc_log_priority_define(&lxc_log_category_##name, INFO) \ lxc_log_priority_define(&lxc_log_category_##name, NOTICE) \ lxc_log_priority_define(&lxc_log_category_##name, WARN) \ lxc_log_priority_define(&lxc_log_category_##name, ERROR) \ lxc_log_priority_define(&lxc_log_category_##name, CRIT) \ lxc_log_priority_define(&lxc_log_category_##name, ALERT) \ lxc_log_priority_define(&lxc_log_category_##name, FATAL) #define lxc_log_category_priority(name) \ (lxc_log_priority_to_string(lxc_log_category_##name.priority)) /* * Helper macro to define errno string. */ #if HAVE_STRERROR_R #ifndef HAVE_DECL_STRERROR_R #ifdef STRERROR_R_CHAR_P char *strerror_r(int errnum, char *buf, size_t buflen); #else int strerror_r(int errnum, char *buf, size_t buflen); #endif #endif #ifdef STRERROR_R_CHAR_P #define lxc_log_strerror_r \ char errno_buf[PATH_MAX / 2] = {"Failed to get errno string"}; \ char *ptr = NULL; \ { \ int saved_errno = errno; \ ptr = strerror_r(errno, errno_buf, sizeof(errno_buf)); \ errno = saved_errno; \ if (!ptr) \ ptr = errno_buf; \ } #else #define lxc_log_strerror_r \ char errno_buf[PATH_MAX / 2] = {"Failed to get errno string"}; \ char *ptr = errno_buf; \ { \ int saved_errno = errno; \ (void)strerror_r(errno, errno_buf, sizeof(errno_buf)); \ errno = saved_errno; \ } #endif #elif ENFORCE_THREAD_SAFETY #error ENFORCE_THREAD_SAFETY was set but cannot be guaranteed #else #define lxc_log_strerror_r \ char *ptr = NULL; \ { \ ptr = strerror(errno); \ } #endif /* * top categories */ #define TRACE(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_TRACE(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define DEBUG(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_DEBUG(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define INFO(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_INFO(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define NOTICE(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_NOTICE(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define WARN(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_WARN(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define ERROR(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_ERROR(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define CRIT(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_CRIT(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define ALERT(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_ALERT(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define FATAL(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_FATAL(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define SYSTRACE(format, ...) \ do { \ lxc_log_strerror_r; \ TRACE("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #define SYSDEBUG(format, ...) \ do { \ lxc_log_strerror_r; \ DEBUG("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #define SYSINFO(format, ...) \ do { \ lxc_log_strerror_r; \ INFO("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #define SYSNOTICE(format, ...) \ do { \ lxc_log_strerror_r; \ NOTICE("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #define SYSWARN(format, ...) \ do { \ lxc_log_strerror_r; \ WARN("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #define SYSERROR(format, ...) \ do { \ lxc_log_strerror_r; \ ERROR("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) extern int lxc_log_fd; extern int lxc_log_set_file(int *fd, const char *fname); extern int lxc_log_set_level(int *dest, int level); extern void lxc_log_set_prefix(const char *prefix); extern const char *lxc_log_get_file(void); extern int lxc_log_get_level(void); extern bool lxc_log_has_valid_level(void); extern const char *lxc_log_get_prefix(void); extern void lxc_log_options_no_override(); #endif lxc-2.0.11/src/lxc/Makefile.am0000644061062106075000000002031413435013473012726 00000000000000pkginclude_HEADERS = \ attach_options.h \ lxccontainer.h \ version.h noinst_HEADERS = \ tools/arguments.h \ attach.h \ storage/storage.h \ storage/aufs.h \ storage/btrfs.h \ storage/dir.h \ storage/loop.h \ storage/lvm.h \ storage/nbd.h \ storage/overlay.h \ storage/rbd.h \ storage/rsync.h \ storage/zfs.h \ storage/storage_utils.h \ cgroups/cgroup.h \ cgroups/cgroup_utils.h \ caps.h \ conf.h \ confile.h \ confile_utils.h \ console.h \ error.h \ initutils.h \ list.h \ log.h \ lxc.h \ lxclock.h \ macro.h \ memory_utils.h \ monitor.h \ namespace.h \ rexec.h \ start.h \ state.h \ utils.h \ criu.h \ ../tests/lxctest.h if IS_BIONIC noinst_HEADERS += \ ../include/fexecve.h \ ../include/getgrgid_r.h \ ../include/ifaddrs.h \ ../include/openpty.h \ ../include/lxcmntent.h endif if !HAVE_GETLINE if HAVE_FGETLN noinst_HEADERS += ../include/getline.h endif endif if !HAVE_GETSUBOPT noinst_HEADERS += ../include/getsubopt.h endif sodir=$(libdir) LSM_SOURCES = \ lsm/nop.c \ lsm/lsm.h lsm/lsm.c if ENABLE_APPARMOR LSM_SOURCES += lsm/apparmor.c endif if ENABLE_SELINUX LSM_SOURCES += lsm/selinux.c endif lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = \ storage/storage.c storage/storage.h \ storage/aufs.c storage/aufs.h \ storage/btrfs.c storage/btrfs.h \ storage/dir.c storage/dir.h \ storage/loop.c storage/loop.h \ storage/lvm.c storage/lvm.h \ storage/nbd.c storage/nbd.h \ storage/overlay.c storage/overlay.h \ storage/rbd.c storage/rbd.h \ storage/rsync.c storage/rsync.h \ storage/zfs.c storage/zfs.h \ storage/storage_utils.c storage/storage_utils.h \ cgroups/cgfs.c \ cgroups/cgfsng.c \ cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ cgroups/cgroup.c cgroups/cgroup.h \ commands.c commands.h \ commands_utils.c commands_utils.h \ start.c start.h \ execute.c \ monitor.c monitor.h \ console.c \ freezer.c \ error.h error.c \ parse.c parse.h \ lxc.h \ initutils.c initutils.h \ utils.c utils.h \ sync.c sync.h \ namespace.h namespace.c \ conf.c conf.h \ confile.c confile.h \ confile_utils.c confile_utils.h \ list.h \ state.c state.h \ log.c log.h \ attach.c attach.h \ criu.c criu.h \ \ network.c network.h \ nl.c nl.h \ rtnl.c rtnl.h \ \ caps.c caps.h \ lxcseccomp.h \ macro.h \ mainloop.c mainloop.h \ memory_utils.h \ af_unix.c af_unix.h \ \ lxcutmp.c lxcutmp.h \ lxclock.h lxclock.c \ lxccontainer.c lxccontainer.h \ version.h \ \ $(LSM_SOURCES) if ENABLE_CGMANAGER liblxc_la_SOURCES += cgroups/cgmanager.c endif if IS_BIONIC liblxc_la_SOURCES += \ ../include/fexecve.c ../include/fexecve.h \ ../include/getgrgid_r.c ../include/getgrgid_r.h \ ../include/ifaddrs.c ../include/ifaddrs.h \ ../include/openpty.c ../include/openpty.h \ ../include/lxcmntent.c ../include/lxcmntent.h endif if !HAVE_GETLINE if HAVE_FGETLN liblxc_la_SOURCES += ../include/getline.c ../include/getline.h endif endif if !HAVE_STRLCPY liblxc_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT liblxc_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif if ENFORCE_MEMFD_REXEC liblxc_la_SOURCES += rexec.c rexec.h endif AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" \ -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ -DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \ -DSBINDIR=\"$(SBINDIR)\" \ -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/lxc \ -I $(top_srcdir)/src/lxc/storage \ -I $(top_srcdir)/src/lxc/cgroups if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif if ENABLE_CGMANAGER AM_CFLAGS += -DHAVE_CGMANAGER endif if ENABLE_SELINUX AM_CFLAGS += -DHAVE_SELINUX endif if USE_CONFIGPATH_LOGS AM_CFLAGS += -DUSE_CONFIGPATH_LOGS endif if ENABLE_SECCOMP AM_CFLAGS += -DHAVE_SECCOMP $(SECCOMP_CFLAGS) liblxc_la_SOURCES += seccomp.c endif liblxc_la_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) -pthread liblxc_la_LDFLAGS = \ -pthread \ -shared \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) $(SELINUX_LIBS) $(SECCOMP_LIBS) if ENABLE_CGMANAGER liblxc_la_LIBADD += $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) liblxc_la_CFLAGS += $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) endif bin_SCRIPTS = tools/lxc-checkconfig EXTRA_DIST = \ tools/lxc-top.lua if ENABLE_DEPRECATED if ENABLE_PYTHON bin_SCRIPTS += tools/lxc-start-ephemeral endif endif bin_PROGRAMS = \ lxc-attach \ lxc-autostart \ lxc-cgroup \ lxc-checkpoint \ lxc-copy \ lxc-config \ lxc-console \ lxc-create \ lxc-destroy \ lxc-device \ lxc-execute \ lxc-freeze \ lxc-info \ lxc-ls \ lxc-monitor \ lxc-snapshot \ lxc-start \ lxc-stop \ lxc-top \ lxc-unfreeze \ lxc-unshare \ lxc-usernsexec \ lxc-wait if ENABLE_DEPRECATED bin_PROGRAMS += lxc-clone endif sbin_PROGRAMS = init.lxc pkglibexec_PROGRAMS = \ lxc-monitord \ lxc-user-nic AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif LDADD=liblxc.la @CAP_LIBS@ @SELINUX_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = tools/lxc_attach.c tools/arguments.c rexec.c rexec.h lxc_autostart_SOURCES = tools/lxc_autostart.c tools/arguments.c lxc_cgroup_SOURCES = tools/lxc_cgroup.c tools/arguments.c lxc_config_SOURCES = tools/lxc_config.c tools/arguments.c lxc_console_SOURCES = tools/lxc_console.c tools/arguments.c lxc_destroy_SOURCES = tools/lxc_destroy.c tools/arguments.c lxc_device_SOURCES = tools/lxc_device.c tools/arguments.c lxc_execute_SOURCES = tools/lxc_execute.c tools/arguments.c lxc_freeze_SOURCES = tools/lxc_freeze.c tools/arguments.c lxc_info_SOURCES = tools/lxc_info.c tools/arguments.c init_lxc_SOURCES = lxc_init.c lxc_monitor_SOURCES = tools/lxc_monitor.c tools/arguments.c lxc_ls_SOURCES = tools/lxc_ls.c tools/arguments.c lxc_copy_SOURCES = tools/lxc_copy.c tools/arguments.c lxc_start_SOURCES = tools/lxc_start.c tools/arguments.c lxc_stop_SOURCES = tools/lxc_stop.c tools/arguments.c lxc_top_SOURCES = tools/lxc_top.c tools/arguments.c lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c tools/arguments.c lxc_unshare_SOURCES = tools/lxc_unshare.c tools/arguments.c lxc_wait_SOURCES = tools/lxc_wait.c tools/arguments.c lxc_create_SOURCES = tools/lxc_create.c tools/arguments.c lxc_snapshot_SOURCES = tools/lxc_snapshot.c tools/arguments.c lxc_usernsexec_SOURCES = tools/lxc_usernsexec.c tools/arguments.c lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c tools/arguments.c lxc_user_nic_SOURCES = lxc_user_nic.c namespace.c network.c tools/arguments.c lxc_monitord_SOURCES = lxc_monitord.c tools/arguments.c if ENABLE_DEPRECATED lxc_clone_SOURCES = tools/lxc_clone.c tools/arguments.c endif if !HAVE_GETSUBOPT lxc_copy_SOURCES += ../include/getsubopt.c ../include/getsubopt.h endif if HAVE_STATIC_LIBCAP sbin_PROGRAMS += init.lxc.static init_lxc_static_SOURCES = lxc_init.c error.c log.c initutils.c caps.c parse.c namespace.c if !HAVE_GETLINE if HAVE_FGETLN init_lxc_static_SOURCES += ../include/getline.c endif endif if !HAVE_STRLCPY init_lxc_static_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT init_lxc_static_SOURCES += ../include/strlcat.c ../include/strlcat.h endif init_lxc_static_LDFLAGS = -all-static init_lxc_static_LDADD = @CAP_LIBS@ init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF endif install-exec-local: install-libLTLIBRARIES mkdir -p $(DESTDIR)$(datadir)/lxc install -c -m 644 lxc.functions $(DESTDIR)$(datadir)/lxc mv $(shell readlink -f $(DESTDIR)$(libdir)/liblxc.so) $(DESTDIR)$(libdir)/liblxc.so.@LXC_ABI@ rm -f $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.1 cd $(DESTDIR)$(libdir); \ ln -sf liblxc.so.@LXC_ABI@ liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)); \ ln -sf liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) liblxc.so install-exec-hook: chmod u+s $(DESTDIR)$(libexecdir)/lxc/lxc-user-nic uninstall-local: $(RM) $(DESTDIR)$(libdir)/liblxc.so* lxc-2.0.11/src/lxc/lxcutmp.h0000644061062106075000000000211213435013473012533 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXCUTMP_H #define __LXC_LXCUTMP_H #include "config.h" struct lxc_handler; struct lxc_epoll_descr; int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_handler *handler); #endif lxc-2.0.11/src/lxc/mainloop.h0000644061062106075000000000321113435013473012656 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_MAINLOOP_H #define __LXC_MAINLOOP_H #include #include "list.h" #define LXC_MAINLOOP_ERROR -1 #define LXC_MAINLOOP_CONTINUE 0 #define LXC_MAINLOOP_CLOSE 1 struct lxc_epoll_descr { int epfd; struct lxc_list handlers; }; typedef int (*lxc_mainloop_callback_t)(int fd, uint32_t event, void *data, struct lxc_epoll_descr *descr); extern int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms); extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, void *data); extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd); extern int lxc_mainloop_open(struct lxc_epoll_descr *descr); extern int lxc_mainloop_close(struct lxc_epoll_descr *descr); #endif lxc-2.0.11/src/lxc/list.h0000644061062106075000000001015313435013473012016 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LIST_H #define __LXC_LIST_H struct lxc_list { void *elem; struct lxc_list *next; struct lxc_list *prev; }; #define lxc_init_list(l) { .next = l, .prev = l } /* * Iterate through an lxc list. An example for an idiom would be: * * struct lxc_list *iterator; * type *tmp; // where "type" can be an int, char * etc. * lxc_list_for_each(iterator, list) { * tmp = iterator->elem; * // Do stuff with tmp. * } * free(iterator); */ #define lxc_list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; \ __iterator != __list; \ __iterator = __iterator->next) /* * Iterate safely through an lxc list. An example for an appropriate use case * would be: * * struct lxc_list *iterator; * lxc_list_for_each_safe(iterator, list, list->next) { * tmp = iterator->elem; * // Do stuff with tmp. * } * free(iterator); */ #define lxc_list_for_each_safe(__iterator, __list, __next) \ for (__iterator = (__list)->next, __next = __iterator->next; \ __iterator != __list; \ __iterator = __next, __next = __next->next) /* Initalize list. */ static inline void lxc_list_init(struct lxc_list *list) { list->elem = NULL; list->next = list->prev = list; } /* Add an element to a list. See lxc_list_add() and lxc_list_add_tail() for an * idiom. */ static inline void lxc_list_add_elem(struct lxc_list *list, void *elem) { list->elem = elem; } /* Retrieve first element of list. */ static inline void *lxc_list_first_elem(struct lxc_list *list) { return list->next->elem; } /* Retrieve last element of list. */ static inline void *lxc_list_last_elem(struct lxc_list *list) { return list->prev->elem; } /* Determine if list is empty. */ static inline int lxc_list_empty(struct lxc_list *list) { return list == list->next; } /* Workhorse to be called from lxc_list_add() and lxc_list_add_tail(). */ static inline void __lxc_list_add(struct lxc_list *new, struct lxc_list *prev, struct lxc_list *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* * Idiom to add an element to the beginning of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) * return 1; * lxc_list_add_elem(tmp, elem); * lxc_list_add(list, tmp); */ static inline void lxc_list_add(struct lxc_list *head, struct lxc_list *list) { __lxc_list_add(list, head, head->next); } /* * Idiom to add an element to the end of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) * return 1; * lxc_list_add_elem(tmp, elem); * lxc_list_add_tail(list, tmp); */ static inline void lxc_list_add_tail(struct lxc_list *head, struct lxc_list *list) { __lxc_list_add(list, head->prev, head); } /* * Idiom to free an lxc list: * * lxc_list_for_each_safe(iterator, list, list->next) { * lxc_list_del(iterator); * free(iterator); * } * free(iterator); */ static inline void lxc_list_del(struct lxc_list *list) { struct lxc_list *next, *prev; next = list->next; prev = list->prev; next->prev = prev; prev->next = next; } /* Return length of the list. */ static inline size_t lxc_list_len(struct lxc_list *list) { size_t i = 0; struct lxc_list *iter; lxc_list_for_each(iter, list) { i++; } return i; } #endif lxc-2.0.11/src/lxc/error.c0000644061062106075000000000324013435013473012166 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "error.h" #include "log.h" lxc_log_define(lxc_error, lxc); /*---------------------------------------------------------------------------*/ /* lxc_error_set_and_log * function is here to convert * the reported status to an exit code as detailed here: * * 0-126 exit code of the application * 128+n signal n received by the application * 255 lxc error */ extern int lxc_error_set_and_log(int pid, int status) { int ret = 0; if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret) INFO("Child <%d> ended on error (%d)", pid, ret); } if (WIFSIGNALED(status)) { int signal = WTERMSIG(status); INFO("Child <%d> ended on signal (%d)", pid, signal); ret = 128 + signal; } return ret; } lxc-2.0.11/src/lxc/lxc_user_nic.c0000644061062106075000000007406113435013473013523 00000000000000/* * * Copyright © 2013 Serge Hallyn . * Copyright © 2013 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "namespace.h" #include "network.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #define usernic_debug_stream(stream, format, ...) \ do { \ fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__, \ __func__, __VA_ARGS__); \ } while (false) #define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__) static void usage(char *me, bool fail) { fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} " "{bridge} {nicname}\n", me); fprintf(stderr, "Usage: %s delete {lxcpath} {name} " "{/proc//ns/net} {type} {bridge} {nicname}\n", me); fprintf(stderr, "{nicname} is the name to use inside the container\n"); if (fail) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } static int open_and_lock(char *path) { int fd, ret; struct flock lk; fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); if (fd < 0) { usernic_error("Failed to open \"%s\": %s\n", path, strerror(errno)); return -1; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; ret = fcntl(fd, F_SETLKW, &lk); if (ret < 0) { usernic_error("Failed to lock \"%s\": %s\n", path, strerror(errno)); close(fd); return -1; } return fd; } static char *get_username(void) { struct passwd pwent; struct passwd *pwentp = NULL; char *buf; char *username; size_t bufsize; int ret; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) usernic_error("%s", "Could not find matched password record\n"); usernic_error("Failed to get username: %s(%u)\n", strerror(errno), getuid()); free(buf); return NULL; } username = strdup(pwent.pw_name); free(buf); return username; } static void free_groupnames(char **groupnames) { int i; if (!groupnames) return; for (i = 0; groupnames[i]; i++) free(groupnames[i]); free(groupnames); } static char **get_groupnames(void) { int ngroups; gid_t *group_ids; int ret, i; char **groupnames; struct group grent; struct group *grentp = NULL; char *buf; size_t bufsize; ngroups = getgroups(0, NULL); if (ngroups < 0) { usernic_error("Failed to get number of groups the user " "belongs to: %s\n", strerror(errno)); return NULL; } if (ngroups == 0) return NULL; group_ids = malloc(sizeof(gid_t) * ngroups); if (!group_ids) { usernic_error("Failed to allocate memory while getting groups " "the user belongs to: %s\n", strerror(errno)); return NULL; } ret = getgroups(ngroups, group_ids); if (ret < 0) { free(group_ids); usernic_error("Failed to get process groups: %s\n", strerror(errno)); return NULL; } groupnames = malloc(sizeof(char *) * (ngroups + 1)); if (!groupnames) { free(group_ids); usernic_error("Failed to allocate memory while getting group " "names: %s\n", strerror(errno)); return NULL; } memset(groupnames, 0, sizeof(char *) * (ngroups + 1)); bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) { free(group_ids); free_groupnames(groupnames); usernic_error("Failed to allocate memory while getting group " "names: %s\n", strerror(errno)); return NULL; } for (i = 0; i < ngroups; i++) { ret = getgrgid_r(group_ids[i], &grent, buf, bufsize, &grentp); if (!grentp) { if (ret == 0) usernic_error("%s", "Could not find matched group record\n"); usernic_error("Failed to get group name: %s(%u)\n", strerror(errno), group_ids[i]); free(buf); free(group_ids); free_groupnames(groupnames); return NULL; } groupnames[i] = strdup(grent.gr_name); if (!groupnames[i]) { usernic_error("Failed to copy group name \"%s\"", grent.gr_name); free(buf); free(group_ids); free_groupnames(groupnames); return NULL; } } free(buf); free(group_ids); return groupnames; } static bool name_is_in_groupnames(char *name, char **groupnames) { while (groupnames) { if (!strcmp(name, *groupnames)) return true; groupnames++; } return false; } struct alloted_s { char *name; int allowed; struct alloted_s *next; }; static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n) { struct alloted_s *cur, *al; if (!head || !name) { /* Sanity check. Parameters should not be null. */ usernic_error("%s\n", "Unexpected NULL argument"); return NULL; } al = malloc(sizeof(struct alloted_s)); if (!al) { usernic_error("Failed to allocate memory: %s\n", strerror(errno)); return NULL; } al->name = strdup(name); if (!al->name) { free(al); return NULL; } al->allowed = n; al->next = NULL; if (!*head) { *head = al; return al; } cur = *head; while (cur->next) cur = cur->next; cur->next = al; return al; } static void free_alloted(struct alloted_s **head) { struct alloted_s *cur; if (!head) return; cur = *head; while (cur) { cur = cur->next; free((*head)->name); free(*head); *head = cur; } } /* The configuration file consists of lines of the form: * * user type bridge count * or * @group type bridge count * * Return the count entry for the calling user if there is one. Else * return -1. */ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted) { int n, ret; char name[100], type[100], br[100]; char **groups; FILE *fin; int count = 0; size_t len = 0; char *line = NULL; fin = fopen(LXC_USERNIC_CONF, "r"); if (!fin) { usernic_error("Failed to open \"%s\": %s\n", LXC_USERNIC_CONF, strerror(errno)); return -1; } groups = get_groupnames(); while ((getline(&line, &len, fin)) != -1) { ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n); if (ret != 4) continue; if (strlen(name) == 0) continue; if (strcmp(name, me)) { if (name[0] != '@') continue; if (!name_is_in_groupnames(name + 1, groups)) continue; } if (strcmp(type, intype)) continue; if (strcmp(link, br)) continue; /* Found the user or group with the appropriate settings, * therefore finish the search. What to do if there are more * than one applicable lines? not specified in the docs. Since * getline is implemented with realloc, we don't need to free * line until exiting func. * * If append_alloted returns NULL, e.g. due to a malloc error, * we set count to 0 and break the loop, allowing cleanup and * then exiting from main(). */ if (!append_alloted(alloted, name, n)) { count = 0; break; } count += n; } free_groupnames(groups); fclose(fin); free(line); /* Now return the total number of nics that this user can create. */ return count; } static char *get_eol(char *s, char *e) { while ((s < e) && *s && (*s != '\n')) s++; return s; } static char *get_eow(char *s, char *e) { while ((s < e) && *s && !isblank(*s) && (*s != '\n')) s++; return s; } static char *find_line(char *buf_start, char *buf_end, char *name, char *net_type, char *net_link, char *net_dev, bool *owner, bool *found, bool *keep) { char *end_of_line, *end_of_word, *line; while (buf_start < buf_end) { size_t len; char netdev_name[IFNAMSIZ]; *found = false; *keep = true; *owner = false; end_of_line = get_eol(buf_start, buf_end); if (end_of_line >= buf_end) return NULL; line = buf_start; if (*buf_start == '#') goto next; while ((buf_start < buf_end) && isblank(*buf_start)) buf_start++; /* Check whether the line contains the caller's name. */ end_of_word = get_eow(buf_start, buf_end); /* corrupt db */ if (!end_of_word) return NULL; if (strncmp(buf_start, name, strlen(name))) *found = false; *owner = true; buf_start = end_of_word + 1; while ((buf_start < buf_end) && isblank(*buf_start)) buf_start++; /* Check whether line is of the right network type. */ end_of_word = get_eow(buf_start, buf_end); /* corrupt db */ if (!end_of_word) return NULL; if (strncmp(buf_start, net_type, strlen(net_type))) *found = false; buf_start = end_of_word + 1; while ((buf_start < buf_end) && isblank(*buf_start)) buf_start++; /* Check whether line is contains the right link. */ end_of_word = get_eow(buf_start, buf_end); /* corrupt db */ if (!end_of_word) return NULL; if (strncmp(buf_start, net_link, strlen(net_link))) *found = false; buf_start = end_of_word + 1; while ((buf_start < buf_end) && isblank(*buf_start)) buf_start++; /* Check whether line contains the right network device. */ end_of_word = get_eow(buf_start, buf_end); /* corrupt db */ if (!end_of_word) return NULL; len = end_of_word - buf_start; /* corrupt db */ if (len >= IFNAMSIZ) return NULL; memcpy(netdev_name, buf_start, len); netdev_name[len] = '\0'; *keep = lxc_nic_exists(netdev_name); if (net_dev && !strcmp(netdev_name, net_dev)) *found = true; return line; next: buf_start = end_of_line + 1; } return NULL; } static int instantiate_veth(char *veth1, char *veth2) { int ret; ret = lxc_veth_create(veth1, veth2); if (ret < 0) { usernic_error("Failed to create %s-%s : %s.\n", veth1, veth2, strerror(-ret)); return -1; } /* Changing the high byte of the mac address to 0xfe, the bridge * interface will always keep the host's mac address and not take the * mac address of a container. */ ret = setup_private_host_hw_addr(veth1); if (ret < 0) usernic_error("Failed to change mac address of host interface " "%s : %s\n", veth1, strerror(-ret)); return netdev_set_flag(veth1, IFF_UP); } static int get_mtu(char *name) { int idx; idx = if_nametoindex(name); if (idx < 0) return -1; return netdev_get_mtu(idx); } static int create_nic(char *nic, char *br, int pid, char **cnic) { char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ]; int mtu, ret; ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic); if (ret < 0 || ret >= IFNAMSIZ) { usernic_error("%s", "Could not create nic name\n"); return -1; } ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf); if (ret < 0 || ret >= IFNAMSIZ) { usernic_error("%s\n", "Could not create nic name"); return -1; } /* create the nics */ ret = instantiate_veth(veth1buf, veth2buf); if (ret < 0) { usernic_error("%s", "Error creating veth tunnel\n"); return -1; } if (strcmp(br, "none")) { /* copy the bridge's mtu to both ends */ mtu = get_mtu(br); if (mtu > 0) { ret = lxc_netdev_set_mtu(veth1buf, mtu); if (ret < 0) { usernic_error("Failed to set mtu to %d on %s\n", mtu, veth1buf); goto out_del; } ret = lxc_netdev_set_mtu(veth2buf, mtu); if (ret < 0) { usernic_error("Failed to set mtu to %d on %s\n", mtu, veth2buf); goto out_del; } } /* attach veth1 to bridge */ ret = lxc_bridge_attach(br, veth1buf); if (ret < 0) { usernic_error("Error attaching %s to %s\n", veth1buf, br); goto out_del; } } /* pass veth2 to target netns */ ret = lxc_netdev_move_by_name(veth2buf, pid, NULL); if (ret < 0) { usernic_error("Error moving %s to network namespace of %d\n", veth2buf, pid); goto out_del; } *cnic = strdup(veth2buf); if (!*cnic) { usernic_error("Failed to copy string \"%s\"\n", veth2buf); return -1; } return 0; out_del: lxc_netdev_delete_by_name(veth1buf); return -1; } struct entry_line { char *start; int len; bool keep; }; static bool cull_entries(int fd, char *name, char *net_type, char *net_link, char *net_dev, bool *found_nicname) { int i, ret; char *buf, *buf_end, *buf_start; struct stat sb; int n = 0; bool found, keep; struct entry_line *entry_lines = NULL; ret = fstat(fd, &sb); if (ret < 0) { usernic_error("Failed to fstat: %s\n", strerror(errno)); return false; } if (!sb.st_size) return false; buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { usernic_error("Failed to establish shared memory mapping: %s\n", strerror(errno)); return false; } buf_start = buf; buf_end = buf + sb.st_size; while ((buf_start = find_line(buf_start, buf_end, name, net_type, net_link, net_dev, &(bool){true}, &found, &keep))) { struct entry_line *newe; newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1)); if (!newe) { free(entry_lines); lxc_strmunmap(buf, sb.st_size); return false; } if (found) *found_nicname = true; entry_lines = newe; entry_lines[n].start = buf_start; entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start; entry_lines[n].keep = keep; n++; buf_start += entry_lines[n - 1].len + 1; if (buf_start >= buf_end) break; } buf_start = buf; for (i = 0; i < n; i++) { if (!entry_lines[i].keep) continue; memcpy(buf_start, entry_lines[i].start, entry_lines[i].len); buf_start += entry_lines[i].len; *buf_start = '\n'; buf_start++; } free(entry_lines); ret = ftruncate(fd, buf_start - buf); lxc_strmunmap(buf, sb.st_size); if (ret < 0) usernic_error("Failed to set new file size: %s\n", strerror(errno)); return true; } static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link) { int count = 0; bool owner = false;; char *buf_end; buf_end = &buf[len]; while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL, &owner, &(bool){true}, &(bool){true}))) { if (owner) count++; buf = get_eol(buf, buf_end) + 1; if (buf >= buf_end) break; } return count; } /* The dbfile has lines of the format: user type bridge nicname. */ static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *intype, char *br, int allowed, char **cnic) { int ret; size_t slen; char *newline, *owner; char nicname[IFNAMSIZ]; struct stat sb; struct alloted_s *n; int count = 0; char *buf = NULL; for (n = names; n != NULL; n = n->next) cull_entries(fd, n->name, intype, br, NULL, NULL); if (allowed == 0) return NULL; owner = names->name; ret = fstat(fd, &sb); if (ret < 0) { usernic_error("Failed to fstat: %s\n", strerror(errno)); return NULL; } if (sb.st_size > 0) { buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { usernic_error("Failed to establish shared memory " "mapping: %s\n", strerror(errno)); return NULL; } owner = NULL; for (n = names; n != NULL; n = n->next) { count = count_entries(buf, sb.st_size, n->name, intype, br); if (count >= n->allowed) continue; owner = n->name; break; } lxc_strmunmap(buf, sb.st_size); } if (owner == NULL) return NULL; ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX"); if (ret < 0 || (size_t)ret >= sizeof(nicname)) return NULL; if (!lxc_mkifname(nicname)) return NULL; ret = create_nic(nicname, br, pid, cnic); if (ret < 0) { usernic_error("%s", "Failed to create new nic\n"); return NULL; } /* strlen(owner) * + * " " * + * strlen(intype) * + * " " * + * strlen(br) * + * " " * + * strlen(nicname) * + * \n * + * \0 */ slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4; newline = malloc(slen + 1); if (!newline) { free(newline); usernic_error("Failed allocate memory: %s\n", strerror(errno)); return NULL; } ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname); if (ret < 0 || (size_t)ret >= (slen + 1)) { if (lxc_netdev_delete_by_name(nicname) != 0) usernic_error("Error unlinking %s\n", nicname); free(newline); return NULL; } /* Note that the file needs to be truncated to the size **without** the * \0 byte! Files are not \0-terminated! */ ret = ftruncate(fd, sb.st_size + slen); if (ret < 0) usernic_error("Failed to truncate file: %s\n", strerror(errno)); buf = lxc_strmmap(NULL, sb.st_size + slen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { usernic_error("Failed to establish shared memory mapping: %s\n", strerror(errno)); if (lxc_netdev_delete_by_name(nicname) != 0) usernic_error("Error unlinking %s\n", nicname); free(newline); return NULL; } /* Note that the memory needs to be moved in the buffer **without** the * \0 byte! Files are not \0-terminated! */ memmove(buf + sb.st_size, newline, slen); free(newline); lxc_strmunmap(buf, sb.st_size + slen); return strdup(nicname); } static bool create_db_dir(char *fnam) { int ret; char *p; size_t len; len = strlen(fnam); p = alloca(len + 1); (void)strlcpy(p, fnam, len + 1); fnam = p; p = p + 1; again: while (*p && *p != '/') p++; if (!*p) return true; *p = '\0'; ret = mkdir(fnam, 0755); if (ret < 0 && errno != EEXIST) { usernic_error("Failed to create %s: %s\n", fnam, strerror(errno)); *p = '/'; return false; } *(p++) = '/'; goto again; } static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname, int *container_veth_ifidx) { int ret; pid_t pid_self; uid_t ruid, suid, euid; char ifname[IFNAMSIZ]; char *string_ret = NULL, *name = NULL; int fd = -1, ifindex = -1, ofd = -1; pid_self = lxc_raw_getpid(); ofd = lxc_preserve_ns(pid_self, "net"); if (ofd < 0) { usernic_error("Failed opening network namespace path for %d", pid_self); return NULL; } fd = lxc_preserve_ns(pid, "net"); if (fd < 0) { usernic_error("Failed opening network namespace path for %d", pid); goto do_partial_cleanup; } ret = getresuid(&ruid, &euid, &suid); if (ret < 0) { usernic_error("Failed to retrieve real, effective, and saved " "user IDs: %s\n", strerror(errno)); goto do_partial_cleanup; } ret = setns(fd, CLONE_NEWNET); close(fd); fd = -1; if (ret < 0) { usernic_error("Failed to setns() to the network namespace of " "the container with PID %d: %s\n", pid, strerror(errno)); goto do_partial_cleanup; } ret = setresuid(ruid, ruid, 0); if (ret < 0) { usernic_error("Failed to drop privilege by setting effective " "user id and real user id to %d, and saved user " "ID to 0: %s\n", ruid, strerror(errno)); /* It's ok to jump to do_full_cleanup here since setresuid() * will succeed when trying to set real, effective, and saved to * values they currently have. */ goto do_full_cleanup; } /* Check if old interface exists. */ ifindex = if_nametoindex(oldname); if (!ifindex) { usernic_error("Failed to get netdev index: %s\n", strerror(errno)); goto do_full_cleanup; } /* When the IFLA_IFNAME attribute is passed something like "%d" * netlink will replace the format specifier with an appropriate index. * So we pass "eth%d". */ if (newname) name = newname; else name = "eth%d"; ret = lxc_netdev_rename_by_name(oldname, name); name = NULL; if (ret < 0) { usernic_error("Error %d renaming netdev %s to %s in container\n", ret, oldname, newname ? newname : "eth%d"); goto do_full_cleanup; } /* Retrieve new name for interface. */ if (!if_indextoname(ifindex, ifname)) { usernic_error("Failed to get new netdev name: %s\n", strerror(errno)); goto do_full_cleanup; } /* Allocation failure for strdup() is checked below. */ name = strdup(ifname); string_ret = name; *container_veth_ifidx = ifindex; do_full_cleanup: ret = setresuid(ruid, euid, suid); if (ret < 0) { usernic_error("Failed to restore privilege by setting " "effective user id to %d, real user id to %d, " "and saved user ID to %d: %s\n", ruid, euid, suid, strerror(errno)); string_ret = NULL; } ret = setns(ofd, CLONE_NEWNET); if (ret < 0) { usernic_error("Failed to setns() to original network namespace " "of PID %d: %s\n", ofd, strerror(errno)); string_ret = NULL; } do_partial_cleanup: if (fd >= 0) close(fd); if (!string_ret && name) free(name); close(ofd); return string_ret; } /* If the caller (real uid, not effective uid) may read the /proc/[pid]/ns/net, * then it is either the caller's netns or one which it created. */ static bool may_access_netns(int pid) { int ret; char s[200]; uid_t ruid, suid, euid; bool may_access = false; ret = getresuid(&ruid, &euid, &suid); if (ret < 0) { usernic_error("Failed to retrieve real, effective, and saved " "user IDs: %s\n", strerror(errno)); return false; } ret = setresuid(ruid, ruid, euid); if (ret < 0) { usernic_error("Failed to drop privilege by setting effective " "user id and real user id to %d, and saved user " "ID to %d: %s\n", ruid, euid, strerror(errno)); return false; } ret = snprintf(s, 200, "/proc/%d/ns/net", pid); if (ret < 0 || ret >= 200) return false; ret = access(s, R_OK); may_access = true; if (ret < 0) { may_access = false; usernic_error("Uid %d may not access %s: %s\n", (int)ruid, s, strerror(errno)); } ret = setresuid(ruid, euid, suid); if (ret < 0) { usernic_error("Failed to restore user id to %d, real user id " "to %d, and saved user ID to %d: %s\n", ruid, euid, suid, strerror(errno)); may_access = false; } return may_access; } struct user_nic_args { char *cmd; char *lxc_path; char *lxc_name; char *pid; char *type; char *link; char *veth_name; }; #define LXC_USERNIC_CREATE 0 #define LXC_USERNIC_DELETE 1 static bool is_privileged_over_netns(int netns_fd) { int ret; pid_t pid_self; uid_t euid, ruid, suid; bool bret = false; int ofd = -1; pid_self = lxc_raw_getpid(); ofd = lxc_preserve_ns(pid_self, "net"); if (ofd < 0) { usernic_error("Failed opening network namespace path for %d", pid_self); return false; } ret = getresuid(&ruid, &euid, &suid); if (ret < 0) { usernic_error("Failed to retrieve real, effective, and saved " "user IDs: %s\n", strerror(errno)); goto do_partial_cleanup; } ret = setns(netns_fd, CLONE_NEWNET); if (ret < 0) { usernic_error("Failed to setns() to network namespace %s\n", strerror(errno)); goto do_partial_cleanup; } ret = setresuid(ruid, ruid, 0); if (ret < 0) { usernic_error("Failed to drop privilege by setting effective " "user id and real user id to %d, and saved user " "ID to 0: %s\n", ruid, strerror(errno)); /* It's ok to jump to do_full_cleanup here since setresuid() * will succeed when trying to set real, effective, and saved to * values they currently have. */ goto do_full_cleanup; } /* Test whether we are privileged over the network namespace. To do this * we try to delete the loopback interface which is not possible. If we * are privileged over the network namespace we will get ENOTSUP. If we * are not privileged over the network namespace we will get EPERM. */ ret = lxc_netdev_delete_by_name("lo"); if (ret == -ENOTSUP) bret = true; do_full_cleanup: ret = setresuid(ruid, euid, suid); if (ret < 0) { usernic_error("Failed to restore privilege by setting " "effective user id to %d, real user id to %d, " "and saved user ID to %d: %s\n", ruid, euid, suid, strerror(errno)); bret = false; } ret = setns(ofd, CLONE_NEWNET); if (ret < 0) { usernic_error("Failed to setns() to original network namespace " "of PID %d: %s\n", ofd, strerror(errno)); bret = false; } do_partial_cleanup: close(ofd); return bret; } int main(int argc, char *argv[]) { int fd, n, pid, request, ret; char *me, *newname; struct user_nic_args args; int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1; char *cnic = NULL, *nicname = NULL; struct alloted_s *alloted = NULL; if (argc < 7 || argc > 8) { usage(argv[0], true); exit(EXIT_FAILURE); } memset(&args, 0, sizeof(struct user_nic_args)); args.cmd = argv[1]; args.lxc_path = argv[2]; args.lxc_name = argv[3]; args.pid = argv[4]; args.type = argv[5]; args.link = argv[6]; if (argc >= 8) args.veth_name = argv[7]; if (!strcmp(args.cmd, "create")) { request = LXC_USERNIC_CREATE; } else if (!strcmp(args.cmd, "delete")) { request = LXC_USERNIC_DELETE; } else { usage(argv[0], true); exit(EXIT_FAILURE); } /* Set a sane env, because we are setuid-root. */ ret = clearenv(); if (ret) { usernic_error("%s", "Failed to clear environment\n"); exit(EXIT_FAILURE); } ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); if (ret < 0) { usernic_error("%s", "Failed to set PATH, exiting\n"); exit(EXIT_FAILURE); } me = get_username(); if (!me) { usernic_error("%s", "Failed to get username\n"); exit(EXIT_FAILURE); } if (request == LXC_USERNIC_CREATE) { ret = lxc_safe_int(args.pid, &pid); if (ret < 0) { usernic_error("Could not read pid: %s\n", args.pid); exit(EXIT_FAILURE); } } else if (request == LXC_USERNIC_DELETE) { char opath[LXC_PROC_PID_FD_LEN]; /* Open the path with O_PATH which will not trigger an actual * open(). Don't report an errno to the caller to not leak * information whether the path exists or not. * When stracing setuid is stripped so this is not a concern * either. */ netns_fd = open(args.pid, O_PATH | O_CLOEXEC); if (netns_fd < 0) { usernic_error("Failed to open \"%s\"\n", args.pid); exit(EXIT_FAILURE); } if (!fhas_fs_type(netns_fd, NSFS_MAGIC)) { usernic_error("Path \"%s\" does not refer to a network namespace path\n", args.pid); close(netns_fd); exit(EXIT_FAILURE); } ret = snprintf(opath, sizeof(opath), "/proc/self/fd/%d", netns_fd); if (ret < 0 || (size_t)ret >= sizeof(opath)) { close(netns_fd); exit(EXIT_FAILURE); } /* Now get an fd that we can use in setns() calls. */ ret = open(opath, O_RDONLY | O_CLOEXEC); if (ret < 0) { usernic_error("Failed to open \"%s\": %s\n", args.pid, strerror(errno)); close(netns_fd); exit(EXIT_FAILURE); } close(netns_fd); netns_fd = ret; } if (!create_db_dir(LXC_USERNIC_DB)) { usernic_error("%s", "Failed to create directory for db file\n"); if (netns_fd >= 0) close(netns_fd); exit(EXIT_FAILURE); } fd = open_and_lock(LXC_USERNIC_DB); if (fd < 0) { usernic_error("Failed to lock %s\n", LXC_USERNIC_DB); if (netns_fd >= 0) close(netns_fd); exit(EXIT_FAILURE); } if (request == LXC_USERNIC_CREATE) { if (!may_access_netns(pid)) { usernic_error("User %s may not modify netns for pid %d\n", me, pid); exit(EXIT_FAILURE); } } else if (request == LXC_USERNIC_DELETE) { bool has_priv; has_priv = is_privileged_over_netns(netns_fd); close(netns_fd); if (!has_priv) { usernic_error("%s", "Process is not privileged over " "network namespace\n"); exit(EXIT_FAILURE); } } n = get_alloted(me, args.type, args.link, &alloted); free(me); if (request == LXC_USERNIC_DELETE) { int ret; struct alloted_s *it; bool found_nicname = false; if (!is_ovs_bridge(args.link)) { usernic_error("%s", "Deletion of non ovs type network " "devices not implemented\n"); close(fd); free_alloted(&alloted); exit(EXIT_FAILURE); } /* Check whether the network device we are supposed to delete * exists in the db. If it doesn't we will not delete it as we * need to assume the network device is not under our control. * As a side effect we also clear any invalid entries from the * database. */ for (it = alloted; it; it = it->next) cull_entries(fd, it->name, args.type, args.link, args.veth_name, &found_nicname); close(fd); free_alloted(&alloted); if (!found_nicname) { usernic_error("Caller is not allowed to delete network " "device \"%s\"\n", args.veth_name); exit(EXIT_FAILURE); } ret = lxc_ovs_delete_port(args.link, args.veth_name); if (ret < 0) { usernic_error("Failed to remove port \"%s\" from " "openvswitch bridge \"%s\"", args.veth_name, args.link); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } if (n > 0) nicname = get_nic_if_avail(fd, alloted, pid, args.type, args.link, n, &cnic); close(fd); free_alloted(&alloted); if (!nicname) { usernic_error("%s", "Quota reached\n"); exit(EXIT_FAILURE); } /* Now rename the link. */ newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name, &container_veth_ifidx); if (!newname) { usernic_error("%s", "Failed to rename the link\n"); ret = lxc_netdev_delete_by_name(cnic); if (ret < 0) usernic_error("Failed to delete \"%s\"\n", cnic); free(nicname); exit(EXIT_FAILURE); } host_veth_ifidx = if_nametoindex(nicname); if (!host_veth_ifidx) { free(newname); free(nicname); usernic_error("Failed to get netdev index: %s\n", strerror(errno)); exit(EXIT_FAILURE); } /* Write names of veth pairs and their ifindeces to stout: * (e.g. eth0:731:veth9MT2L4:730) */ fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname, host_veth_ifidx); free(newname); free(nicname); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/lxc.functions.in0000644061062106075000000000210013435013473014010 00000000000000# # lxc: linux Container library # Authors: # Serge Hallyn # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This file contains helpers for the various lxc shell scripts globalconf=@LXC_GLOBAL_CONF@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ lxc_path=`lxc-config lxc.lxcpath` lxc_vg=`lxc-config lxc.bdev.lvm.vg` lxc_zfsroot=`lxc-config lxc.bdev.zfs.root` lxc-2.0.11/src/lxc/sync.c0000644061062106075000000000704713435013473012022 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include "sync.h" #include "log.h" #include "start.h" #include "utils.h" lxc_log_define(lxc_sync, lxc); static int __sync_wait(int fd, int sequence) { int sync = -1; ssize_t ret; ret = lxc_read_nointr(fd, &sync, sizeof(sync)); if (ret < 0) { ERROR("sync wait failure : %s", strerror(errno)); return -1; } if (!ret) return 0; if ((size_t)ret != sizeof(sync)) { ERROR("unexpected sync size: %zu expected %zu", (size_t)ret, sizeof(sync)); return -1; } if (sync == LXC_SYNC_ERROR) { ERROR("An error occurred in another process " "(expected sequence number %d)", sequence); return -1; } if (sync != sequence) { ERROR("invalid sequence number %d. expected %d", sync, sequence); return -1; } return 0; } static int __sync_wake(int fd, int sequence) { int sync = sequence; if (lxc_write_nointr(fd, &sync, sizeof(sync)) < 0) { SYSERROR("Sync wake failure"); return -1; } return 0; } static int __sync_barrier(int fd, int sequence) { if (__sync_wake(fd, sequence)) return -1; return __sync_wait(fd, sequence+1); } int lxc_sync_barrier_parent(struct lxc_handler *handler, int sequence) { return __sync_barrier(handler->sync_sock[0], sequence); } int lxc_sync_barrier_child(struct lxc_handler *handler, int sequence) { return __sync_barrier(handler->sync_sock[1], sequence); } int lxc_sync_wake_parent(struct lxc_handler *handler, int sequence) { return __sync_wake(handler->sync_sock[0], sequence); } int lxc_sync_wait_parent(struct lxc_handler *handler, int sequence) { return __sync_wait(handler->sync_sock[0], sequence); } int lxc_sync_wait_child(struct lxc_handler *handler, int sequence) { return __sync_wait(handler->sync_sock[1], sequence); } int lxc_sync_wake_child(struct lxc_handler *handler, int sequence) { return __sync_wake(handler->sync_sock[1], sequence); } int lxc_sync_init(struct lxc_handler *handler) { int ret; ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, handler->sync_sock); if (ret) { SYSERROR("failed to create synchronization socketpair"); return -1; } /* Be sure we don't inherit this after the exec */ fcntl(handler->sync_sock[0], F_SETFD, FD_CLOEXEC); return 0; } void lxc_sync_fini_child(struct lxc_handler *handler) { if (handler->sync_sock[0] != -1) { close(handler->sync_sock[0]); handler->sync_sock[0] = -1; } } void lxc_sync_fini_parent(struct lxc_handler *handler) { if (handler->sync_sock[1] != -1) { close(handler->sync_sock[1]); handler->sync_sock[1] = -1; } } void lxc_sync_fini(struct lxc_handler *handler) { lxc_sync_fini_child(handler); lxc_sync_fini_parent(handler); } lxc-2.0.11/src/lxc/parse.c0000644061062106075000000000474013435013473012155 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #undef _GNU_SOURCE #include #include #include #include #include "parse.h" #include "config.h" #include "utils.h" #include "log.h" lxc_log_define(lxc_parse, lxc); int lxc_file_for_each_line(const char *file, lxc_file_cb callback, void *data) { FILE *f; int err = 0; char *line = NULL; size_t len = 0; f = fopen(file, "r"); if (!f) { SYSERROR("failed to open %s", file); return -1; } while (getline(&line, &len, f) != -1) { err = callback(line, data); if (err) { // callback rv > 0 means stop here // callback rv < 0 means error if (err < 0) ERROR("Failed to parse config: %s", line); break; } } free(line); fclose(f); return err; } int lxc_char_left_gc(const char *buffer, size_t len) { size_t i; for (i = 0; i < len; i++) { if (buffer[i] == ' ' || buffer[i] == '\t') continue; return i; } return 0; } int lxc_char_right_gc(const char *buffer, size_t len) { int i; for (i = len - 1; i >= 0; i--) { if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' || buffer[i] == '\0') continue; return i + 1; } return 0; } char *lxc_trim_whitespace_in_place(char *buffer) { buffer += lxc_char_left_gc(buffer, strlen(buffer)); buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0'; return buffer; } int lxc_is_line_empty(const char *line) { int i; size_t len = strlen(line); for (i = 0; i < len; i++) if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n' && line[i] != '\r' && line[i] != '\f' && line[i] != '\0') return 0; return 1; } lxc-2.0.11/src/lxc/network.c0000644061062106075000000021467013435013473012541 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "conf.h" #include "config.h" #include "log.h" #include "network.h" #include "nl.h" #include "utils.h" #if HAVE_IFADDRS_H #include #else #include <../include/ifaddrs.h> #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef IFLA_LINKMODE #define IFLA_LINKMODE 17 #endif #ifndef IFLA_LINKINFO #define IFLA_LINKINFO 18 #endif #ifndef IFLA_NET_NS_PID #define IFLA_NET_NS_PID 19 #endif #ifndef IFLA_INFO_KIND #define IFLA_INFO_KIND 1 #endif #ifndef IFLA_VLAN_ID #define IFLA_VLAN_ID 1 #endif #ifndef IFLA_INFO_DATA #define IFLA_INFO_DATA 2 #endif #ifndef VETH_INFO_PEER #define VETH_INFO_PEER 1 #endif #ifndef IFLA_MACVLAN_MODE #define IFLA_MACVLAN_MODE 1 #endif lxc_log_define(lxc_network, lxc); typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *); static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) { int bridge_index, err; char *veth1, *veth2; char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ]; unsigned int mtu = 0; if (netdev->priv.veth_attr.pair[0] != '\0') { veth1 = netdev->priv.veth_attr.pair; if (handler->conf->reboot) lxc_netdev_delete_by_name(veth1); } else { err = snprintf(veth1buf, sizeof(veth1buf), "vethXXXXXX"); if (err < 0 || (size_t)err >= sizeof(veth1buf)) return -1; veth1 = lxc_mkifname(veth1buf); if (!veth1) return -1; /* store away for deconf */ memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ); } err = snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX"); if (err < 0 || (size_t)err >= sizeof(veth2buf)) return -1; veth2 = lxc_mkifname(veth2buf); if (!veth2) goto out_delete; err = lxc_veth_create(veth1, veth2); if (err) { ERROR("Failed to create veth pair \"%s\" and \"%s\": %s", veth1, veth2, strerror(-err)); goto out_delete; } /* changing the high byte of the mac address to 0xfe, the bridge interface * will always keep the host's mac address and not take the mac address * of a container */ err = setup_private_host_hw_addr(veth1); if (err) { ERROR("Failed to change mac address of host interface \"%s\": %s", veth1, strerror(-err)); goto out_delete; } /* Retrieve ifindex of the host's veth device. */ netdev->priv.veth_attr.ifindex = if_nametoindex(veth1); if (!netdev->priv.veth_attr.ifindex) { ERROR("Failed to retrieve ifindex for \"%s\"", veth1); goto out_delete; } /* Note that we're retrieving the container's ifindex in the host's * network namespace because we need it to move the device from the * host's network namespace to the container's network namespace later * on. */ netdev->ifindex = if_nametoindex(veth2); if (!netdev->ifindex) { ERROR("Failed to retrieve ifindex for \"%s\"", veth2); goto out_delete; } if (netdev->mtu) { if (lxc_safe_uint(netdev->mtu, &mtu) < 0) WARN("Failed to parse mtu"); else INFO("Retrieved mtu %d", mtu); } else if (netdev->link[0] != '\0') { bridge_index = if_nametoindex(netdev->link); if (bridge_index) { mtu = netdev_get_mtu(bridge_index); INFO("Retrieved mtu %d from %s", mtu, netdev->link); } else { mtu = netdev_get_mtu(netdev->ifindex); INFO("Retrieved mtu %d from %s", mtu, veth2); } } if (mtu) { err = lxc_netdev_set_mtu(veth1, mtu); if (!err) err = lxc_netdev_set_mtu(veth2, mtu); if (err) { ERROR("Failed to set mtu \"%d\" for veth pair \"%s\" " "and \"%s\": %s", mtu, veth1, veth2, strerror(-err)); goto out_delete; } } if (netdev->link[0] != '\0') { err = lxc_bridge_attach(netdev->link, veth1); if (err) { ERROR("Failed to attach \"%s\" to bridge \"%s\": %s", veth1, netdev->link, strerror(-err)); goto out_delete; } INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link); } err = lxc_netdev_up(veth1); if (err) { ERROR("Failed to set \"%s\" up: %s", veth1, strerror(-err)); goto out_delete; } if (netdev->upscript) { err = run_script(handler->name, "net", netdev->upscript, "up", "veth", veth1, (char*) NULL); if (err) goto out_delete; } DEBUG("Instantiated veth \"%s/%s\", index is \"%d\"", veth1, veth2, netdev->ifindex); return 0; out_delete: if (netdev->ifindex != 0) lxc_netdev_delete_by_name(veth1); return -1; } static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) { char peerbuf[IFNAMSIZ], *peer; int err; if (netdev->link[0] == '\0') { ERROR("No link for macvlan network device specified"); return -1; } err = snprintf(peerbuf, sizeof(peerbuf), "mcXXXXXX"); if (err < 0 || (size_t)err >= sizeof(peerbuf)) return -1; peer = lxc_mkifname(peerbuf); if (!peer) return -1; err = lxc_macvlan_create(netdev->link, peer, netdev->priv.macvlan_attr.mode); if (err) { ERROR("Failed to create macvlan interface \"%s\" on \"%s\": %s", peer, netdev->link, strerror(-err)); goto on_error; } netdev->ifindex = if_nametoindex(peer); if (!netdev->ifindex) { ERROR("Failed to retrieve ifindex for \"%s\"", peer); goto on_error; } if (netdev->upscript) { err = run_script(handler->name, "net", netdev->upscript, "up", "macvlan", netdev->link, (char*) NULL); if (err) goto on_error; } DEBUG("Instantiated macvlan \"%s\" with ifindex is %d and mode %d", peer, netdev->ifindex, netdev->priv.macvlan_attr.mode); return 0; on_error: lxc_netdev_delete_by_name(peer); return -1; } static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) { char peer[IFNAMSIZ]; int err; static uint16_t vlan_cntr = 0; unsigned int mtu = 0; if (netdev->link[0] == '\0') { ERROR("No link for vlan network device specified"); return -1; } err = snprintf(peer, sizeof(peer), "vlan%d-%d", netdev->priv.vlan_attr.vid, vlan_cntr++); if (err < 0 || (size_t)err >= sizeof(peer)) return -1; err = lxc_vlan_create(netdev->link, peer, netdev->priv.vlan_attr.vid); if (err) { ERROR("Failed to create vlan interface \"%s\" on \"%s\": %s", peer, netdev->link, strerror(-err)); return -1; } netdev->ifindex = if_nametoindex(peer); if (!netdev->ifindex) { ERROR("Failed to retrieve ifindex for \"%s\"", peer); lxc_netdev_delete_by_name(peer); return -1; } DEBUG("Instantiated vlan \"%s\" with ifindex is \"%d\" (vlan1000)", peer, netdev->ifindex); if (netdev->mtu) { if (lxc_safe_uint(netdev->mtu, &mtu) < 0) { ERROR("Failed to retrieve mtu from \"%d\"/\"%s\".", netdev->ifindex, netdev->name[0] != '\0' ? netdev->name : "(null)"); return -1; } err = lxc_netdev_set_mtu(peer, mtu); if (err) { ERROR("Failed to set mtu \"%s\" for \"%s\": %s", netdev->mtu, peer, strerror(-err)); lxc_netdev_delete_by_name(peer); return -1; } } return 0; } static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) { if (netdev->link[0] == '\0') { ERROR("No link for physical interface specified"); return -1; } /* Note that we're retrieving the container's ifindex in the host's * network namespace because we need it to move the device from the * host's network namespace to the container's network namespace later * on. * Note that netdev->link will contain the name of the physical network * device in the host's namespace. */ netdev->ifindex = if_nametoindex(netdev->link); if (!netdev->ifindex) { ERROR("Failed to retrieve ifindex for \"%s\"", netdev->link); return -1; } /* Store the ifindex of the host's network device in the host's * namespace. */ netdev->priv.phys_attr.ifindex = netdev->ifindex; if (netdev->upscript) { int err; err = run_script(handler->name, "net", netdev->upscript, "up", "phys", netdev->link, (char*) NULL); if (err) return -1; } return 0; } static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) { netdev->ifindex = 0; if (netdev->upscript) { int err; err = run_script(handler->name, "net", netdev->upscript, "up", "empty", (char*) NULL); if (err) return -1; } return 0; } static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netdev) { netdev->ifindex = 0; return 0; } static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = { [LXC_NET_VETH] = instantiate_veth, [LXC_NET_MACVLAN] = instantiate_macvlan, [LXC_NET_VLAN] = instantiate_vlan, [LXC_NET_PHYS] = instantiate_phys, [LXC_NET_EMPTY] = instantiate_empty, [LXC_NET_NONE] = instantiate_none, }; static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) { char *veth1; int err; if (netdev->priv.veth_attr.pair[0] != '\0') 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 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; } static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) { 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 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; } static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev) { return 0; } static instantiate_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, [LXC_NET_NONE] = shutdown_none, }; int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL; struct ifinfomsg *ifi; int err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) goto out; ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = ifindex; if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid)) goto out; if (ifname != NULL) { if (nla_put_string(nlmsg, IFLA_IFNAME, ifname)) goto out; } err = netlink_transaction(&nlh, nlmsg, nlmsg); out: netlink_close(&nlh); nlmsg_free(nlmsg); return err; } /* * If we are asked to move a wireless interface, then * we must actually move its phyN device. Detect * that condition and return the physname here. The * physname will be passed to lxc_netdev_move_wlan() * which will free it when done */ #define PHYSNAME "/sys/class/net/%s/phy80211/name" static char * is_wlan(const char *ifname) { char *path, *physname = NULL; size_t len = strlen(ifname) + strlen(PHYSNAME) - 1; struct stat sb; long physlen; FILE *f; int ret, i; path = alloca(len+1); ret = snprintf(path, len, PHYSNAME, ifname); if (ret < 0 || ret >= len) goto bad; ret = stat(path, &sb); if (ret) goto bad; if (!(f = fopen(path, "r"))) goto bad; // feh - sb.st_size is always 4096 fseek(f, 0, SEEK_END); physlen = ftell(f); fseek(f, 0, SEEK_SET); physname = malloc(physlen + 1); if (!physname) { fclose(f); goto bad; } memset(physname, 0, physlen+1); ret = fread(physname, 1, physlen, f); fclose(f); if (ret < 0) goto bad; for (i = 0; i < physlen; i++) { if (physname[i] == '\n') physname[i] = '\0'; if (physname[i] == '\0') break; } return physname; bad: free(physname); return NULL; } static int lxc_netdev_rename_by_name_in_netns(pid_t pid, const char *old, const char *new) { pid_t fpid = fork(); if (fpid < 0) return -1; if (fpid != 0) return wait_for_pid(fpid); if (!switch_to_ns(pid, "net")) return -1; exit(lxc_netdev_rename_by_name(old, new)); } static int lxc_netdev_move_wlan(char *physname, const char *ifname, pid_t pid, const char* newname) { int err = -1; pid_t fpid; char *cmd; /* Move phyN into the container. TODO - do this using netlink. * However, IIUC this involves a bit more complicated work to * talk to the 80211 module, so for now just call out to iw */ cmd = on_path("iw", NULL); if (!cmd) goto out1; free(cmd); fpid = fork(); if (fpid < 0) goto out1; if (fpid == 0) { char pidstr[30]; sprintf(pidstr, "%d", pid); if (execlp("iw", "iw", "phy", physname, "set", "netns", pidstr, (char *)NULL)) exit(1); exit(0); // notreached } if (wait_for_pid(fpid)) goto out1; err = 0; if (newname) err = lxc_netdev_rename_by_name_in_netns(pid, ifname, newname); out1: free(physname); return err; } int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char* newname) { int index; char *physname; if (!ifname) return -EINVAL; index = if_nametoindex(ifname); if (!index) return -EINVAL; if ((physname = is_wlan(ifname))) return lxc_netdev_move_wlan(physname, ifname, pid, newname); return lxc_netdev_move_by_index(index, pid, newname); } int lxc_netdev_delete_by_index(int ifindex) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; int err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; nlmsg->nlmsghdr->nlmsg_type = RTM_DELLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) goto out; ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = ifindex; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_netdev_delete_by_name(const char *name) { int index; index = if_nametoindex(name); if (!index) return -EINVAL; return lxc_netdev_delete_by_index(index); } int lxc_netdev_rename_by_index(int ifindex, const char *newname) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; int len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; len = strlen(newname); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) goto out; ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = ifindex; if (nla_put_string(nlmsg, IFLA_IFNAME, newname)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_netdev_rename_by_name(const char *oldname, const char *newname) { int len, index; len = strlen(oldname); if (len == 1 || len >= IFNAMSIZ) return -EINVAL; index = if_nametoindex(oldname); if (!index) return -EINVAL; return lxc_netdev_rename_by_index(index, newname); } int netdev_set_flag(const char *name, int flag) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; int index, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(name); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; err = -EINVAL; index = if_nametoindex(name); if (!index) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto out; } ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = index; ifi->ifi_change |= IFF_UP; ifi->ifi_flags |= flag; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(nlmsg); nlmsg_free(answer); return err; } int netdev_get_flag(const char* name, int *flag) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; int index, len, err; if (!name) return -EINVAL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(name); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; err = -EINVAL; index = if_nametoindex(name); if (!index) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST; nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto out; } ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = index; err = netlink_transaction(&nlh, nlmsg, answer); if (err) goto out; ifi = NLMSG_DATA(answer->nlmsghdr); *flag = ifi->ifi_flags; out: netlink_close(&nlh); nlmsg_free(nlmsg); nlmsg_free(answer); return err; } /* * \brief Check a interface is up or not. * * \param name: name for the interface. * * \return int. * 0 means interface is down. * 1 means interface is up. * Others means error happened, and ret-value is the error number. */ int lxc_netdev_isup(const char* name) { int flag; int err; err = netdev_get_flag(name, &flag); if (err) goto out; if (flag & IFF_UP) return 1; return 0; out: return err; } int netdev_get_mtu(int ifindex) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; struct nlmsghdr *msg; int err, res; int recv_len = 0, answer_len; int readmore = 0; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; /* Save the answer buffer length, since it will be overwritten * on the first receive (and we might need to receive more than * once. */ answer_len = answer->nlmsghdr->nlmsg_len; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) goto out; ifi->ifi_family = AF_UNSPEC; /* Send the request for addresses, which returns all addresses * on all interfaces. */ err = netlink_send(&nlh, nlmsg); if (err < 0) goto out; do { /* Restore the answer buffer length, it might have been * overwritten by a previous receive. */ answer->nlmsghdr->nlmsg_len = answer_len; /* Get the (next) batch of reply messages */ err = netlink_rcv(&nlh, answer); if (err < 0) goto out; recv_len = err; /* Satisfy the typing for the netlink macros */ msg = answer->nlmsghdr; while (NLMSG_OK(msg, recv_len)) { /* Stop reading if we see an error message */ if (msg->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); err = errmsg->error; goto out; } /* Stop reading if we see a NLMSG_DONE message */ if (msg->nlmsg_type == NLMSG_DONE) { readmore = 0; break; } ifi = NLMSG_DATA(msg); if (ifi->ifi_index == ifindex) { struct rtattr *rta = IFLA_RTA(ifi); int attr_len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); res = 0; while(RTA_OK(rta, attr_len)) { /* Found a local address for the requested interface, * return it. */ if (rta->rta_type == IFLA_MTU) { memcpy(&res, RTA_DATA(rta), sizeof(int)); err = res; goto out; } rta = RTA_NEXT(rta, attr_len); } } /* Keep reading more data from the socket if the * last message had the NLF_F_MULTI flag set */ readmore = (msg->nlmsg_flags & NLM_F_MULTI); /* Look at the next message received in this buffer */ msg = NLMSG_NEXT(msg, recv_len); } } while (readmore); /* If we end up here, we didn't find any result, so signal an * error */ err = -1; out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_netdev_set_mtu(const char *name, int mtu) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; int index, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(name); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; err = -EINVAL; index = if_nametoindex(name); if (!index) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto out; } ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = index; if (nla_put_u32(nlmsg, IFLA_MTU, mtu)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(nlmsg); nlmsg_free(answer); return err; } int lxc_netdev_up(const char *name) { return netdev_set_flag(name, IFF_UP); } int lxc_netdev_down(const char *name) { return netdev_set_flag(name, 0); } int lxc_veth_create(const char *name1, const char *name2) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; struct rtattr *nest1, *nest2, *nest3; int len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(name1); if (len == 1 || len >= IFNAMSIZ) goto out; len = strlen(name2); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) goto out; ifi->ifi_family = AF_UNSPEC; err = -EINVAL; nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO); if (!nest1) goto out; if (nla_put_string(nlmsg, IFLA_INFO_KIND, "veth")) goto out; nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); if (!nest2) goto out; nest3 = nla_begin_nested(nlmsg, VETH_INFO_PEER); if (!nest3) goto out; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto out; } if (nla_put_string(nlmsg, IFLA_IFNAME, name2)) goto out; nla_end_nested(nlmsg, nest3); nla_end_nested(nlmsg, nest2); nla_end_nested(nlmsg, nest1); if (nla_put_string(nlmsg, IFLA_IFNAME, name1)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } /* XXX: merge with lxc_macvlan_create */ int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; struct rtattr *nest, *nest2; int lindex, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(master); if (len == 1 || len >= IFNAMSIZ) goto err3; len = strlen(name); if (len == 1 || len >= IFNAMSIZ) goto err3; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto err3; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto err2; err = -EINVAL; lindex = if_nametoindex(master); if (!lindex) goto err1; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto err1; } ifi->ifi_family = AF_UNSPEC; nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); if (!nest) goto err1; if (nla_put_string(nlmsg, IFLA_INFO_KIND, "vlan")) goto err1; nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); if (!nest2) goto err1; if (nla_put_u16(nlmsg, IFLA_VLAN_ID, vlanid)) goto err1; nla_end_nested(nlmsg, nest2); nla_end_nested(nlmsg, nest); if (nla_put_u32(nlmsg, IFLA_LINK, lindex)) goto err1; if (nla_put_string(nlmsg, IFLA_IFNAME, name)) goto err1; err = netlink_transaction(&nlh, nlmsg, answer); err1: nlmsg_free(answer); err2: nlmsg_free(nlmsg); err3: netlink_close(&nlh); return err; } int lxc_macvlan_create(const char *master, const char *name, int mode) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifinfomsg *ifi; struct rtattr *nest, *nest2; int index, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -EINVAL; len = strlen(master); if (len == 1 || len >= IFNAMSIZ) goto out; len = strlen(name); if (len == 1 || len >= IFNAMSIZ) goto out; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; err = -EINVAL; index = if_nametoindex(master); if (!index) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); if (!ifi) { err = -ENOMEM; goto out; } ifi->ifi_family = AF_UNSPEC; nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); if (!nest) goto out; if (nla_put_string(nlmsg, IFLA_INFO_KIND, "macvlan")) goto out; if (mode) { nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); if (!nest2) goto out; if (nla_put_u32(nlmsg, IFLA_MACVLAN_MODE, mode)) goto out; nla_end_nested(nlmsg, nest2); } nla_end_nested(nlmsg, nest); if (nla_put_u32(nlmsg, IFLA_LINK, index)) goto out; if (nla_put_string(nlmsg, IFLA_IFNAME, name)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } static int proc_sys_net_write(const char *path, const char *value) { int fd, err = 0; fd = open(path, O_WRONLY); if (fd < 0) return -errno; if (lxc_write_nointr(fd, value, strlen(value)) < 0) err = -errno; close(fd); return err; } static int neigh_proxy_set(const char *ifname, int family, int flag) { int ret; char path[MAXPATHLEN]; if (family != AF_INET && family != AF_INET6) return -EINVAL; ret = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/%s", family == AF_INET?"ipv4":"ipv6" , ifname, family == AF_INET?"proxy_arp":"proxy_ndp"); if (ret < 0 || ret >= MAXPATHLEN) return -E2BIG; return proc_sys_net_write(path, flag?"1":"0"); } int lxc_neigh_proxy_on(const char *name, int family) { return neigh_proxy_set(name, family, 1); } int lxc_neigh_proxy_off(const char *name, int family) { return neigh_proxy_set(name, family, 0); } int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr) { unsigned char *data; char c; int i = 0; unsigned val; sockaddr->sa_family = ARPHRD_ETHER; data = (unsigned char *)sockaddr->sa_data; while ((*macaddr != '\0') && (i < ETH_ALEN)) { c = *macaddr++; if (isdigit(c)) val = c - '0'; else if (c >= 'a' && c <= 'f') val = c - 'a' + 10; else if (c >= 'A' && c <= 'F') val = c - 'A' + 10; else { return -EINVAL; } val <<= 4; c = *macaddr; if (isdigit(c)) val |= c - '0'; else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10; else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10; else if (c == ':' || c == 0) val >>= 4; else { return -EINVAL; } if (c != 0) macaddr++; *data++ = (unsigned char) (val & 0377); i++; if (*macaddr == ':') macaddr++; } return 0; } static int ip_addr_add(int family, int ifindex, void *addr, void *bcast, void *acast, int prefix) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifaddrmsg *ifa; int addrlen; int err; addrlen = family == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWADDR; ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); if (!ifa) goto out; ifa->ifa_prefixlen = prefix; ifa->ifa_index = ifindex; ifa->ifa_family = family; ifa->ifa_scope = 0; err = -EINVAL; if (nla_put_buffer(nlmsg, IFA_LOCAL, addr, addrlen)) goto out; if (nla_put_buffer(nlmsg, IFA_ADDRESS, addr, addrlen)) goto out; if (nla_put_buffer(nlmsg, IFA_BROADCAST, bcast, addrlen)) goto out; /* TODO : multicast, anycast with ipv6 */ err = -EPROTONOSUPPORT; if (family == AF_INET6 && (memcmp(bcast, &in6addr_any, sizeof(in6addr_any)) || memcmp(acast, &in6addr_any, sizeof(in6addr_any)))) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, struct in6_addr *mcast, struct in6_addr *acast, int prefix) { return ip_addr_add(AF_INET6, ifindex, addr, mcast, acast, prefix); } int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, struct in_addr *bcast, int prefix) { return ip_addr_add(AF_INET, ifindex, addr, bcast, NULL, prefix); } /* Find an IFA_LOCAL (or IFA_ADDRESS if not IFA_LOCAL is present) * address from the given RTM_NEWADDR message. Allocates memory for the * address and stores that pointer in *res (so res should be an * in_addr** or in6_addr**). */ static int ifa_get_local_ip(int family, struct nlmsghdr *msg, void** res) { struct ifaddrmsg *ifa = NLMSG_DATA(msg); struct rtattr *rta = IFA_RTA(ifa); int attr_len = NLMSG_PAYLOAD(msg, sizeof(struct ifaddrmsg)); int addrlen; if (ifa->ifa_family != family) return 0; addrlen = family == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); /* Loop over the rtattr's in this message */ while(RTA_OK(rta, attr_len)) { /* Found a local address for the requested interface, * return it. */ if (rta->rta_type == IFA_LOCAL || rta->rta_type == IFA_ADDRESS) { /* Sanity check. The family check above should * make sure the address length is correct, but * check here just in case */ if (RTA_PAYLOAD(rta) != addrlen) return -1; /* We might have found an IFA_ADDRESS before, * which we now overwrite with an IFA_LOCAL. */ if (!*res) { *res = malloc(addrlen); if (!*res) return -1; } memcpy(*res, RTA_DATA(rta), addrlen); if (rta->rta_type == IFA_LOCAL) break; } rta = RTA_NEXT(rta, attr_len); } return 0; } static int ip_addr_get(int family, int ifindex, void **res) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct ifaddrmsg *ifa; struct nlmsghdr *msg; int err; int recv_len = 0, answer_len; int readmore = 0; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; /* Save the answer buffer length, since it will be overwritten * on the first receive (and we might need to receive more than * once. */ answer_len = answer->nlmsghdr->nlmsg_len; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT; nlmsg->nlmsghdr->nlmsg_type = RTM_GETADDR; ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); if (!ifa) goto out; ifa->ifa_family = family; /* Send the request for addresses, which returns all addresses * on all interfaces. */ err = netlink_send(&nlh, nlmsg); if (err < 0) goto out; do { /* Restore the answer buffer length, it might have been * overwritten by a previous receive. */ answer->nlmsghdr->nlmsg_len = answer_len; /* Get the (next) batch of reply messages */ err = netlink_rcv(&nlh, answer); if (err < 0) goto out; recv_len = err; err = 0; /* Satisfy the typing for the netlink macros */ msg = answer->nlmsghdr; while (NLMSG_OK(msg, recv_len)) { /* Stop reading if we see an error message */ if (msg->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); err = errmsg->error; goto out; } /* Stop reading if we see a NLMSG_DONE message */ if (msg->nlmsg_type == NLMSG_DONE) { readmore = 0; break; } if (msg->nlmsg_type != RTM_NEWADDR) { err = -1; goto out; } ifa = (struct ifaddrmsg *)NLMSG_DATA(msg); if (ifa->ifa_index == ifindex) { if (ifa_get_local_ip(family, msg, res) < 0) { err = -1; goto out; } /* Found a result, stop searching */ if (*res) goto out; } /* Keep reading more data from the socket if the * last message had the NLF_F_MULTI flag set */ readmore = (msg->nlmsg_flags & NLM_F_MULTI); /* Look at the next message received in this buffer */ msg = NLMSG_NEXT(msg, recv_len); } } while (readmore); /* If we end up here, we didn't find any result, so signal an * error */ err = -1; out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res) { return ip_addr_get(AF_INET6, ifindex, (void**)res); } int lxc_ipv4_addr_get(int ifindex, struct in_addr** res) { return ip_addr_get(AF_INET, ifindex, (void**)res); } static int ip_gateway_add(int family, int ifindex, void *gw) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct rtmsg *rt; int addrlen; int err; addrlen = family == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); if (!rt) goto out; rt->rtm_family = family; rt->rtm_table = RT_TABLE_MAIN; rt->rtm_scope = RT_SCOPE_UNIVERSE; rt->rtm_protocol = RTPROT_BOOT; rt->rtm_type = RTN_UNICAST; /* "default" destination */ rt->rtm_dst_len = 0; err = -EINVAL; if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) goto out; /* Adding the interface index enables the use of link-local * addresses for the gateway */ if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw) { return ip_gateway_add(AF_INET, ifindex, gw); } int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw) { return ip_gateway_add(AF_INET6, ifindex, gw); } static int ip_route_dest_add(int family, int ifindex, void *dest) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; struct rtmsg *rt; int addrlen; int err; addrlen = family == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) return err; err = -ENOMEM; nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); if (!nlmsg) goto out; answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); if (!rt) goto out; rt->rtm_family = family; rt->rtm_table = RT_TABLE_MAIN; rt->rtm_scope = RT_SCOPE_LINK; rt->rtm_protocol = RTPROT_BOOT; rt->rtm_type = RTN_UNICAST; rt->rtm_dst_len = addrlen*8; err = -EINVAL; if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen)) goto out; if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) goto out; err = netlink_transaction(&nlh, nlmsg, answer); out: netlink_close(&nlh); nlmsg_free(answer); nlmsg_free(nlmsg); return err; } int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest) { return ip_route_dest_add(AF_INET, ifindex, dest); } int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest) { return ip_route_dest_add(AF_INET6, ifindex, dest); } bool is_ovs_bridge(const char *bridge) { char brdirname[22 + IFNAMSIZ + 1] = {0}; struct stat sb; snprintf(brdirname, 22 +IFNAMSIZ + 1, "/sys/class/net/%s/bridge", bridge); if (stat(brdirname, &sb) == -1 && errno == ENOENT) return true; return false; } struct ovs_veth_args { const char *bridge; const char *nic; }; /* Called from a background thread - when nic goes away, remove it from the * bridge. */ static int lxc_ovs_delete_port_exec(void *data) { struct ovs_veth_args *args = data; execlp("ovs-vsctl", "ovs-vsctl", "del-port", args->bridge, args->nic, (char *)NULL); return -1; } int lxc_ovs_delete_port(const char *bridge, const char *nic) { int ret; char cmd_output[MAXPATHLEN]; struct ovs_veth_args args; args.bridge = bridge; args.nic = nic; ret = run_command(cmd_output, sizeof(cmd_output), lxc_ovs_delete_port_exec, (void *)&args); if (ret < 0) { ERROR("Failed to delete \"%s\" from openvswitch bridge \"%s\": " "%s", bridge, nic, cmd_output); return -1; } return 0; } static int lxc_ovs_attach_bridge_exec(void *data) { struct ovs_veth_args *args = data; execlp("ovs-vsctl", "ovs-vsctl", "add-port", args->bridge, args->nic, (char *)NULL); return -1; } static int lxc_ovs_attach_bridge(const char *bridge, const char *nic) { int ret; char cmd_output[MAXPATHLEN]; struct ovs_veth_args args; args.bridge = bridge; args.nic = nic; ret = run_command(cmd_output, sizeof(cmd_output), lxc_ovs_attach_bridge_exec, (void *)&args); if (ret < 0) { ERROR("Failed to attach \"%s\" to openvswitch bridge \"%s\": %s", bridge, nic, cmd_output); return -1; } return 0; } int lxc_bridge_attach(const char *bridge, const char *ifname) { int err, fd, index; size_t retlen; struct ifreq ifr; if (strlen(ifname) >= IFNAMSIZ) return -EINVAL; index = if_nametoindex(ifname); if (!index) return -EINVAL; if (is_ovs_bridge(bridge)) return lxc_ovs_attach_bridge(bridge, ifname); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) return -errno; retlen = strlcpy(ifr.ifr_name, bridge, IFNAMSIZ); if (retlen >= IFNAMSIZ) { close(fd); return -E2BIG; } ifr.ifr_ifindex = index; err = ioctl(fd, SIOCBRADDIF, &ifr); close(fd); if (err) err = -errno; return err; } static const char* const lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { [LXC_NET_EMPTY] = "empty", [LXC_NET_VETH] = "veth", [LXC_NET_MACVLAN] = "macvlan", [LXC_NET_PHYS] = "phys", [LXC_NET_VLAN] = "vlan", [LXC_NET_NONE] = "none", }; const char *lxc_net_type_to_str(int type) { if (type < 0 || type > LXC_NET_MAXCONFTYPE) return NULL; return lxc_network_types[type]; } static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char *lxc_mkifname(char *template) { int ret; unsigned int seed; FILE *urandom; struct ifaddrs *ifa, *ifaddr; char name[IFNAMSIZ]; bool exists = false; size_t i = 0; if (strlen(template) >= IFNAMSIZ) return NULL; /* Get all the network interfaces. */ ret = getifaddrs(&ifaddr); if (ret < 0) { ERROR("%s - Failed to get network interfaces", strerror(errno)); return NULL; } /* Initialize the random number generator. */ urandom = fopen("/dev/urandom", "r"); if (urandom != NULL) { if (fread(&seed, sizeof(seed), 1, urandom) <= 0) seed = time(0); fclose(urandom); } else { seed = time(0); } #ifndef HAVE_RAND_R srand(seed); #endif /* Generate random names until we find one that doesn't exist. */ while (true) { name[0] = '\0'; (void)strlcpy(name, template, IFNAMSIZ); exists = false; for (i = 0; i < strlen(name); i++) { if (name[i] == 'X') { #ifdef HAVE_RAND_R name[i] = padchar[rand_r(&seed) % (strlen(padchar) - 1)]; #else name[i] = padchar[rand() % (strlen(padchar) - 1)]; #endif } } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (!strcmp(ifa->ifa_name, name)) { exists = true; break; } } if (!exists) break; } freeifaddrs(ifaddr); (void)strlcpy(template, name, strlen(template) + 1); return template; } int setup_private_host_hw_addr(char *veth1) { struct ifreq ifr; int err; int sockfd; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) return -errno; err = snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1); if (err < 0 || (size_t)err >= IFNAMSIZ) { close(sockfd); return -E2BIG; } err = ioctl(sockfd, SIOCGIFHWADDR, &ifr); if (err < 0) { close(sockfd); return -errno; } ifr.ifr_hwaddr.sa_data[0] = 0xfe; err = ioctl(sockfd, SIOCSIFHWADDR, &ifr); close(sockfd); if (err < 0) return -errno; return 0; } int lxc_find_gateway_addresses(struct lxc_handler *handler) { struct lxc_list *network = &handler->conf->network; struct lxc_list *iterator; struct lxc_netdev *netdev; int link_index; lxc_list_for_each(iterator, network) { netdev = iterator->elem; if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto) continue; if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) { ERROR("Automatic gateway detection is only supported " "for veth and macvlan"); return -1; } if (netdev->link[0] == '\0') { ERROR("Automatic gateway detection needs a link interface"); return -1; } link_index = if_nametoindex(netdev->link); if (!link_index) return -EINVAL; if (netdev->ipv4_gateway_auto) { if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) { ERROR("Failed to automatically find ipv4 gateway " "address from link interface \"%s\"", netdev->link); return -1; } } if (netdev->ipv6_gateway_auto) { if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) { ERROR("Failed to automatically find ipv6 gateway " "address from link interface \"%s\"", netdev->link); return -1; } } } return 0; } #define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic" static int lxc_create_network_unpriv_exec(const char *lxcpath, const char *lxcname, struct lxc_netdev *netdev, pid_t pid) { int ret; pid_t child; int bytes, pipefd[2]; char *token, *saveptr = NULL; char netdev_link[IFNAMSIZ]; char buffer[MAXPATHLEN] = {0}; size_t retlen; if (netdev->type != LXC_NET_VETH) { ERROR("Network type %d not support for unprivileged use", netdev->type); return -1; } ret = pipe(pipefd); if (ret < 0) { SYSERROR("Failed to create pipe"); return -1; } child = fork(); if (child < 0) { SYSERROR("Failed to create new process"); close(pipefd[0]); close(pipefd[1]); return -1; } if (child == 0) { int ret; size_t retlen; char pidstr[LXC_NUMSTRLEN64]; close(pipefd[0]); ret = dup2(pipefd[1], STDOUT_FILENO); if (ret >= 0) ret = dup2(pipefd[1], STDERR_FILENO); close(pipefd[1]); if (ret < 0) { SYSERROR("Failed to duplicate std{err,out} file descriptor"); _exit(EXIT_FAILURE); } if (netdev->link[0] != '\0') retlen = strlcpy(netdev_link, netdev->link, IFNAMSIZ); else retlen = strlcpy(netdev_link, "none", IFNAMSIZ); if (retlen >= IFNAMSIZ) { SYSERROR("Invalid network device name"); _exit(EXIT_FAILURE); } ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid); if (ret < 0 || ret >= LXC_NUMSTRLEN64) _exit(EXIT_FAILURE); pidstr[LXC_NUMSTRLEN64 - 1] = '\0'; INFO("Execing lxc-user-nic create %s %s %s veth %s %s", lxcpath, lxcname, pidstr, netdev_link, netdev->name[0] != '\0' ? netdev->name : "(null)"); if (netdev->name[0] != '\0') execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create", lxcpath, lxcname, pidstr, "veth", netdev_link, netdev->name, (char *)NULL); else execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create", lxcpath, lxcname, pidstr, "veth", netdev_link, (char *)NULL); SYSERROR("Failed to execute lxc-user-nic"); _exit(EXIT_FAILURE); } /* close the write-end of the pipe */ close(pipefd[1]); bytes = lxc_read_nointr(pipefd[0], &buffer, MAXPATHLEN); if (bytes < 0) { SYSERROR("Failed to read from pipe file descriptor"); close(pipefd[0]); } else { buffer[bytes - 1] = '\0'; } ret = wait_for_pid(child); close(pipefd[0]); if (ret != 0 || bytes < 0) { ERROR("lxc-user-nic failed to configure requested network: %s", buffer[0] != '\0' ? buffer : "(null)"); return -1; } TRACE("Received output \"%s\" from lxc-user-nic", buffer); /* netdev->name */ token = strtok_r(buffer, ":", &saveptr); if (!token) { ERROR("Failed to parse lxc-user-nic output"); return -1; } memset(netdev->name, 0, IFNAMSIZ); memcpy(netdev->name, token, IFNAMSIZ - 1); /* netdev->ifindex */ token = strtok_r(NULL, ":", &saveptr); if (!token) { ERROR("Failed to parse lxc-user-nic output"); return -1; } ret = lxc_safe_int(token, &netdev->ifindex); if (ret < 0) { ERROR("%s - Failed to convert string \"%s\" to integer", strerror(-ret), token); return -1; } /* netdev->priv.veth_attr.veth1 */ token = strtok_r(NULL, ":", &saveptr); if (!token) { ERROR("Failed to parse lxc-user-nic output"); return -1; } retlen = strlcpy(netdev->priv.veth_attr.veth1, token, IFNAMSIZ); if (retlen >= IFNAMSIZ) { ERROR("Host side veth device name returned by lxc-user-nic is " "too long"); return -E2BIG; } /* netdev->priv.veth_attr.ifindex */ token = strtok_r(NULL, ":", &saveptr); if (!token) { ERROR("Failed to parse lxc-user-nic output"); return -1; } ret = lxc_safe_int(token, &netdev->priv.veth_attr.ifindex); if (ret < 0) { ERROR("%s - Failed to convert string \"%s\" to integer", strerror(-ret), token); return -1; } return 0; } static int lxc_delete_network_unpriv_exec(const char *lxcpath, const char *lxcname, struct lxc_netdev *netdev, const char *netns_path) { int bytes, ret; pid_t child; int pipefd[2]; char buffer[MAXPATHLEN] = {0}; if (netdev->type != LXC_NET_VETH) { ERROR("Network type %d not support for unprivileged use", netdev->type); return -1; } ret = pipe(pipefd); if (ret < 0) { SYSERROR("Failed to create pipe"); return -1; } child = fork(); if (child < 0) { SYSERROR("Failed to create new process"); close(pipefd[0]); close(pipefd[1]); return -1; } if (child == 0) { char *hostveth; int ret; close(pipefd[0]); ret = dup2(pipefd[1], STDOUT_FILENO); if (ret >= 0) ret = dup2(pipefd[1], STDERR_FILENO); close(pipefd[1]); if (ret < 0) { SYSERROR("Failed to duplicate std{err,out} file descriptor"); _exit(EXIT_FAILURE); } if (netdev->priv.veth_attr.pair[0] != '\0') hostveth = netdev->priv.veth_attr.pair; else hostveth = netdev->priv.veth_attr.veth1; if (hostveth[0] == '\0') { SYSERROR("Host side veth device name is missing"); _exit(EXIT_FAILURE); } if (netdev->link[0] == '\0') { SYSERROR("Network link for network device \"%s\" is " "missing", netdev->priv.veth_attr.veth1); _exit(EXIT_FAILURE); } INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath, lxcname, netns_path, netdev->link, hostveth); execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath, lxcname, netns_path, "veth", netdev->link, hostveth, (char *)NULL); SYSERROR("Failed to exec lxc-user-nic."); _exit(EXIT_FAILURE); } close(pipefd[1]); bytes = lxc_read_nointr(pipefd[0], &buffer, MAXPATHLEN); if (bytes < 0) { SYSERROR("Failed to read from pipe file descriptor."); close(pipefd[0]); } else { buffer[bytes - 1] = '\0'; } ret = wait_for_pid(child); close(pipefd[0]); if (ret != 0 || bytes < 0) { ERROR("lxc-user-nic failed to delete requested network: %s", buffer[0] != '\0' ? buffer : "(null)"); return -1; } return 0; } bool lxc_delete_network_unpriv(struct lxc_handler *handler) { int ret; struct lxc_list *iterator; struct lxc_list *network = &handler->conf->network; /* strlen("/proc/") = 6 * + * LXC_NUMSTRLEN64 * + * strlen("/fd/") = 4 * + * LXC_NUMSTRLEN64 * + * \0 */ char netns_path[6 + LXC_NUMSTRLEN64 + 4 + LXC_NUMSTRLEN64 + 1]; *netns_path = '\0'; if (handler->netnsfd < 0) { DEBUG("Cannot not guarantee safe deletion of network devices. " "Manual cleanup maybe needed"); return false; } ret = snprintf(netns_path, sizeof(netns_path), "/proc/%d/fd/%d", lxc_raw_getpid(), handler->netnsfd); if (ret < 0 || ret >= sizeof(netns_path)) return false; lxc_list_for_each(iterator, network) { char *hostveth = NULL; struct lxc_netdev *netdev = iterator->elem; /* We can only delete devices whose ifindex we have. If we don't * have the index it means that we didn't create it. */ if (!netdev->ifindex) continue; if (netdev->type == LXC_NET_PHYS) { ret = lxc_netdev_rename_by_index(netdev->ifindex, netdev->link); if (ret < 0) WARN("Failed to rename interface with index %d " "to its initial name \"%s\"", netdev->ifindex, netdev->link); else TRACE("Renamed interface with index %d to its " "initial name \"%s\"", netdev->ifindex, netdev->link); goto clear_ifindices; } ret = netdev_deconf[netdev->type](handler, netdev); if (ret < 0) WARN("Failed to deconfigure network device"); if (netdev->type != LXC_NET_VETH) goto clear_ifindices; if (netdev->link[0] == '\0' || !is_ovs_bridge(netdev->link)) goto clear_ifindices; if (netdev->priv.veth_attr.pair[0] != '\0') hostveth = netdev->priv.veth_attr.pair; else hostveth = netdev->priv.veth_attr.veth1; if (hostveth[0] == '\0') goto clear_ifindices; ret = lxc_delete_network_unpriv_exec(handler->lxcpath, handler->name, netdev, netns_path); if (ret < 0) { WARN("Failed to remove port \"%s\" from openvswitch " "bridge \"%s\"", hostveth, netdev->link); goto clear_ifindices; } INFO("Removed interface \"%s\" from \"%s\"", hostveth, netdev->link); clear_ifindices: /* We need to clear any ifindeces we recorded so liblxc won't * have cached stale data which would cause it to fail on reboot * we're we don't re-read the on-disk config file. */ netdev->ifindex = 0; if (netdev->type == LXC_NET_PHYS) { netdev->priv.phys_attr.ifindex = 0; } else if (netdev->type == LXC_NET_VETH) { netdev->priv.veth_attr.veth1[0] = '\0'; netdev->priv.veth_attr.ifindex = 0; } } return true; } int lxc_create_network_priv(struct lxc_handler *handler) { struct lxc_list *iterator; struct lxc_list *network = &handler->conf->network; if (!handler->am_root) return 0; lxc_list_for_each(iterator, network) { struct lxc_netdev *netdev = iterator->elem; if (netdev->type < 0 || netdev->type > LXC_NET_MAXCONFTYPE) { ERROR("Invalid network configuration type %d", netdev->type); return -1; } if (netdev_conf[netdev->type](handler, netdev)) { ERROR("Failed to create network device"); return -1; } } return 0; } int lxc_network_move_created_netdev_priv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid) { int ret; char ifname[IFNAMSIZ]; struct lxc_list *iterator; if (am_guest_unpriv()) return 0; lxc_list_for_each(iterator, network) { struct lxc_netdev *netdev = iterator->elem; if (!netdev->ifindex) continue; /* retrieve the name of the interface */ if (!if_indextoname(netdev->ifindex, ifname)) { ERROR("No interface corresponding to ifindex \"%d\"", netdev->ifindex); return -1; } ret = lxc_netdev_move_by_name(ifname, pid, NULL); if (ret) { ERROR("Failed to move network device \"%s\" to " "network namespace %d: %s", ifname, pid, strerror(-ret)); return -1; } DEBUG("Moved network device \"%s\"/\"%s\" to network namespace " "of %d", ifname, netdev->name[0] != '\0' ? netdev->name : "(null)", pid); } return 0; } int lxc_create_network_unpriv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid) { struct lxc_list *iterator; if (!am_guest_unpriv()) return 0; lxc_list_for_each(iterator, network) { struct lxc_netdev *netdev = iterator->elem; if (netdev->type == LXC_NET_EMPTY) continue; if (netdev->type == LXC_NET_NONE) continue; if (netdev->type != LXC_NET_VETH) { ERROR("Networks of type %s are not supported by " "unprivileged containers", lxc_net_type_to_str(netdev->type)); return -1; } if (netdev->mtu) INFO("mtu ignored due to insufficient privilege"); if (lxc_create_network_unpriv_exec(lxcpath, lxcname, netdev, pid)) return -1; } return 0; } bool lxc_delete_network_priv(struct lxc_handler *handler) { int ret; struct lxc_list *iterator; struct lxc_list *network = &handler->conf->network; lxc_list_for_each(iterator, network) { char *hostveth = NULL; struct lxc_netdev *netdev = iterator->elem; /* We can only delete devices whose ifindex we have. If we don't * have the index it means that we didn't create it. */ if (!netdev->ifindex) continue; if (netdev->type == LXC_NET_PHYS) { ret = lxc_netdev_rename_by_index(netdev->ifindex, netdev->link); if (ret < 0) WARN("Failed to rename interface with index %d " "from \"%s\" to its initial name \"%s\"", netdev->ifindex, netdev->name, netdev->link); else TRACE("Renamed interface with index %d from " "\"%s\" to its initial name \"%s\"", netdev->ifindex, netdev->name, netdev->link); goto clear_ifindices; } ret = netdev_deconf[netdev->type](handler, netdev); if (ret < 0) WARN("Failed to deconfigure network device"); /* Recent kernels remove the virtual interfaces when the network * namespace is destroyed but in case we did not move the * interface to the network namespace, we have to destroy it. */ ret = lxc_netdev_delete_by_index(netdev->ifindex); if (-ret == ENODEV) { INFO("Interface \"%s\" with index %d already " "deleted or existing in different network " "namespace", netdev->name[0] != '\0' ? netdev->name : "(null)", netdev->ifindex); } else if (ret < 0) { WARN("Failed to remove interface \"%s\" with " "index %d: %s", netdev->name[0] != '\0' ? netdev->name : "(null)", netdev->ifindex, strerror(-ret)); goto clear_ifindices; } INFO("Removed interface \"%s\" with index %d", netdev->name[0] != '\0' ? netdev->name : "(null)", netdev->ifindex); if (netdev->type != LXC_NET_VETH) goto clear_ifindices; /* Explicitly delete host veth device to prevent lingering * devices. We had issues in LXD around this. */ if (netdev->priv.veth_attr.pair[0] != '\0') hostveth = netdev->priv.veth_attr.pair; else hostveth = netdev->priv.veth_attr.veth1; if (hostveth[0] == '\0') goto clear_ifindices; ret = lxc_netdev_delete_by_name(hostveth); if (ret < 0) { WARN("Failed to remove interface \"%s\" from \"%s\": %s", hostveth, netdev->link, strerror(-ret)); goto clear_ifindices; } INFO("Removed interface \"%s\" from \"%s\"", hostveth, netdev->link); if (netdev->link[0] == '\0' || !is_ovs_bridge(netdev->link)) { netdev->priv.veth_attr.veth1[0] = '\0'; netdev->ifindex = 0; netdev->priv.veth_attr.ifindex = 0; goto clear_ifindices; } /* Delete the openvswitch port. */ ret = lxc_ovs_delete_port(netdev->link, hostveth); if (ret < 0) WARN("Failed to remove port \"%s\" from openvswitch " "bridge \"%s\"", hostveth, netdev->link); else INFO("Removed port \"%s\" from openvswitch bridge \"%s\"", hostveth, netdev->link); clear_ifindices: /* We need to clear any ifindeces we recorded so liblxc won't * have cached stale data which would cause it to fail on reboot * we're we don't re-read the on-disk config file. */ netdev->ifindex = 0; if (netdev->type == LXC_NET_PHYS) { netdev->priv.phys_attr.ifindex = 0; } else if (netdev->type == LXC_NET_VETH) { netdev->priv.veth_attr.veth1[0] = '\0'; netdev->priv.veth_attr.ifindex = 0; } } return true; } int lxc_requests_empty_network(struct lxc_handler *handler) { struct lxc_list *network = &handler->conf->network; struct lxc_list *iterator; bool found_none = false, found_nic = false; if (lxc_list_empty(network)) return 0; lxc_list_for_each(iterator, network) { struct lxc_netdev *netdev = iterator->elem; if (netdev->type == LXC_NET_NONE) found_none = true; else found_nic = true; } if (found_none && !found_nic) return 1; return 0; } /* try to move physical nics to the init netns */ int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler) { int ret; int oldfd; char ifname[IFNAMSIZ]; struct lxc_list *iterator; int netnsfd = handler->netnsfd; struct lxc_conf *conf = handler->conf; /* We need CAP_NET_ADMIN in the parent namespace in order to setns() to * the parent network namespace. We won't have this capability if we are * unprivileged. */ if (!handler->am_root) return 0; TRACE("Moving physical network devices back to parent network namespace"); oldfd = lxc_preserve_ns(lxc_raw_getpid(), "net"); if (oldfd < 0) { SYSERROR("Failed to preserve network namespace"); return -1; } ret = setns(netnsfd, CLONE_NEWNET); if (ret < 0) { SYSERROR("Failed to enter network namespace"); close(oldfd); return -1; } lxc_list_for_each(iterator, &conf->network) { struct lxc_netdev *netdev = iterator->elem; if (netdev->type != LXC_NET_PHYS) continue; /* Retrieve the name of the interface in the container's network * namespace. */ if (!if_indextoname(netdev->ifindex, ifname)) { WARN("No interface corresponding to ifindex %d", netdev->ifindex); continue; } ret = lxc_netdev_move_by_name(ifname, 1, netdev->link); if (ret < 0) WARN("Error moving network device \"%s\" back to " "network namespace", ifname); else TRACE("Moved network device \"%s\" back to network " "namespace", ifname); } ret = setns(oldfd, CLONE_NEWNET); close(oldfd); if (ret < 0) { SYSERROR("Failed to enter network namespace"); return -1; } return 0; } static int setup_hw_addr(char *hwaddr, const char *ifname) { struct sockaddr sockaddr; struct ifreq ifr; int ret, fd, saved_errno; ret = lxc_convert_mac(hwaddr, &sockaddr); if (ret) { ERROR("Mac address \"%s\" conversion failed: %s", hwaddr, strerror(-ret)); return -1; } memcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = '\0'; memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr)); fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; ret = ioctl(fd, SIOCSIFHWADDR, &ifr); saved_errno = errno; close(fd); if (ret) ERROR("Failed to perform ioctl: %s", strerror(saved_errno)); DEBUG("Mac address \"%s\" on \"%s\" has been setup", hwaddr, ifr.ifr_name); return ret; } static int setup_ipv4_addr(struct lxc_list *ip, int ifindex) { struct lxc_list *iterator; int err; lxc_list_for_each(iterator, ip) { struct lxc_inetdev *inetdev = iterator->elem; err = lxc_ipv4_addr_add(ifindex, &inetdev->addr, &inetdev->bcast, inetdev->prefix); if (err) { ERROR("Failed to setup ipv4 address for network device " "with eifindex %d: %s", ifindex, strerror(-err)); return -1; } } return 0; } static int setup_ipv6_addr(struct lxc_list *ip, int ifindex) { struct lxc_list *iterator; int err; lxc_list_for_each(iterator, ip) { struct lxc_inet6dev *inet6dev = iterator->elem; err = lxc_ipv6_addr_add(ifindex, &inet6dev->addr, &inet6dev->mcast, &inet6dev->acast, inet6dev->prefix); if (err) { ERROR("Failed to setup ipv6 address for network device " "with eifindex %d: %s", ifindex, strerror(-err)); return -1; } } return 0; } static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) { char ifname[IFNAMSIZ]; int err; const char *net_type_name; char *current_ifname = ifname; /* empty network namespace */ if (!netdev->ifindex) { if (netdev->flags & IFF_UP) { err = lxc_netdev_up("lo"); if (err) { ERROR("Failed to set the loopback network " "device up: %s", strerror(-err)); return -1; } } if (netdev->type == LXC_NET_EMPTY) return 0; if (netdev->type == LXC_NET_NONE) return 0; if (netdev->type != LXC_NET_VETH) { net_type_name = lxc_net_type_to_str(netdev->type); ERROR("%s networks are not supported for containers " "not setup up by privileged users", net_type_name); return -1; } netdev->ifindex = if_nametoindex(netdev->name); } /* get the new ifindex in case of physical netdev */ if (netdev->type == LXC_NET_PHYS) { netdev->ifindex = if_nametoindex(netdev->link); if (!netdev->ifindex) { ERROR("Failed to get ifindex for network device \"%s\"", netdev->link); return -1; } } /* retrieve the name of the interface */ if (!if_indextoname(netdev->ifindex, current_ifname)) { ERROR("Failed get name for network device with ifindex %d", netdev->ifindex); return -1; } /* Default: let the system to choose one interface name. * When the IFLA_IFNAME attribute is passed something like "%d" * netlink will replace the format specifier with an appropriate index. */ if (netdev->name[0] == '\0') { if (netdev->type == LXC_NET_PHYS) (void)strlcpy(netdev->name, netdev->link, IFNAMSIZ); else (void)strlcpy(netdev->name, "eth%d", IFNAMSIZ); } /* rename the interface name */ if (strcmp(ifname, netdev->name) != 0) { err = lxc_netdev_rename_by_name(ifname, netdev->name); if (err) { ERROR("Failed to rename network device \"%s\" to " "\"%s\": %s", ifname, netdev->name, strerror(-err)); return -1; } } /* Re-read the name of the interface because its name has changed * and would be automatically allocated by the system */ if (!if_indextoname(netdev->ifindex, current_ifname)) { ERROR("Failed get name for network device with ifindex %d", netdev->ifindex); return -1; } /* Now update the recorded name of the network device to reflect the * name of the network device in the child's network namespace. We will * later on send this information back to the parent. */ (void)strlcpy(netdev->name, current_ifname, IFNAMSIZ); /* set a mac address */ if (netdev->hwaddr) { if (setup_hw_addr(netdev->hwaddr, current_ifname)) { ERROR("Failed to setup hw address for network device \"%s\"", current_ifname); return -1; } } /* setup ipv4 addresses on the interface */ if (setup_ipv4_addr(&netdev->ipv4, netdev->ifindex)) { ERROR("Failed to setup ip addresses for network device \"%s\"", ifname); return -1; } /* setup ipv6 addresses on the interface */ if (setup_ipv6_addr(&netdev->ipv6, netdev->ifindex)) { ERROR("Failed to setup ipv6 addresses for network device \"%s\"", ifname); return -1; } /* set the network device up */ if (netdev->flags & IFF_UP) { int err; err = lxc_netdev_up(current_ifname); if (err) { ERROR("Failed to set network device \"%s\" up: %s", current_ifname, strerror(-err)); return -1; } /* the network is up, make the loopback up too */ err = lxc_netdev_up("lo"); if (err) { ERROR("Failed to set the loopback network device up: %s", strerror(-err)); return -1; } } /* We can only set up the default routes after bringing * up the interface, sine bringing up the interface adds * the link-local routes and we can't add a default * route if the gateway is not reachable. */ /* setup ipv4 gateway on the interface */ if (netdev->ipv4_gateway) { if (!(netdev->flags & IFF_UP)) { ERROR("Cannot add ipv4 gateway for network device " "\"%s\" when not bringing up the interface", ifname); return -1; } if (lxc_list_empty(&netdev->ipv4)) { ERROR("Cannot add ipv4 gateway for network device " "\"%s\" when not assigning an address", ifname); return -1; } err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); if (err) { err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway); if (err) { ERROR("Failed to add ipv4 dest for network " "device \"%s\": %s", ifname, strerror(-err)); } err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); if (err) { ERROR("Failed to setup ipv4 gateway for " "network device \"%s\": %s", ifname, strerror(-err)); if (netdev->ipv4_gateway_auto) { char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); ERROR("Fried to set autodetected ipv4 gateway \"%s\"", buf); } return -1; } } } /* setup ipv6 gateway on the interface */ if (netdev->ipv6_gateway) { if (!(netdev->flags & IFF_UP)) { ERROR("Cannot add ipv6 gateway for network device " "\"%s\" when not bringing up the interface", ifname); return -1; } if (lxc_list_empty(&netdev->ipv6) && !IN6_IS_ADDR_LINKLOCAL(netdev->ipv6_gateway)) { ERROR("Cannot add ipv6 gateway for network device " "\"%s\" when not assigning an address", ifname); return -1; } err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); if (err) { err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway); if (err) { ERROR("Failed to add ipv6 dest for network " "device \"%s\": %s", ifname, strerror(-err)); } err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); if (err) { ERROR("Failed to setup ipv6 gateway for " "network device \"%s\": %s", ifname, strerror(-err)); if (netdev->ipv6_gateway_auto) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); ERROR("Tried to set autodetected ipv6 " "gateway for network device " "\"%s\"", buf); } return -1; } } } DEBUG("Network device \"%s\" has been setup", current_ifname); return 0; } int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, struct lxc_list *network) { struct lxc_list *iterator; struct lxc_netdev *netdev; lxc_list_for_each(iterator, network) { netdev = iterator->elem; /* REMOVE in LXC 3.0 */ if (netdev->idx < 0) { ERROR("WARNING: using \"lxc.network.*\" keys to define " "networks is DEPRECATED, please switch to using " "\"lxc.net.[i].* keys\""); } if (lxc_setup_netdev_in_child_namespaces(netdev)) { ERROR("failed to setup netdev"); return -1; } } if (!lxc_list_empty(network)) INFO("network has been setup"); return 0; } int lxc_network_send_veth_names_to_child(struct lxc_handler *handler) { struct lxc_list *iterator; struct lxc_list *network = &handler->conf->network; int data_sock = handler->data_sock[0]; if (handler->am_root) return 0; lxc_list_for_each(iterator, network) { int ret; struct lxc_netdev *netdev = iterator->elem; if (netdev->type != LXC_NET_VETH) continue; ret = send(data_sock, netdev->name, IFNAMSIZ, 0); if (ret < 0) return -1; TRACE("Sent network device name \"%s\" to child", netdev->name); } return 0; } int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler) { struct lxc_list *iterator; struct lxc_list *network = &handler->conf->network; int data_sock = handler->data_sock[1]; if (handler->am_root) return 0; lxc_list_for_each(iterator, network) { int ret; struct lxc_netdev *netdev = iterator->elem; if (netdev->type != LXC_NET_VETH) continue; ret = recv(data_sock, netdev->name, IFNAMSIZ, 0); if (ret < 0) return -1; TRACE("Received network device name \"%s\" from parent", netdev->name); } return 0; } int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler) { struct lxc_list *iterator, *network; int data_sock = handler->data_sock[0]; if (!handler->am_root) return 0; network = &handler->conf->network; lxc_list_for_each(iterator, network) { int ret; struct lxc_netdev *netdev = iterator->elem; /* Send network device name in the child's namespace to parent. */ ret = send(data_sock, netdev->name, IFNAMSIZ, 0); if (ret < 0) return -1; /* Send network device ifindex in the child's namespace to * parent. */ ret = send(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0); if (ret < 0) return -1; } TRACE("Sent network device names and ifindeces to parent"); return 0; } int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler) { struct lxc_list *iterator, *network; int data_sock = handler->data_sock[1]; if (!handler->am_root) return 0; network = &handler->conf->network; lxc_list_for_each(iterator, network) { int ret; struct lxc_netdev *netdev = iterator->elem; /* Receive network device name in the child's namespace to * parent. */ ret = recv(data_sock, netdev->name, IFNAMSIZ, 0); if (ret < 0) return -1; /* Receive network device ifindex in the child's namespace to * parent. */ ret = recv(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0); if (ret < 0) return -1; } return 0; } void lxc_delete_network(struct lxc_handler *handler) { bool bret; if (handler->am_root) bret = lxc_delete_network_priv(handler); else bret = lxc_delete_network_unpriv(handler); if (!bret) DEBUG("Failed to delete network devices"); else DEBUG("Deleted network devices"); } lxc-2.0.11/src/lxc/monitor.h0000644061062106075000000000612613435013473012537 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_MONITOR_H #define __LXC_MONITOR_H #include #include #include #include typedef enum { lxc_msg_state, lxc_msg_priority, lxc_msg_exit_code, } lxc_msg_type_t; struct lxc_msg { lxc_msg_type_t type; char name[NAME_MAX+1]; int value; }; extern int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr); extern int lxc_monitor_fifo_name(const char *lxcpath, char *fifo_path, size_t fifo_path_sz, int do_mkdirp); extern void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath); extern void lxc_monitor_send_exit_code(const char *name, int exit_code, const char *lxcpath); extern int lxc_monitord_spawn(const char *lxcpath); /* * Open the monitoring mechanism for a specific container * The function will return an fd corresponding to the events * Returns a file descriptor on success, < 0 otherwise */ extern int lxc_monitor_open(const char *lxcpath); /* * Blocking read for the next container state change * @fd : the file descriptor provided by lxc_monitor_open * @msg : the variable which will be filled with the state * Returns 0 if the monitored container has exited, > 0 if * data was read, < 0 otherwise */ extern int lxc_monitor_read(int fd, struct lxc_msg *msg); /* * Blocking read for the next container state change with timeout * @fd : the file descriptor provided by lxc_monitor_open * @msg : the variable which will be filled with the state * @timeout : the timeout in seconds to wait for a state change * Returns 0 if the monitored container has exited, > 0 if * data was read, < 0 otherwise */ extern int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout); /* * Blocking read from multiple monitors for the next container state * change with timeout * @fds : struct pollfd descripting the fds to use * @nfds : the number of entries in fds * @msg : the variable which will be filled with the state * @timeout : the timeout in seconds to wait for a state change * Returns 0 if the monitored container has exited, > 0 if * data was read, < 0 otherwise */ extern int lxc_monitor_read_fdset(struct pollfd *fds, nfds_t nfds, struct lxc_msg *msg, int timeout); #endif lxc-2.0.11/src/lxc/tools/0000755061062106075000000000000013435013523012106 500000000000000lxc-2.0.11/src/lxc/tools/lxc-start-ephemeral.in0000644061062106075000000003467513435013473016262 00000000000000#!/usr/bin/env 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 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # import argparse import gettext import lxc import locale import os import sys import subprocess import tempfile _ = gettext.gettext gettext.textdomain("lxc-start-ephemeral") # Other functions def printstderr(*args): print("lxc-start-ephemeral is deprecated in favor of lxc-copy\n", *args, file=sys.stderr) 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)) def get_rundir(): if os.geteuid() == 0: return "@RUNTIME_PATH@" if "XDG_RUNTIME_DIR" in os.environ: return os.environ["XDG_RUNTIME_DIR"] if "HOME" in os.environ: return "%s/.cache/lxc/run/" % os.environ["HOME"] raise Exception("Unable to find a runtime directory") # Inform that lxc-start-ephemeral is deprecated printstderr() # 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("--lxcpath", "-P", dest="lxcpath", metavar="PATH", help=_("Use specified container path"), default=None) parser.add_argument("--orig", "-o", type=str, required=True, help=_("name of the original container")) parser.add_argument("--name", "-n", type=str, help=_("name of the target container")) parser.add_argument("--bdir", "-b", type=str, action="append", default=[], help=_("directory to bind mount into container, " "either --bdir=/src-path or --bdir=/src-path:/dst-path")) parser.add_argument("--cdir", "-c", type=str, action="append", default=[], help=_("directory to cow mount into container")) parser.add_argument("--user", "-u", type=str, help=_("the user to run the command as")) parser.add_argument("--key", "-S", type=str, help=_("the path to the key to use to connect " "(when using ssh)")) parser.add_argument("--daemon", "-d", action="store_true", help=_("run in the background")) parser.add_argument("--storage-type", "-s", type=str, default=None, choices=("tmpfs", "dir"), help=("type of storage use by the container")) 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=_("don't wipe everything clean at the end")) parser.add_argument("command", metavar='CMD', type=str, nargs="*", help=_("Run specific command in container " "(command as argument)")) parser.add_argument("--version", action="version", version=lxc.version) args = parser.parse_args() # 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.")) # Check that -k isn't used with -s tmpfs if not args.storage_type: if args.keep_data: args.storage_type = "dir" else: args.storage_type = "tmpfs" if args.keep_data and args.storage_type == "tmpfs": parser.error(_("You can't use -k with the tmpfs storage type.")) # Load the orig container orig = lxc.Container(args.orig, args.lxcpath) if not orig.defined: parser.error(_("Source container '%s' doesn't exist." % args.orig)) # Create the new container paths if not args.lxcpath: lxc_path = lxc.default_config_path else: lxc_path = args.lxcpath if args.name: if os.path.exists("%s/%s" % (lxc_path, args.name)): parser.error(_("A container named '%s' already exists." % args.name)) dest_path = "%s/%s" % (lxc_path, args.name) os.mkdir(dest_path) else: dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir=lxc_path) os.mkdir(os.path.join(dest_path, "rootfs")) os.chmod(dest_path, 0o770) # Setup the new container's configuration dest = lxc.Container(os.path.basename(dest_path), args.lxcpath) 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")) print("setting rootfs to .%s.", os.path.join(dest_path, "rootfs")) for nic in dest.network: if hasattr(nic, 'hwaddr'): nic.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")) fields = line.split() # Skip invalid entries if len(fields) < 4: continue # Non-bind mounts are kept as-is if "bind" not in fields[3]: dest_fd.write("%s\n" % line) continue # Bind mounts of virtual filesystems are also kept as-is src_path = fields[0].split("/") if len(src_path) > 1 and src_path[1] in ("proc", "sys"): dest_fd.write("%s\n" % line) continue # Skip invalid mount points dest_mount = os.path.abspath(os.path.join("%s/rootfs/" % ( dest_path), fields[1])) if "%s/rootfs/" % dest_path not in dest_mount: print(_("Skipping mount entry '%s' as it's outside " "of the container rootfs.") % line) # Setup an overlay for anything remaining overlay_dirs += [(fields[0], dest_mount)] for entry in args.cdir: if not os.path.exists(entry): print(_("Path '%s' doesn't exist, won't be cow-mounted.") % entry) else: src_path = os.path.abspath(entry) dst_path = "%s/rootfs/%s" % (dest_path, src_path) overlay_dirs += [(src_path, dst_path)] # do we have the new overlay fs which requires workdir, or the older # overlayfs which does not? have_new_overlay = False with open("/proc/filesystems", "r") as fd: for line in fd: if line == "nodev\toverlay\n": have_new_overlay = True # 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: tmpdir = "%s/tmpfs" % dest_path fd.write("mkdir -p %s\n" % (tmpdir)) if args.storage_type == "tmpfs": fd.write("mount -n -t tmpfs -o mode=0755 none %s\n" % (tmpdir)) deltdir = "%s/delta%s" % (tmpdir, count) workdir = "%s/work%s" % (tmpdir, count) fd.write("mkdir -p %s %s\n" % (deltdir, entry[1])) if have_new_overlay: fd.write("mkdir -p %s\n" % workdir) fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" % (entry[0], deltdir)) fd.write("getfacl -a %s | setfacl --set-file=- %s || true\n" % (entry[0], entry[1])) if args.union_type == "overlayfs": if have_new_overlay: fd.write("mount -n -t overlay" " -oupperdir=%s,lowerdir=%s,workdir=%s none %s\n" % ( deltdir, entry[0], workdir, entry[1])) else: fd.write("mount -n -t overlayfs" " -oupperdir=%s,lowerdir=%s none %s\n" % ( deltdir, entry[0], entry[1])) elif args.union_type == "aufs": xino_path = "/dev/shm/aufs.xino" if not os.path.exists(os.path.basename(xino_path)): os.makedirs(os.path.basename(xino_path)) fd.write("mount -n -t aufs " "-o br=%s=rw:%s=ro,noplink,xino=%s none %s\n" % ( deltdir, entry[0], xino_path, entry[1])) count += 1 for entry in args.bdir: if ':' in entry: src_path, dst_path = entry.split(":") else: src_path = entry dst_path = os.path.abspath(entry) if not os.path.exists(src_path): print(_("Path '%s' doesn't exist, won't be bind-mounted.") % src_path) else: src_path = os.path.abspath(src_path) dst_path = "%s/rootfs/%s" % (dest_path, dst_path) 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")) if not args.keep_data: dest.set_config_item("lxc.ephemeral", "1") 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() if not dest.shutdown(timeout=5): dest.stop() 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) if os.path.exists("/proc/self/ns/pid"): def attach_as_user(command): try: username = "root" if args.user: username = args.user # This should really just use universal_newlines=True, but we do # the decoding by hand instead for compatibility with Python # 3.2; that used locale.getpreferredencoding() internally rather # than locale.getpreferredencoding(False), and the former breaks # here because we can't reload codecs at this point unless the # container has the same version of Python installed. line = subprocess.check_output(["getent", "passwd", username]) line = line.decode(locale.getpreferredencoding(False)).rstrip("\n") _, _, pw_uid, pw_gid, _, pw_dir, _ = line.split(":", 6) pw_uid = int(pw_uid) pw_gid = int(pw_gid) os.setgid(pw_gid) os.initgroups(username, pw_gid) os.setuid(pw_uid) os.chdir(pw_dir) os.environ['HOME'] = pw_dir except: print(_("Unable to switch to user: %s" % username)) sys.exit(1) return lxc.attach_run_command(command) retval = dest.attach_wait(attach_as_user, args.command, env_policy=lxc.LXC_ATTACH_CLEAR_ENV) else: cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] if args.user: cmd += ["-l", args.user] if args.key: cmd += ["-i", 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 if not dest.shutdown(timeout=5): dest.stop() sys.exit(retval) lxc-2.0.11/src/lxc/tools/lxc_unfreeze.c0000644061062106075000000000550413435013473014673 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" static const struct option my_longopts[] = { LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-unfreeze", .help = "\ --name=NAME\n\ \n\ lxc-unfreeze unfreezes a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = NULL, .checker = NULL, }; int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "No such container: %s:%s\n", my_args.lxcpath[0], my_args.name); exit(EXIT_FAILURE); } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s:%s\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->unfreeze(c)) { fprintf(stderr, "Failed to unfreeze %s:%s\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } lxc_container_put(c); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/arguments.c0000644061062106075000000001502313435013473014204 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Michel Normand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "utils.h" static int build_shortopts(const struct option *a_options, char *a_shortopts, size_t a_size) { size_t i = 0; const struct option *opt; if (!a_options || !a_shortopts || !a_size) return -1; for (opt = a_options; opt->name; opt++) { if (!isascii(opt->val)) continue; if (i < a_size) a_shortopts[i++] = opt->val; else goto is2big; if (opt->has_arg == no_argument) continue; if (i < a_size) a_shortopts[i++] = ':'; else goto is2big; if (opt->has_arg == required_argument) continue; if (i < a_size) a_shortopts[i++] = ':'; else goto is2big; } if (i < a_size) a_shortopts[i] = '\0'; else goto is2big; return 0; is2big: errno = E2BIG; return -1; } static void print_usage_exit(const struct option longopts[], const struct lxc_arguments *a_args) { int i; const struct option *opt; fprintf(stderr, "Usage: %s ", a_args->progname); for (opt = longopts, i = 1; opt->name; opt++, i++) { fprintf(stderr, "["); if (isprint(opt->val)) fprintf(stderr, "-%c|", opt->val); fprintf(stderr, "--%s", opt->name); if ((opt->has_arg == required_argument) || (opt->has_arg == optional_argument)) { int j; char *uppername; uppername = strdup(opt->name); if (!uppername) exit(-ENOMEM); for (j = 0; uppername[j]; j++) uppername[j] = toupper(uppername[j]); if (opt->has_arg == required_argument) fprintf(stderr, "=%s", uppername); else // optional_argument fprintf(stderr, "[=%s]", uppername); free(uppername); } fprintf(stderr, "] "); if (!(i % 4)) fprintf(stderr, "\n\t"); } fprintf(stderr, "\n"); exit(0); } static void print_version_exit() { printf("%s\n", lxc_get_version()); exit(0); } static void print_help_exit(const struct lxc_arguments *args, int code) { fprintf(stderr, "\ Usage: %s %s\ \n\ Common options :\n\ -o, --logfile=FILE Output log to FILE instead of stderr\n\ -l, --logpriority=LEVEL Set log priority to LEVEL\n\ -q, --quiet Don't produce any output\n\ -P, --lxcpath=PATH Use specified container path\n\ -?, --help Give this help list\n\ --usage Give a short usage message\n\ --version Print the version number\n\ \n\ Mandatory or optional arguments to long options are also mandatory or optional\n\ for any corresponding short options.\n\ \n\ See the %s man page for further information.\n\n", args->progname, args->help, args->progname); if (args->helpfn) args->helpfn(args); exit(code); } static int lxc_arguments_lxcpath_add(struct lxc_arguments *args, const char *lxcpath) { if (args->lxcpath_additional != -1 && args->lxcpath_cnt > args->lxcpath_additional) { fprintf(stderr, "This command only accepts %d -P,--lxcpath arguments\n", args->lxcpath_additional + 1); exit(EXIT_FAILURE); } args->lxcpath = realloc( args->lxcpath, (args->lxcpath_cnt + 1) * sizeof(args->lxcpath[0])); if (args->lxcpath == NULL) { lxc_error(args, "no memory"); return -ENOMEM; } args->lxcpath[args->lxcpath_cnt++] = lxcpath; return 0; } extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, char *const argv[]) { int ret = 0; char shortopts[256]; ret = build_shortopts(args->options, shortopts, sizeof(shortopts)); if (ret < 0) { lxc_error(args, "build_shortopts() failed : %s", strerror(errno)); return ret; } while (true) { int c; int index = 0; c = getopt_long(argc, argv, shortopts, args->options, &index); if (c == -1) break; switch (c) { case 'n': args->name = optarg; break; case 'o': args->log_file = optarg; break; case 'l': args->log_priority = optarg; break; case 'q': args->quiet = 1; break; case OPT_RCFILE: args->rcfile = optarg; break; case 'P': remove_trailing_slashes(optarg); ret = lxc_arguments_lxcpath_add(args, optarg); if (ret < 0) return ret; break; case OPT_USAGE: print_usage_exit(args->options, args); case OPT_VERSION: print_version_exit(); case '?': print_help_exit(args, 1); case 'h': print_help_exit(args, 0); default: if (args->parser) { ret = args->parser(args, c, optarg); if (ret) goto error; } } } /* * Reclaim the remaining command arguments */ args->argv = &argv[optind]; args->argc = argc - optind; /* If no lxcpaths were given, use default */ if (!args->lxcpath_cnt) { ret = lxc_arguments_lxcpath_add( args, lxc_global_config_value("lxc.lxcpath")); if (ret < 0) return ret; } /* Check the command options */ if (!args->name && strcmp(args->progname, "lxc-autostart") != 0) { if (args->argv) { args->name = argv[optind]; optind++; args->argv = &argv[optind]; args->argc = argc - optind; } if (!args->name) { lxc_error(args, "No container name specified"); return -1; } } if (args->checker) ret = args->checker(args); error: if (ret) lxc_error(args, "could not parse command line"); return ret; } int lxc_arguments_str_to_int(struct lxc_arguments *args, const char *str) { long val; char *endptr; errno = 0; val = strtol(str, &endptr, 10); if (errno) { lxc_error(args, "invalid statefd '%s' : %s", str, strerror(errno)); return -1; } if (*endptr) { lxc_error(args, "invalid digit for statefd '%s'", str); return -1; } return (int)val; } lxc-2.0.11/src/lxc/tools/lxc_clone.c0000644061062106075000000001405513435013473014151 00000000000000/* * * Copyright © 2013 Serge Hallyn . * Copyright © 2013 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 #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "config.h" #include "lxc.h" #include "conf.h" #include "state.h" /* we pass fssize in bytes */ static uint64_t get_fssize(char *s) { uint64_t ret; char *end; ret = strtoull(s, &end, 0); if (end == s) { fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s); return 0; } while (isblank(*end)) end++; if (*end == '\0') ret *= 1024ULL * 1024ULL; // MB by default else if (*end == 'b' || *end == 'B') ret *= 1ULL; else if (*end == 'k' || *end == 'K') ret *= 1024ULL; else if (*end == 'm' || *end == 'M') ret *= 1024ULL * 1024ULL; else if (*end == 'g' || *end == 'G') ret *= 1024ULL * 1024ULL * 1024ULL; else if (*end == 't' || *end == 'T') ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; else { fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s); return 0; } return ret; } static void usage(const char *me) { printf("Usage: %s [-s] [-B backingstore] [-L size[unit]] [-K] [-M] [-H]\n", me); printf(" [-p lxcpath] [-P newlxcpath] orig new\n"); printf("\n"); printf(" -s: snapshot rather than copy\n"); printf(" -B: use specified new backingstore. Default is the same as\n"); printf(" the original. Options include aufs, btrfs, lvm, overlayfs, \n"); printf(" dir and loop\n"); printf(" -L: for blockdev-backed backingstore, use specified size * specified\n"); printf(" unit. Default size is the size of the source blockdev, default\n"); printf(" unit is MB\n"); printf(" -K: Keep name - do not change the container name\n"); printf(" -M: Keep macaddr - do not choose a random new mac address\n"); printf(" -p: use container orig from custom lxcpath\n"); printf(" -P: create container new in custom lxcpath\n"); printf(" -R: rename existing container\n"); exit(EXIT_SUCCESS); } static struct option options[] = { { "snapshot", no_argument, 0, 's'}, { "backingstore", required_argument, 0, 'B'}, { "size", required_argument, 0, 'L'}, { "orig", required_argument, 0, 'o'}, { "new", required_argument, 0, 'n'}, { "vgname", required_argument, 0, 'v'}, { "rename", no_argument, 0, 'R'}, { "keepname", no_argument, 0, 'K'}, { "keepmac", no_argument, 0, 'M'}, { "lxcpath", required_argument, 0, 'p'}, { "newpath", required_argument, 0, 'P'}, { "fstype", required_argument, 0, 't'}, { "help", no_argument, 0, 'h'}, { 0, 0, 0, 0 }, }; int main(int argc, char *argv[]) { struct lxc_container *c1 = NULL, *c2 = NULL; int snapshot = 0, keepname = 0, keepmac = 0, rename = 0; int flags = 0, option_index; uint64_t newsize = 0; char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL; char *orig = NULL, *new = NULL, *vgname = NULL; char **args = NULL; int c; bool ret; fprintf(stderr, "lxc-clone is deprecated in favor of lxc-copy.\n\n"); if (argc < 3) usage(argv[0]); while (1) { c = getopt_long(argc, argv, "sB:L:o:n:v:KMHp:P:Rt:h", options, &option_index); if (c == -1) break; switch (c) { case 's': snapshot = 1; break; case 'B': bdevtype = optarg; break; case 'L': newsize = get_fssize(optarg); break; case 'o': orig = optarg; break; case 'n': new = optarg; break; case 'v': vgname = optarg; break; case 'K': keepname = 1; break; case 'M': keepmac = 1; break; case 'p': lxcpath = optarg; break; case 'P': newpath = optarg; break; case 'R': rename = 1; break; case 't': fstype = optarg; break; case 'h': usage(argv[0]); default: break; } } if (optind < argc && !orig) orig = argv[optind++]; if (optind < argc && !new) new = argv[optind++]; if (optind < argc) /* arguments for the clone hook */ args = &argv[optind]; if (!new || !orig) { printf("Error: you must provide orig and new names\n"); usage(argv[0]); } if (snapshot) flags |= LXC_CLONE_SNAPSHOT; if (keepname) flags |= LXC_CLONE_KEEPNAME; if (keepmac) flags |= LXC_CLONE_KEEPMACADDR; // vgname and fstype could be supported by sending them through the // bdevdata. However, they currently are not yet. I'm not convinced // they are worthwhile. if (vgname) { printf("Error: vgname not supported\n"); usage(argv[0]); } if (fstype) { printf("Error: fstype not supported\n"); usage(argv[0]); } c1 = lxc_container_new(orig, lxcpath); if (!c1) exit(EXIT_FAILURE); if (!c1->may_control(c1)) { fprintf(stderr, "Insufficent privileges to control %s\n", orig); lxc_container_put(c1); exit(EXIT_FAILURE); } if (!c1->is_defined(c1)) { fprintf(stderr, "Error: container %s is not defined\n", orig); lxc_container_put(c1); exit(EXIT_FAILURE); } if (rename) { ret = c1->rename(c1, new); if (!ret) { fprintf(stderr, "Error: Renaming container %s to %s failed\n", c1->name, new); lxc_container_put(c1); exit(EXIT_FAILURE); } } else { c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize, args); if (c2 == NULL) { lxc_container_put(c1); fprintf(stderr, "clone failed\n"); exit(EXIT_FAILURE); } printf("Created container %s as %s of %s\n", new, snapshot ? "snapshot" : "copy", orig); lxc_container_put(c2); } lxc_container_put(c1); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_config.c0000644061062106075000000000425013435013473014312 00000000000000/* lxc_config * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "config.h" struct lxc_config_items { char *name; }; static struct lxc_config_items items[] = { { .name = "lxc.default_config", }, { .name = "lxc.lxcpath", }, { .name = "lxc.bdev.lvm.vg", }, { .name = "lxc.bdev.lvm.thin_pool", }, { .name = "lxc.bdev.zfs.root", }, { .name = "lxc.cgroup.use", }, { .name = "lxc.cgroup.pattern", }, { .name = NULL, }, }; static void usage(char *me) { printf("Usage: %s -l: list all available configuration items\n", me); printf(" %s item: print configuration item\n", me); exit(EXIT_SUCCESS); } static void list_config_items(void) { struct lxc_config_items *i; for (i = &items[0]; i->name; i++) printf("%s\n", i->name); exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { struct lxc_config_items *i; const char *value; if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(argv[0]); if (strcmp(argv[1], "-l") == 0) list_config_items(); for (i = &items[0]; i->name; i++) { if (strcmp(argv[1], i->name) == 0) { value = lxc_get_global_config_item(i->name); if (value) printf("%s\n", value); else printf("%s is not set.\n", argv[1]); exit(EXIT_SUCCESS); } } printf("Unknown configuration item: %s\n", argv[1]); exit(EXIT_FAILURE); } lxc-2.0.11/src/lxc/tools/lxc_checkpoint.c0000644061062106075000000001334513435013473015201 00000000000000/* * * Copyright © 2014 Tycho Andersen . * Copyright © 2014 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 #include #include #include #include #include #include "log.h" #include "config.h" #include "lxc.h" #include "arguments.h" #include "utils.h" static char *checkpoint_dir = NULL; static bool stop = false; static bool verbose = false; static bool do_restore = false; static bool daemonize_set = false; static const struct option my_longopts[] = { {"checkpoint-dir", required_argument, 0, 'D'}, {"stop", no_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {"restore", no_argument, 0, 'r'}, {"daemon", no_argument, 0, 'd'}, {"foreground", no_argument, 0, 'F'}, LXC_COMMON_OPTIONS }; static int my_checker(const struct lxc_arguments *args) { if (do_restore && stop) { lxc_error(args, "-s not compatible with -r."); return -1; } else if (!do_restore && daemonize_set) { lxc_error(args, "-d/-F not compatible with -r."); return -1; } if (checkpoint_dir == NULL) { lxc_error(args, "-D is required."); return -1; } return 0; } static int my_parser(struct lxc_arguments *args, int c, char *arg) { switch (c) { case 'D': checkpoint_dir = strdup(arg); if (!checkpoint_dir) return -1; break; case 's': stop = true; break; case 'v': verbose = true; break; case 'r': do_restore = true; break; case 'd': args->daemonize = 1; daemonize_set = true; break; case 'F': args->daemonize = 0; daemonize_set = true; break; } return 0; } static struct lxc_arguments my_args = { .progname = "lxc-checkpoint", .help = "\ --name=NAME\n\ \n\ lxc-checkpoint checkpoints and restores a container\n\ Serializes a container's running state to disk to allow restoring it in\n\ its running state at a later time.\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -r, --restore Restore container\n\ -D, --checkpoint-dir=DIR directory to save the checkpoint in\n\ -v, --verbose Enable verbose criu logs\n\ Checkpoint options:\n\ -s, --stop Stop the container after checkpointing.\n\ Restore options:\n\ -d, --daemon Daemonize the container (default)\n\ -F, --foreground Start with the current tty attached to /dev/console\n\ --rcfile=FILE Load configuration file FILE\n\ ", .options = my_longopts, .parser = my_parser, .daemonize = 1, .checker = my_checker, }; static bool checkpoint(struct lxc_container *c) { bool ret; if (!c->is_running(c)) { fprintf(stderr, "%s not running, not checkpointing.\n", my_args.name); lxc_container_put(c); return false; } ret = c->checkpoint(c, checkpoint_dir, stop, verbose); lxc_container_put(c); if (!ret) { fprintf(stderr, "Checkpointing %s failed.\n", my_args.name); return false; } return true; } static bool restore_finalize(struct lxc_container *c) { bool ret = c->restore(c, checkpoint_dir, verbose); if (!ret) { fprintf(stderr, "Restoring %s failed.\n", my_args.name); } lxc_container_put(c); return ret; } static bool restore(struct lxc_container *c) { if (c->is_running(c)) { fprintf(stderr, "%s is running, not restoring.\n", my_args.name); lxc_container_put(c); return false; } if (my_args.daemonize) { pid_t pid; pid = fork(); if (pid < 0) { perror("fork"); return false; } if (pid == 0) { close(0); close(1); exit(!restore_finalize(c)); } else { return wait_for_pid(pid) == 0; } } else { int status; if (!restore_finalize(c)) return false; if (waitpid(-1, &status, 0) < 0) return false; return WIFEXITED(status) && WEXITSTATUS(status) == 0; } } int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; bool ret; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "System error loading %s\n", my_args.name); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->is_defined(c)) { fprintf(stderr, "%s is not defined\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (do_restore) ret = restore(c); else ret = checkpoint(c); if (!ret) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc-checkconfig.in0000644061062106075000000001704213435013473015415 00000000000000#!/bin/sh # Allow environment variables to override config : ${CONFIG:=/proc/config.gz} : ${MODNAME:=configs} CAT="cat" if [ -t 1 ]; then SETCOLOR_SUCCESS="printf \\033[1;32m" SETCOLOR_FAILURE="printf \\033[1;31m" SETCOLOR_WARNING="printf \\033[1;33m" SETCOLOR_NORMAL="printf \\033[0;39m" else SETCOLOR_SUCCESS=":" SETCOLOR_FAILURE=":" SETCOLOR_WARNING=":" SETCOLOR_NORMAL=":" fi is_set() { $CAT $CONFIG | grep "$1=[y|m]" > /dev/null return $? } is_enabled() { mandatory=$2 is_set $1 RES=$? RET=1 if [ $RES -eq 0 ]; then $SETCOLOR_SUCCESS && echo -n "enabled" && $SETCOLOR_NORMAL RET=0 else if [ ! -z "$mandatory" ] && [ "$mandatory" = yes ]; then $SETCOLOR_FAILURE && echo -n "required" && $SETCOLOR_NORMAL else $SETCOLOR_WARNING && echo -n "missing" && $SETCOLOR_NORMAL fi fi return $RET } is_probed() { lsmod | grep $1 > /dev/null if [ $? -eq 0 ]; then echo -n ", loaded" else echo -n ", not loaded" fi } if [ ! -f $CONFIG ]; then echo "Kernel configuration not found at $CONFIG; searching..." KVER="`uname -r`" HEADERS_CONFIG="/lib/modules/$KVER/build/.config" BOOT_CONFIG="/boot/config-$KVER" [ -f "${HEADERS_CONFIG}" ] && CONFIG=${HEADERS_CONFIG} [ -f "${BOOT_CONFIG}" ] && CONFIG=${BOOT_CONFIG} if [ ! -f "$CONFIG" ]; then MODULEFILE=$(modinfo -k $KVER -n $MODNAME 2> /dev/null) # don't want to modprobe, so give user a hint # although scripts/extract-ikconfig could be used to extract contents without loading kernel module # http://svn.pld-linux.org/trac/svn/browser/geninitrd/trunk/geninitrd?rev=12696#L327 fi if [ ! -f $CONFIG ]; then echo "$(basename $0): unable to retrieve kernel configuration" >&2 echo >&2 if [ -f "$MODULEFILE" ]; then echo "Try modprobe $MODNAME module, or" >&2 fi echo "Try recompiling with IKCONFIG_PROC, installing the kernel headers," >&2 echo "or specifying the kernel configuration path with:" >&2 echo " CONFIG= $(basename $0)" >&2 exit 1 else echo "Kernel configuration found at $CONFIG" fi fi if gunzip -tq < $CONFIG 2>/dev/null; then CAT="zcat" fi KVER_MAJOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \ sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/') if [ "$KVER_MAJOR" = "2" ]; then KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \ sed -r 's/.* 2.6.([0-9]{2}).*/\1/') else KVER_MINOR=$($CAT $CONFIG | grep '^# Linux.*Kernel Configuration' | \ sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/') fi echo "--- Namespaces ---" echo -n "Namespaces: " && is_enabled CONFIG_NAMESPACES yes echo echo -n "Utsname namespace: " && is_enabled CONFIG_UTS_NS echo echo -n "Ipc namespace: " && is_enabled CONFIG_IPC_NS yes echo echo -n "Pid namespace: " && is_enabled CONFIG_PID_NS yes echo echo -n "User namespace: " && is_enabled CONFIG_USER_NS echo if is_set CONFIG_USER_NS; then if which newuidmap > /dev/null 2>&1; then f=`which newuidmap` if [ ! -u "${f}" ]; then echo "Warning: newuidmap is not setuid-root" fi else echo "newuidmap is not installed" fi if which newgidmap > /dev/null 2>&1; then f=`which newgidmap` if [ ! -u "${f}" ]; then echo "Warning: newgidmap is not setuid-root" fi else echo "newgidmap is not installed" fi fi echo -n "Network namespace: " && is_enabled CONFIG_NET_NS echo if ([ $KVER_MAJOR -lt 4 ]) || ([ $KVER_MAJOR -eq 4 ] && [ $KVER_MINOR -lt 7 ]); then echo -n "Multiple /dev/pts instances: " && is_enabled DEVPTS_MULTIPLE_INSTANCES echo fi echo echo "--- Control groups ---" echo -n "Cgroups: " && is_enabled CONFIG_CGROUPS echo print_cgroups() { # print all mountpoints for cgroup filesystems awk '$1 !~ /#/ && $3 == mp { print $2; } ; END { exit(0); } ' "mp=$1" "$2" ; } CGROUP_V1_MNTS=`print_cgroups cgroup /proc/self/mounts` echo echo "Cgroup v1 mount points: " echo "$CGROUP_V1_MNTS" echo CGROUP_V2_MNTS=`print_cgroups cgroup2 /proc/self/mounts` echo "Cgroup v2 mount points: " echo "$CGROUP_V2_MNTS" echo CGROUP_SYSTEMD_MNTPT=`echo "$CGROUP_V1_MNTS" | grep "/systemd"` if [ -z "$CGROUP_SYSTEMD_MNTPT" ]; then echo -n "Cgroup v1 systemd controller: " $SETCOLOR_FAILURE && echo -n "missing" && $SETCOLOR_NORMAL echo fi CGROUP_FREEZER_MNTPT=`echo "$CGROUP_V1_MNTS" | grep "/freezer"` if [ -z "$CGROUP_FREEZER_MNTPT" ]; then echo -n "Cgroup v1 freezer controller: " $SETCOLOR_FAILURE && echo -n "missing" && $SETCOLOR_NORMAL echo fi CGROUP_MNT_PATH=`echo "$CGROUP_V1_MNTS" | head -n 1` if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then echo -n "Cgroup v1 clone_children flag: " && $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL else echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes echo fi echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE echo echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED echo echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT echo echo -n "Cgroup memory controller: " if ([ $KVER_MAJOR -ge 3 ] && [ $KVER_MINOR -ge 6 ]) || ([ $KVER_MAJOR -gt 3 ]); then is_enabled CONFIG_MEMCG else is_enabled CONFIG_CGROUP_MEM_RES_CTLR fi echo is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS && echo echo echo "--- Misc ---" echo -n "Veth pair device: " && is_enabled CONFIG_VETH && is_probed veth echo echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN && is_probed macvlan echo echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q && is_probed 8021q echo echo -n "Bridges: " && is_enabled CONFIG_BRIDGE && is_probed bridge echo echo -n "Advanced netfilter: " && is_enabled CONFIG_NETFILTER_ADVANCED && is_probed nf_tables echo echo -n "CONFIG_NF_NAT_IPV4: " && is_enabled CONFIG_NF_NAT_IPV4 && is_probed nf_nat_ipv4 echo echo -n "CONFIG_NF_NAT_IPV6: " && is_enabled CONFIG_NF_NAT_IPV6 && is_probed nf_nat_ipv6 echo echo -n "CONFIG_IP_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP_NF_TARGET_MASQUERADE && is_probed nf_nat_masquerade_ipv4 echo echo -n "CONFIG_IP6_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP6_NF_TARGET_MASQUERADE && is_probed nf_nat_masquerade_ipv6 echo echo -n "CONFIG_NETFILTER_XT_TARGET_CHECKSUM: " && is_enabled CONFIG_NETFILTER_XT_TARGET_CHECKSUM && is_probed xt_CHECKSUM echo echo -n "CONFIG_NETFILTER_XT_MATCH_COMMENT: " && is_enabled CONFIG_NETFILTER_XT_MATCH_COMMENT && is_probed xt_comment echo echo -n "FUSE (for use with lxcfs): " && is_enabled CONFIG_FUSE_FS && is_probed fuse echo echo echo "--- Checkpoint/Restore ---" echo -n "checkpoint restore: " && is_enabled CONFIG_CHECKPOINT_RESTORE echo echo -n "CONFIG_FHANDLE: " && is_enabled CONFIG_FHANDLE echo echo -n "CONFIG_EVENTFD: " && is_enabled CONFIG_EVENTFD echo echo -n "CONFIG_EPOLL: " && is_enabled CONFIG_EPOLL echo echo -n "CONFIG_UNIX_DIAG: " && is_enabled CONFIG_UNIX_DIAG echo echo -n "CONFIG_INET_DIAG: " && is_enabled CONFIG_INET_DIAG echo echo -n "CONFIG_PACKET_DIAG: " && is_enabled CONFIG_PACKET_DIAG echo echo -n "CONFIG_NETLINK_DIAG: " && is_enabled CONFIG_NETLINK_DIAG echo echo -n "File capabilities: " && \ ( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \ is_enabled CONFIG_SECURITY_FILE_CAPABILITIES; echo ) || \ ( ( [ "${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" echo "usage : CONFIG=/path/to/config $0" echo lxc-2.0.11/src/lxc/tools/lxc_execute.c0000644061062106075000000001075613435013473014517 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "caps.h" #include "conf.h" #include "config.h" #include "confile.h" #include "log.h" #include "lxc.h" #include "start.h" #include "utils.h" static struct lxc_list defines; static int my_checker(const struct lxc_arguments* args) { if (!args->argc) { lxc_error(args, "missing command to execute !"); return -1; } return 0; } static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'f': args->rcfile = arg; break; case 's': return lxc_config_define_add(&defines, arg); break; case 'u': if (lxc_safe_uint(arg, &args->uid) < 0) return -1; break; case 'g': if (lxc_safe_uint(arg, &args->gid) < 0) return -1; } return 0; } static const struct option my_longopts[] = { {"rcfile", required_argument, 0, 'f'}, {"define", required_argument, 0, 's'}, {"uid", required_argument, 0, 'u'}, {"gid", required_argument, 0, 'g'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-execute", .help = "\ --name=NAME -- COMMAND\n\ \n\ lxc-execute creates a container with the identifier NAME\n\ and execs COMMAND into this container.\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -f, --rcfile=FILE Load configuration file FILE\n\ -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\ -u, --uid=UID Execute COMMAND with UID inside the container\n\ -g, --gid=GID Execute COMMAND with GID inside the container\n", .options = my_longopts, .parser = my_parser, .checker = my_checker, }; int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; int ret; bool bret; lxc_list_init(&defines); if (lxc_caps_init()) exit(EXIT_FAILURE); if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "Failed to create lxc_container\n"); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->lxc_conf) { fprintf(stderr, "Executing a container with no configuration file may crash the host\n"); lxc_container_put(c); exit(EXIT_FAILURE); } ret = lxc_config_define_load(&defines, c->lxc_conf); if (ret) { lxc_container_put(c); exit(EXIT_FAILURE); } if (my_args.uid) c->lxc_conf->init_uid = my_args.uid; if (my_args.gid) c->lxc_conf->init_gid = my_args.gid; c->daemonize = false; bret = c->start(c, 1, my_args.argv); lxc_container_put(c); if (!bret) { fprintf(stderr, "Failed run an application inside container\n"); exit(EXIT_FAILURE); } if (c->daemonize) exit(EXIT_SUCCESS); else { if (WIFEXITED(c->error_num)) { exit(WEXITSTATUS(c->error_num)); } else { /* Try to die with the same signal the task did. */ kill(0, WTERMSIG(c->error_num)); exit(EXIT_FAILURE); } } } lxc-2.0.11/src/lxc/tools/lxc-top.lua0000755061062106075000000001643513435013473014137 00000000000000#!/usr/bin/env lua -- -- top(1) like monitor for lxc containers -- -- Copyright © 2012 Oracle. -- -- Authors: -- Dwight Engen -- -- This library 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. -- local lxc = require("lxc") local core = require("lxc.core") local getopt = require("alt_getopt") local USER_HZ = 100 local ESC = string.format("%c", 27) local TERMCLEAR = ESC.."[H"..ESC.."[J" local TERMNORM = ESC.."[0m" local TERMBOLD = ESC.."[1m" local TERMRVRS = ESC.."[7m" local containers = {} local stats = {} local stats_total = {} local max_containers function printf(...) local function wrapper(...) io.write(string.format(...)) end local status, result = pcall(wrapper, ...) if not status then error(result, 2) end end function string:split(delim, max_cols) local cols = {} local start = 1 local nextc repeat nextc = string.find(self, delim, start) if (nextc and #cols ~= max_cols - 1) then table.insert(cols, string.sub(self, start, nextc-1)) start = nextc + #delim else table.insert(cols, string.sub(self, start, string.len(self))) nextc = nil end until nextc == nil or start > #self return cols end function strsisize(size, width) local KiB = 1024 local MiB = 1048576 local GiB = 1073741824 local TiB = 1099511627776 local PiB = 1125899906842624 local EiB = 1152921504606846976 local ZiB = 1180591620717411303424 if (size >= ZiB) then return string.format("%d.%2.2d ZB", size / ZiB, (math.floor(size % ZiB) * 100) / ZiB) end if (size >= EiB) then return string.format("%d.%2.2d EB", size / EiB, (math.floor(size % EiB) * 100) / EiB) end if (size >= PiB) then return string.format("%d.%2.2d PB", size / PiB, (math.floor(size % PiB) * 100) / PiB) end if (size >= TiB) then return string.format("%d.%2.2d TB", size / TiB, (math.floor(size % TiB) * 100) / TiB) end if (size >= GiB) then return string.format("%d.%2.2d GB", size / GiB, (math.floor(size % GiB) * 100) / GiB) end if (size >= MiB) then return string.format("%d.%2.2d MB", size / MiB, (math.floor(size % MiB) * 1000) / (MiB * 10)) end if (size >= KiB) then return string.format("%d.%2.2d KB", size / KiB, (math.floor(size % KiB) * 1000) / (KiB * 10)) end return string.format("%3d.00 ", size) end function tty_lines() local rows = 25 local f = assert(io.popen("stty -a | head -n 1")) for line in f:lines() do local stty_rows _,_,stty_rows = string.find(line, "rows (%d+)") if (stty_rows ~= nil) then rows = stty_rows break end end f:close() return rows end function container_sort(a, b) if (optarg["r"]) then if (optarg["s"] == "n") then return (a > b) elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos < stats[b].cpu_use_nanos) elseif (optarg["s"] == "d") then return (stats[a].blkio < stats[b].blkio) elseif (optarg["s"] == "m") then return (stats[a].mem_used < stats[b].mem_used) elseif (optarg["s"] == "k") then return (stats[a].kmem_used < stats[b].kmem_used) end else if (optarg["s"] == "n") then return (a < b) elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos > stats[b].cpu_use_nanos) elseif (optarg["s"] == "d") then return (stats[a].blkio > stats[b].blkio) elseif (optarg["s"] == "m") then return (stats[a].mem_used > stats[b].mem_used) elseif (optarg["s"] == "k") then return (stats[a].kmem_used > stats[b].kmem_used) end end end function container_list_update() local now_running now_running = lxc.containers_running(true) -- check for newly started containers for _,v in ipairs(now_running) do if (containers[v] == nil) then local ct = lxc.container:new(v) -- note, this is a "mixed" table, ie both dictionary and list containers[v] = ct table.insert(containers, v) end end -- check for newly stopped containers local indx = 1 while (indx <= #containers) do local ctname = containers[indx] if (now_running[ctname] == nil) then containers[ctname] = nil stats[ctname] = nil table.remove(containers, indx) else indx = indx + 1 end end -- get stats for all current containers and resort the list lxc.stats_clear(stats_total) for _,ctname in ipairs(containers) do stats[ctname] = containers[ctname]:stats_get(stats_total) end table.sort(containers, container_sort) end function stats_print_header(stats_total) printf(TERMRVRS .. TERMBOLD) printf("%-15s %8s %8s %8s %10s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem") if (stats_total.kmem_used > 0) then printf(" %10s", "KMem") end printf("\n") printf("%-15s %8s %8s %8s %10s %10s", "Name", "Used", "Sys", "User", "Total", "Used") if (stats_total.kmem_used > 0) then printf(" %10s", "Used") end printf("\n") printf(TERMNORM) end function stats_print(name, stats, stats_total) printf("%-15s %8.2f %8.2f %8.2f %10s %10s", name, stats.cpu_use_nanos / 1000000000, stats.cpu_use_sys / USER_HZ, stats.cpu_use_user / USER_HZ, strsisize(stats.blkio), strsisize(stats.mem_used)) if (stats_total.kmem_used > 0) then printf(" %10s", strsisize(stats.kmem_used)) end end function usage() printf("Usage: lxc-top [options]\n" .. " -h|--help print this help message\n" .. " -m|--max display maximum number of containers\n" .. " -d|--delay delay in seconds between refreshes (default: 3.0)\n" .. " -s|--sort sort by [n,c,d,m] (default: n) where\n" .. " n = Name\n" .. " c = CPU use\n" .. " d = Disk I/O use\n" .. " m = Memory use\n" .. " k = Kernel memory use\n" .. " -r|--reverse sort in reverse (descending) order\n" ) os.exit(1) end local long_opts = { help = "h", delay = "d", max = "m", reverse = "r", sort = "s", } optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts) optarg["d"] = tonumber(optarg["d"]) or 3.0 optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3) optarg["r"] = optarg["r"] or false optarg["s"] = optarg["s"] or "n" if (optarg["h"] ~= nil) then usage() end while true do container_list_update() -- if some terminal we care about doesn't support the simple escapes, we -- may fall back to this, or ncurses. ug. --os.execute("tput clear") printf(TERMCLEAR) stats_print_header(stats_total) for index,ctname in ipairs(containers) do stats_print(ctname, stats[ctname], stats_total) printf("\n") if (index >= optarg["m"]) then break end end stats_print(string.format("TOTAL (%-2d)", #containers), stats_total, stats_total) io.flush() core.usleep(optarg["d"] * 1000000) end lxc-2.0.11/src/lxc/tools/lxc_wait.c0000644061062106075000000000655613435013473014024 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" static int my_checker(const struct lxc_arguments* args) { if (!args->states) { lxc_error(args, "missing state option to wait for."); return -1; } return 0; } 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 }; static struct lxc_arguments my_args = { .progname = "lxc-wait", .help = "\ --name=NAME --state=STATE\n\ \n\ lxc-wait waits for NAME container state to reach STATE\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -s, --state=STATE ORed states to wait for\n\ STOPPED, STARTING, RUNNING, STOPPING,\n\ ABORTING, FREEZING, FROZEN, THAWED\n\ -t, --timeout=TMO Seconds to wait for state changes\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .checker = my_checker, .timeout = -1, }; int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) exit(EXIT_FAILURE); if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", c->name); lxc_container_put(c); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->wait(c, my_args.states, my_args.timeout)) { lxc_container_put(c); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_unshare.c0000644061062106075000000002265113435013473014517 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "caps.h" #include "cgroup.h" #include "error.h" #include "log.h" #include "namespace.h" #include "network.h" #include "utils.h" struct my_iflist { char *mi_ifname; struct my_iflist *mi_next; }; static void usage(char *cmd) { fprintf(stderr, "%s command [command_arguments]\n", basename(cmd)); fprintf(stderr, "Options are:\n"); fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \ "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n"); fprintf(stderr, "\t -u : new id to be set if -s USER is specified\n"); fprintf(stderr, "\t -i : Interface name to be moved into container (presumably with NETWORK unsharing set)\n"); fprintf(stderr, "\t -H : Set the hostname in the container\n"); fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n"); fprintf(stderr, "\t -M : Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n"); _exit(EXIT_SUCCESS); } static bool lookup_user(const char *optarg, uid_t *uid) { char name[MAXPATHLEN]; struct passwd pwent; struct passwd *pwentp = NULL; char *buf; size_t bufsize; int ret; if (!optarg || (optarg[0] == '\0')) return false; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return false; if (sscanf(optarg, "%u", uid) < 1) { /* not a uid -- perhaps a username */ if (sscanf(optarg, "%s", name) < 1) { free(buf); return false; } ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) fprintf(stderr, "could not find matched password record\n"); fprintf(stderr, "invalid username %s\n", name); free(buf); return false; } *uid = pwent.pw_uid; } else { ret = getpwuid_r(*uid, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) fprintf(stderr, "could not find matched password record\n"); fprintf(stderr, "invalid uid %u\n", *uid); free(buf); return false; } } free(buf); return true; } struct start_arg { char ***args; int *flags; uid_t *uid; bool setuid; int want_default_mounts; int wait_fd; const char *want_hostname; }; static int mount_fs(const char *source, const char *target, const char *type) { /* the umount may fail */ if (umount(target) < 0) if (mount(source, target, type, 0, NULL) < 0) return -1; return 0; } static void lxc_setup_fs(void) { (void)mount_fs("proc", "/proc", "proc"); /* if /dev has been populated by us, /dev/shm does not exist */ if (access("/dev/shm", F_OK)) (void)mkdir("/dev/shm", 0777); /* if we can't mount /dev/shm, continue anyway */ (void)mount_fs("shmfs", "/dev/shm", "tmpfs"); /* If we were able to mount /dev/shm, then /dev exists */ /* Sure, but it's read-only per config :) */ if (access("/dev/mqueue", F_OK)) (void)mkdir("/dev/mqueue", 0666); /* continue even without posix message queue support */ (void)mount_fs("mqueue", "/dev/mqueue", "mqueue"); } static int do_start(void *arg) { int ret; uint64_t wait_val; struct start_arg *start_arg = arg; char **args = *start_arg->args; int flags = *start_arg->flags; uid_t uid = *start_arg->uid; int want_default_mounts = start_arg->want_default_mounts; const char *want_hostname = start_arg->want_hostname; int wait_fd = start_arg->wait_fd; if (start_arg->setuid) { /* waiting until uid maps is set */ ret = read(wait_fd, &wait_val, sizeof(wait_val)); if (ret == -1) { close(wait_fd); fprintf(stderr, "read eventfd failed\n"); exit(EXIT_FAILURE); } } if ((flags & CLONE_NEWNS) && want_default_mounts) lxc_setup_fs(); if ((flags & CLONE_NEWUTS) && want_hostname) if (sethostname(want_hostname, strlen(want_hostname)) < 0) { fprintf(stderr, "failed to set hostname %s: %s\n", want_hostname, strerror(errno)); exit(EXIT_FAILURE); } // Setuid is useful even without a new user id space if (start_arg->setuid && setuid(uid)) { fprintf(stderr, "failed to set uid %d: %s\n", uid, strerror(errno)); exit(EXIT_FAILURE); } execvp(args[0], args); fprintf(stderr, "failed to exec: '%s': %s\n", args[0], strerror(errno)); return 1; } int main(int argc, char *argv[]) { char *del; char **it, **args; int opt, status; int ret; char *namespaces = NULL; int flags = 0, daemonize = 0; uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */ pid_t pid; uint64_t wait_val = 1; struct my_iflist *tmpif, *my_iflist = NULL; struct start_arg start_arg = { .args = &args, .uid = &uid, .setuid = false, .flags = &flags, .want_hostname = NULL, .want_default_mounts = 0, }; while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) { switch (opt) { case 's': namespaces = optarg; break; case 'i': if (!(tmpif = malloc(sizeof(*tmpif)))) { perror("malloc"); exit(EXIT_FAILURE); } tmpif->mi_ifname = optarg; tmpif->mi_next = my_iflist; my_iflist = tmpif; break; case 'd': daemonize = 1; break; case 'M': start_arg.want_default_mounts = 1; break; case 'H': start_arg.want_hostname = optarg; break; case 'h': usage(argv[0]); break; case 'u': if (!lookup_user(optarg, &uid)) exit(EXIT_FAILURE); start_arg.setuid = true; } } if (argv[optind] == NULL) { fprintf(stderr, "a command to execute in the new namespace is required\n"); exit(EXIT_FAILURE); } args = &argv[optind]; ret = lxc_caps_init(); if (ret) exit(EXIT_FAILURE); /* The identifiers for namespaces used with lxc-unshare as given on the * manpage do not align with the standard identifiers. This affects * network, mount, and uts namespaces. The standard identifiers are: * "mnt", "uts", and "net" whereas lxc-unshare uses "MOUNT", "UTSNAME", * and "NETWORK". So let's use some cheap memmove()s to replace them by * their standard identifiers. Let's illustrate this with an example: * Assume the string: * * "IPC|MOUNT|PID" * * then we memmove() * * dest: del + 1 == OUNT|PID * src: del + 3 == NT|PID */ if (!namespaces) usage(argv[0]); while ((del = strstr(namespaces, "MOUNT"))) memmove(del + 1, del + 3, strlen(del) - 2); for (it = (char *[]){"NETWORK", "UTSNAME", NULL}; it && *it; it++) while ((del = strstr(namespaces, *it))) memmove(del + 3, del + 7, strlen(del) - 6); ret = lxc_fill_namespace_flags(namespaces, &flags); if (ret) usage(argv[0]); if (!(flags & CLONE_NEWNET) && my_iflist) { fprintf(stderr, "-i needs -s NETWORK option\n"); exit(EXIT_FAILURE); } if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) { fprintf(stderr, "-H needs -s UTSNAME option\n"); exit(EXIT_FAILURE); } if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) { fprintf(stderr, "-M needs -s MOUNT option\n"); exit(EXIT_FAILURE); } if (start_arg.setuid) { start_arg.wait_fd = eventfd(0, EFD_CLOEXEC); if (start_arg.wait_fd < 0) { fprintf(stderr, "failed to create eventfd\n"); exit(EXIT_FAILURE); } } pid = lxc_clone(do_start, &start_arg, flags); if (pid < 0) { fprintf(stderr, "failed to clone\n"); exit(EXIT_FAILURE); } if (start_arg.setuid) { /* enough space to accommodate uids */ char *umap = (char *)alloca(100); /* create new uid mapping using current UID and the one * specified as parameter */ ret = snprintf(umap, 100, "%d %d 1\n" , *(start_arg.uid), getuid()); if (ret < 0 || ret >= 100) { close(start_arg.wait_fd); fprintf(stderr, "snprintf failed"); exit(EXIT_FAILURE); } ret = write_id_mapping(ID_TYPE_UID, pid, umap, strlen(umap)); if (ret < 0) { close(start_arg.wait_fd); fprintf(stderr, "uid mapping failed\n"); exit(EXIT_FAILURE); } ret = write(start_arg.wait_fd, &wait_val, sizeof(wait_val)); if (ret < 0) { close(start_arg.wait_fd); fprintf(stderr, "write to eventfd failed\n"); exit(EXIT_FAILURE); } } if (my_iflist) { for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) { if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid, NULL) < 0) fprintf(stderr,"Could not move interface %s into container %d: %s\n", tmpif->mi_ifname, pid, strerror(errno)); } } if (daemonize) exit(EXIT_SUCCESS); if (waitpid(pid, &status, 0) < 0) { fprintf(stderr, "failed to wait for '%d'\n", pid); exit(EXIT_FAILURE); } /* Call exit() directly on this function because it retuns an exit code. */ exit(lxc_error_set_and_log(pid, status)); } lxc-2.0.11/src/lxc/tools/lxc_snapshot.c0000644061062106075000000001567213435013473014716 00000000000000/* * * Copyright © 2013 Serge Hallyn . * Copyright © 2013 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 "confile.h" #include #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" #include "storage.h" #include "utils.h" static int my_parser(struct lxc_arguments *args, int c, char *arg); static const struct option my_longopts[] = { {"list", no_argument, 0, 'L'}, {"restore", required_argument, 0, 'r'}, {"newname", required_argument, 0, 'N'}, {"destroy", required_argument, 0, 'd'}, {"comment", required_argument, 0, 'c'}, {"showcomments", no_argument, 0, 'C'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-snapshot", .help = "\ --name=NAME [-P lxcpath] [-L [-C]] [-c commentfile] [-r snapname [-N newname]]\n\ \n\ lxc-snapshot snapshots a container\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -L, --list list all snapshots\n\ -r, --restore=NAME restore snapshot NAME, e.g. 'snap0'\n\ -N, --newname=NEWNAME NEWNAME for the restored container\n\ -d, --destroy=NAME destroy snapshot NAME, e.g. 'snap0'\n\ use ALL to destroy all snapshots\n\ -c, --comment=FILE add FILE as a comment\n\ -C, --showcomments show snapshot comments\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .checker = NULL, .task = SNAP, }; static int do_snapshot(struct lxc_container *c, char *commentfile); static int do_snapshot_destroy(struct lxc_container *c, char *snapname); static int do_snapshot_list(struct lxc_container *c, int print_comments); static int do_snapshot_restore(struct lxc_container *c, struct lxc_arguments *args); static int do_snapshot_task(struct lxc_container *c, enum task task); static void print_file(char *path); int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; int ret; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (geteuid()) { if (access(my_args.lxcpath[0], O_RDONLY) < 0) { fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(EXIT_FAILURE); } } c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "System error loading container\n"); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->lxc_conf) { fprintf(stderr, "No container config specified\n"); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } ret = do_snapshot_task(c, my_args.task); lxc_container_put(c); if (ret == 0) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } static int do_snapshot_task(struct lxc_container *c, enum task task) { int ret = 0; switch (task) { case DESTROY: ret = do_snapshot_destroy(c, my_args.snapname); break; case LIST: ret = do_snapshot_list(c, my_args.print_comments); break; case RESTORE: ret = do_snapshot_restore(c, &my_args); break; case SNAP: ret = do_snapshot(c, my_args.commentfile); break; default: ret = 0; break; } return ret; } static int my_parser(struct lxc_arguments *args, int c, char *arg) { switch (c) { case 'L': args->task = LIST; break; case 'r': args->task = RESTORE; args->snapname = arg; break; case 'N': args->newname = arg; break; case 'd': args->task = DESTROY; args->snapname = arg; break; case 'c': args->commentfile = arg; break; case 'C': args->print_comments = 1; break; } return 0; } static int do_snapshot(struct lxc_container *c, char *commentfile) { int ret; ret = c->snapshot(c, commentfile); if (ret < 0) { fprintf(stderr, "Error creating a snapshot\n"); return -1; } return 0; } static int do_snapshot_destroy(struct lxc_container *c, char *snapname) { bool ret; if (strcmp(snapname, "ALL") == 0) ret = c->snapshot_destroy_all(c); else ret = c->snapshot_destroy(c, snapname); if (!ret) { fprintf(stderr, "Error destroying snapshot %s\n", snapname); return -1; } return 0; } static int do_snapshot_list(struct lxc_container *c, int print_comments) { struct lxc_snapshot *s; int i, n; n = c->snapshot_list(c, &s); if (n < 0) { fprintf(stderr, "Error listing snapshots\n"); return -1; } if (n == 0) { printf("No snapshots\n"); return 0; } for (i = 0; i < n; i++) { printf("%s (%s) %s\n", s[i].name, s[i].lxcpath, s[i].timestamp); if (print_comments) print_file(s[i].comment_pathname); s[i].free(&s[i]); } free(s); return 0; } static int do_snapshot_restore(struct lxc_container *c, struct lxc_arguments *args) { int bret; /* When restoring a snapshot, the last optional argument if not given * explicitly via the corresponding command line option is the name to * use for the restored container. If no name is given, then the * original container will be destroyed and the restored container will * take its place. */ if ((!args->newname) && (args->argc > 1)) { lxc_error(args, "Too many arguments"); return -1; } if ((!args->newname) && (args->argc == 1)) args->newname = args->argv[0]; bret = c->snapshot_restore(c, args->snapname, args->newname); if (!bret) { fprintf(stderr, "Error restoring snapshot %s\n", args->snapname); return -1; } return 0; } static void print_file(char *path) { if (!path) return; FILE *f = fopen(path, "r"); char *line = NULL; size_t sz = 0; if (!f) return; while (getline(&line, &sz, f) != -1) { printf("%s", line); } free(line); fclose(f); } lxc-2.0.11/src/lxc/tools/lxc_cgroup.c0000644061062106075000000000722013435013473014344 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" static int my_checker(const struct lxc_arguments* args) { if (!args->argc) { lxc_error(args, "missing state object"); return -1; } return 0; } static const struct option my_longopts[] = { LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-cgroup", .help = "\ --name=NAME state-object [value]\n\ \n\ Get or set the value of a state object (for example, 'cpuset.cpus')\n\ in the container's cgroup for the corresponding subsystem.\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = NULL, .checker = my_checker, }; int main(int argc, char *argv[]) { char *state_object = NULL, *value = NULL; struct lxc_container *c; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); state_object = my_args.argv[0]; c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) exit(EXIT_FAILURE); if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s:%s\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->is_running(c)) { fprintf(stderr, "'%s:%s' is not running\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if ((my_args.argc) > 1) { value = my_args.argv[1]; if (!c->set_cgroup_item(c, state_object, value)) { fprintf(stderr, "failed to assign '%s' value to '%s' for '%s'\n", value, state_object, my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } } else { char buffer[MAXPATHLEN]; int ret = c->get_cgroup_item(c, state_object, buffer, MAXPATHLEN); if (ret < 0) { fprintf(stderr, "failed to retrieve value of '%s' for '%s:%s'\n", state_object, my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } printf("%*s", ret, buffer); } lxc_container_put(c); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_freeze.c0000644061062106075000000000552413435013473014332 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" static const struct option my_longopts[] = { LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-freeze", .help = "\ --name=NAME\n\ \n\ lxc-freeze freezes a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = NULL, .checker = NULL, }; int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "No such container: %s:%s\n", my_args.lxcpath[0], my_args.name); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s:%s\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->freeze(c)) { fprintf(stderr, "Failed to freeze %s:%s\n", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } lxc_container_put(c); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_autostart.c0000644061062106075000000003122513435013473015075 00000000000000/* lxc_autostart * * Copyright © 2013 Stéphane Graber * Copyright © 2013 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "arguments.h" #include "list.h" #include "log.h" #include "utils.h" static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list); struct lxc_list *cmd_groups_list = NULL; static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'k': args->hardstop = 1; break; case 'L': args->list = 1; break; case 'r': args->reboot = 1; break; case 's': args->shutdown = 1; break; case 'a': args->all = 1; break; case 'A': args->ignore_auto = 1; break; case 'g': cmd_groups_list = accumulate_list(arg, ",", cmd_groups_list); break; case 't': if (lxc_safe_long(arg, &args->timeout) < 0) return -1; break; } return 0; } static const struct option my_longopts[] = { {"kill", no_argument, 0, 'k'}, {"list", no_argument, 0, 'L'}, {"reboot", no_argument, 0, 'r'}, {"shutdown", no_argument, 0, 's'}, {"all", no_argument, 0, 'a'}, {"ignore-auto", no_argument, 0, 'A'}, {"groups", required_argument, 0, 'g'}, {"timeout", required_argument, 0, 't'}, {"help", no_argument, 0, 'h'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-autostart", .help = "\ \n\ lxc-autostart managed auto-started containers\n\ \n\ Options:\n\ -k, --kill kill the containers instead of starting them\n\ -L, --list list all affected containers and wait delay\n\ -r, --reboot reboot the containers instead of starting them\n\ -s, --shutdown shutdown the containers instead of starting them\n\ \n\ -a, --all list all auto-started containers (ignore groups)\n\ -A, --ignore-auto ignore lxc.start.auto and select all matching containers\n\ -g, --groups list of groups (comma separated) to select\n\ -t, --timeout=T wait T seconds before hard-stopping\n", .options = my_longopts, .parser = my_parser, .checker = NULL, .timeout = 60, }; int list_contains_entry( char *str_ptr, struct lxc_list *p1 ) { struct lxc_list *it1; /* * If the entry is NULL or the empty string and the list * is NULL, we have a match */ if (! p1 && ! str_ptr) return 1; if (! p1 && ! *str_ptr) return 1; if (!p1) return 0; lxc_list_for_each(it1, p1) { if (strcmp(it1->elem, str_ptr) == 0) return 1; } return 0; } int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) { struct lxc_list *it1; struct lxc_list *it2; if (!p1 && !p2) return 1; if (!p1) return 0; if (!p2) return 0; lxc_list_for_each(it1, p1) { lxc_list_for_each(it2, p2) { if (strcmp(it1->elem, it2->elem) == 0) return 1; } } return 0; } /* * This is a variation of get_list below it. * This version allows two additional features. * If a list is passed to it, it adds to it. * It allows for empty entries (i.e. "group1,,group2") generating * and empty list entry. */ static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list) { char *workstr = NULL; char *workptr = NULL; char *next_ptr = NULL; struct lxc_list *worklist; struct lxc_list *workstr_list; workstr = strdup(input); if (!workstr) { return NULL; } workstr_list = str_list; if ( ! workstr_list ) { workstr_list = malloc(sizeof(*workstr_list)); lxc_list_init(workstr_list); } for (workptr = workstr; workptr; workptr = next_ptr) { /* * We can't use strtok_r here because it collapses * multiple delimiters into 1 making empty fields * impossible... */ /* token = strtok_r(workptr, delimiter, &sptr); */ next_ptr = strchr( workptr, *delimiter ); if( next_ptr ) { *next_ptr++ = '\0'; } /* * At this point, we'd like to check to see if this * group is already contained in the list and ignore * it if it is... This also helps us with any * corner cases where a string begins or ends with a * delimiter. */ if ( list_contains_entry( workptr, workstr_list ) ) { if ( *workptr ) { fprintf(stderr, "Duplicate group \"%s\" in list - ignoring\n", workptr ); fflush(stderr); } else { fprintf(stderr, "Duplicate NULL group in list - ignoring\n" ); fflush(stderr); } } else { worklist = malloc(sizeof(*worklist)); if (!worklist) break; worklist->elem = strdup(workptr); if (!worklist->elem) { free(worklist); break; } lxc_list_add_tail(workstr_list, worklist); } } free(workstr); return workstr_list; } static struct lxc_list *get_list(char *input, char *delimiter) { char *workstr = NULL; char *workptr = NULL; char *sptr = NULL; char *token = NULL; struct lxc_list *worklist; struct lxc_list *workstr_list; workstr_list = malloc(sizeof(*workstr_list)); lxc_list_init(workstr_list); workstr = strdup(input); if (!workstr) { free(workstr_list); return NULL; } for (workptr = workstr;;workptr = NULL) { token = strtok_r(workptr, delimiter, &sptr); if (!token) { break; } worklist = malloc(sizeof(*worklist)); if (!worklist) break; worklist->elem = strdup(token); if (!worklist->elem) { free(worklist); break; } lxc_list_add_tail(workstr_list, worklist); } free(workstr); return workstr_list; } static struct lxc_list *get_config_list(struct lxc_container *c, char *key) { int len = 0; char* value = NULL; struct lxc_list *config_list = NULL; len = c->get_config_item(c, key, NULL, 0); if (len < 0) return NULL; value = (char*) malloc(sizeof(char)*len + 1); if (value == NULL) return NULL; if (c->get_config_item(c, key, value, len + 1) != len) { free(value); return NULL; } if (strlen(value) == 0) { free(value); return NULL; } config_list = get_list(value, "\n"); free(value); return config_list; } static int get_config_integer(struct lxc_container *c, char *key) { int len = 0; int ret = 0; char* value = NULL; len = c->get_config_item(c, key, NULL, 0); if (len < 0) return 0; value = (char*) malloc(sizeof(char)*len + 1); if (value == NULL) return 0; if (c->get_config_item(c, key, value, len + 1) != len) { free(value); return 0; } if (lxc_safe_int(value, &ret) < 0) printf("Could not parse config item.\n"); free(value); return ret; } static int cmporder(const void *p1, const void *p2) { struct lxc_container *c1 = *(struct lxc_container **)p1; struct lxc_container *c2 = *(struct lxc_container **)p2; int c1_order = get_config_integer(c1, "lxc.start.order"); int c2_order = get_config_integer(c2, "lxc.start.order"); if (c1_order == c2_order) return strcmp(c1->name, c2->name); else return (c1_order - c2_order); } static int toss_list( struct lxc_list *c_groups_list ) { struct lxc_list *it, *next; if (c_groups_list) { lxc_list_for_each_safe(it, c_groups_list, next) { lxc_list_del(it); free(it->elem); free(it); } free(c_groups_list); } return 1; } int main(int argc, char *argv[]) { int count = 0; int i = 0; int ret = 0; struct lxc_container **containers = NULL; struct lxc_list **c_groups_lists = NULL; struct lxc_list *cmd_group; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); count = list_defined_containers(my_args.lxcpath[0], NULL, &containers); if (count < 0) exit(EXIT_FAILURE); if (!my_args.all) { /* Allocate an array for our container group lists */ c_groups_lists = calloc( count, sizeof( struct lxc_list * ) ); } qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder); if (cmd_groups_list && my_args.all) { fprintf(stderr, "Specifying -a (all) with -g (groups) doesn't make sense. All option overrides.\n"); fflush(stderr); } if (!cmd_groups_list) { /* * We need a default cmd_groups_list even for the -a * case in order to force a pass through the loop for * the NULL group. This, someday, could be taken from * a config file somewhere... */ cmd_groups_list = accumulate_list( "" , ",", NULL ); } lxc_list_for_each(cmd_group, cmd_groups_list) { /* * Prograpmmers Note: * Because we may take several passes through the container list * We'll switch on if the container pointer is NULL and if we process a * container (run it or decide to ignore it) and call lxc_container_put * then we'll NULL it out and not check it again. */ for (i = 0; i < count; i++) { struct lxc_container *c = containers[i]; if (!c) /* Skip - must have been already processed */ continue; /* * We haven't loaded the container groups yet so * these next two checks don't need to free them * if they fail. They'll fail on the first pass. */ if (!c->may_control(c)) { /* We're done with this container */ if ( lxc_container_put(c) > 0 ) containers[i] = NULL; continue; } if (!my_args.ignore_auto && get_config_integer(c, "lxc.start.auto") != 1) { /* We're done with this container */ if ( lxc_container_put(c) > 0 ) containers[i] = NULL; continue; } if (!my_args.all) { /* Filter by group */ if( ! c_groups_lists[i] ) { /* Now we're loading up a container's groups */ c_groups_lists[i] = get_config_list(c, "lxc.group"); } ret = list_contains_entry(cmd_group->elem, c_groups_lists[i]); if ( ret == 0 ) { /* Not in the target group this pass */ /* Leave in the list for subsequent passes */ continue; } } /* We have a candidate continer to process */ c->want_daemonize(c, 1); if (my_args.shutdown) { /* Shutdown the container */ if (c->is_running(c)) { if (my_args.list) { printf("%s\n", c->name); fflush(stdout); } else { if (!c->shutdown(c, my_args.timeout)) { if (!c->stop(c)) { fprintf(stderr, "Error shutting down container: %s\n", c->name); fflush(stderr); } } } } } else if (my_args.hardstop) { /* Kill the container */ if (c->is_running(c)) { if (my_args.list) { printf("%s\n", c->name); fflush(stdout); } else { if (!c->stop(c)) { fprintf(stderr, "Error killing container: %s\n", c->name); fflush(stderr); } } } } else if (my_args.reboot) { /* Reboot the container */ if (c->is_running(c)) { if (my_args.list) { printf("%s %d\n", c->name, get_config_integer(c, "lxc.start.delay")); fflush(stdout); } else { if (!c->reboot(c)) { fprintf(stderr, "Error rebooting container: %s\n", c->name); fflush(stderr); } else sleep(get_config_integer(c, "lxc.start.delay")); } } } else { /* Start the container */ if (!c->is_running(c)) { if (my_args.list) { printf("%s %d\n", c->name, get_config_integer(c, "lxc.start.delay")); fflush(stdout); } else { if (!c->start(c, 0, NULL)) { fprintf(stderr, "Error starting container: %s\n", c->name); fflush(stderr); } else sleep(get_config_integer(c, "lxc.start.delay")); } } } /* * If we get this far and we haven't hit any skip "continue" * then we're done with this container... We can dump any * c_groups_list and the container itself. */ if ( lxc_container_put(c) > 0 ) { containers[i] = NULL; } if ( c_groups_lists ) { toss_list(c_groups_lists[i]); c_groups_lists[i] = NULL; } } } /* clean up any lingering detritus */ for (i = 0; i < count; i++) { if ( containers[i] ) { lxc_container_put(containers[i]); } if ( c_groups_lists && c_groups_lists[i] ) { toss_list(c_groups_lists[i]); } } free(c_groups_lists); toss_list( cmd_groups_list ); free(containers); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_copy.c0000644061062106075000000004522413435013473014025 00000000000000/* * * Copyright © 2015 Christian Brauner . * * 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. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "attach.h" #include "log.h" #include "confile.h" #include "arguments.h" #include "lxc.h" #include "conf.h" #include "state.h" #include "storage.h" #include "utils.h" #ifndef HAVE_GETSUBOPT #include <../include/getsubopt.h> #endif enum mnttype { LXC_MNT_BIND, LXC_MNT_AUFS, LXC_MNT_OVL, }; struct mnts { enum mnttype mnt_type; char *src; char *dest; char *options; char *upper; char *workdir; char *lower; }; static unsigned int mnt_table_size = 0; static struct mnts *mnt_table = NULL; static int my_parser(struct lxc_arguments *args, int c, char *arg); static const struct option my_longopts[] = { { "newname", required_argument, 0, 'N'}, { "newpath", required_argument, 0, 'p'}, { "rename", no_argument, 0, 'R'}, { "snapshot", no_argument, 0, 's'}, { "foreground", no_argument, 0, 'F'}, { "daemon", no_argument, 0, 'd'}, { "ephemeral", no_argument, 0, 'e'}, { "mount", required_argument, 0, 'm'}, { "backingstorage", required_argument, 0, 'B'}, { "fssize", required_argument, 0, 'L'}, { "keepdata", no_argument, 0, 'D'}, { "keepname", no_argument, 0, 'K'}, { "keepmac", no_argument, 0, 'M'}, LXC_COMMON_OPTIONS }; /* mount keys */ static char *const keys[] = { [LXC_MNT_BIND] = "bind", [LXC_MNT_AUFS] = "aufs", [LXC_MNT_OVL] = "overlay", NULL }; static struct lxc_arguments my_args = { .progname = "lxc-copy", .help = "\n\ --name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]] -- hook options\n\ --name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,aufs,overlay}=/src:/dest] -- hook options\n\ --name=NAME [-P lxcpath] -N newname -R\n\ \n\ lxc-copy clone a container\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -N, --newname=NEWNAME NEWNAME for the restored container\n\ -p, --newpath=NEWPATH NEWPATH for the container to be stored\n\ -R, --rename rename container\n\ -s, --snapshot create snapshot instead of clone\n\ -F, --foreground start with current tty attached to /dev/console\n\ -d, --daemon daemonize the container (default)\n\ -e, --ephemeral start ephemeral container\n\ -m, --mount directory to mount into container, either \n\ {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\ -B, --backingstorage=TYPE backingstorage type for the container\n\ -L, --fssize size of the new block device for block device containers\n\ -D, --keedata pass together with -e start a persistent snapshot \n\ -K, --keepname keep the hostname of the original container\n\ -- hook options arguments passed to the hook program\n\ -M, --keepmac keep the MAC address of the original container\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .task = CLONE, .daemonize = 1, .quiet = false, }; static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, enum mnttype type); static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_arguments *arg); static char *construct_path(char *path, bool as_prefix); static char *set_mnt_entry(struct mnts *m); static int do_clone(struct lxc_container *c, char *newname, char *newpath, int flags, char *bdevtype, uint64_t fssize, enum task task, char **args); static int do_clone_ephemeral(struct lxc_container *c, struct lxc_arguments *arg, char **args, int flags); static int do_clone_rename(struct lxc_container *c, char *newname); static int do_clone_task(struct lxc_container *c, enum task task, int flags, char **args); static void free_mnts(void); static uint64_t get_fssize(char *s); static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters); static int parse_aufs_mnt(char *mntstring, enum mnttype type); static int parse_bind_mnt(char *mntstring, enum mnttype type); static int parse_ovl_mnt(char *mntstring, enum mnttype type); int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; int flags = 0; int ret = EXIT_FAILURE; if (lxc_arguments_parse(&my_args, argc, argv)) exit(ret); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); if (geteuid()) { if (access(my_args.lxcpath[0], O_RDONLY) < 0) { if (!my_args.quiet) fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(ret); } } if (!my_args.newname && !(my_args.task == DESTROY)) { if (!my_args.quiet) printf("Error: You must provide a NEWNAME for the clone.\n"); exit(ret); } if (my_args.task == SNAP || my_args.task == DESTROY) flags |= LXC_CLONE_SNAPSHOT; if (my_args.keepname) flags |= LXC_CLONE_KEEPNAME; if (my_args.keepmac) flags |= LXC_CLONE_KEEPMACADDR; if (!my_args.newpath) my_args.newpath = (char *)my_args.lxcpath[0]; c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) exit(ret); if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); goto out; } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); goto out; } } if (!c->may_control(c)) { if (!my_args.quiet) fprintf(stderr, "Insufficent privileges to control %s\n", c->name); goto out; } if (!c->is_defined(c)) { if (!my_args.quiet) fprintf(stderr, "Error: container %s is not defined\n", c->name); goto out; } ret = do_clone_task(c, my_args.task, flags, &argv[optind]); out: lxc_container_put(c); if (ret == 0) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, enum mnttype type) { struct mnts *m, *n; n = realloc(*mnts, (*num + 1) * sizeof(struct mnts)); if (!n) return NULL; *mnts = n; m = *mnts + *num; (*num)++; *m = (struct mnts) {.mnt_type = type}; return m; } static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_arguments *arg) { char upperdir[MAXPATHLEN]; char workdir[MAXPATHLEN]; unsigned int i; int ret; struct mnts *m = NULL; for (i = 0, m = mnts; i < num; i++, m++) { if ((m->mnt_type == LXC_MNT_OVL) || (m->mnt_type == LXC_MNT_AUFS)) { ret = snprintf(upperdir, MAXPATHLEN, "%s/%s/delta#XXXXXX", arg->newpath, arg->newname); if (ret < 0 || ret >= MAXPATHLEN) return -1; if (!mkdtemp(upperdir)) return -1; m->upper = strdup(upperdir); if (!m->upper) return -1; } if (m->mnt_type == LXC_MNT_OVL) { ret = snprintf(workdir, MAXPATHLEN, "%s/%s/work#XXXXXX", arg->newpath, arg->newname); if (ret < 0 || ret >= MAXPATHLEN) return -1; if (!mkdtemp(workdir)) return -1; m->workdir = strdup(workdir); if (!m->workdir) return -1; } } return 0; } static char *construct_path(char *path, bool as_prefix) { char **components = NULL; char *cleanpath = NULL; components = lxc_normalize_path(path); if (!components) return NULL; cleanpath = lxc_string_join("/", (const char **)components, as_prefix); lxc_free_array((void **)components, free); return cleanpath; } static char *set_mnt_entry(struct mnts *m) { char *mntentry = NULL; int ret = 0; size_t len = 0; if (m->mnt_type == LXC_MNT_AUFS) { len = strlen(" aufs br==rw:=ro,xino=,create=dir") + 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) + strlen(m->workdir) + 1; mntentry = malloc(len); if (!mntentry) goto err; ret = snprintf(mntentry, len, "%s %s aufs br=%s=rw:%s=ro,xino=%s,create=dir", m->src, m->dest, m->upper, m->src, m->workdir); if (ret < 0 || (size_t)ret >= len) goto err; } else if (m->mnt_type == LXC_MNT_OVL) { len = strlen(" overlay lowerdir=,upperdir=,workdir=,create=dir") + 2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) + strlen(m->workdir) + 1; mntentry = malloc(len); if (!mntentry) goto err; ret = snprintf(mntentry, len, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir", m->src, m->dest, m->src, m->upper, m->workdir); if (ret < 0 || (size_t)ret >= len) goto err; } else if (m->mnt_type == LXC_MNT_BIND) { len = strlen(" none bind,optional,, 0 0") + strlen(is_dir(m->src) ? "create=dir" : "create=file") + strlen(m->src) + strlen(m->dest) + strlen(m->options) + 1; mntentry = malloc(len); if (!mntentry) goto err; ret = snprintf(mntentry, len, "%s %s none bind,optional,%s,%s 0 0", m->src, m->dest, m->options, is_dir(m->src) ? "create=dir" : "create=file"); if (ret < 0 || (size_t)ret >= len) goto err; } return mntentry; err: free(mntentry); return NULL; } static int do_clone(struct lxc_container *c, char *newname, char *newpath, int flags, char *bdevtype, uint64_t fssize, enum task task, char **args) { struct lxc_container *clone; clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize, args); if (!clone) { if (!my_args.quiet) fprintf(stderr, "clone failed\n"); return -1; } lxc_container_put(clone); return 0; } static int do_clone_ephemeral(struct lxc_container *c, struct lxc_arguments *arg, char **args, int flags) { char randname[MAXPATHLEN]; unsigned int i; int ret = 0; bool bret = true, started = false; struct lxc_container *clone; lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; attach_options.env_policy = LXC_ATTACH_CLEAR_ENV; if (!arg->newname) { ret = snprintf(randname, MAXPATHLEN, "%s/%s_XXXXXX", arg->newpath, arg->name); if (ret < 0 || ret >= MAXPATHLEN) return -1; if (!mkdtemp(randname)) return -1; if (chmod(randname, 0770) < 0) { (void)remove(randname); return -1; } arg->newname = randname + strlen(arg->newpath) + 1; } clone = c->clone(c, arg->newname, arg->newpath, flags, arg->bdevtype, NULL, arg->fssize, args); if (!clone) return -1; if (!arg->keepdata) if (!clone->set_config_item(clone, "lxc.ephemeral", "1")) goto destroy_and_put; /* allocate and create random upper- and workdirs for overlay mounts */ if (mk_rand_ovl_dirs(mnt_table, mnt_table_size, arg) < 0) goto destroy_and_put; /* allocate and set mount entries */ struct mnts *n = NULL; for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) { char *mntentry = NULL; mntentry = set_mnt_entry(n); if (!mntentry) goto destroy_and_put; bret = clone->set_config_item(clone, "lxc.mount.entry", mntentry); free(mntentry); if (!bret) goto destroy_and_put; } if (!clone->save_config(clone, NULL)) goto destroy_and_put; if (!my_args.quiet) printf("Created %s as clone of %s\n", arg->newname, arg->name); if (!arg->daemonize && arg->argc) { clone->want_daemonize(clone, true); arg->daemonize = 1; } else if (!arg->daemonize) { clone->want_daemonize(clone, false); } started = clone->start(clone, 0, NULL); if (!started) goto destroy_and_put; if (arg->daemonize && arg->argc) { ret = clone->attach_run_wait(clone, &attach_options, arg->argv[0], (const char *const *)arg->argv); if (ret < 0) goto destroy_and_put; clone->shutdown(clone, -1); } free_mnts(); lxc_container_put(clone); return 0; destroy_and_put: if (started) clone->shutdown(clone, -1); if (!started || clone->lxc_conf->ephemeral != 1) clone->destroy(clone); free_mnts(); lxc_container_put(clone); return -1; } static int do_clone_rename(struct lxc_container *c, char *newname) { if (!c->rename(c, newname)) { fprintf(stderr, "Error: Renaming container %s to %s failed\n", c->name, newname); return -1; } return 0; } static int do_clone_task(struct lxc_container *c, enum task task, int flags, char **args) { int ret = 0; switch (task) { case DESTROY: ret = do_clone_ephemeral(c, &my_args, args, flags); break; case RENAME: ret = do_clone_rename(c, my_args.newname); break; default: ret = do_clone(c, my_args.newname, my_args.newpath, flags, my_args.bdevtype, my_args.fssize, my_args.task, args); break; } return ret; } static void free_mnts() { unsigned int i; struct mnts *n = NULL; for (i = 0, n = mnt_table; i < mnt_table_size; i++, n++) { free(n->src); free(n->dest); free(n->options); free(n->upper); free(n->workdir); } free(mnt_table); mnt_table = NULL; mnt_table_size = 0; } /* we pass fssize in bytes */ static uint64_t get_fssize(char *s) { uint64_t ret; char *end; ret = strtoull(s, &end, 0); if (end == s) { if (!my_args.quiet) fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s); return 0; } while (isblank(*end)) end++; if (*end == '\0') { ret *= 1024ULL * 1024ULL; // MB by default } else if (*end == 'b' || *end == 'B') { ret *= 1ULL; } else if (*end == 'k' || *end == 'K') { ret *= 1024ULL; } else if (*end == 'm' || *end == 'M') { ret *= 1024ULL * 1024ULL; } else if (*end == 'g' || *end == 'G') { ret *= 1024ULL * 1024ULL * 1024ULL; } else if (*end == 't' || *end == 'T') { ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; } else { if (!my_args.quiet) fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end, s); return 0; } return ret; } static int my_parser(struct lxc_arguments *args, int c, char *arg) { char *subopts = NULL; char *mntparameters = NULL; switch (c) { case 'N': args->newname = arg; break; case 'p': args->newpath = arg; break; case 'R': args->task = RENAME; break; case 's': args->task = SNAP; break; case 'F': args->daemonize = 0; break; case 'd': args->daemonize = 1; break; case 'e': args->task = DESTROY; break; case 'm': subopts = optarg; if (parse_mntsubopts(subopts, keys, mntparameters) < 0) return -1; break; case 'B': if (strcmp(arg, "overlay") == 0) arg = "overlayfs"; args->bdevtype = arg; break; case 'L': args->fssize = get_fssize(optarg); break; case 'D': args->keepdata = 1; break; case 'K': args->keepname = 1; break; case 'M': args->keepmac = 1; break; } return 0; } static int parse_aufs_mnt(char *mntstring, enum mnttype type) { int len = 0; const char *xinopath = "/dev/shm/aufs.xino"; char **mntarray = NULL; struct mnts *m = NULL; m = add_mnt(&mnt_table, &mnt_table_size, type); if (!m) goto err; mntarray = lxc_string_split(mntstring, ':'); if (!mntarray) goto err; m->src = construct_path(mntarray[0], true); if (!m->src) goto err; len = lxc_array_len((void **)mntarray); if (len == 1) /* aufs=src */ m->dest = construct_path(mntarray[0], false); else if (len == 2) /* aufs=src:dest */ m->dest = construct_path(mntarray[1], false); else printf("Excess elements in mount specification\n"); if (!m->dest) goto err; m->workdir = strdup(xinopath); if (!m->workdir) goto err; lxc_free_array((void **)mntarray, free); return 0; err: free_mnts(); lxc_free_array((void **)mntarray, free); return -1; } static int parse_bind_mnt(char *mntstring, enum mnttype type) { int len = 0; char **mntarray = NULL; struct mnts *m = NULL; m = add_mnt(&mnt_table, &mnt_table_size, type); if (!m) goto err; mntarray = lxc_string_split(mntstring, ':'); if (!mntarray) goto err; m->src = construct_path(mntarray[0], true); if (!m->src) goto err; len = lxc_array_len((void **)mntarray); if (len == 1) { /* bind=src */ m->dest = construct_path(mntarray[0], false); } else if (len == 2) { /* bind=src:option or bind=src:dest */ if (strncmp(mntarray[1], "rw", strlen(mntarray[1])) == 0) m->options = strdup("rw"); if (strncmp(mntarray[1], "ro", strlen(mntarray[1])) == 0) m->options = strdup("ro"); if (m->options) m->dest = construct_path(mntarray[0], false); else m->dest = construct_path(mntarray[1], false); } else if (len == 3) { /* bind=src:dest:option */ m->dest = construct_path(mntarray[1], false); m->options = strdup(mntarray[2]); } else { printf("Excess elements in mount specification\n"); } if (!m->dest) goto err; if (!m->options) m->options = strdup("rw"); if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) && strncmp(m->options, "ro", strlen(m->options)))) goto err; lxc_free_array((void **)mntarray, free); return 0; err: free_mnts(); lxc_free_array((void **)mntarray, free); return -1; } static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters) { while (*subopts != '\0') { switch (getsubopt(&subopts, keys, &mntparameters)) { case LXC_MNT_BIND: if (parse_bind_mnt(mntparameters, LXC_MNT_BIND) < 0) return -1; break; case LXC_MNT_OVL: if (parse_ovl_mnt(mntparameters, LXC_MNT_OVL) < 0) return -1; break; case LXC_MNT_AUFS: if (parse_aufs_mnt(mntparameters, LXC_MNT_AUFS) < 0) return -1; break; default: break; } } return 0; } static int parse_ovl_mnt(char *mntstring, enum mnttype type) { int len = 0; char **mntarray = NULL; struct mnts *m; m = add_mnt(&mnt_table, &mnt_table_size, type); if (!m) goto err; mntarray = lxc_string_split(mntstring, ':'); if (!mntarray) goto err; m->src = construct_path(mntarray[0], true); if (!m->src) goto err; len = lxc_array_len((void **)mntarray); if (len == 1) /* overlay=src */ m->dest = construct_path(mntarray[0], false); else if (len == 2) /* overlay=src:dest */ m->dest = construct_path(mntarray[1], false); else printf("Excess elements in mount specification\n"); if (!m->dest) goto err; lxc_free_array((void **)mntarray, free); return 0; err: free_mnts(); lxc_free_array((void **)mntarray, free); return -1; } lxc-2.0.11/src/lxc/tools/lxc_stop.c0000644061062106075000000001430613435013473014035 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "lxc.h" #include "log.h" #include "arguments.h" #include "commands.h" #include "utils.h" #define OPT_NO_LOCK OPT_USAGE + 1 #define OPT_NO_KILL OPT_USAGE + 2 static int my_parser(struct lxc_arguments *args, int c, char *arg) { switch (c) { case 'r': args->reboot = 1; break; case 'W': args->nowait = 1; break; case 't': if (lxc_safe_long(arg, &args->timeout) < 0) return -1; break; case 'k': args->hardstop = 1; break; case OPT_NO_LOCK: args->nolock = 1; break; case OPT_NO_KILL: args->nokill = 1; break; } return 0; } static const struct option my_longopts[] = { {"reboot", no_argument, 0, 'r'}, {"nowait", no_argument, 0, 'W'}, {"timeout", required_argument, 0, 't'}, {"kill", no_argument, 0, 'k'}, {"nokill", no_argument, 0, OPT_NO_KILL}, {"nolock", no_argument, 0, OPT_NO_LOCK}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-stop", .help = "\ --name=NAME\n\ \n\ lxc-stop stops a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -r, --reboot reboot the container\n\ -W, --nowait don't wait for shutdown or reboot to complete\n\ -t, --timeout=T wait T seconds before hard-stopping\n\ -k, --kill kill container rather than request clean shutdown\n\ --nolock Avoid using API locks\n\ --nokill Only request clean shutdown, don't force kill after timeout\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .checker = NULL, .timeout = -2, }; /* returns -1 on failure, 0 on success */ static int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c) { int ret; pid_t pid; pid_t newpid; int timeout = a->timeout; pid = c->init_pid(c); if (pid == -1) return -1; if (!c->reboot(c)) return -1; if (a->nowait) return 0; if (timeout == 0) goto out; for (;;) { /* can we use c-> wait for this, assuming it will * re-enter RUNNING? For now just sleep */ int elapsed_time, curtime = 0; struct timeval tv; newpid = c->init_pid(c); if (newpid != -1 && newpid != pid) return 0; if (timeout != -1) { ret = gettimeofday(&tv, NULL); if (ret) break; curtime = tv.tv_sec; } sleep(1); if (timeout != -1) { ret = gettimeofday(&tv, NULL); if (ret) break; elapsed_time = tv.tv_sec - curtime; if (timeout - elapsed_time <= 0) break; timeout -= elapsed_time; } } out: newpid = c->init_pid(c); if (newpid == -1 || newpid == pid) { printf("Reboot did not complete before timeout\n"); return -1; } return 0; } int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; bool s; int ret = EXIT_FAILURE; if (lxc_arguments_parse(&my_args, argc, argv)) exit(ret); log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); /* Set default timeout */ if (my_args.timeout == -2) { if (my_args.hardstop) my_args.timeout = 0; else my_args.timeout = 60; } if (my_args.nowait) my_args.timeout = 0; /* some checks */ if (!my_args.hardstop && my_args.timeout < -1) { fprintf(stderr, "invalid timeout\n"); exit(ret); } if (my_args.hardstop && my_args.nokill) { fprintf(stderr, "-k can't be used with --nokill\n"); exit(ret); } if (my_args.hardstop && my_args.reboot) { fprintf(stderr, "-k can't be used with -r\n"); exit(ret); } if (my_args.hardstop && my_args.timeout) { fprintf(stderr, "-k doesn't allow timeouts\n"); exit(ret); } if (my_args.nolock && !my_args.hardstop) { fprintf(stderr, "--nolock may only be used with -k\n"); exit(ret); } /* shortcut - if locking is bogus, we should be able to kill * containers at least */ if (my_args.nolock) { ret = lxc_cmd_stop(my_args.name, my_args.lxcpath[0]); exit(ret); } c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "Error opening container\n"); goto out; } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); goto out; } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); goto out; } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", c->name); goto out; } if (!c->is_running(c)) { fprintf(stderr, "%s is not running\n", c->name); /* Per our manpage we need to exit with exit code: * 2: The specified container exists but was not running. */ ret = 2; goto out; } /* kill */ if (my_args.hardstop) { ret = c->stop(c) ? EXIT_SUCCESS : EXIT_FAILURE; goto out; } /* reboot */ if (my_args.reboot) { ret = do_reboot_and_check(&my_args, c) < 0 ? EXIT_SUCCESS : EXIT_FAILURE; goto out; } /* shutdown */ s = c->shutdown(c, my_args.timeout); if (!s) { if (my_args.timeout == 0) ret = EXIT_SUCCESS; else if (my_args.nokill) ret = EXIT_FAILURE; else ret = c->stop(c) ? EXIT_SUCCESS : EXIT_FAILURE; } else { ret = EXIT_SUCCESS; } out: lxc_container_put(c); exit(ret); } lxc-2.0.11/src/lxc/tools/lxc_top.c0000644061062106075000000003050113435013473013645 00000000000000/* * lxc: linux Container library * * Copyright © 2014 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "log.h" #include "lxc.h" #include "mainloop.h" #include "utils.h" #define USER_HZ 100 #define ESC "\033" #define TERMCLEAR ESC "[H" ESC "[J" #define TERMNORM ESC "[0m" #define TERMBOLD ESC "[1m" #define TERMRVRS ESC "[7m" struct stats { uint64_t mem_used; uint64_t mem_limit; uint64_t kmem_used; uint64_t kmem_limit; uint64_t cpu_use_nanos; uint64_t cpu_use_user; uint64_t cpu_use_sys; uint64_t blkio; }; struct ct { struct lxc_container *c; struct stats *stats; }; static int delay = 3; static char sort_by = 'n'; static int sort_reverse = 0; static struct termios oldtios; static struct ct *ct = NULL; static int ct_alloc_cnt = 0; static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'd': if (lxc_safe_int(arg, &delay) < 0) return -1; break; case 's': sort_by = arg[0]; break; case 'r': sort_reverse = 1; break; } return 0; } static const struct option my_longopts[] = { {"delay", required_argument, 0, 'd'}, {"sort", required_argument, 0, 's'}, {"reverse", no_argument, 0, 'r'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-top", .help = "\ \n\ \n\ lxc-top monitors the state of the active containers\n\ \n\ Options :\n\ -d, --delay delay in seconds between refreshes (default: 3.0)\n\ -s, --sort sort by [n,c,b,m] (default: n) where\n\ n = Name\n\ c = CPU use\n\ b = Block I/O use\n\ m = Memory use\n\ k = Kernel memory use\n\ -r, --reverse sort in reverse (descending) order\n", .name = ".*", .options = my_longopts, .parser = my_parser, .checker = NULL, .lxcpath_additional = -1, }; static void stdin_tios_restore(void) { (void)tcsetattr(0, TCSAFLUSH, &oldtios); fprintf(stderr, "\n"); } static int stdin_tios_setup(void) { struct termios newtios; if (!isatty(0)) { fprintf(stderr, "stdin is not a tty\n"); return -1; } if (tcgetattr(0, &oldtios)) { fprintf(stderr, "failed to get current terminal settings\n"); return -1; } newtios = oldtios; /* turn off echo and line buffering */ newtios.c_iflag &= ~IGNBRK; newtios.c_iflag &= BRKINT; newtios.c_lflag &= ~(ECHO|ICANON); newtios.c_cc[VMIN] = 1; newtios.c_cc[VTIME] = 0; if (tcsetattr(0, TCSAFLUSH, &newtios)) { fprintf(stderr, "failed to set new terminal settings\n"); return -1; } return 0; } static int stdin_tios_rows(void) { struct winsize wsz; if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0) return wsz.ws_row; return 25; } static int stdin_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { char *in_char = data; if (events & EPOLLIN) { int rc; rc = read(fd, in_char, sizeof(*in_char)); if (rc <= 0) *in_char = '\0'; } if (events & EPOLLHUP) *in_char = 'q'; return 1; } static void sig_handler(int sig) { exit(EXIT_SUCCESS); } static void size_humanize(unsigned long long val, char *buf, size_t bufsz) { int ret; if (val > 1 << 30) { ret = snprintf(buf, bufsz, "%u.%2.2u GiB", (unsigned int)(val >> 30), (unsigned int)(val & ((1 << 30) - 1)) / 10737419); } else if (val > 1 << 20) { unsigned int x = val + 5243; /* for rounding */ ret = snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (val > 1 << 10) { unsigned int x = val + 5; /* for rounding */ ret = snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { ret = snprintf(buf, bufsz, "%3u.00 ", (unsigned int)val); } if (ret < 0 || (size_t)ret >= bufsz) fprintf(stderr, "Failed to create string\n"); } static uint64_t stat_get_int(struct lxc_container *c, const char *item) { char buf[80]; int len; uint64_t val; len = c->get_cgroup_item(c, item, buf, sizeof(buf)); if (len <= 0) { fprintf(stderr, "unable to read cgroup item %s\n", item); return 0; } val = strtoull(buf, NULL, 0); return val; } static uint64_t stat_match_get_int(struct lxc_container *c, const char *item, const char *match, int column) { char buf[4096]; int i,j,len; uint64_t val = 0; char **lines, **cols; size_t matchlen; len = c->get_cgroup_item(c, item, buf, sizeof(buf)); if (len <= 0) { fprintf(stderr, "unable to read cgroup item %s\n", item); goto out; } lines = lxc_string_split_and_trim(buf, '\n'); if (!lines) goto out; matchlen = strlen(match); for (i = 0; lines[i]; i++) { if (strncmp(lines[i], match, matchlen) == 0) { cols = lxc_string_split_and_trim(lines[i], ' '); if (!cols) goto err1; for (j = 0; cols[j]; j++) { if (j == column) { val = strtoull(cols[j], NULL, 0); break; } } lxc_free_array((void **)cols, free); break; } } err1: lxc_free_array((void **)lines, free); out: return val; } static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total) { ct->c = c; ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes"); ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes"); ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes"); ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes"); ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage"); ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1); ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1); ct->stats->blkio = stat_match_get_int(c, "blkio.throttle.io_service_bytes", "Total", 1); if (total) { total->mem_used = total->mem_used + ct->stats->mem_used; total->mem_limit = total->mem_limit + ct->stats->mem_limit; total->kmem_used = total->kmem_used + ct->stats->kmem_used; total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit; total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos; total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user; total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys; total->blkio = total->blkio + ct->stats->blkio; } } static void stats_print_header(struct stats *stats) { printf(TERMRVRS TERMBOLD); printf("%-18s %12s %12s %12s %14s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem"); if (stats->kmem_used > 0) printf(" %10s", "KMem"); printf("\n"); printf("%-18s %12s %12s %12s %14s %10s", "Name", "Used", "Sys", "User", "Total", "Used"); if (stats->kmem_used > 0) printf(" %10s", "Used"); printf("\n"); printf(TERMNORM); } static void stats_print(const char *name, const struct stats *stats, const struct stats *total) { char blkio_str[20]; char mem_used_str[20]; char kmem_used_str[20]; size_humanize(stats->blkio, blkio_str, sizeof(blkio_str)); size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str)); printf("%-18.18s %12.2f %12.2f %12.2f %14s %10s", name, (float)stats->cpu_use_nanos / 1000000000, (float)stats->cpu_use_sys / USER_HZ, (float)stats->cpu_use_user / USER_HZ, blkio_str, mem_used_str); if (total->kmem_used > 0) { size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str)); printf(" %10s", kmem_used_str); } } static int cmp_name(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; const struct ct *ct2 = sct2; if (sort_reverse) return strcmp(ct2->c->name, ct1->c->name); return strcmp(ct1->c->name, ct2->c->name); } static int cmp_cpuuse(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; const struct ct *ct2 = sct2; if (sort_reverse) return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos; return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos; } static int cmp_blkio(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; const struct ct *ct2 = sct2; if (sort_reverse) return ct2->stats->blkio < ct1->stats->blkio; return ct1->stats->blkio < ct2->stats->blkio; } static int cmp_memory(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; const struct ct *ct2 = sct2; if (sort_reverse) return ct2->stats->mem_used < ct1->stats->mem_used; return ct1->stats->mem_used < ct2->stats->mem_used; } static int cmp_kmemory(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; const struct ct *ct2 = sct2; if (sort_reverse) return ct2->stats->kmem_used < ct1->stats->kmem_used; return ct1->stats->kmem_used < ct2->stats->kmem_used; } static void ct_sort(int active) { int (*cmp_func)(const void *, const void *); switch(sort_by) { default: case 'n': cmp_func = cmp_name; break; case 'c': cmp_func = cmp_cpuuse; break; case 'b': cmp_func = cmp_blkio; break; case 'm': cmp_func = cmp_memory; break; case 'k': cmp_func = cmp_kmemory; break; } qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func); } static void ct_free(void) { int i; for (i = 0; i < ct_alloc_cnt; i++) { if (ct[i].c) { lxc_container_put(ct[i].c); ct[i].c = NULL; } free(ct[i].stats); ct[i].stats = NULL; } } static void ct_realloc(int active_cnt) { int i; if (active_cnt > ct_alloc_cnt) { ct_free(); ct = realloc(ct, sizeof(*ct) * active_cnt); if (!ct) { fprintf(stderr, "cannot alloc mem\n"); exit(EXIT_FAILURE); } for (i = 0; i < active_cnt; i++) { ct[i].stats = malloc(sizeof(*ct[0].stats)); if (!ct[i].stats) { fprintf(stderr, "cannot alloc mem\n"); exit(EXIT_FAILURE); } } ct_alloc_cnt = active_cnt; } } int main(int argc, char *argv[]) { struct lxc_epoll_descr descr; int ret, ct_print_cnt; char in_char; ret = EXIT_FAILURE; if (lxc_arguments_parse(&my_args, argc, argv)) goto out; ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */ if (stdin_tios_setup() < 0) { fprintf(stderr, "failed to setup terminal\n"); goto out; } /* ensure the terminal gets restored */ atexit(stdin_tios_restore); signal(SIGINT, sig_handler); signal(SIGQUIT, sig_handler); if (lxc_mainloop_open(&descr)) { fprintf(stderr, "failed to create mainloop\n"); goto out; } ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char); if (ret) { fprintf(stderr, "failed to add stdin handler\n"); ret = EXIT_FAILURE; goto err1; } for(;;) { struct lxc_container **active; int i, active_cnt; struct stats total; char total_name[30]; active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active); ct_realloc(active_cnt); memset(&total, 0, sizeof(total)); for (i = 0; i < active_cnt; i++) stats_get(active[i], &ct[i], &total); ct_sort(active_cnt); printf(TERMCLEAR); stats_print_header(&total); for (i = 0; i < active_cnt && i < ct_print_cnt; i++) { stats_print(ct[i].c->name, ct[i].stats, &total); printf("\n"); } sprintf(total_name, "TOTAL %d of %d", i, active_cnt); stats_print(total_name, &total, &total); fflush(stdout); for (i = 0; i < active_cnt; i++) { lxc_container_put(ct[i].c); ct[i].c = NULL; } in_char = '\0'; ret = lxc_mainloop(&descr, 1000 * delay); if (ret != 0 || in_char == 'q') break; switch(in_char) { case 'r': sort_reverse ^= 1; break; case 'n': case 'c': case 'b': case 'm': case 'k': if (sort_by == in_char) sort_reverse ^= 1; else sort_reverse = 0; sort_by = in_char; } } ret = EXIT_SUCCESS; err1: lxc_mainloop_close(&descr); out: exit(ret); } lxc-2.0.11/src/lxc/tools/lxc_info.c0000644061062106075000000002370313435013473014004 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "utils.h" #include "commands.h" #include "arguments.h" static bool ips; static bool state; static bool pid; static bool stats; static bool humanize = true; static char **key = NULL; static int keys = 0; static int filter_count = 0; static int my_parser(struct lxc_arguments* args, int c, char* arg) { char **newk; switch (c) { case 'c': newk = realloc(key, (keys + 1) * sizeof(key[0])); if (!newk) return -1; key = newk; key[keys] = arg; keys++; break; case 'i': ips = true; filter_count += 1; break; case 's': state = true; filter_count += 1; break; case 'p': pid = true; filter_count += 1; break; case 'S': stats = true; filter_count += 5; break; case 'H': humanize = false; break; } return 0; } static const struct option my_longopts[] = { {"config", required_argument, 0, 'c'}, {"ips", no_argument, 0, 'i'}, {"state", no_argument, 0, 's'}, {"pid", no_argument, 0, 'p'}, {"stats", no_argument, 0, 'S'}, {"no-humanize", no_argument, 0, 'H'}, LXC_COMMON_OPTIONS, }; static struct lxc_arguments my_args = { .progname = "lxc-info", .help = "\ --name=NAME\n\ \n\ lxc-info display some information about a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -c, --config=KEY show configuration variable KEY from running container\n\ -i, --ips shows the IP addresses\n\ -p, --pid shows the process id of the init container\n\ -S, --stats shows usage stats\n\ -H, --no-humanize shows stats as raw numbers, not humanized\n\ -s, --state shows the state of the container\n\ --rcfile=FILE Load configuration file FILE\n", .name = NULL, .options = my_longopts, .parser = my_parser, .checker = NULL, }; static void str_chomp(char *buf) { char *ch; /* remove trailing whitespace from buf */ for(ch = &buf[strlen(buf)-1]; ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' '); ch--) *ch = '\0'; } static void size_humanize(unsigned long long val, char *buf, size_t bufsz) { if (val > 1 << 30) { snprintf(buf, bufsz, "%u.%2.2u GiB", (unsigned int)(val >> 30), (unsigned int)(val & ((1 << 30) - 1)) / 10737419); } else if (val > 1 << 20) { unsigned int x = val + 5243; /* for rounding */ snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (val > 1 << 10) { unsigned int x = val + 5; /* for rounding */ snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { snprintf(buf, bufsz, "%u bytes", (unsigned int)val); } } static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz) { unsigned long long val; char *end = NULL; val = strtoull(iobuf, &end, 0); if (humanize) { if (*end == '\0' || *end == '\n') size_humanize(val, iobuf, iobufsz); else *iobuf = '\0'; } return val; } static void print_net_stats(struct lxc_container *c) { int rc,netnr; unsigned long long rx_bytes = 0, tx_bytes = 0; char *ifname, *type; char path[PATH_MAX]; char buf[256]; for(netnr = 0; ;netnr++) { sprintf(buf, "lxc.network.%d.type", netnr); type = c->get_running_config_item(c, buf); if (!type) break; if (!strcmp(type, "veth")) { sprintf(buf, "lxc.network.%d.veth.pair", netnr); } else { sprintf(buf, "lxc.network.%d.link", netnr); } free(type); ifname = c->get_running_config_item(c, buf); if (!ifname) return; printf("%-15s %s\n", "Link:", ifname); fflush(stdout); /* XXX: tx and rx are reversed from the host vs container * perspective, print them from the container perspective */ snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname); rc = lxc_read_from_file(path, buf, sizeof(buf)); if (rc > 0) { buf[rc - 1] = '\0'; str_chomp(buf); rx_bytes = str_size_humanize(buf, sizeof(buf)); printf("%-15s %s\n", " TX bytes:", buf); fflush(stdout); } snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname); rc = lxc_read_from_file(path, buf, sizeof(buf)); if (rc > 0) { buf[rc - 1] = '\0'; str_chomp(buf); tx_bytes = str_size_humanize(buf, sizeof(buf)); printf("%-15s %s\n", " RX bytes:", buf); fflush(stdout); } sprintf(buf, "%llu", rx_bytes + tx_bytes); str_size_humanize(buf, sizeof(buf)); printf("%-15s %s\n", " Total bytes:", buf); fflush(stdout); free(ifname); } } static void print_stats(struct lxc_container *c) { int i, ret; char buf[4096]; ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf)); if (ret > 0 && ret < sizeof(buf)) { str_chomp(buf); if (humanize) { float seconds = strtof(buf, NULL) / 1000000000.0; printf("%-15s %.2f seconds\n", "CPU use:", seconds); } else { printf("%-15s %s\n", "CPU use:", buf); } fflush(stdout); } ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf)); if (ret > 0 && ret < sizeof(buf)) { char *ch; /* put ch on last "Total" line */ str_chomp(buf); for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--) ; if (*ch == '\n') ch++; if (strncmp(ch, "Total", 5) == 0) { ch += 6; memmove(buf, ch, strlen(ch)+1); str_size_humanize(buf, sizeof(buf)); printf("%-15s %s\n", "BlkIO use:", buf); } fflush(stdout); } static const struct { const char *name; const char *file; } lxstat[] = { { "Memory use:", "memory.usage_in_bytes" }, { "KMem use:", "memory.kmem.usage_in_bytes" }, { NULL, NULL }, }; for (i = 0; lxstat[i].name; i++) { ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf)); if (ret > 0 && ret < sizeof(buf)) { str_chomp(buf); str_size_humanize(buf, sizeof(buf)); printf("%-15s %s\n", lxstat[i].name, buf); fflush(stdout); } } } static void print_info_msg_int(const char *key, int value) { if (humanize) printf("%-15s %d\n", key, value); else { if (filter_count == 1) printf("%d\n", value); else printf("%-15s %d\n", key, value); } fflush(stdout); } static void print_info_msg_str(const char *key, const char *value) { if (humanize) printf("%-15s %s\n", key, value); else { if (filter_count == 1) printf("%s\n", value); else printf("%-15s %s\n", key, value); } fflush(stdout); } static int print_info(const char *name, const char *lxcpath) { int i; struct lxc_container *c; c = lxc_container_new(name, lxcpath); if (!c) { fprintf(stderr, "Failure to retrieve information on %s:%s\n", lxcpath ? lxcpath : "null", name ? name : "null"); return -1; } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); return -1; } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); return -1; } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", c->name); lxc_container_put(c); return -1; } if (!c->is_running(c) && !c->is_defined(c)) { fprintf(stderr, "%s doesn't exist\n", c->name); lxc_container_put(c); return -1; } if (!state && !pid && !ips && !stats && keys <= 0) { state = pid = ips = stats = true; print_info_msg_str("Name:", c->name); } if (state) { print_info_msg_str("State:", c->state(c)); } if (c->is_running(c)) { if (pid) { pid_t initpid; initpid = c->init_pid(c); if (initpid >= 0) print_info_msg_int("PID:", initpid); } if (ips) { fflush(stdout); char **addresses = c->get_ips(c, NULL, NULL, 0); if (addresses) { char *address; i = 0; while (addresses[i]) { address = addresses[i]; print_info_msg_str("IP:", address); i++; } } } } if (stats) { print_stats(c); print_net_stats(c); } for(i = 0; i < keys; i++) { int len = c->get_config_item(c, key[i], NULL, 0); if (len > 0) { char *val = (char*) malloc(sizeof(char)*len + 1); if (c->get_config_item(c, key[i], val, len + 1) != len) { fprintf(stderr, "unable to read %s from configuration\n", key[i]); } else { if (!humanize && keys == 1) printf("%s\n", val); else printf("%s = %s\n", key[i], val); } free(val); } else if (len == 0) { if (!humanize && keys == 1) printf("\n"); else printf("%s =\n", key[i]); } else { fprintf(stderr, "%s invalid\n", key[i]); } fflush(stdout); } lxc_container_put(c); return 0; } int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(ret); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); if (print_info(my_args.name, my_args.lxcpath[0]) == 0) ret = EXIT_SUCCESS; exit(ret); } lxc-2.0.11/src/lxc/tools/lxc_monitor.c0000644061062106075000000001107613435013473014540 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "monitor.h" #include "arguments.h" #include "lxccontainer.h" static bool quit_monitord; static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'Q': quit_monitord = true; break; } return 0; } static const struct option my_longopts[] = { {"quit", no_argument, 0, 'Q'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-monitor", .help = "\ [--name=NAME]\n\ \n\ lxc-monitor monitors the state of the NAME container\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ NAME may be a regular expression\n\ -Q, --quit tell lxc-monitord to quit\n", .name = ".*", .options = my_longopts, .parser = my_parser, .checker = NULL, .lxcpath_additional = -1, }; static void close_fds(struct pollfd *fds, nfds_t nfds) { nfds_t i; if (nfds < 1) return; for (i = 0; i < nfds; ++i) { close(fds[i].fd); } } int main(int argc, char *argv[]) { char *regexp; struct lxc_msg msg; regex_t preg; struct pollfd *fds; nfds_t nfds; int len, rc_main, rc_snp, i; struct lxc_log log; rc_main = EXIT_FAILURE; if (lxc_arguments_parse(&my_args, argc, argv)) exit(rc_main); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(rc_main); lxc_log_options_no_override(); if (quit_monitord) { int ret = EXIT_SUCCESS; for (i = 0; i < my_args.lxcpath_cnt; i++) { int fd; fd = lxc_monitor_open(my_args.lxcpath[i]); if (fd < 0) { fprintf(stderr, "Unable to open monitor on path: %s\n", my_args.lxcpath[i]); ret = EXIT_FAILURE; continue; } if (write(fd, "quit", 4) < 0) { fprintf(stderr, "Unable to close monitor on path: %s\n", my_args.lxcpath[i]); ret = EXIT_FAILURE; close(fd); continue; } close(fd); } exit(ret); } len = strlen(my_args.name) + 3; regexp = malloc(len + 3); if (!regexp) { fprintf(stderr, "failed to allocate memory\n"); exit(rc_main); } rc_snp = snprintf(regexp, len, "^%s$", my_args.name); if (rc_snp < 0 || rc_snp >= len) { fprintf(stderr, "Name too long\n"); goto error; } if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) { fprintf(stderr, "failed to compile the regex '%s'\n", my_args.name); goto error; } fds = malloc(my_args.lxcpath_cnt * sizeof(struct pollfd)); if (!fds) { fprintf(stderr, "out of memory\n"); goto cleanup; } nfds = my_args.lxcpath_cnt; for (i = 0; i < nfds; i++) { int fd; lxc_monitord_spawn(my_args.lxcpath[i]); fd = lxc_monitor_open(my_args.lxcpath[i]); if (fd < 0) { close_fds(fds, i); goto cleanup; } fds[i].fd = fd; fds[i].events = POLLIN; fds[i].revents = 0; } setlinebuf(stdout); for (;;) { if (lxc_monitor_read_fdset(fds, nfds, &msg, -1) < 0) { goto close_and_clean; } msg.name[sizeof(msg.name)-1] = '\0'; if (regexec(&preg, msg.name, 0, NULL, 0)) continue; switch (msg.type) { case lxc_msg_state: printf("'%s' changed state to [%s]\n", msg.name, lxc_state2str(msg.value)); break; case lxc_msg_exit_code: printf("'%s' exited with status [%d]\n", msg.name, WEXITSTATUS(msg.value)); break; default: /* ignore garbage */ break; } } rc_main = 0; close_and_clean: close_fds(fds, nfds); cleanup: regfree(&preg); free(fds); error: free(regexp); exit(rc_main); } lxc-2.0.11/src/lxc/tools/lxc_create.c0000644061062106075000000002223013435013473014306 00000000000000/* * * Copyright © 2013 Serge Hallyn . * Copyright © 2013 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 #include #include #include #include #include #include #include #include "arguments.h" #include "log.h" #include "lxc.h" #include "storage.h" #include "storage_utils.h" #include "utils.h" static uint64_t get_fssize(char *s) { uint64_t ret; char *end; ret = strtoull(s, &end, 0); if (end == s) { fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s); return 0; } while (isblank(*end)) end++; if (*end == '\0') ret *= 1024ULL * 1024ULL; // MB by default else if (*end == 'b' || *end == 'B') ret *= 1ULL; else if (*end == 'k' || *end == 'K') ret *= 1024ULL; else if (*end == 'm' || *end == 'M') ret *= 1024ULL * 1024ULL; else if (*end == 'g' || *end == 'G') ret *= 1024ULL * 1024ULL * 1024ULL; else if (*end == 't' || *end == 'T') ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; else { fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s); return 0; } return ret; } static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'B': args->bdevtype = arg; break; case 'f': args->configfile = arg; break; case 't': args->template = arg; break; case '0': args->lvname = arg; break; case '1': args->vgname = arg; break; case '2': args->thinpool = arg; break; case '3': args->fstype = arg; break; case '4': args->fssize = get_fssize(arg); break; case '5': args->zfsroot = arg; break; case '6': args->dir = arg; break; case '7': args->rbdname = arg; break; case '8': args->rbdpool = arg; break; } return 0; } static const struct option my_longopts[] = { {"bdev", required_argument, 0, 'B'}, {"config", required_argument, 0, 'f'}, {"template", required_argument, 0, 't'}, {"lvname", required_argument, 0, '0'}, {"vgname", required_argument, 0, '1'}, {"thinpool", required_argument, 0, '2'}, {"fstype", required_argument, 0, '3'}, {"fssize", required_argument, 0, '4'}, {"zfsroot", required_argument, 0, '5'}, {"dir", required_argument, 0, '6'}, {"rbdname", required_argument, 0, '7'}, {"rbdpool", required_argument, 0, '8'}, LXC_COMMON_OPTIONS }; static void create_helpfn(const struct lxc_arguments *args) { char *argv[3], *path; pid_t pid; if (!args->template) return; pid = fork(); if (pid) { (void)wait_for_pid(pid); return; } path = get_template_path(args->template); argv[0] = path; argv[1] = "-h"; argv[2] = NULL; execv(path, argv); fprintf(stderr, "Error executing %s -h\n", path); exit(EXIT_FAILURE); } static struct lxc_arguments my_args = { .progname = "lxc-create", .helpfn = create_helpfn, .help = "\ --name=NAME --template=TEMPLATE [OPTION...]\n\ \n\ lxc-create creates a container\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -f, --config=CONFIG Initial configuration file\n\ -t, --template=TEMPLATE Template to use to setup container\n\ -B, --bdev=BDEV Backing store type to use\n\ --dir=DIR Place rootfs directory under DIR\n\ \n\ BDEV options for LVM (with -B/--bdev lvm):\n\ --lvname=LVNAME Use LVM lv name LVNAME\n\ (Default: container name)\n\ --vgname=VG Use LVM vg called VG\n\ (Default: lxc)\n\ --thinpool=TP Use LVM thin pool called TP\n\ (Default: lxc)\n\ \n\ BDEV options for Ceph RBD (with -B/--bdev rbd) :\n\ --rbdname=RBDNAME Use Ceph RBD name RBDNAME\n\ (Default: container name)\n\ --rbdpool=POOL Use Ceph RBD pool name POOL\n\ (Default: lxc)\n\ \n\ BDEV option for ZFS (with -B/--bdev zfs) :\n\ --zfsroot=PATH Create zfs under given zfsroot\n\ (Default: tank/lxc)\n\ \n\ BDEV options for LVM or Loop (with -B/--bdev lvm/loop) :\n\ --fstype=TYPE Create fstype TYPE\n\ (Default: ext4)\n\ --fssize=SIZE[U] Create filesystem of\n\ size SIZE * unit U (bBkKmMgGtT)\n\ (Default: 1G, default unit: M)\n", .options = my_longopts, .parser = my_parser, .checker = NULL, }; static bool validate_bdev_args(struct lxc_arguments *a) { if (strcmp(a->bdevtype, "best") != 0) { if (a->fstype || a->fssize) { if (strcmp(a->bdevtype, "lvm") != 0 && strcmp(a->bdevtype, "loop") != 0 && strcmp(a->bdevtype, "rbd") != 0) { fprintf(stderr, "filesystem type and size are only valid with block devices\n"); return false; } } if (strcmp(a->bdevtype, "lvm") != 0) { if (a->lvname || a->vgname || a->thinpool) { fprintf(stderr, "--lvname, --vgname and --thinpool are only valid with -B lvm\n"); return false; } } if (strcmp(a->bdevtype, "rbd") != 0) { if (a->rbdname || a->rbdpool) { fprintf(stderr, "--rbdname and --rbdpool are only valid with -B rbd\n"); return false; } } if (strcmp(a->bdevtype, "zfs") != 0) { if (a->zfsroot) { fprintf(stderr, "zfsroot is only valid with -B zfs\n"); return false; } } } return true; } int main(int argc, char *argv[]) { struct lxc_container *c; struct bdev_specs spec; struct lxc_log log; int flags = 0; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); /* Only create log if explicitly instructed */ if (my_args.log_file || my_args.log_priority) { log.name = my_args.name; if (!my_args.log_file) log.file = "none"; else log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); } if (!my_args.template) { fprintf(stderr, "A template must be specified.\n"); fprintf(stderr, "Use \"none\" if you really want a container without a rootfs.\n"); exit(EXIT_FAILURE); } if (strcmp(my_args.template, "none") == 0) my_args.template = NULL; memset(&spec, 0, sizeof(spec)); if (!my_args.bdevtype) my_args.bdevtype = "_unset"; if (!validate_bdev_args(&my_args)) exit(EXIT_FAILURE); if (strcmp(my_args.bdevtype, "none") == 0) my_args.bdevtype = "dir"; // Final check whether the user gave use a valid bdev type. if (strcmp(my_args.bdevtype, "best") && strcmp(my_args.bdevtype, "_unset") && !is_valid_storage_type(my_args.bdevtype)) { fprintf(stderr, "%s is not a valid backing storage type.\n", my_args.bdevtype); exit(EXIT_FAILURE); } if (!my_args.lxcpath[0]) my_args.lxcpath[0] = lxc_get_global_config_item("lxc.lxcpath"); if (mkdir_p(my_args.lxcpath[0], 0755)) exit(EXIT_FAILURE); if (geteuid()) if (access(my_args.lxcpath[0], O_RDONLY) < 0) { fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(EXIT_FAILURE); } c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "Failed to create lxc container.\n"); exit(EXIT_FAILURE); } if (c->is_defined(c)) { lxc_container_put(c); fprintf(stderr, "Container already exists\n"); exit(EXIT_FAILURE); } if (my_args.configfile) c->load_config(c, my_args.configfile); else c->load_config(c, lxc_global_config_value("lxc.default_config")); if (my_args.fstype) spec.fstype = my_args.fstype; if (my_args.fssize) spec.fssize = my_args.fssize; if ((strcmp(my_args.bdevtype, "zfs") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) { if (my_args.zfsroot) spec.zfs.zfsroot = my_args.zfsroot; } if ((strcmp(my_args.bdevtype, "lvm") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) { if (my_args.lvname) spec.lvm.lv = my_args.lvname; if (my_args.vgname) spec.lvm.vg = my_args.vgname; if (my_args.thinpool) spec.lvm.thinpool = my_args.thinpool; } if ((strcmp(my_args.bdevtype, "rbd") == 0) || (strcmp(my_args.bdevtype, "best") == 0)) { if (my_args.rbdname) spec.rbd.rbdname = my_args.rbdname; if (my_args.rbdpool) spec.rbd.rbdpool = my_args.rbdpool; } if (my_args.dir) spec.dir = my_args.dir; if (strcmp(my_args.bdevtype, "_unset") == 0) my_args.bdevtype = NULL; if (my_args.quiet) flags = LXC_CREATE_QUIET; if (!c->create(c, my_args.template, my_args.bdevtype, &spec, flags, &argv[optind])) { fprintf(stderr, "Error creating container %s\n", c->name); lxc_container_put(c); exit(EXIT_FAILURE); } lxc_container_put(c); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_destroy.c0000644061062106075000000001517613435013473014547 00000000000000/* * * Copyright © 2013 Serge Hallyn . * Copyright © 2013 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. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include "arguments.h" #include "log.h" #include "lxc.h" #include "utils.h" static int my_parser(struct lxc_arguments* args, int c, char* arg); static bool quiet; static const struct option my_longopts[] = { {"force", no_argument, 0, 'f'}, {"snapshots", no_argument, 0, 's'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-destroy", .help = "\ --name=NAME [-f] [-P lxcpath]\n\ \n\ lxc-destroy destroys a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -s, --snapshots destroy including all snapshots\n\ -f, --force wait for the container to shut down\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .checker = NULL, .task = DESTROY, }; static bool do_destroy(struct lxc_container *c); static bool do_destroy_with_snapshots(struct lxc_container *c); int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; bool bret; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (my_args.quiet) quiet = true; c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { if (!quiet) fprintf(stderr, "System error loading container\n"); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { if (!quiet) fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->is_defined(c)) { if (!quiet) fprintf(stderr, "Container is not defined\n"); lxc_container_put(c); exit(EXIT_FAILURE); } if (my_args.task == SNAP) { bret = do_destroy_with_snapshots(c); if (bret && !quiet) printf("Destroyed container %s including snapshots \n", my_args.name); } else { bret = do_destroy(c); if (bret && !quiet) printf("Destroyed container %s\n", my_args.name); } lxc_container_put(c); if (bret) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } static int my_parser(struct lxc_arguments *args, int c, char *arg) { switch (c) { case 'f': args->force = 1; break; case 's': args->task = SNAP; break; } return 0; } static bool do_destroy(struct lxc_container *c) { bool bret = true; char path[MAXPATHLEN]; /* First check whether the container has dependent clones or snapshots. */ int ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; if (file_exists(path)) { if (!quiet) fprintf(stdout, "Destroying %s failed: %s has clones.\n", c->name, c->name); return false; } ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; if (dir_exists(path)) { if (!quiet) fprintf(stdout, "Destroying %s failed: %s has snapshots.\n", c->name, c->name); return false; } if (c->is_running(c)) { if (!my_args.force && !quiet) { fprintf(stderr, "%s is running\n", my_args.name); return false; } /* If the container was ephemeral it will be removed on shutdown. */ c->stop(c); } /* If the container was ephemeral we have already removed it when we * stopped it. */ if (c->is_defined(c) && !c->lxc_conf->ephemeral) bret = c->destroy(c); if (!bret) { if (!quiet) fprintf(stderr, "Destroying %s failed\n", my_args.name); return false; } return true; } static bool do_destroy_with_snapshots(struct lxc_container *c) { struct lxc_container *c1; struct stat fbuf; bool bret = false; char path[MAXPATHLEN]; char *buf = NULL; char *lxcpath = NULL; char *lxcname = NULL; char *scratch = NULL; int fd; int ret; int counter = 0; ssize_t bytes; /* Destroy clones. */ ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; fd = open(path, O_RDONLY | O_CLOEXEC); if (fd >= 0) { ret = fstat(fd, &fbuf); if (ret < 0) { close(fd); return false; } /* Make sure that the string is \0 terminated. */ buf = calloc(fbuf.st_size + 1, sizeof(char)); if (!buf) { fprintf(stderr, "failed to allocate memory\n"); close(fd); return false; } bytes = lxc_read_nointr(fd, buf, fbuf.st_size); close(fd); if (bytes != (ssize_t)fbuf.st_size) { fprintf(stderr, "Could not read %s", path); free(buf); return false; } close(fd); while ((lxcpath = strtok_r(!counter ? buf : NULL, "\n", &scratch))) { if (!(lxcname = strtok_r(NULL, "\n", &scratch))) break; c1 = lxc_container_new(lxcname, lxcpath); if (!c1) { counter++; continue; } /* We do not destroy recursively. If a clone of a clone * has clones or snapshots the user should remove it * explicitly. */ if (!do_destroy(c1)) { lxc_container_put(c1); free(buf); return false; } lxc_container_put(c1); counter++; } free(buf); } /* Destroy snapshots located in the containers snap/ folder. */ ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; if (dir_exists(path)) bret = c->destroy_with_snapshots(c); else bret = do_destroy(c); return bret; } lxc-2.0.11/src/lxc/tools/arguments.h0000644061062106075000000001077713435013473014224 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Michel Normand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ARGUMENTS_H #define __LXC_ARGUMENTS_H #include #include #include #include struct lxc_arguments; typedef int (*lxc_arguments_parser_t)(struct lxc_arguments *, int, char *); typedef int (*lxc_arguments_checker_t)(const struct lxc_arguments *); struct lxc_arguments { const char *help; void (*helpfn)(const struct lxc_arguments *); const char *progname; const struct option *options; lxc_arguments_parser_t parser; lxc_arguments_checker_t checker; const char *name; char *log_file; char *log_priority; int quiet; int daemonize; const char *rcfile; const char *console; const char *console_log; const char *pidfile; const char **lxcpath; int lxcpath_cnt; /* set to 0 to accept only 1 lxcpath, -1 for unlimited */ int lxcpath_additional; /* for lxc-start */ const char *share_ns[32]; // size must be greater than LXC_NS_MAX /* for lxc-console */ unsigned int ttynum; char escape; /* for lxc-wait */ char *states; long timeout; /* for lxc-autostart */ int shutdown; /* for lxc-stop */ int hardstop; int nokill; int nolock; int nowait; int reboot; /* for lxc-destroy */ int force; /* close fds from parent? */ int close_all_fds; /* lxc-create */ char *bdevtype, *configfile, *template; char *fstype; uint64_t fssize; char *lvname, *vgname, *thinpool; char *rbdname, *rbdpool; char *zfsroot, *lowerdir, *dir; /* lxc-execute */ uid_t uid; gid_t gid; /* auto-start */ int all; int ignore_auto; int list; char *groups; /* also used by lxc-ls */ /* lxc-snapshot and lxc-copy */ enum task { CLONE, DESTROY, LIST, RESTORE, SNAP, RENAME, } task; int print_comments; char *commentfile; char *newname; char *newpath; char *snapname; int keepdata; int keepname; int keepmac; /* lxc-ls */ char *ls_fancy_format; char *ls_filter; unsigned int ls_nesting; /* maximum allowed nesting level */ bool ls_active; bool ls_fancy; bool ls_frozen; bool ls_line; bool ls_running; bool ls_stopped; bool ls_defined; /* remaining arguments */ char *const *argv; int argc; /* private arguments */ void *data; }; #define LXC_COMMON_OPTIONS \ { "name", required_argument, 0, 'n' }, \ { "help", no_argument, 0, 'h' }, \ { "usage", no_argument, 0, OPT_USAGE }, \ { "version", no_argument, 0, OPT_VERSION }, \ { "quiet", no_argument, 0, 'q' }, \ { "logfile", required_argument, 0, 'o' }, \ { "logpriority", required_argument, 0, 'l' }, \ { "lxcpath", required_argument, 0, 'P' }, \ { "rcfile", required_argument, 0, OPT_RCFILE }, \ { 0, 0, 0, 0 } /* option keys for long only options */ #define OPT_USAGE 0x1000 #define OPT_VERSION OPT_USAGE - 1 #define OPT_RCFILE OPT_USAGE - 2 #define OPT_SHARE_NET OPT_USAGE - 3 #define OPT_SHARE_IPC OPT_USAGE - 4 #define OPT_SHARE_UTS OPT_USAGE - 5 #define OPT_SHARE_PID OPT_USAGE - 6 extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, char *const argv[]); extern int lxc_arguments_str_to_int(struct lxc_arguments *args, const char *str); #define lxc_error(arg, fmt, args...) \ if (!(arg)->quiet) \ fprintf(stderr, "%s: " fmt "\n", (arg)->progname, ##args) #endif /* __LXC_ARGUMENTS_H */ lxc-2.0.11/src/lxc/tools/lxc_console.c0000644061062106075000000000747013435013473014516 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "commands.h" #include "error.h" #include "log.h" #include "lxc.h" #include "mainloop.h" #include "utils.h" static char etoc(const char *expr) { /* returns "control code" of given expression */ char c = expr[0] == '^' ? expr[1] : expr[0]; return 1 + ((c > 'Z') ? (c - 'a') : (c - 'Z')); } static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 't': if (lxc_safe_uint(arg, &args->ttynum) < 0) return -1; break; case 'e': args->escape = etoc(arg); break; } return 0; } static const struct option my_longopts[] = { {"tty", required_argument, 0, 't'}, {"escape", required_argument, 0, 'e'}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-console", .help = "\ --name=NAME [--tty NUMBER]\n\ \n\ lxc-console logs on the container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -t, --tty=NUMBER console tty number\n\ -e, --escape=PREFIX prefix for escape command\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = my_parser, .checker = NULL, .ttynum = -1, .escape = 1, }; int main(int argc, char *argv[]) { int ret; struct lxc_container *c; struct lxc_log log; ret = lxc_arguments_parse(&my_args, argc, argv); if (ret) return EXIT_FAILURE; if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; ret = lxc_log_init(&log); if (ret) return EXIT_FAILURE; lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "System error loading container\n"); exit(EXIT_FAILURE); } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } if (!c->is_running(c)) { fprintf(stderr, "%s is not running\n", my_args.name); lxc_container_put(c); exit(EXIT_FAILURE); } ret = c->console(c, my_args.ttynum, 0, 1, 2, my_args.escape); if (ret < 0) { lxc_container_put(c); exit(EXIT_FAILURE); } lxc_container_put(c); exit(EXIT_SUCCESS); } lxc-2.0.11/src/lxc/tools/lxc_start.c0000644061062106075000000002277013435013473014211 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "caps.h" #include "lxc.h" #include "conf.h" #include "cgroup.h" #include "utils.h" #include "confile.h" #include "arguments.h" static struct lxc_list defines; static int ensure_path(char **confpath, const char *path) { int err = -1, fd; char *fullpath = NULL; if (path) { if (access(path, W_OK)) { fd = creat(path, 0600); if (fd < 0 && errno != EEXIST) { fprintf(stderr, "failed to create '%s'\n", path); goto err; } if (fd >= 0) close(fd); } fullpath = realpath(path, NULL); if (!fullpath) { fprintf(stderr, "failed to get the real path of '%s'\n", path); goto err; } *confpath = fullpath; } err = EXIT_SUCCESS; err: return err; } static int pid_from_lxcname(const char *lxcname_or_pid, const char *lxcpath) { char *eptr; int pid = strtol(lxcname_or_pid, &eptr, 10); if (*eptr != '\0' || pid < 1) { struct lxc_container *s; s = lxc_container_new(lxcname_or_pid, lxcpath); if (!s) { fprintf(stderr, "'%s' is not a valid pid nor a container name\n", lxcname_or_pid); return -1; } if (!s->may_control(s)) { fprintf(stderr, "Insufficient privileges to control container '%s'\n", s->name); lxc_container_put(s); return -1; } pid = s->init_pid(s); if (pid < 1) { fprintf(stderr, "Is container '%s' running?\n", s->name); lxc_container_put(s); return -1; } lxc_container_put(s); } if (kill(pid, 0) < 0) { fprintf(stderr, "Can't send signal to pid %d\n", pid); return -1; } return pid; } static int open_ns(int pid, const char *ns_proc_name) { int fd; char path[MAXPATHLEN]; snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns_proc_name); fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "failed to open %s\n", path); return -1; } return fd; } static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'c': args->console = arg; break; case 'L': args->console_log = arg; break; case 'd': args->daemonize = 1; break; case 'F': args->daemonize = 0; break; 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; case OPT_SHARE_NET: args->share_ns[LXC_NS_NET] = arg; break; case OPT_SHARE_IPC: args->share_ns[LXC_NS_IPC] = arg; break; case OPT_SHARE_UTS: args->share_ns[LXC_NS_UTS] = arg; break; } return 0; } static const struct option my_longopts[] = { {"daemon", no_argument, 0, 'd'}, {"foreground", no_argument, 0, 'F'}, {"rcfile", required_argument, 0, 'f'}, {"define", required_argument, 0, 's'}, {"console", required_argument, 0, 'c'}, {"console-log", required_argument, 0, 'L'}, {"close-all-fds", no_argument, 0, 'C'}, {"pidfile", required_argument, 0, 'p'}, {"share-net", required_argument, 0, OPT_SHARE_NET}, {"share-ipc", required_argument, 0, OPT_SHARE_IPC}, {"share-uts", required_argument, 0, OPT_SHARE_UTS}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-start", .help = "\ --name=NAME -- COMMAND\n\ \n\ lxc-start start COMMAND in specified container NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -d, --daemon Daemonize the container (default)\n\ -F, --foreground Start with the current tty attached to /dev/console\n\ -p, --pidfile=FILE Create a file with the process id\n\ -f, --rcfile=FILE Load configuration file FILE\n\ -c, --console=FILE Use specified FILE for the container console\n\ -L, --console-log=FILE Log container console output to FILE\n\ -C, --close-all-fds If any fds are inherited, close them\n\ If not specified, exit with failure instead\n\ Note: --daemon implies --close-all-fds\n\ -s, --define KEY=VAL Assign VAL to configuration variable KEY\n\ --share-[net|ipc|uts]=NAME Share a namespace with another container or pid\n\ ", .options = my_longopts, .parser = my_parser, .checker = NULL, .daemonize = 1, .pidfile = NULL, }; int main(int argc, char *argv[]) { int i, err = EXIT_FAILURE; struct lxc_conf *conf; struct lxc_log log; char *const *args; char *rcfile = NULL; char *const default_args[] = { "/sbin/init", NULL, }; struct lxc_container *c; lxc_list_init(&defines); if (lxc_caps_init()) exit(err); if (lxc_arguments_parse(&my_args, argc, argv)) exit(err); if (!my_args.argc) args = default_args; else args = my_args.argv; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(err); lxc_log_options_no_override(); if (access(my_args.lxcpath[0], O_RDONLY) < 0) { if (!my_args.quiet) fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(err); } const char *lxcpath = my_args.lxcpath[0]; /* * rcfile possibilities: * 1. rcfile from random path specified in cli option * 2. rcfile not specified, use $lxcpath/$lxcname/config * 3. rcfile not specified and does not exist. */ /* rcfile is specified in the cli option */ if (my_args.rcfile) { rcfile = (char *)my_args.rcfile; c = lxc_container_new(my_args.name, lxcpath); if (!c) { fprintf(stderr, "Failed to create lxc_container\n"); exit(err); } c->clear_config(c); if (!c->load_config(c, rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(err); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); goto out; } } else { int rc; rc = asprintf(&rcfile, "%s/%s/config", lxcpath, my_args.name); if (rc == -1) { fprintf(stderr, "failed to allocate memory\n"); exit(err); } /* container configuration does not exist */ if (access(rcfile, F_OK)) { free(rcfile); rcfile = NULL; } c = lxc_container_new(my_args.name, lxcpath); if (!c) { fprintf(stderr, "Failed to create lxc_container\n"); exit(err); } } /* We do not check here whether the container is defined, because we * support volatile containers. Which means the container does not need * to be created for it to be started. You can just pass a configuration * file as argument and start the container right away. */ if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", c->name); goto out; } if (c->is_running(c)) { fprintf(stderr, "Container is already running.\n"); err = EXIT_SUCCESS; goto out; } /* * We should use set_config_item() over &defines, which would handle * unset c->lxc_conf for us and let us not use lxc_config_define_load() */ if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); conf = c->lxc_conf; if (lxc_config_define_load(&defines, conf)) goto out; if (!rcfile && !strcmp("/sbin/init", args[0])) { fprintf(stderr, "Executing '/sbin/init' with no configuration file may crash the host\n"); goto out; } if (my_args.pidfile != NULL) { if (ensure_path(&c->pidfile, my_args.pidfile) < 0) { fprintf(stderr, "failed to ensure pidfile '%s'\n", my_args.pidfile); goto out; } } if (my_args.console) if (!c->set_config_item(c, "lxc.console.path", my_args.console)) goto out; if (my_args.console_log) if (!c->set_config_item(c, "lxc.console.logfile", my_args.console_log)) goto out; for (i = 0; i < LXC_NS_MAX; i++) { if (my_args.share_ns[i] == NULL) continue; int pid = pid_from_lxcname(my_args.share_ns[i], lxcpath); if (pid < 1) goto out; int fd = open_ns(pid, ns_info[i].proc_name); if (fd < 0) goto out; conf->inherit_ns_fd[i] = fd; } if (!my_args.daemonize) { c->want_daemonize(c, false); } if (my_args.close_all_fds) c->want_close_all_fds(c, true); if (args == default_args) err = c->start(c, 0, NULL) ? EXIT_SUCCESS : EXIT_FAILURE; else err = c->start(c, 0, args) ? EXIT_SUCCESS : EXIT_FAILURE; if (err) { fprintf(stderr, "The container failed to start.\n"); if (my_args.daemonize) fprintf(stderr, "To get more details, run the container in foreground mode.\n"); fprintf(stderr, "Additional information can be obtained by setting the " "--logfile and --logpriority options.\n"); err = c->error_num; lxc_container_put(c); exit(err); } out: lxc_container_put(c); exit(err); } lxc-2.0.11/src/lxc/tools/lxc_usernsexec.c0000644061062106075000000002222613435013473015234 00000000000000/* * (C) Copyright IBM Corp. 2008 * (C) Copyright Canonical, Inc 2010-2013 * * Authors: * Serge Hallyn * (Once upon a time, this was based on nsexec from the IBM * container tools) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf.h" #include "namespace.h" #include "utils.h" #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MS_SLAVE #define MS_SLAVE (1 << 19) #endif extern int lxc_log_fd; int unshare(int flags); static void usage(const char *name) { printf("usage: %s [-h] [-m ] -- [command [arg ..]]\n", name); printf("\n"); printf(" -h this message\n"); printf("\n"); printf(" -m uid maps to use\n"); printf("\n"); printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n"); printf(" [u|g|b]: map user id, group id, or both\n"); printf(" ns_id: the base id in the new namespace\n"); printf(" host_id: the base id in the parent namespace\n"); printf(" range: how many ids to map\n"); printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n"); printf(" As such, /etc/subuid and /etc/subgid must grant the\n"); printf(" calling user permission to use the mapped ranges\n"); } static void opentty(const char *tty, int which) { int fd, flags; if (tty[0] == '\0') return; fd = open(tty, O_RDWR | O_NONBLOCK); if (fd < 0) { printf("WARN: could not reopen tty: %s\n", strerror(errno)); return; } flags = fcntl(fd, F_GETFL); flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { printf("WARN: could not set fd flags: %s\n", strerror(errno)); close(fd); return; } close(which); if (fd != which) { (void)dup2(fd, which); close(fd); } } // Code copy end static int do_child(void *vargv) { char **argv = (char **)vargv; // Assume we want to become root if (setgid(0) < 0) { perror("setgid"); return -1; } if (setuid(0) < 0) { perror("setuid"); return -1; } if (setgroups(0, NULL) < 0) { perror("setgroups"); return -1; } if (unshare(CLONE_NEWNS) < 0) { perror("unshare CLONE_NEWNS"); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { printf("Failed to make / rslave\n"); return -1; } } execvp(argv[0], argv); perror("execvpe"); return -1; } static struct lxc_list active_map; /* * given a string like "b:0:100000:10", map both uids and gids * 0-10 to 100000 to 100010 */ static int parse_map(char *map) { struct id_map *newmap; struct lxc_list *tmp = NULL; int ret; int i; char types[2] = {'u', 'g'}; char which; long host_id, ns_id, range; if (!map) return -1; ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range); if (ret != 4) return -1; if (which != 'b' && which != 'u' && which != 'g') return -1; for (i = 0; i < 2; i++) { if (which != types[i] && which != 'b') continue; newmap = malloc(sizeof(*newmap)); if (!newmap) return -1; newmap->hostid = host_id; newmap->nsid = ns_id; newmap->range = range; if (types[i] == 'u') newmap->idtype = ID_TYPE_UID; else newmap->idtype = ID_TYPE_GID; tmp = malloc(sizeof(*tmp)); if (!tmp) { free(newmap); return -1; } tmp->elem = newmap; lxc_list_add_tail(&active_map, tmp); } return 0; } /* * This is called if the user did not pass any uid ranges in * through -m flags. It's called once to get the default uid * map, and once for the default gid map. * Go through /etc/subuids and /etc/subgids to find this user's * allowed map. We only use the first one for each of uid and * gid, because otherwise we're not sure which entries the user * wanted. */ static int read_default_map(char *fnam, int which, char *username) { FILE *fin; char *line = NULL; size_t sz = 0; struct id_map *newmap; struct lxc_list *tmp = NULL; char *p1, *p2; fin = fopen(fnam, "r"); if (!fin) return -1; while (getline(&line, &sz, fin) != -1) { if (sz <= strlen(username) || strncmp(line, username, strlen(username)) != 0 || line[strlen(username)] != ':') continue; p1 = strchr(line, ':'); if (!p1) continue; p2 = strchr(p1+1, ':'); if (!p2) continue; newmap = malloc(sizeof(*newmap)); if (!newmap) { fclose(fin); free(line); return -1; } newmap->hostid = atol(p1+1); newmap->range = atol(p2+1); newmap->nsid = 0; newmap->idtype = which; tmp = malloc(sizeof(*tmp)); if (!tmp) { fclose(fin); free(line); free(newmap); return -1; } tmp->elem = newmap; lxc_list_add_tail(&active_map, tmp); break; } free(line); fclose(fin); return 0; } static int find_default_map(void) { struct passwd pwent; struct passwd *pwentp = NULL; char *buf; size_t bufsize; int ret; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return -1; ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) printf("WARN: could not find matched password record\n"); printf("Failed to get password record - %u\n", getuid()); free(buf); return -1; } if (read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name) < 0) { free(buf); return -1; } if (read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name) < 0) { free(buf); return -1; } free(buf); return 0; } int main(int argc, char *argv[]) { int c; unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS; char ttyname0[256], ttyname1[256], ttyname2[256]; int status; int ret; int pid; char *default_args[] = {"/bin/sh", NULL}; char buf[1]; int pipe_fds1[2], /* child tells parent it has unshared */ pipe_fds2[2]; /* parent tells child it is mapped and may proceed */ lxc_log_fd = STDERR_FILENO; memset(ttyname0, '\0', sizeof(ttyname0)); memset(ttyname1, '\0', sizeof(ttyname1)); memset(ttyname2, '\0', sizeof(ttyname2)); if (isatty(0)) { ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0)); if (ret < 0) { perror("unable to open stdin."); exit(EXIT_FAILURE); } ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1)); if (ret < 0) { printf("Warning: unable to open stdout, continuing.\n"); memset(ttyname1, '\0', sizeof(ttyname1)); } ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2)); if (ret < 0) { printf("Warning: unable to open stderr, continuing.\n"); memset(ttyname2, '\0', sizeof(ttyname2)); } } lxc_list_init(&active_map); while ((c = getopt(argc, argv, "m:h")) != EOF) { switch (c) { case 'm': if (parse_map(optarg)) { usage(argv[0]); exit(EXIT_FAILURE); } break; case 'h': usage(argv[0]); exit(EXIT_SUCCESS); default: usage(argv[0]); exit(EXIT_FAILURE); } }; if (lxc_list_empty(&active_map)) { if (find_default_map()) { fprintf(stderr, "You have no allocated subuids or subgids\n"); exit(EXIT_FAILURE); } } argv = &argv[optind]; argc = argc - optind; if (argc < 1) argv = default_args; if (pipe2(pipe_fds1, O_CLOEXEC) < 0 || pipe2(pipe_fds2, O_CLOEXEC) < 0) { perror("pipe"); exit(EXIT_FAILURE); } if ((pid = fork()) == 0) { // Child. close(pipe_fds1[0]); close(pipe_fds2[1]); opentty(ttyname0, 0); opentty(ttyname1, 1); opentty(ttyname2, 2); ret = unshare(flags); if (ret < 0) { perror("unshare"); return 1; } buf[0] = '1'; if (lxc_write_nointr(pipe_fds1[1], buf, 1) < 1) { perror("write pipe"); exit(EXIT_FAILURE); } if (lxc_read_nointr(pipe_fds2[0], buf, 1) < 1) { perror("read pipe"); exit(EXIT_FAILURE); } if (buf[0] != '1') { fprintf(stderr, "parent had an error, child exiting\n"); exit(EXIT_FAILURE); } close(pipe_fds1[1]); close(pipe_fds2[0]); return do_child((void*)argv); } close(pipe_fds1[1]); close(pipe_fds2[0]); if (lxc_read_nointr(pipe_fds1[0], buf, 1) < 1) { perror("read pipe"); exit(EXIT_FAILURE); } buf[0] = '1'; if (lxc_map_ids(&active_map, pid)) fprintf(stderr, "error mapping child\n"); if (lxc_write_nointr(pipe_fds2[1], buf, 1) < 0) { perror("write to pipe"); exit(EXIT_FAILURE); } if ((ret = waitpid(pid, &status, __WALL)) < 0) { printf("waitpid() returns %d, errno %d\n", ret, errno); exit(EXIT_FAILURE); } exit(WEXITSTATUS(status)); } lxc-2.0.11/src/lxc/tools/lxc_ls.c0000644061062106075000000010115613435013473013466 00000000000000/* * * Copyright © 2016 Christian Brauner . * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "conf.h" #include "confile.h" #include "log.h" #include "lxc.h" #include "utils.h" /* Per default we only allow five levels of recursion to protect the stack at * least a little bit. */ #define MAX_NESTLVL 5 #define LS_FROZEN 1 #define LS_STOPPED 2 #define LS_ACTIVE 3 #define LS_RUNNING 4 #define LS_NESTING 5 #define LS_FILTER 6 #define LS_DEFINED 7 #ifndef SOCK_CLOEXEC # define SOCK_CLOEXEC 02000000 #endif /* Store container info. */ struct ls { char *name; char *state; char *groups; char *interface; char *ipv4; char *ipv6; unsigned int nestlvl; pid_t init; double ram; double swap; bool autostart; bool running; }; /* Keep track of field widths for printing. */ struct lengths { unsigned int name_length; unsigned int state_length; unsigned int groups_length; unsigned int interface_length; unsigned int ipv4_length; unsigned int ipv6_length; unsigned int init_length; unsigned int ram_length; unsigned int swap_length; unsigned int autostart_length; }; static int ls_deserialize(int rpipefd, struct ls **m, size_t *len); static void ls_field_width(const struct ls *l, const size_t size, struct lengths *lht); static void ls_free(struct ls *l, size_t size); static void ls_free_arr(char **arr, size_t size); static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args, const char *basepath, const char *parent, unsigned int lvl, char **lockpath, size_t len_lockpath, char **grps_must, size_t grps_must_len); static char *ls_get_cgroup_item(struct lxc_container *c, const char *item); static char *ls_get_config_item(struct lxc_container *c, const char *item, bool running); static char *ls_get_groups(struct lxc_container *c, bool running); static char *ls_get_ips(struct lxc_container *c, const char *inet); static int ls_recv_str(int fd, char **buf); static int ls_send_str(int fd, const char *buf); struct wrapargs { const struct lxc_arguments *args; char **grps_must; size_t grps_must_len; int pipefd[2]; size_t *size; const char *parent; unsigned int nestlvl; }; /* * Takes struct wrapargs as argument. */ static int ls_get_wrapper(void *wrap); /* * To calculate swap usage we should not simply check memory.usage_in_bytes and * memory.memsw.usage_in_bytes and then do: * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes; * because we might receive an incorrect/negative value. * Instead we check memory.stat and check the "swap" value. */ static double ls_get_swap(struct lxc_container *c); static unsigned int ls_get_term_width(void); static char *ls_get_interface(struct lxc_container *c); static bool ls_has_all_grps(const char *has, char **must, size_t must_len); static struct ls *ls_new(struct ls **ls, size_t *size); /* * Print user-specified fancy format. */ static void ls_print_fancy_format(struct ls *l, struct lengths *lht, size_t size, const char *fancy_fmt); /* * Only print names of containers. */ static void ls_print_names(struct ls *l, struct lengths *lht, size_t ls_arr, size_t termwidth, bool list); /* * Print default fancy format. */ static void ls_print_table(struct ls *l, struct lengths *lht, size_t size); /* * id can only be 79 + \0 chars long. */ static int ls_remove_lock(const char *path, const char *name, char **lockpath, size_t *len_lockpath, bool recalc); static int ls_serialize(int wpipefd, struct ls *n); static int my_parser(struct lxc_arguments *args, int c, char *arg); static const struct option my_longopts[] = { {"line", no_argument, 0, '1'}, {"fancy", no_argument, 0, 'f'}, {"fancy-format", required_argument, 0, 'F'}, {"active", no_argument, 0, LS_ACTIVE}, {"running", no_argument, 0, LS_RUNNING}, {"frozen", no_argument, 0, LS_FROZEN}, {"stopped", no_argument, 0, LS_STOPPED}, {"defined", no_argument, 0, LS_DEFINED}, {"nesting", optional_argument, 0, LS_NESTING}, {"groups", required_argument, 0, 'g'}, {"filter", required_argument, 0, LS_FILTER}, LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-ls", .help = "\n\ [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\ [-1] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\ [-f] [-P lxcpath] [--active] [--running] [--frozen] [--stopped] [--nesting] [-g groups] [--filter regex]\n\ \n\ lxc-ls list containers\n\ \n\ Options :\n\ -1, --line show one entry per line\n\ -f, --fancy use a fancy, column-based output\n\ -F, --fancy-format comma separated list of columns to show in the fancy output\n\ valid columns are: NAME, STATE, PID, RAM, SWAP, AUTOSTART,\n\ GROUPS, INTERFACE, IPV4 and IPV6\n\ --active list only active containers\n\ --running list only running containers\n\ --frozen list only frozen containers\n\ --stopped list only stopped containers\n\ --defined list only defined containers\n\ --nesting=NUM list nested containers up to NUM (default is 5) levels of nesting\n\ --filter=REGEX filter container names by regular expression\n\ -g --groups comma separated list of groups a container must have to be displayed\n", .options = my_longopts, .parser = my_parser, .ls_nesting = 0, }; int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; struct lxc_log log; /* * The lxc parser requires that my_args.name is set. So let's satisfy * that condition by setting a dummy name which is never used. */ my_args.name = ""; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; /* * We set the first argument that usually takes my_args.name to NULL so * that the log is only used when the user specifies a file. */ log.name = NULL; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); struct lengths max_len = { /* default header length */ .name_length = 4, /* NAME */ .state_length = 5, /* STATE */ .groups_length = 6, /* GROUPS */ .interface_length = 9, /* INTERFACE */ .ipv4_length = 4, /* IPV4 */ .ipv6_length = 4, /* IPV6 */ .init_length = 3, /* PID */ .ram_length = 3, /* RAM */ .swap_length = 4, /* SWAP */ .autostart_length = 9, /* AUTOSTART */ }; char **grps = NULL; size_t ngrps = 0; if (my_args.groups) { grps = lxc_string_split_and_trim(my_args.groups, ','); ngrps = lxc_array_len((void **)grps); } struct ls *ls_arr = NULL; size_t ls_size = 0; /* &(char *){NULL} is no magic. It's just a compound literal which * avoids having a pointless variable in main() that serves no purpose * here. */ int status = ls_get(&ls_arr, &ls_size, &my_args, "", NULL, 0, &(char *){NULL}, 0, grps, ngrps); if (!ls_arr && status == 0) /* We did not fail. There was just nothing to do. */ exit(EXIT_SUCCESS); else if (!ls_arr || status == -1) goto out; ls_field_width(ls_arr, ls_size, &max_len); if (my_args.ls_fancy && !my_args.ls_fancy_format) { ls_print_table(ls_arr, &max_len, ls_size); } else if (my_args.ls_fancy && my_args.ls_fancy_format) { ls_print_fancy_format(ls_arr, &max_len, ls_size, my_args.ls_fancy_format); } else { unsigned int cols = 0; if (!my_args.ls_line) cols = ls_get_term_width(); ls_print_names(ls_arr, &max_len, ls_size, cols, my_args.ls_line); } ret = EXIT_SUCCESS; out: ls_free(ls_arr, ls_size); lxc_free_array((void **)grps, free); exit(ret); } static void ls_free(struct ls *l, size_t size) { size_t i; struct ls *m = NULL; for (i = 0, m = l; i < size; i++, m++) { free(m->groups); free(m->interface); free(m->ipv4); free(m->ipv6); free(m->name); free(m->state); } free(l); } static char *ls_get_config_item(struct lxc_container *c, const char *item, bool running) { if (running) return c->get_running_config_item(c, item); size_t len = c->get_config_item(c, item, NULL, 0); if (len <= 0) return NULL; char *val = malloc((len + 1) * sizeof(*val)); if (!val) return NULL; if ((size_t)c->get_config_item(c, item, val, len + 1) != len) { free(val); val = NULL; } return val; } static void ls_free_arr(char **arr, size_t size) { size_t i; for (i = 0; i < size; i++) free(arr[i]); free(arr); } static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args, const char *basepath, const char *parent, unsigned int lvl, char **lockpath, size_t len_lockpath, char **grps_must, size_t grps_must_len) { /* As ls_get() is non-tail recursive we face the inherent danger of * blowing up the stack at some level of nesting. To have at least some * security we define MAX_NESTLVL to be 5. That should be sufficient for * most users. The argument lvl can be used to keep track of the level * of nesting we are at. If lvl is greater than the allowed default * level or the level the user specified on the command line we return * and unwind the stack. */ if (lvl > args->ls_nesting) return 0; int num = 0, ret = -1; char **containers = NULL; /* If we, at some level of nesting, encounter a stopped container but * want to retrieve nested containers we need to build an absolute path * beginning from it. Initially, at nesting level 0, basepath will * simply be the empty string and path will simply be whatever the * default lxcpath or the path the user gave us is. Basepath will also * be the empty string in case we encounter a running container since we * can simply attach to its namespace to retrieve nested containers. */ char *path = lxc_append_paths(basepath, args->lxcpath[0]); if (!path) goto out; if (!dir_exists(path)) { ret = 0; goto out; } /* Do not do more work than is necessary right from the start. */ if (args->ls_active || args->ls_frozen) num = list_active_containers(path, &containers, NULL); else num = list_all_containers(path, &containers, NULL); if (num == -1) { num = 0; goto out; } char *tmp = NULL; int check; struct ls *l = NULL; struct lxc_container *c = NULL; size_t i; for (i = 0; i < (size_t)num; i++) { char *name = containers[i]; /* Filter container names by regex the user gave us. */ if (args->ls_filter || args->argc == 1) { regex_t preg; tmp = args->ls_filter ? args->ls_filter : args->argv[0]; check = regcomp(&preg, tmp, REG_NOSUB | REG_EXTENDED); if (check == REG_ESPACE) /* we're out of memory */ goto out; else if (check != 0) continue; check = regexec(&preg, name, 0, NULL, 0); regfree(&preg); if (check != 0) continue; } errno = 0; c = lxc_container_new(name, path); if ((errno == ENOMEM) && !c) goto out; else if (!c) continue; if (args->ls_defined && !c->is_defined(c)){ goto put_and_next; } /* This does not allocate memory so no worries about freeing it * when we goto next or out. */ const char *state_tmp = c->state(c); if (!state_tmp) state_tmp = "UNKNOWN"; if (args->ls_running && !c->is_running(c)) goto put_and_next; if (args->ls_frozen && !args->ls_active && strcmp(state_tmp, "FROZEN")) goto put_and_next; if (args->ls_stopped && strcmp(state_tmp, "STOPPED")) goto put_and_next; bool running = c->is_running(c); char *grp_tmp = ls_get_groups(c, running); if (!ls_has_all_grps(grp_tmp, grps_must, grps_must_len)) { free(grp_tmp); goto put_and_next; } /* Now it makes sense to allocate memory. */ l = ls_new(m, size); if (!l) { free(grp_tmp); goto put_and_next; } /* How deeply nested are we? */ l->nestlvl = lvl; l->groups = grp_tmp; l->running = running; if (parent && args->ls_nesting && (args->ls_line || !args->ls_fancy)) /* Prepend the name of the container with all its parents when * the user requests it. */ l->name = lxc_append_paths(parent, name); else /* Otherwise simply record the name. */ l->name = strdup(name); if (!l->name) goto put_and_next; /* Do not record stuff the user did not explictly request. */ if (args->ls_fancy) { /* Maybe we should even consider the name sensitive and * hide it when you're not allowed to control the * container. */ if (!c->may_control(c)) goto put_and_next; l->state = strdup(state_tmp); if (!l->state) goto put_and_next; tmp = ls_get_config_item(c, "lxc.start.auto", running); if (tmp) { unsigned int astart = 0; if (lxc_safe_uint(tmp, &astart) < 0) printf("Could not parse value for 'lxc.start.auto'.\n"); if (astart > 1) printf("Wrong value for 'lxc.start.auto = %d'.\n", astart); l->autostart = astart == 1 ? true : false; } free(tmp); if (running) { l->init = c->init_pid(c); l->interface = ls_get_interface(c); l->ipv4 = ls_get_ips(c, "inet"); l->ipv6 = ls_get_ips(c, "inet6"); tmp = ls_get_cgroup_item(c, "memory.usage_in_bytes"); if (tmp) { l->ram = strtoull(tmp, NULL, 0); l->ram = l->ram / 1024 /1024; free(tmp); } l->swap = ls_get_swap(c); } } /* Get nested containers: Only do this after we have gathered * all other information we need. */ if (args->ls_nesting && running) { struct wrapargs wargs = (struct wrapargs){.args = NULL}; /* Open a socket so that the child can communicate with us. */ check = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wargs.pipefd); if (check == -1) goto put_and_next; /* Set the next nesting level. */ wargs.nestlvl = lvl + 1; /* Send in the parent for the next nesting level. */ wargs.parent = l->name; wargs.args = args; wargs.grps_must = grps_must; wargs.grps_must_len = grps_must_len; pid_t out; lxc_attach_options_t aopt = LXC_ATTACH_OPTIONS_DEFAULT; aopt.env_policy = LXC_ATTACH_CLEAR_ENV; /* fork(): Attach to the namespace of the container and * run ls_get() in it which is called in ls_get_wrapper(). */ check = c->attach(c, ls_get_wrapper, &wargs, &aopt, &out); /* close the socket */ close(wargs.pipefd[1]); /* Retrieve all information we want from the child. */ if (check == 0) if (ls_deserialize(wargs.pipefd[0], m, size) == -1) goto put_and_next; /* Wait for the child to finish. */ wait_for_pid(out); /* We've done all the communication we need so shutdown * the socket and close it. */ shutdown(wargs.pipefd[0], SHUT_RDWR); close(wargs.pipefd[0]); } else if (args->ls_nesting && !running) { /* This way of extracting the rootfs is not safe since * it will return very different things depending on the * storage backend that is used for the container. We * need a path-extractor function. We face the same * problem with the ovl_mkdir() function in * lxcoverlay.{c,h}. */ char *curr_path = ls_get_config_item(c, "lxc.rootfs", running); if (!curr_path) goto put_and_next; /* Since the container is not running and we cannot * attach to it we need another strategy to retrieve * nested containers. What we do is simply create a * growing path which will lead us into the rootfs of * the next container where it stores its containers. */ char *newpath = lxc_append_paths(basepath, curr_path); free(curr_path); if (!newpath) goto put_and_next; /* We want to remove all locks we create under * /run/lxc/lock so we create a string pointing us to * the lock path for the current container. */ if (ls_remove_lock(path, name, lockpath, &len_lockpath, true) == -1) { free(newpath); goto put_and_next; } ls_get(m, size, args, newpath, l->name, lvl + 1, lockpath, len_lockpath, grps_must, grps_must_len); free(newpath); /* Remove the lock. No need to check for failure here. */ ls_remove_lock(path, name, lockpath, &len_lockpath, false); } put_and_next: lxc_container_put(c); } ret = 0; out: ls_free_arr(containers, num); free(path); /* lockpath is shared amongst all non-fork()ing recursive calls to * ls_get() so only free it on the uppermost level. */ if (lvl == 0) free(*lockpath); return ret; } static char *ls_get_cgroup_item(struct lxc_container *c, const char *item) { size_t len = c->get_cgroup_item(c, item, NULL, 0); if (len <= 0) return NULL; char *val = malloc((len + 1) * sizeof(*val)); if (!val) return NULL; if ((size_t)c->get_cgroup_item(c, item, val, len + 1) != len) { free(val); val = NULL; } return val; } static char *ls_get_groups(struct lxc_container *c, bool running) { size_t len = 0; char *val = NULL; if (running) val = c->get_running_config_item(c, "lxc.group"); else len = c->get_config_item(c, "lxc.group", NULL, 0); if (!val && (len > 0)) { val = malloc((len + 1) * sizeof(*val)); if ((size_t)c->get_config_item(c, "lxc.group", val, len + 1) != len) { free(val); return NULL; } } if (val) { char *tmp; if ((tmp = strrchr(val, '\n'))) *tmp = '\0'; tmp = lxc_string_replace("\n", ", ", val); free(val); val = tmp; } return val; } static char *ls_get_ips(struct lxc_container *c, const char *inet) { char *ips = NULL; char **iptmp = c->get_ips(c, NULL, inet, 0); if (iptmp) ips = lxc_string_join(", ", (const char **)iptmp, false); lxc_free_array((void **)iptmp, free); return ips; } static char *ls_get_interface(struct lxc_container *c) { char **interfaces = c->get_interfaces(c); if (!interfaces) return NULL; char *interface = lxc_string_join(", ", (const char **)interfaces, false); lxc_free_array((void **)interfaces, free); return interface; } /* * To calculate swap usage we should not simply check memory.usage_in_bytes and * memory.memsw.usage_in_bytes and then do: * swap = memory.memsw.usage_in_bytes - memory.usage_in_bytes; * because we might receive an incorrect/negative value. * Instead we check memory.stat and check the "swap" value. */ static double ls_get_swap(struct lxc_container *c) { unsigned long long int num = 0; char *stat = ls_get_cgroup_item(c, "memory.stat"); if (!stat) goto out; char *swap = strstr(stat, "\nswap"); if (!swap) goto out; swap = 1 + swap + 4 + 1; // start_of_swap_value = '\n' + strlen(swap) + ' ' char *tmp = strchr(swap, '\n'); // find end of swap value if (!tmp) goto out; *tmp = '\0'; num = strtoull(swap, NULL, 0); num = num / 1024 / 1024; out: free(stat); return num; } static unsigned int ls_get_term_width(void) { struct winsize ws; if (((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) && (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1) && (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)) || (ws.ws_col == 0)) return 0; return ws.ws_col; } static bool ls_has_all_grps(const char *has, char **must, size_t must_len) { bool bret = false; if (!has && must) return false; else if (!must) return true; char **tmp_has = lxc_string_split_and_trim(has, ','); size_t tmp_has_len = lxc_array_len((void **)tmp_has); /* Don't do any unnecessary work. */ if (must_len > tmp_has_len) goto out; size_t i, j; for (i = 0; i < must_len; i++) { for (j = 0; j < tmp_has_len; j++) if (strcmp(must[i], tmp_has[j]) == 0) break; if (j == tmp_has_len) break; } if (i == must_len) bret = true; out: lxc_free_array((void **)tmp_has, free); return bret; } static struct ls *ls_new(struct ls **ls, size_t *size) { struct ls *m, *n; n = realloc(*ls, (*size + 1) * sizeof(struct ls)); if (!n) return NULL; *ls = n; m = *ls + *size; (*size)++; *m = (struct ls){.name = NULL, .init = -1}; return m; } static void ls_print_names(struct ls *l, struct lengths *lht, size_t size, size_t termwidth, bool list) { /* If list is empty do nothing. */ if (size == 0) return; size_t i, len = 0; struct ls *m = NULL; for (i = 0, m = l; i < size; i++, m++) { if (list) { printf("%s\n", m->name ? m->name : "-"); } else { printf("%-*s", lht->name_length, m->name ? m->name : "-"); len += lht->name_length; if ((len + lht->name_length) >= termwidth) { printf("\n"); len = 0; } else { printf(" "); len++; } } } if (len > 0) printf("\n"); } static void ls_print_fancy_format(struct ls *l, struct lengths *lht, size_t size, const char *fancy_fmt) { /* If list is empty do nothing. */ if (size == 0) return; char **tmp = lxc_string_split_and_trim(fancy_fmt, ','); if (!tmp) return; char **s; /* Check for invalid keys. */ for (s = tmp; s && *s; s++) { if (strcasecmp(*s, "NAME") && strcasecmp(*s, "STATE") && strcasecmp(*s, "PID") && strcasecmp(*s, "RAM") && strcasecmp(*s, "SWAP") && strcasecmp(*s, "AUTOSTART") && strcasecmp(*s, "GROUPS") && strcasecmp(*s, "INTERFACE") && strcasecmp(*s, "IPV4") && strcasecmp(*s, "IPV6")) { fprintf(stderr, "Invalid key: %s\n", *s); lxc_free_array((void **)tmp, free); return; } } /* print header */ for (s = tmp; s && *s; s++) { if (strcasecmp(*s, "NAME") == 0) printf("%-*s ", lht->name_length, "NAME"); else if (strcasecmp(*s, "STATE") == 0) printf("%-*s ", lht->state_length, "STATE"); else if (strcasecmp(*s, "PID") == 0) printf("%-*s ", lht->init_length, "PID"); else if (strcasecmp(*s, "RAM") == 0) printf("%-*s ", lht->ram_length + 2, "RAM"); else if (strcasecmp(*s, "SWAP") == 0) printf("%-*s ", lht->swap_length + 2, "SWAP"); else if (strcasecmp(*s, "AUTOSTART") == 0) printf("%-*s ", lht->autostart_length, "AUTOSTART"); else if (strcasecmp(*s, "GROUPS") == 0) printf("%-*s ", lht->groups_length, "GROUPS"); else if (strcasecmp(*s, "INTERFACE") == 0) printf("%-*s ", lht->interface_length, "INTERFACE"); else if (strcasecmp(*s, "IPV4") == 0) printf("%-*s ", lht->ipv4_length, "IPV4"); else if (strcasecmp(*s, "IPV6") == 0) printf("%-*s ", lht->ipv6_length, "IPV6"); } printf("\n"); struct ls *m = NULL; size_t i; for (i = 0, m = l; i < size; i++, m++) { for (s = tmp; s && *s; s++) { if (strcasecmp(*s, "NAME") == 0) { if (m->nestlvl > 0) { printf("%*s", m->nestlvl, "\\"); printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-"); } else { printf("%-*s ", lht->name_length, m->name ? m->name : "-"); } } else if (strcasecmp(*s, "STATE") == 0) { printf("%-*s ", lht->state_length, m->state ? m->state : "-"); } else if (strcasecmp(*s, "PID") == 0) { if (m->init > 0) printf("%-*d ", lht->init_length, m->init); else printf("%-*s ", lht->init_length, "-"); } else if (strcasecmp(*s, "RAM") == 0) { if ((m->ram >= 0) && m->running) printf("%*.2fMB ", lht->ram_length, m->ram); else printf("%-*s ", lht->ram_length, "-"); } else if (strcasecmp(*s, "SWAP") == 0) { if ((m->swap >= 0) && m->running) printf("%*.2fMB ", lht->swap_length, m->swap); else printf("%-*s ", lht->swap_length, "-"); } else if (strcasecmp(*s, "AUTOSTART") == 0) { printf("%-*d ", lht->autostart_length, m->autostart); } else if (strcasecmp(*s, "GROUPS") == 0) { printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-"); } else if (strcasecmp(*s, "INTERFACE") == 0) { printf("%-*s ", lht->interface_length, m->interface ? m->interface : "-"); } else if (strcasecmp(*s, "IPV4") == 0) { printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-"); } else if (strcasecmp(*s, "IPV6") == 0) { printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-"); } } printf("\n"); } lxc_free_array((void **)tmp, free); } static void ls_print_table(struct ls *l, struct lengths *lht, size_t size) { /* If list is empty do nothing. */ if (size == 0) return; struct ls *m = NULL; /* print header */ printf("%-*s ", lht->name_length, "NAME"); printf("%-*s ", lht->state_length, "STATE"); printf("%-*s ", lht->autostart_length, "AUTOSTART"); printf("%-*s ", lht->groups_length, "GROUPS"); printf("%-*s ", lht->ipv4_length, "IPV4"); printf("%-*s ", lht->ipv6_length, "IPV6"); printf("\n"); size_t i; for (i = 0, m = l; i < size; i++, m++) { if (m->nestlvl > 0) { printf("%*s", m->nestlvl, "\\"); printf("%-*s ", lht->name_length - m->nestlvl, m->name ? m->name : "-"); } else { printf("%-*s ", lht->name_length, m->name ? m->name : "-"); } printf("%-*s ", lht->state_length, m->state ? m->state : "-"); printf("%-*d ", lht->autostart_length, m->autostart); printf("%-*s ", lht->groups_length, m->groups ? m->groups : "-"); printf("%-*s ", lht->ipv4_length, m->ipv4 ? m->ipv4 : "-"); printf("%-*s ", lht->ipv6_length, m->ipv6 ? m->ipv6 : "-"); printf("\n"); } } static int my_parser(struct lxc_arguments *args, int c, char *arg) { char *invalid; unsigned long int m, n = MAX_NESTLVL; switch (c) { case '1': args->ls_line = true; break; case 'f': args->ls_fancy = true; break; case LS_ACTIVE: args->ls_active = true; break; case LS_FROZEN: args->ls_frozen = true; break; case LS_RUNNING: args->ls_running = true; break; case LS_STOPPED: args->ls_stopped = true; break; case LS_DEFINED: args->ls_defined = true; break; case LS_NESTING: /* In case strtoul() receives a string that represents a * negative number it will return ULONG_MAX - the number that * the string represents if the number the string represents is * < ULONG_MAX and ULONG_MAX otherwise. But it will consider * this valid input and not set errno. So we check manually if * the first character of num_string == '-'. Otherwise the * default level remains set. */ if (arg && !(*arg == '-')) { errno = 0; m = strtoul(arg, &invalid, 0); /* ls_nesting has type unsigned int. */ if (!errno && (*invalid == '\0') && (m <= UINT_MAX)) n = m; } args->ls_nesting = n; break; case 'g': args->groups = arg; break; case LS_FILTER: args->ls_filter = arg; break; case 'F': args->ls_fancy_format = arg; break; } return 0; } static int ls_get_wrapper(void *wrap) { int ret = -1; size_t len = 0; struct wrapargs *wargs = (struct wrapargs *)wrap; struct ls *m = NULL, *n = NULL; /* close pipe */ close(wargs->pipefd[0]); /* &(char *){NULL} is no magic. It's just a compound literal which * allows us to avoid keeping a pointless variable around. */ ls_get(&m, &len, wargs->args, "", wargs->parent, wargs->nestlvl, &(char *){NULL}, 0, wargs->grps_must, wargs->grps_must_len); if (!m) goto out; /* send length */ if (lxc_write_nointr(wargs->pipefd[1], &len, sizeof(len)) <= 0) goto out; size_t i; for (i = 0, n = m; i < len; i++, n++) { if (ls_serialize(wargs->pipefd[1], n) == -1) goto out; } ret = 0; out: shutdown(wargs->pipefd[1], SHUT_RDWR); close(wargs->pipefd[1]); ls_free(m, len); return ret; } static int ls_remove_lock(const char *path, const char *name, char **lockpath, size_t *len_lockpath, bool recalc) { int ret = -1; char *rundir; /* lockfile will be: * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root * or * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root */ rundir = get_rundir(); if (!rundir) goto out; /* Avoid doing unnecessary work if we can. */ if (recalc) { size_t newlen = strlen(path) + strlen(name) + strlen(rundir) + /* / + lxc + / + lock + / + / = */ 11 + 1; if (newlen > *len_lockpath) { char *tmp = realloc(*lockpath, newlen * 2); if (!tmp) goto out; *lockpath = tmp; *len_lockpath = newlen * 2; } } int check = snprintf(*lockpath, *len_lockpath, "%s/lxc/lock/%s/%s", rundir, path, name); if (check < 0 || (size_t)check >= *len_lockpath) goto out; lxc_rmdir_onedev(*lockpath, NULL); ret = 0; out: free(rundir); return ret; } static int ls_send_str(int fd, const char *buf) { size_t slen = 0; if (buf) slen = strlen(buf); if (lxc_write_nointr(fd, &slen, sizeof(slen)) != sizeof(slen)) return -1; if (slen > 0) { if (lxc_write_nointr(fd, buf, slen) != (ssize_t)slen) return -1; } return 0; } static int ls_serialize(int wpipefd, struct ls *n) { ssize_t nbytes = sizeof(n->ram); if (lxc_write_nointr(wpipefd, &n->ram, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->swap); if (lxc_write_nointr(wpipefd, &n->swap, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->init); if (lxc_write_nointr(wpipefd, &n->init, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->autostart); if (lxc_write_nointr(wpipefd, &n->autostart, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->running); if (lxc_write_nointr(wpipefd, &n->running, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->nestlvl); if (lxc_write_nointr(wpipefd, &n->nestlvl, (size_t)nbytes) != nbytes) return -1; /* NAME */ if (ls_send_str(wpipefd, n->name) < 0) return -1; /* STATE */ if (ls_send_str(wpipefd, n->state) < 0) return -1; /* GROUPS */ if (ls_send_str(wpipefd, n->groups) < 0) return -1; /* INTERFACE */ if (ls_send_str(wpipefd, n->interface) < 0) return -1; /* IPV4 */ if (ls_send_str(wpipefd, n->ipv4) < 0) return -1; /* IPV6 */ if (ls_send_str(wpipefd, n->ipv6) < 0) return -1; return 0; } static int ls_recv_str(int fd, char **buf) { ssize_t ret; size_t slen = 0; ret = lxc_read_nointr(fd, &slen, sizeof(slen)); if (ret != sizeof(slen)) return -1; if (slen > 0) { *buf = malloc(sizeof(char) * (slen + 1)); if (!*buf) return -1; ret = lxc_read_nointr(fd, *buf, slen); if (ret != (ssize_t)slen) { free(*buf); return -1; } (*buf)[slen] = '\0'; } return 0; } static int ls_deserialize(int rpipefd, struct ls **m, size_t *len) { struct ls *n; size_t sublen = 0; ssize_t nbytes = 0; /* get length */ nbytes = sizeof(sublen); if (lxc_read_nointr(rpipefd, &sublen, (size_t)nbytes) != nbytes) return -1; while (sublen-- > 0) { n = ls_new(m, len); if (!n) return -1; nbytes = sizeof(n->ram); if (lxc_read_nointr(rpipefd, &n->ram, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->swap); if (lxc_read_nointr(rpipefd, &n->swap, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->init); if (lxc_read_nointr(rpipefd, &n->init, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->autostart); if (lxc_read_nointr(rpipefd, &n->autostart, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->running); if (lxc_read_nointr(rpipefd, &n->running, (size_t)nbytes) != nbytes) return -1; nbytes = sizeof(n->nestlvl); if (lxc_read_nointr(rpipefd, &n->nestlvl, (size_t)nbytes) != nbytes) return -1; /* NAME */ if (ls_recv_str(rpipefd, &n->name) < 0) return -1; /* STATE */ if (ls_recv_str(rpipefd, &n->state) < 0) return -1; /* GROUPS */ if (ls_recv_str(rpipefd, &n->groups) < 0) return -1; /* INTERFACE */ if (ls_recv_str(rpipefd, &n->interface) < 0) return -1; /* IPV4 */ if (ls_recv_str(rpipefd, &n->ipv4) < 0) return -1; /* IPV6 */ if (ls_recv_str(rpipefd, &n->ipv6) < 0) return -1; } return 0; } static void ls_field_width(const struct ls *l, const size_t size, struct lengths *lht) { const struct ls *m; size_t i, len = 0; for (i = 0, m = l; i < size; i++, m++) { if (m->name) { len = strlen(m->name) + m->nestlvl; if (len > lht->name_length) lht->name_length = len; } if (m->state) { len = strlen(m->state); if (len > lht->state_length) lht->state_length = len; } if (m->interface) { len = strlen(m->interface); if (len > lht->interface_length) lht->interface_length = len; } if (m->groups) { len = strlen(m->groups); if (len > lht->groups_length) lht->groups_length = len; } if (m->ipv4) { len = strlen(m->ipv4); if (len > lht->ipv4_length) lht->ipv4_length = len; } if (m->ipv6) { len = strlen(m->ipv6); if (len > lht->ipv6_length) lht->ipv6_length = len; } if ((len = snprintf(NULL, 0, "%.2f", m->ram)) > lht->ram_length) lht->ram_length = len; if ((len = snprintf(NULL, 0, "%.2f", m->swap)) > lht->swap_length) lht->swap_length = len; if (m->init != -1) { if ((len = snprintf(NULL, 0, "%d", m->init)) > lht->init_length) lht->init_length = len; } } } lxc-2.0.11/src/lxc/tools/lxc_device.c0000644061062106075000000001101613435013473014302 00000000000000/* * lxc: linux Container library * * Authors: * Dongsheng Yang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "utils.h" #include "lxc.h" #include "log.h" #include "arguments.h" #if HAVE_IFADDRS_H #include #else #include <../include/ifaddrs.h> #endif static const struct option my_longopts[] = { LXC_COMMON_OPTIONS }; static struct lxc_arguments my_args = { .progname = "lxc-device", .help = "\ --name=NAME -- add|del DEV\n\ \n\ lxc-device attach or detach DEV to or from container.\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ --rcfile=FILE Load configuration file FILE\n", .options = my_longopts, .parser = NULL, .checker = NULL, }; static bool is_interface(const char* dev_name, pid_t pid) { pid_t p = fork(); if (p < 0) { fprintf(stderr, "failed to fork task.\n"); exit(EXIT_FAILURE); } if (p == 0) { struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; if (!switch_to_ns(pid, "net")) { fprintf(stderr, "failed to enter netns of container.\n"); exit(-1); } /* Grab the list of interfaces */ if (getifaddrs(&interfaceArray)) { fprintf(stderr, "failed to get interfaces list\n"); exit(-1); } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { if (strcmp(tempIfAddr->ifa_name, dev_name) == 0) { exit(EXIT_SUCCESS); } } exit(EXIT_FAILURE); } if (wait_for_pid(p) == 0) { return true; } return false; } int main(int argc, char *argv[]) { struct lxc_container *c; struct lxc_log log; char *cmd, *dev_name, *dst_name; bool ret = false; if (geteuid() != 0) { fprintf(stderr, "%s must be run as root\n", argv[0]); exit(EXIT_FAILURE); } if (lxc_arguments_parse(&my_args, argc, argv)) goto err; if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; if (lxc_log_init(&log)) goto err; lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "%s doesn't exist\n", my_args.name); goto err; } if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); goto err1; } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); goto err1; } } if (!c->is_running(c)) { fprintf(stderr, "Container %s is not running.\n", c->name); goto err1; } if (my_args.argc < 2) { fprintf(stderr, "Error: no command given (Please see --help output)\n"); goto err1; } cmd = my_args.argv[0]; dev_name = my_args.argv[1]; if (my_args.argc < 3) dst_name = dev_name; else dst_name = my_args.argv[2]; if (strcmp(cmd, "add") == 0) { if (is_interface(dev_name, 1)) { ret = c->attach_interface(c, dev_name, dst_name); } else { ret = c->add_device_node(c, dev_name, dst_name); } if (ret != true) { fprintf(stderr, "Failed to add %s to %s.\n", dev_name, c->name); goto err1; } } else if (strcmp(cmd, "del") == 0) { if (is_interface(dev_name, c->init_pid(c))) { ret = c->detach_interface(c, dev_name, dst_name); } else { ret = c->remove_device_node(c, dev_name, dst_name); } if (ret != true) { fprintf(stderr, "Failed to del %s from %s.\n", dev_name, c->name); goto err1; } } else { fprintf(stderr, "Error: Please use add or del (Please see --help output)\n"); goto err1; } exit(EXIT_SUCCESS); err1: lxc_container_put(c); err: exit(EXIT_FAILURE); } lxc-2.0.11/src/lxc/tools/lxc_attach.c0000644061062106075000000003517113435013473014317 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2010 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "arguments.h" #include "attach.h" #include "caps.h" #include "conf.h" #include "config.h" #include "confile.h" #include "console.h" #include "list.h" #include "log.h" #include "mainloop.h" #include "rexec.h" #include "utils.h" #if HAVE_PTY_H #include #else #include <../include/openpty.h> #endif /** * This function will copy any binary that calls liblxc into a memory file and * will use the memfd to rexecute the binary. This is done to prevent attacks * through the /proc/self/exe symlink to corrupt the host binary when host and * container are in the same user namespace or have set up an identity id * mapping: CVE-2019-5736. */ #ifdef ENFORCE_MEMFD_REXEC __attribute__((constructor)) static void lxc_attach_rexec(void) { if (!getenv("LXC_MEMFD_REXEC") && lxc_rexec("lxc-attach")) { fprintf(stderr, "Failed to re-execute lxc-attach via memory file descriptor\n"); _exit(EXIT_FAILURE); } } #endif static const struct option my_longopts[] = { {"elevated-privileges", optional_argument, 0, 'e'}, {"arch", required_argument, 0, 'a'}, {"namespaces", required_argument, 0, 's'}, {"remount-sys-proc", no_argument, 0, 'R'}, /* TODO: decide upon short option names */ {"clear-env", no_argument, 0, 500}, {"keep-env", no_argument, 0, 501}, {"keep-var", required_argument, 0, 502}, {"set-var", required_argument, 0, 'v'}, {"pty-log", required_argument, 0, 'L'}, {"rcfile", required_argument, 0, 'f'}, 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 lxc_attach_env_policy_t env_policy = LXC_ATTACH_KEEP_ENV; static char **extra_env = NULL; static ssize_t extra_env_size = 0; static char **extra_keep = NULL; static ssize_t extra_keep_size = 0; static int add_to_simple_array(char ***array, ssize_t *capacity, char *value) { ssize_t count = 0; if (!array) return -1; if (*array) for (; (*array)[count]; count++); /* we have to reallocate */ if (count >= *capacity - 1) { ssize_t new_capacity = ((count + 1) / 32 + 1) * 32; char **new_array = realloc((void*)*array, sizeof(char *) * new_capacity); if (!new_array) return -1; memset(&new_array[count], 0, sizeof(char*)*(new_capacity - count)); *array = new_array; *capacity = new_capacity; } if (!(*array)) return -1; (*array)[count] = value; return 0; } static int my_parser(struct lxc_arguments* args, int c, char* arg) { char **it; char *del; int ret; switch (c) { case 'e': ret = lxc_fill_elevated_privileges(arg, &elevated_privileges); if (ret) return -1; break; case 'R': remount_sys_proc = 1; break; case 'a': new_personality = lxc_config_parse_arch(arg); if (new_personality < 0) { lxc_error(args, "invalid architecture specified: %s", arg); return -1; } break; case 's': namespace_flags = 0; /* The identifiers for namespaces used with lxc-attach as given * on the manpage do not align with the standard identifiers. * This affects network, mount, and uts namespaces. The standard * identifiers are: "mnt", "uts", and "net" whereas lxc-attach * uses "MOUNT", "UTSNAME", and "NETWORK". So let's use some * cheap memmove()s to replace them by their standard * identifiers. Let's illustrate this with an example: * Assume the string: * * "IPC|MOUNT|PID" * * then we memmove() * * dest: del + 1 == OUNT|PID * src: del + 3 == NT|PID */ while ((del = strstr(arg, "MOUNT"))) memmove(del + 1, del + 3, strlen(del) - 2); for (it = (char *[]){"NETWORK", "UTSNAME", NULL}; it && *it; it++) while ((del = strstr(arg, *it))) memmove(del + 3, del + 7, strlen(del) - 6); ret = lxc_fill_namespace_flags(arg, &namespace_flags); if (ret) return -1; /* -s implies -e */ lxc_fill_elevated_privileges(NULL, &elevated_privileges); break; case 500: /* clear-env */ env_policy = LXC_ATTACH_CLEAR_ENV; break; case 501: /* keep-env */ env_policy = LXC_ATTACH_KEEP_ENV; break; case 502: /* keep-var */ ret = add_to_simple_array(&extra_keep, &extra_keep_size, arg); if (ret < 0) { lxc_error(args, "memory allocation error"); return -1; } break; case 'v': ret = add_to_simple_array(&extra_env, &extra_env_size, arg); if (ret < 0) { lxc_error(args, "memory allocation error"); return -1; } break; case 'L': args->console_log = arg; break; case 'f': args->rcfile = arg; break; } return 0; } static struct lxc_arguments my_args = { .progname = "lxc-attach", .help = "\ --name=NAME [-- COMMAND]\n\ \n\ Execute the specified COMMAND - enter the container NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME of the container\n\ -e, --elevated-privileges=PRIVILEGES\n\ Use elevated privileges instead of those of the\n\ container. If you don't specify privileges to be\n\ elevated as OR'd list: CAP, CGROUP and LSM (capabilities,\n\ cgroup and restrictions, respectively) then all of them\n\ will be elevated.\n\ WARNING: This may leak privileges into the container.\n\ Use with care.\n\ -a, --arch=ARCH Use ARCH for program instead of container's own\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 with all privileges\n\ elevated, it may therefore leak privileges into the\n\ 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\ --clear-env Clear all environment variables before attaching.\n\ The attached shell/program will start with only\n\ container=lxc set.\n\ --keep-env Keep all current environment variables. This\n\ is the current default behaviour, but is likely to\n\ change in the future.\n\ -L, --pty-log=FILE\n\ Log pty output to FILE\n\ -v, --set-var Set an additional variable that is seen by the\n\ attached program in the container. May be specified\n\ multiple times.\n\ --keep-var Keep an additional environment variable. Only\n\ applicable if --clear-env is specified. May be used\n\ multiple times.\n\ -f, --rcfile=FILE\n\ Load configuration file FILE\n\ ", .options = my_longopts, .parser = my_parser, .checker = NULL, }; struct wrapargs { lxc_attach_options_t *options; lxc_attach_command_t *command; struct lxc_console *console; int ptyfd; }; /* Minimalistic login_tty() implementation. */ static int login_pty(int fd) { setsid(); if (ioctl(fd, TIOCSCTTY, NULL) < 0) return -1; if (lxc_console_set_stdfds(fd) < 0) return -1; if (fd > STDERR_FILENO) close(fd); return 0; } static int get_pty_on_host_callback(void *p) { struct wrapargs *wrap = p; close(wrap->console->master); if (login_pty(wrap->console->slave) < 0) return -1; if (wrap->command->program) lxc_attach_run_command(wrap->command); else lxc_attach_run_shell(NULL); return -1; } static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *pid) { struct lxc_epoll_descr descr; struct lxc_conf *conf; struct lxc_tty_state *ts; int ret = -1; struct wrapargs *args = wrap; if (!isatty(args->ptyfd)) { fprintf(stderr, "Standard file descriptor does not refer to a pty\n"); return -1; } if (c->lxc_conf) { conf = c->lxc_conf; } else { /* If the container is not defined and the user didn't specify a * config file to load we will simply init a dummy config here. */ conf = lxc_conf_init(); if (!conf) { fprintf(stderr, "Failed to allocate dummy config file for the container\n"); return -1; } /* We also need a dummy rootfs path otherwise * lxc_console_create() will not let us create a console. Note, * I don't want this change to make it into * lxc_console_create()'s since this function will only be * responsible for proper /dev/{console,tty} devices. * lxc-attach is just abusing it to also handle the pty case * because it is very similar. However, with LXC 3.0 lxc-attach * will need to move away from using lxc_console_create() since * this is actually an internal symbol and we only want the * tools to use the API with LXC 3.0. */ conf->rootfs.path = strdup("dummy"); if (!conf->rootfs.path) return -1; } free(conf->console.log_path); if (my_args.console_log) conf->console.log_path = strdup(my_args.console_log); else conf->console.log_path = NULL; /* In the case of lxc-attach our peer pty will always be the current * controlling terminal. We clear whatever was set by the user for * lxc.console.path here and set it NULL. lxc_console_peer_default() * will then try to open /dev/tty. If the process doesn't have a * controlling terminal we should still proceed. */ free(conf->console.path); conf->console.path = NULL; /* Create pty on the host. */ if (lxc_console_create(conf) < 0) return -1; ts = conf->console.tty_state; conf->console.descr = &descr; /* Shift ttys to container. */ ret = lxc_pty_map_ids(conf, &conf->console); if (ret < 0) { fprintf(stderr, "Failed to shift tty into container\n"); goto err1; } /* Send wrapper function on its way. */ wrap->console = &conf->console; if (c->attach(c, get_pty_on_host_callback, wrap, wrap->options, pid) < 0) goto err1; close(conf->console.slave); /* Close slave side. */ conf->console.slave = -1; ret = lxc_mainloop_open(&descr); if (ret) { fprintf(stderr, "failed to create mainloop\n"); goto err2; } if (lxc_console_mainloop_add(&descr, &conf->console) < 0) { fprintf(stderr, "Failed to add handlers to lxc mainloop.\n"); goto err3; } ret = lxc_mainloop(&descr, -1); if (ret) { fprintf(stderr, "mainloop returned an error\n"); goto err3; } ret = 0; err3: lxc_mainloop_close(&descr); err2: if (ts && ts->sigfd != -1) lxc_console_signal_fini(ts); err1: lxc_console_delete(&conf->console); return ret; } static int stdfd_is_pty(void) { if (isatty(STDIN_FILENO)) return STDIN_FILENO; if (isatty(STDOUT_FILENO)) return STDOUT_FILENO; if (isatty(STDERR_FILENO)) return STDERR_FILENO; return -1; } int main(int argc, char *argv[]) { int ret = -1, r; int wexit = 0; struct lxc_log log; pid_t pid; lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL}; r = lxc_caps_init(); if (r) exit(EXIT_FAILURE); r = lxc_arguments_parse(&my_args, argc, argv); if (r) exit(EXIT_FAILURE); if (!my_args.log_file) my_args.log_file = "none"; log.name = my_args.name; log.file = my_args.log_file; log.level = my_args.log_priority; log.prefix = my_args.progname; log.quiet = my_args.quiet; log.lxcpath = my_args.lxcpath[0]; r = lxc_log_init(&log); if (r) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (geteuid()) { if (access(my_args.lxcpath[0], O_RDONLY) < 0) { if (!my_args.quiet) fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(EXIT_FAILURE); } } struct lxc_container *c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) exit(EXIT_FAILURE); if (my_args.rcfile) { c->clear_config(c); if (!c->load_config(c, my_args.rcfile)) { fprintf(stderr, "Failed to load rcfile\n"); lxc_container_put(c); exit(EXIT_FAILURE); } c->configfile = strdup(my_args.rcfile); if (!c->configfile) { fprintf(stderr, "Out of memory setting new config filename\n"); lxc_container_put(c); exit(EXIT_FAILURE); } } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", c->name); lxc_container_put(c); exit(EXIT_FAILURE); } if (remount_sys_proc) attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS; if (elevated_privileges) attach_options.attach_flags &= ~(elevated_privileges); attach_options.namespaces = namespace_flags; attach_options.personality = new_personality; attach_options.env_policy = env_policy; attach_options.extra_env_vars = extra_env; attach_options.extra_keep_env = extra_keep; if (my_args.argc > 0) { command.program = my_args.argv[0]; command.argv = (char**)my_args.argv; } struct wrapargs wrap = (struct wrapargs){ .command = &command, .options = &attach_options }; wrap.ptyfd = stdfd_is_pty(); if (wrap.ptyfd >= 0) { if ((!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) && my_args.console_log) { fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n"); goto out; } ret = get_pty_on_host(c, &wrap, &pid); } else { if (my_args.console_log) { fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n"); goto out; } if (command.program) ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid); else ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid); } if (ret < 0) goto out; ret = lxc_wait_for_pid_status(pid); if (ret < 0) goto out; if (WIFEXITED(ret)) wexit = WEXITSTATUS(ret); out: lxc_container_put(c); if (ret >= 0) exit(wexit); exit(EXIT_FAILURE); } lxc-2.0.11/src/lxc/commands_utils.c0000644061062106075000000001265413435013473014067 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 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. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include "af_unix.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "initutils.h" #include "log.h" #include "lxclock.h" #include "monitor.h" #include "state.h" #include "utils.h" lxc_log_define(lxc_commands_utils, lxc); int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout) { int ret; struct lxc_msg msg; struct timeval out; if (timeout >= 0) { memset(&out, 0, sizeof(out)); out.tv_sec = timeout; ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, sizeof(out)); if (ret < 0) { SYSERROR("Failed to set %ds timeout on container " "state socket", timeout); return -1; } } memset(&msg, 0, sizeof(msg)); again: ret = recv(state_client_fd, &msg, sizeof(msg), 0); if (ret < 0) { if (errno == EINTR) { TRACE("Caught EINTR; retrying"); goto again; } ERROR("Failed to receive message: %s", strerror(errno)); return -1; } TRACE("Received state %s from state client %d", lxc_state2str(msg.value), state_client_fd); return msg.value; } /* Register a new state client and retrieve state from command socket. */ int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int timeout) { int ret; int state_client_fd; ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd); if (ret < 0) return -1; if (ret < MAX_STATE) return ret; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); close(state_client_fd); return ret; } int lxc_make_abstract_socket_name(char *path, size_t pathlen, const char *lxcname, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { const char *name; char *offset; char *tmppath; size_t len; size_t tmplen; uint64_t hash; int ret; if (!path) return -1; offset = &path[1]; /* -2 here because this is an abstract unix socket so it needs a * leading \0, and we null terminate, so it needs a trailing \0. * Although null termination isn't required by the API, we do it anyway * because we print the sockname out sometimes. */ len = pathlen - 2; name = lxcname; if (!name) name = ""; if (hashed_sock_name != NULL) { ret = snprintf(offset, len, "lxc/%s/%s", hashed_sock_name, suffix); if (ret < 0 || ret >= len) { ERROR("Failed to create abstract socket name"); return -1; } return 0; } if (!lxcpath) { lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) { ERROR("Failed to allocate memory"); return -1; } } ret = snprintf(offset, len, "%s/%s/%s", lxcpath, name, suffix); if (ret < 0) { ERROR("Failed to create abstract socket name"); return -1; } if (ret < len) return 0; /* ret >= len; lxcpath or name is too long. hash both */ tmplen = strlen(name) + strlen(lxcpath) + 2; tmppath = alloca(tmplen); ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name); if (ret < 0 || (size_t)ret >= tmplen) { ERROR("Failed to create abstract socket name"); return -1; } hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT); ret = snprintf(offset, len, "lxc/%016" PRIx64 "/%s", hash, suffix); if (ret < 0 || ret >= len) { ERROR("Failed to create abstract socket name"); return -1; } return 0; } int lxc_cmd_connect(const char *name, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { int ret, client_fd; char path[LXC_AUDS_ADDR_LEN] = {0}; ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, hashed_sock_name, suffix); if (ret < 0) return -1; /* Get new client fd. */ client_fd = lxc_abstract_unix_connect(path); if (client_fd < 0) return -1; return client_fd; } int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, lxc_state_t states[MAX_STATE]) { int state; struct state_client *newclient; struct lxc_list *tmplist; newclient = malloc(sizeof(*newclient)); if (!newclient) return -ENOMEM; /* copy requested states */ memcpy(newclient->states, states, sizeof(newclient->states)); newclient->clientfd = state_client_fd; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) { free(newclient); return -ENOMEM; } state = handler->state; if (states[state] != 1) { lxc_list_add_elem(tmplist, newclient); lxc_list_add_tail(&handler->state_clients, tmplist); } else { free(newclient); free(tmplist); return state; } TRACE("Added state client %d to state client list", state_client_fd); return MAX_STATE; } lxc-2.0.11/src/lxc/attach.c0000644061062106075000000011042113435013473012301 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_DECL_PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif #include "af_unix.h" #include "attach.h" #include "caps.h" #include "cgroup.h" #include "commands.h" #include "conf.h" #include "config.h" #include "confile.h" #include "console.h" #include "log.h" #include "lsm/lsm.h" #include "lxclock.h" #include "lxcseccomp.h" #include "mainloop.h" #include "namespace.h" #include "utils.h" #if HAVE_SYS_PERSONALITY_H #include #endif #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 02000000 #endif #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MS_SLAVE #define MS_SLAVE (1 << 19) #endif #define LXC_ATTACH_ALLOCATE_PTY 0x00000000 lxc_log_define(lxc_attach, lxc); /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ #define __PROC_STATUS_LEN (5 + (LXC_NUMSTRLEN64) + 7 + 1) static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { int ret; bool found; FILE *proc_file; char proc_fn[__PROC_STATUS_LEN]; size_t line_bufsz = 0; char *line = NULL; struct lxc_proc_context_info *info = NULL; /* Read capabilities. */ ret = snprintf(proc_fn, __PROC_STATUS_LEN, "/proc/%d/status", pid); if (ret < 0 || ret >= __PROC_STATUS_LEN) goto on_error; proc_file = fopen(proc_fn, "r"); if (!proc_file) { SYSERROR("Could not open %s.", proc_fn); goto on_error; } info = calloc(1, sizeof(*info)); if (!info) { SYSERROR("Could not allocate memory."); fclose(proc_file); return NULL; } found = false; while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) { found = true; break; } } free(line); fclose(proc_file); if (!found) { SYSERROR("Could not read capability bounding set from %s.", proc_fn); errno = ENOENT; goto on_error; } info->lsm_label = lsm_process_label_get(pid); info->ns_inherited = 0; memset(info->ns_fd, -1, sizeof(int) * LXC_NS_MAX); return info; on_error: free(info); return NULL; } static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx) { int i; for (i = 0; i < LXC_NS_MAX; i++) { if (ctx->ns_fd[i] < 0) continue; close(ctx->ns_fd[i]); ctx->ns_fd[i] = -EBADF; } } static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx) { free(ctx->lsm_label); ctx->lsm_label = NULL; if (ctx->container) { lxc_container_put(ctx->container); ctx->container = NULL; } lxc_proc_close_ns_fd(ctx); free(ctx); } /** * in_same_namespace - Check whether two processes are in the same namespace. * @pid1 - PID of the first process. * @pid2 - PID of the second process. * @ns - Name of the namespace to check. Must correspond to one of the names * for the namespaces as shown in /proc/= 0) close(ns_fd1); if (ns_fd2 >= 0) close(ns_fd2); return ret; } static int lxc_attach_to_ns(pid_t pid, struct lxc_proc_context_info *ctx) { int i, ret; for (i = 0; i < LXC_NS_MAX; i++) { if (ctx->ns_fd[i] < 0) continue; ret = setns(ctx->ns_fd[i], ns_info[i].clone_flag); if (ret < 0) { SYSERROR("Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); return -1; } DEBUG("Attached to %s namespace of %d", ns_info[i].proc_name, pid); } return 0; } static int lxc_attach_remount_sys_proc(void) { int ret; ret = unshare(CLONE_NEWNS); if (ret < 0) { SYSERROR("Failed to unshare mount namespace."); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { SYSERROR("Failed to make / rslave."); ERROR("Continuing..."); } } /* 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; } static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) { int cap, last_cap; last_cap = lxc_caps_last_cap(); for (cap = 0; cap <= last_cap; cap++) { if (ctx->capability_mask & (1LL << cap)) continue; if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) { SYSERROR("Failed to drop capability %d", cap); return -1; } TRACE("Dropped capability %d", cap); } return 0; } static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx, enum lxc_attach_env_policy_t policy, char **extra_env, char **extra_keep) { int ret; struct lxc_list *iterator; if (policy == LXC_ATTACH_CLEAR_ENV) { int path_kept = 0; char **extra_keep_store = NULL; if (extra_keep) { size_t count, i; for (count = 0; extra_keep[count]; count++) ; extra_keep_store = calloc(count, sizeof(char *)); if (!extra_keep_store) return -1; for (i = 0; i < count; i++) { char *v = getenv(extra_keep[i]); if (v) { extra_keep_store[i] = strdup(v); if (!extra_keep_store[i]) { while (i > 0) free(extra_keep_store[--i]); free(extra_keep_store); return -1; } if (strcmp(extra_keep[i], "PATH") == 0) path_kept = 1; } } } if (clearenv()) { if (extra_keep_store) { char **p; for (p = extra_keep_store; *p; p++) free(*p); free(extra_keep_store); } SYSERROR("Failed to clear environment"); return -1; } if (extra_keep_store) { size_t i; for (i = 0; extra_keep[i]; i++) { if (extra_keep_store[i]) { ret = setenv(extra_keep[i], extra_keep_store[i], 1); if (ret < 0) WARN("%s - Failed to set environment variable", strerror(errno)); } free(extra_keep_store[i]); } free(extra_keep_store); } /* Always set a default path; shells and execlp tend to be fine * without it, but there is a disturbing number of C programs * out there that just assume that getenv("PATH") is never NULL * and then die a painful segfault death. */ if (!path_kept) { ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); if (ret < 0) WARN("%s - Failed to set environment variable", strerror(errno)); } } ret = putenv("container=lxc"); if (ret < 0) { WARN("%s - Failed to set environment variable", strerror(errno)); return -1; } /* Set container environment variables.*/ if (init_ctx && init_ctx->container && init_ctx->container->lxc_conf) { lxc_list_for_each(iterator, &init_ctx->container->lxc_conf->environment) { char *env_tmp; env_tmp = strdup((char *)iterator->elem); if (!env_tmp) return -1; ret = putenv(env_tmp); if (ret < 0) { SYSERROR("Failed to set environment variable: %s", (char *)iterator->elem); return -1; } } } /* Set extra environment variables. */ if (extra_env) { for (; *extra_env; extra_env++) { char *p; /* We just assume the user knows what they are doing, so * we don't do any checks. */ p = strdup(*extra_env); if (!p) return -1; ret = putenv(p); if (ret < 0) WARN("%s - Failed to set environment variable", strerror(errno)); } } return 0; } static char *lxc_attach_getpwshell(uid_t uid) { int fd, ret; pid_t pid; int pipes[2]; FILE *pipe_f; bool found = false; size_t line_bufsz = 0; char *line = NULL, *result = NULL; /* We need to fork off a process that runs the getent program, and we * need to capture its output, so we use a pipe for that purpose. */ ret = pipe2(pipes, O_CLOEXEC); if (ret < 0) return NULL; pid = fork(); if (pid < 0) { close(pipes[0]); close(pipes[1]); return NULL; } if (!pid) { char uid_buf[32]; char *arguments[] = { "getent", "passwd", uid_buf, NULL }; close(pipes[0]); /* We want to capture stdout. */ ret = dup2(pipes[1], STDOUT_FILENO); close(pipes[1]); if (ret < 0) exit(EXIT_FAILURE); /* Get rid of stdin/stderr, so we try to associate it with * /dev/null. */ fd = open_devnull(); if (fd < 0) { close(STDIN_FILENO); close(STDERR_FILENO); } else { (void)dup3(fd, STDIN_FILENO, O_CLOEXEC); (void)dup3(fd, STDOUT_FILENO, O_CLOEXEC); close(fd); } /* Finish argument list. */ ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long)uid); if (ret <= 0 || ret >= sizeof(uid_buf)) exit(EXIT_FAILURE); /* Try to run getent program. */ (void)execvp("getent", arguments); exit(EXIT_FAILURE); } close(pipes[1]); pipe_f = fdopen(pipes[0], "r"); while (getline(&line, &line_bufsz, pipe_f) != -1) { int i; long value; char *token; char *endptr = NULL, *saveptr = NULL; /* If we already found something, just continue to read * until the pipe doesn't deliver any more data, but * don't modify the existing data structure. */ if (found) continue; if (!line) continue; /* Trim line on the right hand side. */ for (i = strlen(line); i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) line[i - 1] = '\0'; /* Split into tokens: first: user name. */ token = strtok_r(line, ":", &saveptr); if (!token) continue; /* next: dummy password field */ token = strtok_r(NULL, ":", &saveptr); if (!token) continue; /* next: user id */ token = strtok_r(NULL, ":", &saveptr); value = token ? strtol(token, &endptr, 10) : 0; if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) continue; /* dummy sanity check: user id matches */ if ((uid_t)value != uid) continue; /* skip fields: gid, gecos, dir, go to next field 'shell' */ for (i = 0; i < 4; i++) { token = strtok_r(NULL, ":", &saveptr); if (!token) continue; } if (!token) continue; free(result); result = strdup(token); /* Sanity check that there are no fields after that. */ token = strtok_r(NULL, ":", &saveptr); if (token) continue; found = true; } free(line); fclose(pipe_f); ret = wait_for_pid(pid); if (ret < 0) { free(result); return NULL; } if (!found) { free(result); return NULL; } return result; } static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid) { FILE *proc_file; char proc_fn[__PROC_STATUS_LEN]; int ret; char *line = NULL; size_t line_bufsz = 0; long value = -1; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; ret = snprintf(proc_fn, __PROC_STATUS_LEN, "/proc/%d/status", 1); if (ret < 0 || ret >= __PROC_STATUS_LEN) return; proc_file = fopen(proc_fn, "r"); if (!proc_file) return; while (getline(&line, &line_bufsz, proc_file) != -1) { /* Format is: real, effective, saved set user, fs we only care * about real uid. */ ret = sscanf(line, "Uid: %ld", &value); if (ret != EOF && ret == 1) { uid = (uid_t)value; } else { ret = sscanf(line, "Gid: %ld", &value); if (ret != EOF && ret == 1) gid = (gid_t)value; } if (uid != (uid_t)-1 && gid != (gid_t)-1) break; } fclose(proc_file); free(line); /* Only override arguments if we found something. */ if (uid != (uid_t)-1) *init_uid = uid; if (gid != (gid_t)-1) *init_gid = gid; /* TODO: we should also parse supplementary groups and use * setgroups() to set them. */ } /* Help the optimizer along if it doesn't know that exit always exits. */ #define rexit(c) \ do { \ int __c = (c); \ _exit(__c); \ return __c; \ } while (0) /* Define default options if no options are supplied by the user. */ static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_DEFAULT; static bool fetch_seccomp(struct lxc_container *c, lxc_attach_options_t *options) { char *path; if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) { free(c->lxc_conf->seccomp); c->lxc_conf->seccomp = NULL; return true; } /* Remove current setting. */ if (!c->set_config_item(c, "lxc.seccomp", "")) return false; /* Fetch the current profile path over the cmd interface. */ path = c->get_running_config_item(c, "lxc.seccomp"); if (!path) { INFO("Failed to get running config item for lxc.seccomp"); return true; } /* Copy the value into the new lxc_conf. */ if (!c->set_config_item(c, "lxc.seccomp", path)) { free(path); return false; } free(path); /* Attempt to parse the resulting config. */ if (lxc_read_seccomp_config(c->lxc_conf) < 0) { ERROR("Error reading seccomp policy."); return false; } INFO("Retrieved seccomp policy."); return true; } static signed long get_personality(const char *name, const char *lxcpath) { char *p; signed long ret; p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); if (!p) return -1; ret = lxc_config_parse_arch(p); free(p); return ret; } struct attach_clone_payload { int ipc_socket; int pty_fd; lxc_attach_options_t *options; struct lxc_proc_context_info *init_ctx; lxc_attach_exec_t exec_function; void *exec_payload; }; static void lxc_put_attach_clone_payload(struct attach_clone_payload *p) { if (p->ipc_socket >= 0) { close(p->ipc_socket); p->ipc_socket = -EBADF; } if (p->pty_fd >= 0) { close(p->pty_fd); p->pty_fd = -EBADF; } if (p->init_ctx) { lxc_proc_put_context_info(p->init_ctx); p->init_ctx = NULL; } } static int attach_child_main(struct attach_clone_payload *payload) { int fd, lsm_fd, ret; uid_t new_uid; gid_t new_gid; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; bool needs_lsm = (options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label; /* 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 (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) goto on_error; TRACE("Remounted \"/proc\" and \"/sys\""); } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { long new_personality; if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; ret = personality(new_personality); if (ret < 0) goto on_error; TRACE("Set new personality"); } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) goto on_error; TRACE("Dropped capabilities"); } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(init_ctx, options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) goto on_error; TRACE("Set up environment"); /* This remark only affects fully unprivileged containers: * Receive fd for LSM security module before we set{g,u}id(). The reason * is that on set{g,u}id() the kernel will a) make us undumpable and b) * we will change our effective uid. This means our effective uid will * be different from the effective uid of the process that created us * which means that this processs no longer has capabilities in our * namespace including CAP_SYS_PTRACE. This means we will not be able to * read and /proc/ files for the process anymore when /proc is * mounted with hidepid={1,2}. So let's get the lsm label fd before the * set{g,u}id(). */ if (needs_lsm) { ret = lxc_abstract_unix_recv_fds(payload->ipc_socket, &lsm_fd, 1, NULL, 0); if (ret <= 0) goto on_error; TRACE("Received LSM label file descriptor %d from parent", lsm_fd); } if (options->stdin_fd > 0 && isatty(options->stdin_fd)) { ret = lxc_make_controlling_pty(options->stdin_fd); if (ret < 0) goto on_error; } /* Set {u,g}id. */ new_uid = 0; new_gid = 0; /* Ignore errors, we will fall back to root in that case (/proc was not * mounted etc.). */ if (options->namespaces & CLONE_NEWUSER) lxc_attach_get_init_uidgid(&new_uid, &new_gid); if (options->uid != (uid_t)-1) new_uid = options->uid; if (options->gid != (gid_t)-1) new_gid = options->gid; /* Try to set the {u,g}id combination. */ if (new_uid != 0 || new_gid != 0 || options->namespaces & CLONE_NEWUSER) { ret = lxc_switch_uid_gid(new_uid, new_gid); if (ret < 0) goto on_error; ret = lxc_setgroups(0, NULL); if (ret < 0) goto on_error; } if (needs_lsm) { bool on_exec; /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; ret = lsm_process_label_set_at(lsm_fd, init_ctx->lsm_label, on_exec); close(lsm_fd); if (ret < 0) goto on_error; TRACE("Set %s LSM label to \"%s\"", lsm_name(), init_ctx->lsm_label); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp) { ret = lxc_seccomp_load(init_ctx->container->lxc_conf); if (ret < 0) goto on_error; TRACE("Loaded seccomp profile"); } close(payload->ipc_socket); payload->ipc_socket = -EBADF; lxc_proc_put_context_info(init_ctx); payload->init_ctx = NULL; /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO) (void)dup2(options->stdin_fd, STDIN_FILENO); if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO) (void)dup2(options->stdout_fd, STDOUT_FILENO); if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO) (void)dup2(options->stderr_fd, STDERR_FILENO); /* close the old fds */ if (options->stdin_fd > STDERR_FILENO) close(options->stdin_fd); if (options->stdout_fd > STDERR_FILENO) close(options->stdout_fd); if (options->stderr_fd > STDERR_FILENO) close(options->stderr_fd); /* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { int flags; flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if ((flags & FD_CLOEXEC) == 0) continue; ret = fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to clear FD_CLOEXEC from file descriptor %d", fd); goto on_error; } } if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { ret = lxc_login_pty(payload->pty_fd); if (ret < 0) { SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd); goto on_error; } TRACE("Prepared pty file descriptor %d", payload->pty_fd); } /* We're done, so we can now do whatever the user intended us to do. */ rexit(payload->exec_function(payload->exec_payload)); on_error: lxc_put_attach_clone_payload(payload); rexit(EXIT_FAILURE); } static int lxc_attach_pty(struct lxc_conf *conf, struct lxc_console *pty) { int ret; lxc_pty_init(pty); ret = lxc_pty_create(pty); if (ret < 0) { SYSERROR("Failed to create pty"); return -1; } /* Shift ttys to container. */ ret = lxc_pty_map_ids(conf, pty); if (ret < 0) { ERROR("Failed to shift pty"); goto on_error; } return 0; on_error: lxc_console_delete(pty); lxc_pty_conf_free(pty); return -1; } static int lxc_attach_pty_mainloop_init(struct lxc_console *pty, struct lxc_epoll_descr *descr) { int ret; ret = lxc_mainloop_open(descr); if (ret < 0) { ERROR("Failed to create mainloop"); return -1; } ret = lxc_console_mainloop_add(descr, pty); if (ret < 0) { ERROR("Failed to add handlers to mainloop"); lxc_mainloop_close(descr); return -1; } return 0; } static inline void lxc_attach_pty_close_master(struct lxc_console *pty) { if (pty->master < 0) return; close(pty->master); pty->master = -EBADF; } static inline void lxc_attach_pty_close_slave(struct lxc_console *pty) { if (pty->slave < 0) return; close(pty->slave); pty->slave = -EBADF; } static inline void lxc_attach_pty_close_peer(struct lxc_console *pty) { if (pty->peer < 0) return; close(pty->peer); pty->peer = -EBADF; } static inline void lxc_attach_pty_close_log(struct lxc_console *pty) { if (pty->log_fd < 0) return; close(pty->log_fd); pty->log_fd = -EBADF; } int lxc_attach(const char *name, const char *lxcpath, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int i, ret, status; int ipc_sockets[2]; char *cwd, *new_cwd; signed long personality; pid_t attached_pid, init_pid, pid; struct lxc_proc_context_info *init_ctx; struct lxc_console pty; struct attach_clone_payload payload = {0}; ret = access("/proc/self/ns", X_OK); if (ret) { ERROR("Does this kernel version support namespaces?"); return -1; } if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { ERROR("Failed to get init pid."); return -1; } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("Failed to get context of init process: %ld", (long)init_pid); return -1; } personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { ERROR("Failed to get personality of the container"); lxc_proc_put_context_info(init_ctx); return -1; } init_ctx->personality = personality; init_ctx->container = lxc_container_new(name, lxcpath); if (!init_ctx->container) { lxc_proc_put_context_info(init_ctx); return -1; } if (!init_ctx->container->lxc_conf) { init_ctx->container->lxc_conf = lxc_conf_init(); if (!init_ctx->container->lxc_conf) { lxc_proc_put_context_info(init_ctx); return -ENOMEM; } } if (!fetch_seccomp(init_ctx->container, options)) WARN("Failed to get seccomp policy."); cwd = getcwd(NULL, 0); /* Determine which namespaces the container was created with * by asking lxc-start, if necessary. */ if (options->namespaces == -1) { options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath); /* call failed */ if (options->namespaces == -1) { ERROR("Failed to automatically determine the " "namespaces which the container uses"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } for (i = 0; i < LXC_NS_MAX; i++) { if (ns_info[i].clone_flag & CLONE_NEWCGROUP) if (!(options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) || !cgns_supported()) continue; if (ns_info[i].clone_flag & options->namespaces) continue; init_ctx->ns_inherited |= ns_info[i].clone_flag; } } pid = lxc_raw_getpid(); for (i = 0; i < LXC_NS_MAX; i++) { int j, saved_errno; if (options->namespaces & ns_info[i].clone_flag) init_ctx->ns_fd[i] = lxc_preserve_ns(init_pid, ns_info[i].proc_name); else if (init_ctx->ns_inherited & ns_info[i].clone_flag) init_ctx->ns_fd[i] = in_same_namespace(pid, init_pid, ns_info[i].proc_name); else continue; if (init_ctx->ns_fd[i] >= 0) continue; if (init_ctx->ns_fd[i] == -EINVAL) { DEBUG("Inheriting %s namespace from %d", ns_info[i].proc_name, pid); init_ctx->ns_inherited &= ~ns_info[i].clone_flag; continue; } /* We failed to preserve the namespace. */ 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(init_ctx->ns_fd[j]); errno = saved_errno; SYSERROR("Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { ret = lxc_attach_pty(init_ctx->container->lxc_conf, &pty); if (ret < 0) { ERROR("Failed to allocate pty"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } } else { lxc_pty_init(&pty); } /* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order * to make sure we don't irritate other threads that want to fork+exec * away * * IMPORTANT: if the initial process is multithreaded and another call * just fork()s away without exec'ing directly after, the socket fd will * exist in the forked process from the other thread and any close() in * our own child process will not really cause the socket to close * properly, potentiall causing the parent to hang. * * For this reason, while IPC is still active, we have to use shutdown() * if the child exits prematurely in order to signal that the socket is * closed and cannot assume that the child exiting will automatically do * that. * * IPC mechanism: (X is receiver) * initial process intermediate attached * X <--- send pid of * attached proc, * then exit * send 0 ------------------------------------> X * [do initialization] * X <------------------------------------ send 1 * [add to cgroup, ...] * send 2 ------------------------------------> X * [set LXC_ATTACH_NO_NEW_PRIVS] * X <------------------------------------ send 3 * [open LSM label fd] * send 4 ------------------------------------> X * [set LSM label] * close socket close socket * run program */ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); if (ret < 0) { SYSERROR("Could not set up required IPC mechanism for attaching."); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } /* Create intermediate subprocess, two reasons: * 1. We can't setns() in the child itself, since we want to make * sure we are properly attached to the pidns. * 2. Also, the initial thread has to put the attached process * into the cgroup, which we can only do if we didn't already * setns() (otherwise, user namespaces will hate us). */ pid = fork(); if (pid < 0) { SYSERROR("Failed to create first subprocess."); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (pid) { int ret_parent = -1; pid_t to_cleanup_pid = pid; struct lxc_epoll_descr descr = {0}; /* close unneeded file descriptors */ close(ipc_sockets[1]); free(cwd); lxc_proc_close_ns_fd(init_ctx); if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) lxc_attach_pty_close_slave(&pty); /* Attach to cgroup, if requested. */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (!cgroup_attach(name, lxcpath, pid)) goto on_error; TRACE("Moved intermediate process %d into container's " "cgroups", pid); } if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { ret = lxc_attach_pty_mainloop_init(&pty, &descr); if (ret < 0) goto on_error; TRACE("Initalized pty mainloop"); } /* Let the child process know to go ahead. */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret != sizeof(status)) goto close_mainloop; TRACE("Told intermediate process to start initializing"); /* Get pid of attached process from intermediate process. */ ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid)); if (ret != sizeof(attached_pid)) goto close_mainloop; TRACE("Received pid %d of attached process in parent pid namespace", attached_pid); /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */ if (options->stdin_fd == 0) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } /* Reap intermediate process. */ ret = wait_for_pid(pid); if (ret < 0) goto close_mainloop; TRACE("Intermediate process %d exited", pid); /* We will always have to reap the attached process now. */ to_cleanup_pid = attached_pid; /* Open LSM fd and send it to child. */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int ret = -1; int labelfd; bool on_exec; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; labelfd = lsm_process_label_fd_get(attached_pid, on_exec); if (labelfd < 0) goto close_mainloop; TRACE("Opened LSM label file descriptor %d", labelfd); /* Send child fd of the LSM security module to write to. */ ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); close(labelfd); if (ret <= 0) { SYSERROR("%d", (int)ret); goto close_mainloop; } TRACE("Sent LSM label file descriptor %d to child", labelfd); } /* We're done, the child process should now execute whatever it * is that the user requested. The parent can now track it with * waitpid() or similar. */ *attached_process = attached_pid; /* Now shut down communication with child, we're done. */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); ipc_sockets[0] = -1; ret_parent = 0; to_cleanup_pid = -1; if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { ret = lxc_mainloop(&descr, -1); if (ret < 0) { ret_parent = -1; to_cleanup_pid = attached_pid; } } close_mainloop: if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) lxc_mainloop_close(&descr); on_error: if (ipc_sockets[0] >= 0) { shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); } if (to_cleanup_pid > 0) (void)wait_for_pid(to_cleanup_pid); if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { lxc_console_delete(&pty); lxc_pty_conf_free(&pty); } lxc_proc_put_context_info(init_ctx); return ret_parent; } /* close unneeded file descriptors */ close(ipc_sockets[0]); ipc_sockets[0] = -EBADF; if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { lxc_attach_pty_close_master(&pty); lxc_attach_pty_close_peer(&pty); lxc_attach_pty_close_log(&pty); } /* Wait for the parent to have setup cgroups. */ ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status)); if (ret != sizeof(status)) { shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); rexit(-1); } TRACE("Intermediate process starting to initialize"); /* Attach now, create another subprocess later, since pid namespaces * only really affect the children of the current process. */ ret = lxc_attach_to_ns(init_pid, init_ctx); if (ret < 0) { ERROR("Failed to enter namespaces"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); rexit(-1); } /* close namespace file descriptors */ lxc_proc_close_ns_fd(init_ctx); /* Attach succeeded, try to cwd. */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; if (new_cwd) { ret = chdir(new_cwd); if (ret < 0) WARN("Could not change directory to \"%s\"", new_cwd); } free(cwd); /* Create attached process. */ payload.ipc_socket = ipc_sockets[1]; payload.options = options; payload.init_ctx = init_ctx; payload.pty_fd = pty.slave; payload.exec_function = exec_function; payload.exec_payload = exec_payload; pid = lxc_raw_clone(CLONE_PARENT); if (pid < 0) { SYSERROR("Failed to clone attached process"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); rexit(-1); } if (pid == 0) { ret = attach_child_main(&payload); if (ret < 0) ERROR("Failed to exec"); _exit(EXIT_FAILURE); } if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) lxc_attach_pty_close_slave(&pty); /* Tell grandparent the pid of the pid of the newly created child. */ ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ret != sizeof(pid)) { /* If this really happens here, this is very unfortunate, since * the parent will not know the pid of the attached process and * will not be able to wait for it (and we won't either due to * CLONE_PARENT) so the parent won't be able to reap it and the * attached process will remain a zombie. */ shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); rexit(-1); } TRACE("Sending pid %d of attached process", pid); /* The rest is in the hands of the initial and the attached process. */ lxc_proc_put_context_info(init_ctx); rexit(0); } int lxc_attach_run_command(void *payload) { int ret = -1; lxc_attach_command_t *cmd = payload; ret = execvp(cmd->program, cmd->argv); if (ret < 0) { switch (errno) { case ENOEXEC: ret = 126; break; case ENOENT: ret = 127; break; } } SYSERROR("Failed to exec \"%s\"", cmd->program); return ret; } int lxc_attach_run_shell(void* payload) { uid_t uid; struct passwd pwent; struct passwd *pwentp = NULL; char *user_shell; char *buf; size_t bufsize; int ret; /* Ignore payload parameter. */ (void)payload; uid = getuid(); bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (buf) { ret = getpwuid_r(uid, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) WARN("Could not find matched password record."); WARN("Failed to get password record - %u", uid); } } /* This probably happens because of incompatible nss implementations in * host and container (remember, this code is still using the host's * glibc but our mount namespace is in the container) we may try to get * the information by spawning a [getent passwd uid] process and parsing * the result. */ if (!pwentp) user_shell = lxc_attach_getpwshell(uid); else user_shell = pwent.pw_shell; if (user_shell) execlp(user_shell, user_shell, (char *)NULL); /* Executed if either no passwd entry or execvp fails, we will fall back * on /bin/sh as a default shell. */ execlp("/bin/sh", "/bin/sh", (char *)NULL); SYSERROR("Failed to execute shell"); if (!pwentp) free(user_shell); free(buf); return -1; } lxc-2.0.11/src/lxc/confile_utils.h0000644061062106075000000000467413435013473013715 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 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. */ #ifndef __LXC_CONFILE_UTILS_H #define __LXC_CONFILE_UTILS_H #include #include "conf.h" #include "confile_utils.h" #define strprint(str, inlen, ...) \ do { \ if (str) \ len = snprintf(str, inlen, ##__VA_ARGS__); \ else \ len = snprintf((char *){""}, 0, ##__VA_ARGS__); \ if (len < 0) { \ SYSERROR("failed to create string"); \ return -1; \ }; \ fulllen += len; \ if (inlen > 0) { \ if (str) \ str += len; \ inlen -= len; \ if (inlen < 0) \ inlen = 0; \ } \ } while (0); extern int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, unsigned long *hostid, unsigned long *range); extern bool lxc_config_value_empty(const char *value); extern struct lxc_netdev *lxc_find_netdev_by_idx(struct lxc_conf *conf, unsigned int idx); extern struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf, unsigned int idx); extern void lxc_log_configured_netdevs(const struct lxc_conf *conf); extern int network_ifname(char *valuep, const char *value); #endif /* __LXC_CONFILE_UTILS_H */ lxc-2.0.11/src/lxc/namespace.c0000644061062106075000000001324113435013473012773 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "namespace.h" #include "utils.h" lxc_log_define(lxc_namespace, lxc); struct clone_arg { int (*fn)(void *); void *arg; }; static int do_clone(void *arg) { struct clone_arg *clone_arg = arg; return clone_arg->fn(clone_arg->arg); } pid_t lxc_clone(int (*fn)(void *), void *arg, int flags) { struct clone_arg clone_arg = { .fn = fn, .arg = arg, }; size_t stack_size = lxc_getpagesize(); void *stack = alloca(stack_size); pid_t ret; #ifdef __ia64__ ret = __clone2(do_clone, stack, stack_size, flags | SIGCHLD, &clone_arg); #else ret = clone(do_clone, stack + stack_size, flags | SIGCHLD, &clone_arg); #endif if (ret < 0) ERROR("Failed to clone (%#x): %s.", flags, strerror(errno)); return ret; } /** * This is based on raw_clone in systemd but adapted to our needs. This uses * copy on write semantics and doesn't pass a stack. CLONE_VM is tricky and * doesn't really matter to us so disallow it. * * The nice thing about this is that we get fork() behavior. That is * lxc_raw_clone() returns 0 in the child and the child pid in the parent. */ pid_t lxc_raw_clone(unsigned long flags) { /* These flags don't interest at all so we don't jump through any hoopes * of retrieving them and passing them to the kernel. */ errno = EINVAL; if ((flags & (CLONE_VM | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS))) return -EINVAL; #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) /* On s390/s390x and cris the order of the first and second arguments * of the system call is reversed. */ return (int)syscall(__NR_clone, NULL, flags | SIGCHLD); #elif defined(__sparc__) && defined(__arch64__) { /** * sparc64 always returns the other process id in %o0, and * a boolean flag whether this is the child or the parent in * %o1. Inline assembly is needed to get the flag returned * in %o1. */ int in_child; int child_pid; asm volatile("mov %2, %%g1\n\t" "mov %3, %%o0\n\t" "mov 0 , %%o1\n\t" "t 0x6d\n\t" "mov %%o1, %0\n\t" "mov %%o0, %1" : "=r"(in_child), "=r"(child_pid) : "i"(__NR_clone), "r"(flags | SIGCHLD) : "%o1", "%o0", "%g1"); if (in_child) return 0; else return child_pid; } #elif defined(__ia64__) /* On ia64 the stack and stack size are passed as separate arguments. */ return (int)syscall(__NR_clone, flags | SIGCHLD, NULL, 0); #else return (int)syscall(__NR_clone, flags | SIGCHLD, NULL); #endif } pid_t lxc_raw_clone_cb(int (*fn)(void *), void *args, unsigned long flags) { pid_t pid; pid = lxc_raw_clone(flags); if (pid < 0) return -1; /* exit() is not thread-safe and might mess with the parent's signal * handlers and other stuff when exec() fails. */ if (pid == 0) _exit(fn(args)); return pid; } /* Leave the user namespace at the first position in the array of structs so * that we always attach to it first when iterating over the struct and using * setns() to switch namespaces. This especially affects lxc_attach(): Suppose * you cloned a new user namespace and mount namespace as an unprivileged user * on the host and want to setns() to the mount namespace. This requires you to * attach to the user namespace first otherwise the kernel will fail this check: * * if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || * !ns_capable(current_user_ns(), CAP_SYS_CHROOT) || * !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) * return -EPERM; * * in * * linux/fs/namespace.c:mntns_install(). */ const struct ns_info ns_info[LXC_NS_MAX] = { [LXC_NS_USER] = {"user", CLONE_NEWUSER, "CLONE_NEWUSER"}, [LXC_NS_MNT] = {"mnt", CLONE_NEWNS, "CLONE_NEWNS"}, [LXC_NS_PID] = {"pid", CLONE_NEWPID, "CLONE_NEWPID"}, [LXC_NS_UTS] = {"uts", CLONE_NEWUTS, "CLONE_NEWUTS"}, [LXC_NS_IPC] = {"ipc", CLONE_NEWIPC, "CLONE_NEWIPC"}, [LXC_NS_NET] = {"net", CLONE_NEWNET, "CLONE_NEWNET"}, [LXC_NS_CGROUP] = {"cgroup", CLONE_NEWCGROUP, "CLONE_NEWCGROUP"} }; int lxc_namespace_2_cloneflag(char *namespace) { int i; for (i = 0; i < LXC_NS_MAX; i++) if (!strcasecmp(ns_info[i].proc_name, namespace)) return ns_info[i].clone_flag; 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("At least one namespace is needed."); 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; } lxc-2.0.11/src/lxc/execute.c0000644061062106075000000000717513435013473012512 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include "conf.h" #include "log.h" #include "start.h" #include "utils.h" lxc_log_define(lxc_execute, lxc_start); struct execute_args { char *const *argv; int quiet; }; static int execute_start(struct lxc_handler *handler, void* data) { int argc_add, j; char **argv; int argc = 0, i = 0, logfd = -1; struct execute_args *my_args = data; char *initpath; char logfile[LXC_PROC_PID_FD_LEN]; while (my_args->argv[argc++]) ; /* lxc-init -n name -- [argc] NULL -> 5 */ argc_add = 5; if (my_args->quiet) argc_add++; if (!handler->conf->rootfs.path) argc_add += 2; if (lxc_log_has_valid_level()) argc_add += 2; if (current_config->logfd != -1 || lxc_log_fd != -1) argc_add += 2; argv = malloc((argc + argc_add) * sizeof(*argv)); if (!argv) goto out1; initpath = choose_init(NULL); if (!initpath) { ERROR("Failed to find an init.lxc or init.lxc.static"); goto out2; } argv[i++] = initpath; argv[i++] = "-n"; argv[i++] = (char *)handler->name; if (lxc_log_has_valid_level()) { argv[i++] = "-l"; argv[i++] = (char *)lxc_log_priority_to_string(lxc_log_get_level()); } if (current_config->logfd != -1 || lxc_log_fd != -1) { int ret; int to_dup = current_config->logfd; if (current_config->logfd == -1) to_dup = lxc_log_fd; logfd = dup(to_dup); if (logfd < 0) { SYSERROR("Failed to duplicate log file descriptor"); goto out2; } ret = snprintf(logfile, sizeof(logfile), "/proc/1/fd/%d", logfd); if (ret < 0 || (size_t)ret >= sizeof(logfile)) goto out3; argv[i++] = "-o"; argv[i++] = logfile; } if (my_args->quiet) argv[i++] = "--quiet"; if (!handler->conf->rootfs.path) { argv[i++] = "-P"; argv[i++] = (char *)handler->lxcpath; } argv[i++] = "--"; for (j = 0; j < argc; j++) argv[i++] = my_args->argv[j]; argv[i++] = NULL; NOTICE("Exec'ing \"%s\"", my_args->argv[0]); execvp(argv[0], argv); SYSERROR("Failed to exec %s", argv[0]); free(initpath); out3: close(logfd); out2: free(argv); out1: return 1; } static int execute_post_start(struct lxc_handler *handler, void* data) { struct execute_args *my_args = data; NOTICE("'%s' started with pid '%d'", my_args->argv[0], handler->pid); return 0; } static struct lxc_operations execute_start_ops = { .start = execute_start, .post_start = execute_post_start }; int lxc_execute(const char *name, char *const argv[], int quiet, struct lxc_handler *handler, const char *lxcpath, bool backgrounded, int *error_num) { struct execute_args args = {.argv = argv, .quiet = quiet}; handler->conf->is_execute = 1; return __lxc_start(name, handler, &execute_start_ops, &args, lxcpath, backgrounded, error_num); } lxc-2.0.11/src/lxc/attach_options.h0000644061062106075000000001337713435013473014075 00000000000000/*! \file * * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ATTACH_OPTIONS_H #define __LXC_ATTACH_OPTIONS_H #include #ifdef __cplusplus extern "C" { #endif /*! * LXC environment policy. */ typedef enum lxc_attach_env_policy_t { LXC_ATTACH_KEEP_ENV, /*!< Retain the environment */ LXC_ATTACH_CLEAR_ENV /*!< Clear the environment */ } lxc_attach_env_policy_t; enum { /* The following are on by default: */ LXC_ATTACH_MOVE_TO_CGROUP = 0x00000001, /*!< Move to cgroup */ LXC_ATTACH_DROP_CAPABILITIES = 0x00000002, /*!< Drop capabilities */ LXC_ATTACH_SET_PERSONALITY = 0x00000004, /*!< Set personality */ LXC_ATTACH_LSM_EXEC = 0x00000008, /*!< Execute under a Linux Security Module */ /* The following are off by default: */ LXC_ATTACH_REMOUNT_PROC_SYS = 0x00010000, /*!< Remount /proc filesystem */ LXC_ATTACH_LSM_NOW = 0x00020000, /*!< FIXME: unknown */ /* We have 16 bits for things that are on by default and 16 bits that * are off by default, that should be sufficient to keep binary * compatibility for a while */ LXC_ATTACH_DEFAULT = 0x0000FFFF /*!< Mask of flags to apply by default */ }; /*! All Linux Security Module flags */ #define LXC_ATTACH_LSM (LXC_ATTACH_LSM_EXEC | LXC_ATTACH_LSM_NOW) /*! LXC attach function type. * * Function to run in container. * * \param payload \ref lxc_attach_command_t to run. * * \return Function should return \c 0 on success, and any other value to denote failure. */ typedef int (*lxc_attach_exec_t)(void* payload); /*! * LXC attach options for \ref lxc_container \c attach(). */ typedef struct lxc_attach_options_t { /*! Any combination of LXC_ATTACH_* flags */ int attach_flags; /*! The namespaces to attach to (CLONE_NEW... flags) */ int namespaces; /*! Initial personality (\c -1 to autodetect). * \warning This may be ignored if lxc is compiled without personality * support) */ long personality; /*! Initial current directory, use \c NULL to use cwd. * If the current directory does not exist in the container, the root * directory will be used instead because of kernel defaults. */ char* initial_cwd; /*! The user-id to run as. * * \note Set to \c -1 for default behaviour (init uid for userns * containers or \c 0 (super-user) if detection fails). */ uid_t uid; /*! The group-id to run as. * * \note Set to \c -1 for default behaviour (init gid for userns * containers or \c 0 (super-user) if detection fails). */ gid_t gid; /*! Environment policy */ lxc_attach_env_policy_t env_policy; /*! Extra environment variables to set in the container environment */ char** extra_env_vars; /*! Names of environment variables in existing environment to retain * in container environment. */ char** extra_keep_env; /**@{*/ /*! File descriptors for stdin, stdout and stderr, * \c dup2() will be used before calling exec_function, * (assuming not \c 0, \c 1 and \c 2 are specified) and the * original fds are closed before passing control * over. Any \c O_CLOEXEC flag will be removed after * that. */ int stdin_fd; /*!< stdin file descriptor */ int stdout_fd; /*!< stdout file descriptor */ int stderr_fd; /*!< stderr file descriptor */ /**@}*/ } lxc_attach_options_t; /*! Default attach options to use */ #define LXC_ATTACH_OPTIONS_DEFAULT \ { \ /* .attach_flags = */ LXC_ATTACH_DEFAULT, \ /* .namespaces = */ -1, \ /* .personality = */ -1, \ /* .initial_cwd = */ NULL, \ /* .uid = */ (uid_t)-1, \ /* .gid = */ (gid_t)-1, \ /* .env_policy = */ LXC_ATTACH_KEEP_ENV, \ /* .extra_env_vars = */ NULL, \ /* .extra_keep_env = */ NULL, \ /* .stdin_fd = */ 0, \ /* .stdout_fd = */ 1, \ /* .stderr_fd = */ 2, \ } /*! * Representation of a command to run in a container. */ typedef struct lxc_attach_command_t { char* program; /*!< The program to run (passed to execvp) */ char** argv; /*!< The argv pointer of that program, including the program itself in argv[0] */ } lxc_attach_command_t; /*! * \brief Run a command in the container. * * \param payload \ref lxc_attach_command_t to run. * * \return \c -1 on error, exit code of lxc_attach_command_t program on success. */ extern int lxc_attach_run_command(void* payload); /*! * \brief Run a shell command in the container. * * \param payload Not used. * * \return Exit code of shell. */ extern int lxc_attach_run_shell(void* payload); #ifdef __cplusplus } #endif #endif lxc-2.0.11/src/lxc/af_unix.h0000644061062106075000000000312013435013473012470 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_AF_UNIX_H #define __LXC_AF_UNIX_H #include /* does not enforce \0-termination */ extern int lxc_abstract_unix_open(const char *path, int type, int flags); extern int lxc_abstract_unix_close(int fd); /* does not enforce \0-termination */ extern int lxc_abstract_unix_connect(const char *path); extern int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size); extern int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size); extern int lxc_abstract_unix_send_credential(int fd, void *data, size_t size); extern int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size); #endif /* __LXC_AF_UNIX_H */ lxc-2.0.11/src/lxc/lxc_monitord.c0000644061062106075000000002565413435013473013553 00000000000000/* * lxc: linux Container library * * Copyright © 2012 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "log.h" #include "mainloop.h" #include "monitor.h" #include "utils.h" #include "lxccontainer.h" #define CLIENTFDS_CHUNK 64 lxc_log_define(lxc_monitord, lxc); sigjmp_buf mark; static void lxc_monitord_cleanup(void); /* * Defines the structure to store the monitor information * @lxcpath : the path being monitored * @fifofd : the file descriptor for publishers (containers) to write state * @listenfd : the file descriptor for subscribers (lxc-monitors) to connect * @clientfds : accepted client file descriptors * @clientfds_size : number of file descriptors clientfds can hold * @clientfds_cnt : the count of valid fds in clientfds * @descr : the lxc_mainloop state */ struct lxc_monitor { const char *lxcpath; int fifofd; int listenfd; int *clientfds; int clientfds_size; int clientfds_cnt; struct lxc_epoll_descr descr; }; static struct lxc_monitor mon; static int quit; static int lxc_monitord_fifo_create(struct lxc_monitor *mon) { struct flock lk; char fifo_path[PATH_MAX]; int ret; ret = lxc_monitor_fifo_name(mon->lxcpath, fifo_path, sizeof(fifo_path), 1); if (ret < 0) return ret; ret = mknod(fifo_path, S_IFIFO|S_IRUSR|S_IWUSR, 0); if (ret < 0 && errno != EEXIST) { INFO("Failed to mknod monitor fifo %s: %s.", fifo_path, strerror(errno)); return -1; } mon->fifofd = open(fifo_path, O_RDWR); if (mon->fifofd < 0) { unlink(fifo_path); ERROR("Failed to open monitor fifo."); return -1; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; if (fcntl(mon->fifofd, F_SETLK, &lk) != 0) { /* another lxc-monitord is already running, don't start up */ DEBUG("lxc-monitord already running on lxcpath %s.", mon->lxcpath); close(mon->fifofd); return -1; } return 0; } static int lxc_monitord_fifo_delete(struct lxc_monitor *mon) { char fifo_path[PATH_MAX]; int ret; ret = lxc_monitor_fifo_name(mon->lxcpath, fifo_path, sizeof(fifo_path), 0); if (ret < 0) return ret; unlink(fifo_path); return 0; } static void lxc_monitord_sockfd_remove(struct lxc_monitor *mon, int fd) { int i; if (lxc_mainloop_del_handler(&mon->descr, fd)) CRIT("File descriptor %d not found in mainloop.", fd); close(fd); for (i = 0; i < mon->clientfds_cnt; i++) { if (mon->clientfds[i] == fd) break; } if (i >= mon->clientfds_cnt) { CRIT("File descriptor %d not found in clients array.", fd); lxc_monitord_cleanup(); exit(EXIT_FAILURE); } memmove(&mon->clientfds[i], &mon->clientfds[i+1], (mon->clientfds_cnt - i - 1) * sizeof(mon->clientfds[0])); mon->clientfds_cnt--; } static int lxc_monitord_sock_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { struct lxc_monitor *mon = data; if (events & EPOLLIN) { int rc; char buf[4]; rc = lxc_read_nointr(fd, buf, sizeof(buf)); if (rc > 0 && !strncmp(buf, "quit", 4)) quit = 1; } if (events & EPOLLHUP) lxc_monitord_sockfd_remove(mon, fd); return quit; } static int lxc_monitord_sock_accept(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ret,clientfd; struct lxc_monitor *mon = data; struct ucred cred; socklen_t credsz = sizeof(cred); ret = -1; clientfd = accept(fd, NULL, 0); if (clientfd < 0) { SYSERROR("Failed to accept connection for client file descriptor %d.", fd); goto out; } if (fcntl(clientfd, F_SETFD, FD_CLOEXEC)) { SYSERROR("Failed to set FD_CLOEXEC on client socket connection %d.", clientfd); goto err1; } if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz)) { ERROR("Failed to get credentials on client socket connection %d.", clientfd); goto err1; } if (cred.uid && cred.uid != geteuid()) { WARN("Monitor denied for uid %d on client socket connection %d.", cred.uid, clientfd); ret = -EACCES; goto err1; } if (mon->clientfds_cnt + 1 > mon->clientfds_size) { int *clientfds; clientfds = realloc(mon->clientfds, (mon->clientfds_size + CLIENTFDS_CHUNK) * sizeof(mon->clientfds[0])); if (clientfds == NULL) { ERROR("Failed to realloc memory for %d client file " "descriptors.", mon->clientfds_size + CLIENTFDS_CHUNK); goto err1; } mon->clientfds = clientfds; mon->clientfds_size += CLIENTFDS_CHUNK; } ret = lxc_mainloop_add_handler(&mon->descr, clientfd, lxc_monitord_sock_handler, mon); if (ret) { ERROR("Failed to add socket handler."); goto err1; } mon->clientfds[mon->clientfds_cnt++] = clientfd; INFO("Accepted client file descriptor %d. Number of accepted file descriptors is now %d.", clientfd, mon->clientfds_cnt); goto out; err1: close(clientfd); out: return ret; } static int lxc_monitord_sock_create(struct lxc_monitor *mon) { struct sockaddr_un addr; int fd; if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) return -1; fd = lxc_abstract_unix_open(addr.sun_path, SOCK_STREAM, O_TRUNC); if (fd < 0) { ERROR("Failed to open unix socket: %s.", strerror(errno)); return -1; } mon->listenfd = fd; return 0; } static int lxc_monitord_sock_delete(struct lxc_monitor *mon) { struct sockaddr_un addr; if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) return -1; if (addr.sun_path[0]) unlink(addr.sun_path); return 0; } static int lxc_monitord_create(struct lxc_monitor *mon) { int ret; ret = lxc_monitord_fifo_create(mon); if (ret < 0) return ret; ret = lxc_monitord_sock_create(mon); return ret; } static void lxc_monitord_delete(struct lxc_monitor *mon) { int i; lxc_mainloop_del_handler(&mon->descr, mon->listenfd); close(mon->listenfd); lxc_monitord_sock_delete(mon); lxc_mainloop_del_handler(&mon->descr, mon->fifofd); lxc_monitord_fifo_delete(mon); close(mon->fifofd); for (i = 0; i < mon->clientfds_cnt; i++) { lxc_mainloop_del_handler(&mon->descr, mon->clientfds[i]); close(mon->clientfds[i]); } mon->clientfds_cnt = 0; } static int lxc_monitord_fifo_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ret,i; struct lxc_msg msglxc; struct lxc_monitor *mon = data; ret = lxc_read_nointr(fd, &msglxc, sizeof(msglxc)); if (ret != sizeof(msglxc)) { SYSERROR("Reading from fifo failed: %s.", strerror(errno)); return 1; } for (i = 0; i < mon->clientfds_cnt; i++) { ret = lxc_write_nointr(mon->clientfds[i], &msglxc, sizeof(msglxc)); if (ret < 0) ERROR("Failed to send message to client file descriptor %d: %s.", mon->clientfds[i], strerror(errno)); } return 0; } static int lxc_monitord_mainloop_add(struct lxc_monitor *mon) { int ret; ret = lxc_mainloop_add_handler(&mon->descr, mon->fifofd, lxc_monitord_fifo_handler, mon); if (ret < 0) { ERROR("Failed to add to mainloop monitor handler for fifo."); return -1; } ret = lxc_mainloop_add_handler(&mon->descr, mon->listenfd, lxc_monitord_sock_accept, mon); if (ret < 0) { ERROR("Failed to add to mainloop monitor handler for listen socket."); return -1; } return 0; } static void lxc_monitord_cleanup(void) { lxc_monitord_delete(&mon); } static void lxc_monitord_sig_handler(int sig) { siglongjmp(mark, 1); } int main(int argc, char *argv[]) { int ret, pipefd; char logpath[PATH_MAX]; sigset_t mask; char *lxcpath = argv[1]; bool mainloop_opened = false; bool monitord_created = false; struct lxc_log log; if (argc != 3) { fprintf(stderr, "Usage: lxc-monitord lxcpath sync-pipe-fd\n\n" "NOTE: lxc-monitord is intended for use by lxc internally\n" " and does not need to be run by hand\n\n"); exit(EXIT_FAILURE); } ret = snprintf(logpath, sizeof(logpath), "%s/lxc-monitord.log", (strcmp(LXCPATH, lxcpath) ? lxcpath : LOGPATH )); if (ret < 0 || ret >= sizeof(logpath)) exit(EXIT_FAILURE); log.name = NULL; log.file = logpath; log.level = "DEBUG"; log.prefix = "lxc-monitord"; log.quiet = 0; log.lxcpath = lxcpath; ret = lxc_log_init(&log); if (ret) INFO("Failed to open log file %s, log will be lost.", lxcpath); lxc_log_options_no_override(); if (lxc_safe_int(argv[2], &pipefd) < 0) exit(EXIT_FAILURE); if (sigfillset(&mask) || sigdelset(&mask, SIGILL) || sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGBUS) || sigdelset(&mask, SIGTERM) || pthread_sigmask(SIG_BLOCK, &mask, NULL)) { SYSERROR("Failed to set signal mask."); exit(EXIT_FAILURE); } signal(SIGILL, lxc_monitord_sig_handler); signal(SIGSEGV, lxc_monitord_sig_handler); signal(SIGBUS, lxc_monitord_sig_handler); signal(SIGTERM, lxc_monitord_sig_handler); if (sigsetjmp(mark, 1) != 0) goto on_signal; ret = EXIT_FAILURE; memset(&mon, 0, sizeof(mon)); mon.lxcpath = lxcpath; if (lxc_mainloop_open(&mon.descr)) { ERROR("Failed to create mainloop."); goto on_error; } mainloop_opened = true; if (lxc_monitord_create(&mon)) goto on_error; monitord_created = true; /* sync with parent, we're ignoring the return from write * because regardless if it works or not, the following * close will sync us with the parent process. the * if-empty-statement construct is to quiet the * warn-unused-result warning. */ if (lxc_write_nointr(pipefd, "S", 1)) ; close(pipefd); if (lxc_monitord_mainloop_add(&mon)) { ERROR("Failed to add mainloop handlers."); goto on_error; } NOTICE("lxc-monitord with pid %d is now monitoring lxcpath %s.", lxc_raw_getpid(), mon.lxcpath); for (;;) { ret = lxc_mainloop(&mon.descr, 1000 * 30); if (ret) { ERROR("mainloop returned an error"); break; } if (mon.clientfds_cnt <= 0) { NOTICE("No remaining clients. lxc-monitord is exiting."); break; } if (quit == 1) { NOTICE("got quit command. lxc-monitord is exitting."); break; } } on_signal: ret = EXIT_SUCCESS; on_error: if (monitord_created) lxc_monitord_cleanup(); if (mainloop_opened) lxc_mainloop_close(&mon.descr); exit(ret); } lxc-2.0.11/src/lxc/caps.h0000644061062106075000000000634313435013473011777 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CAPS_H #define __LXC_CAPS_H #include "config.h" #include #if HAVE_LIBCAP #include /* workaround for libcap < 2.17 bug */ #include extern int lxc_caps_down(void); extern int lxc_caps_up(void); extern int lxc_ambient_caps_up(void); extern int lxc_ambient_caps_down(void); extern int lxc_caps_init(void); extern int lxc_caps_last_cap(void); extern bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag); extern bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag); #else static inline int lxc_caps_down(void) { return 0; } static inline int lxc_caps_up(void) { return 0; } static inline int lxc_ambient_caps_up(void) { return 0; } static inline int lxc_ambient_caps_down(void) { return 0; } static inline int lxc_caps_init(void) { return 0; } static inline int lxc_caps_last_cap(void) { return 0; } typedef int cap_value_t; typedef int cap_flag_t; static inline bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) { return false; } static inline bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) { return false; } #endif #define lxc_priv(__lxc_function) \ ({ \ __label__ out; \ int __ret, __ret2, ___errno = 0; \ __ret = lxc_caps_up(); \ if (__ret) \ goto out; \ __ret = __lxc_function; \ if (__ret) \ ___errno = errno; \ __ret2 = lxc_caps_down(); \ out: \ __ret ? errno = ___errno, __ret : __ret2; \ }) #define lxc_unpriv(__lxc_function) \ ({ \ __label__ out; \ int __ret, __ret2, ___errno = 0; \ __ret = lxc_caps_down(); \ if (__ret) \ goto out; \ __ret = __lxc_function; \ if (__ret) \ ___errno = errno; \ __ret2 = lxc_caps_up(); \ out: \ __ret ? errno = ___errno, __ret : __ret2; \ }) #endif lxc-2.0.11/src/lxc/lxclock.h0000644061062106075000000001127213435013473012505 00000000000000/*! \file * * liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXCLOCK_H #define __LXC_LXCLOCK_H #include #include #include #include #include #include #include #ifndef F_OFD_GETLK #define F_OFD_GETLK 36 #endif #ifndef F_OFD_SETLK #define F_OFD_SETLK 37 #endif #ifndef F_OFD_SETLKW #define F_OFD_SETLKW 38 #endif #define LXC_LOCK_ANON_SEM 1 /*!< Anonymous semaphore lock */ #define LXC_LOCK_FLOCK 2 /*!< flock(2) lock */ // private /*! * LXC Lock */ struct lxc_lock { short type; //!< Lock type union { sem_t *sem; //!< Anonymous semaphore (LXC_LOCK_ANON_SEM) /*! LXC_LOCK_FLOCK details */ struct { int fd; //!< fd on which a lock is held (if not -1) char *fname; //!< Name of lock } f; } u; //!< Container for lock type elements }; /*! * \brief Create a new (unlocked) lock. * * \param lxcpath lxcpath lock should relate to. * \param name Name for lock. * * \return Newly-allocated lxclock on success, \c NULL on failure. * \note If \p name is not given, create an unnamed semaphore * (used to protect against racing threads). * * \note Note that an unnamed sem was malloced by us and needs to be freed. * * \internal \ref sem is initialized to a value of \c 1. * A 'sem_t *' which can be passed to \ref lxclock() and \ref lxcunlock() * will be placed in \c l->u.sem. * * If \ref lxcpath and \ref name are given (both must be given if either is * given) then a lockfile is created as \c /run/lxc/lock/$lxcpath/.$name if root, * or \c $XDG_RUNTIME_DIR/lxc/lock/$lxcpath/.$name if non-root. * The lock is used to protect the containers on-disk representation. * * \internal This function allocates the pathname for the given lock in memory * such that it can be can quickly opened and locked by \ref lxclock(). * \c l->u.f.fname will contain the malloc'ed name (which must be * freed when the container is freed), and \c u.f.fd = -1. * */ extern struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name); /*! * \brief Take an existing lock. * * \param lock Lock to operate on. * \param timeout Seconds to wait to take lock (\c 0 signifies an * indefinite wait). * * \return \c 0 if lock obtained, \c -2 on failure to set timeout, * or \c -1 on any other error (\c errno will be set by \c sem_wait(3) * or \c fcntl(2)). * * \note \p timeout is (currently?) only supported for privlock, not * for slock. Since currently there is not a single use of the timeout * (except in the test case) I may remove the support for it in sem as * well. */ extern int lxclock(struct lxc_lock *lock, int timeout); /*! * \brief Unlock specified lock previously locked using \ref lxclock(). * * \param lock \ref lxc_lock. * * \return \c 0 on success, \c -2 if provided lock was not already held, * otherwise \c -1 with \c errno saved from \c fcntl(2) or sem_post function. */ extern int lxcunlock(struct lxc_lock *lock); /*! * \brief Free a lock created by \ref lxc_newlock(). * * \param lock Lock. */ extern void lxc_putlock(struct lxc_lock *lock); /*! * \brief Lock the current process. */ extern void process_lock(void); /*! * \brief Unlock the current process. */ extern void process_unlock(void); struct lxc_container; /*! * \brief Lock the containers memory. * * \param c Container. * * \return As for \ref lxclock(). */ extern int container_mem_lock(struct lxc_container *c); /*! * \brief Unlock the containers memory. * * \param c Container. */ extern void container_mem_unlock(struct lxc_container *c); /*! * \brief Lock the containers disk data. * * \param c Container. * * \return \c 0 on success, or an \ref lxclock() error return * values on error. */ extern int container_disk_lock(struct lxc_container *c); /*! * \brief Unlock the containers disk data. * * \param c Container. * */ extern void container_disk_unlock(struct lxc_container *c); #endif lxc-2.0.11/src/lxc/console.h0000644061062106075000000002101413435013473012503 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2010 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONSOLE_H #define __LXC_CONSOLE_H #include #include #include "conf.h" #include "list.h" struct lxc_epoll_descr; /* defined in mainloop.h */ struct lxc_container; /* defined in lxccontainer.h */ struct lxc_tty_state { struct lxc_list node; int stdinfd; int stdoutfd; int masterfd; /* Escape sequence to use for exiting the pty. A single char can be * specified. The pty can then exited by doing: Ctrl + specified_char + q. * This field is checked by lxc_console_cb_tty_stdin(). Set to -1 to * disable exiting the pty via a escape sequence. */ int escape; /* Used internally by lxc_console_cb_tty_stdin() to check whether an * escape sequence has been received. */ int saw_escape; /* Name of the container to forward the SIGWINCH event to. */ const char *winch_proxy; /* Path of the container to forward the SIGWINCH event to. */ const char *winch_proxy_lxcpath; /* File descriptor that accepts signals. If set to -1 no signal handler * could be installed. This also means that the sigset_t oldmask member * is meaningless. */ int sigfd; sigset_t oldmask; }; /* * lxc_console_allocate: allocate the console or a tty * * @conf : the configuration of the container to allocate from * @sockfd : the socket fd whose remote side when closed, will be an * indication that the console or tty is no longer in use * @ttyreq : the tty requested to be opened, -1 for any, 0 for the console */ extern int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttynum); /* * Create a new pty: * - calls openpty() to allocate a master/slave pty pair * - sets the FD_CLOEXEC flag on the master/slave fds * - allocates either the current controlling pty (default) or a user specified * pty as peer pty for the newly created master/slave pair * - sets up SIGWINCH handler, winsz, and new terminal settings * (Handlers for SIGWINCH and I/O are not registered in a mainloop.) * (For an unprivileged container the created pty on the host is not * automatically chowned to the uid/gid of the unprivileged user. For this * ttys_shift_ids() can be called.) */ extern int lxc_pty_create(struct lxc_console *console); /** * lxc_console_create: Create a new pty. * - In addition to lxc_pty_create() also sets up all pty logs. */ extern int lxc_console_create(struct lxc_conf *); /* * Delete a pty created via lxc_console_create(): * - set old terminal settings * - memory allocated via lxc_console_create() is free()ed. * - close master/slave pty pair and allocated fd for the peer (usually * /dev/tty) * Registered handlers in a mainloop are not automatically deleted. */ extern void lxc_console_delete(struct lxc_console *); /* * lxc_console_free: mark the console or a tty as unallocated, free any * resources allocated by lxc_console_allocate(). * * @conf : the configuration of the container whose tty was closed * @fd : the socket fd whose remote side was closed, which indicated * the console or tty is no longer in use. this is used to match * which console/tty is being freed. */ extern void lxc_console_free(struct lxc_conf *conf, int fd); /* * Register pty event handlers in an open mainloop */ extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_console *); /* * Handle SIGWINCH events on the allocated ptys. */ extern void lxc_console_sigwinch(int sig); /* * Connect to one of the ptys given to the container via lxc.tty. * - allocates either the current controlling pty (default) or a user specified * pty as peer pty for the containers tty * - sets up SIGWINCH handler, winsz, and new terminal settings * - opens mainloop * - registers SIGWINCH, I/O handlers in the mainloop * - performs all necessary cleanup operations */ extern int lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape); /* * Allocate one of the ptys given to the container via lxc.tty. Returns an open * fd to the allocated pty. * Set ttynum to -1 to allocate the first available pty, or to a value within * the range specified by lxc.tty to allocate a specific pty. */ extern int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd); /* * Make fd a duplicate of the standard file descriptors: * fd is made a duplicate of a specific standard file descriptor iff the * standard file descriptor refers to a pty. */ extern int lxc_console_set_stdfds(int fd); /* * Handler for events on the stdin fd of the pty. To be registered via the * corresponding functions declared and defined in mainloop.{c,h} or * lxc_console_mainloop_add(). * This function exits the loop cleanly when an EPOLLHUP event is received. */ extern int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /* * Handler for events on the master fd of the pty. To be registered via the * corresponding functions declared and defined in mainloop.{c,h} or * lxc_console_mainloop_add(). * This function exits the loop cleanly when an EPOLLHUP event is received. */ extern int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /* * Setup new terminal properties. The old terminal settings are stored in * oldtios. */ extern int lxc_setup_tios(int fd, struct termios *oldtios); /* * lxc_console_winsz: propagte winsz from one terminal to another * * @srcfd : terminal to get size from (typically a slave pty) * @dstfd : terminal to set size on (typically a master pty) */ extern void lxc_console_winsz(int srcfd, int dstfd); /* * lxc_console_signal_init: install signal handler * * @srcfd : src for winsz in SIGWINCH handler * @dstfd : dst for winsz in SIGWINCH handler * * Returns lxc_tty_state structure on success or NULL on failure. The sigfd * member of the returned lxc_tty_state can be select()/poll()ed/epoll()ed * on (ie added to a mainloop) for signals. * * Must be called with process_lock held to protect the lxc_ttys list, or * from a non-threaded context. * * Note that the signal handler isn't installed as a classic asychronous * handler, rather signalfd(2) is used so that we can handle the signal when * we're ready for it. This avoids deadlocks since a signal handler (ie * lxc_console_sigwinch()) would need to take the thread mutex to prevent * lxc_ttys list corruption, but using the fd we can provide the tty_state * needed to the callback (lxc_console_cb_signal_fd()). * * This function allocates memory. It is up to the caller to free it. */ extern struct lxc_tty_state *lxc_console_signal_init(int srcfd, int dstfd); /* * Handler for signal events. To be registered via the corresponding functions * declared and defined in mainloop.{c,h} or lxc_console_mainloop_add(). */ extern int lxc_console_cb_signal_fd(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /* * lxc_console_signal_fini: uninstall signal handler * * @ts : the lxc_tty_state returned by lxc_console_signal_init * * Restore the saved signal handler that was in effect at the time * lxc_console_signal_init() was called. * * Must be called with process_lock held to protect the lxc_ttys list, or * from a non-threaded context. */ extern void lxc_console_signal_fini(struct lxc_tty_state *ts); extern int lxc_console_cb_con(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr); extern int lxc_make_controlling_pty(int fd); extern int lxc_login_pty(int fd); extern void lxc_pty_conf_free(struct lxc_console *console); extern void lxc_pty_info_init(struct lxc_pty_info *pty); extern void lxc_pty_init(struct lxc_console *pty); extern int lxc_pty_map_ids(struct lxc_conf *c, struct lxc_console *pty); #endif lxc-2.0.11/src/lxc/utils.c0000644061062106075000000014366113435013473012211 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "lxclock.h" #include "memory_utils.h" #include "namespace.h" #include "parse.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 #endif lxc_log_define(lxc_utils, lxc); /* * if path is btrfs, tries to remove it and any subvolumes beneath it */ extern bool btrfs_try_remove_subvol(const char *path); static int _recursive_rmdir(char *dirname, dev_t pdev, const char *exclude, int level, bool onedev) { struct dirent *direntp; DIR *dir; int ret, failed=0; char pathname[MAXPATHLEN]; bool hadexclude = false; dir = opendir(dirname); if (!dir) { ERROR("failed to open %s", dirname); return -1; } while ((direntp = readdir(dir))) { struct stat mystat; int rc; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("pathname too long"); failed=1; continue; } if (!level && exclude && !strcmp(direntp->d_name, exclude)) { ret = rmdir(pathname); if (ret < 0) { switch(errno) { case ENOTEMPTY: INFO("Not deleting snapshot %s", pathname); hadexclude = true; break; case ENOTDIR: ret = unlink(pathname); if (ret) INFO("Failed to remove %s", pathname); break; default: SYSERROR("Failed to rmdir %s", pathname); failed = 1; break; } } continue; } ret = lstat(pathname, &mystat); if (ret) { ERROR("Failed to stat %s", pathname); failed = 1; continue; } if (onedev && mystat.st_dev != pdev) { /* TODO should we be checking /proc/self/mountinfo for * pathname and not doing this if found? */ if (btrfs_try_remove_subvol(pathname)) INFO("Removed btrfs subvolume at %s\n", pathname); continue; } if (S_ISDIR(mystat.st_mode)) { if (_recursive_rmdir(pathname, pdev, exclude, level+1, onedev) < 0) failed=1; } else { if (unlink(pathname) < 0) { SYSERROR("Failed to delete %s", pathname); failed=1; } } } if (rmdir(dirname) < 0 && !btrfs_try_remove_subvol(dirname) && !hadexclude) { ERROR("Failed to delete %s", dirname); failed=1; } ret = closedir(dir); if (ret) { ERROR("Failed to close directory %s", dirname); failed=1; } return failed ? -1 : 0; } /* We have two different magic values for overlayfs, yay. */ #ifndef OVERLAYFS_SUPER_MAGIC #define OVERLAYFS_SUPER_MAGIC 0x794c764f #endif #ifndef OVERLAY_SUPER_MAGIC #define OVERLAY_SUPER_MAGIC 0x794c7630 #endif /* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the * lxc_rmdir_onedev() */ static bool is_native_overlayfs(const char *path) { if (has_fs_type(path, OVERLAY_SUPER_MAGIC) || has_fs_type(path, OVERLAYFS_SUPER_MAGIC)) return true; return false; } /* returns 0 on success, -1 if there were any failures */ extern int lxc_rmdir_onedev(char *path, const char *exclude) { struct stat mystat; bool onedev = true; if (is_native_overlayfs(path)) { onedev = false; } if (lstat(path, &mystat) < 0) { if (errno == ENOENT) return 0; ERROR("Failed to stat %s", path); return -1; } return _recursive_rmdir(path, mystat.st_dev, exclude, 0, onedev); } /* borrowed from iproute2 */ extern int get_u16(unsigned short *val, const char *arg, int base) { unsigned long res; char *ptr; if (!arg || !*arg) return -1; errno = 0; res = strtoul(arg, &ptr, base); if (!ptr || ptr == arg || *ptr || res > 0xFFFF || errno != 0) return -1; *val = res; return 0; } extern int mkdir_p(const char *dir, mode_t mode) { const char *tmp = dir; const char *orig = dir; char *makeme; do { dir = tmp + strspn(tmp, "/"); tmp = dir + strcspn(dir, "/"); makeme = strndup(orig, dir - orig); if (*makeme) { if (mkdir(makeme, mode) && errno != EEXIST) { SYSERROR("failed to create directory '%s'", makeme); free(makeme); return -1; } } free(makeme); } while(tmp != dir); return 0; } char *get_rundir() { char *rundir; const char *homedir; struct stat sb; if (stat(RUNTIME_PATH, &sb) < 0) { return NULL; } if (geteuid() == sb.st_uid || getegid() == sb.st_gid) { rundir = strdup(RUNTIME_PATH); return rundir; } rundir = getenv("XDG_RUNTIME_DIR"); if (rundir) { rundir = strdup(rundir); return rundir; } INFO("XDG_RUNTIME_DIR isn't set in the environment."); homedir = getenv("HOME"); if (!homedir) { ERROR("HOME isn't set in the environment."); return NULL; } rundir = malloc(sizeof(char) * (17 + strlen(homedir))); sprintf(rundir, "%s/.cache/lxc/run/", homedir); return rundir; } int wait_for_pid(pid_t pid) { int status, ret; again: ret = waitpid(pid, &status, 0); if (ret == -1) { if (errno == EINTR) goto again; return -1; } if (ret != pid) goto again; if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return -1; return 0; } int lxc_wait_for_pid_status(pid_t pid) { int status, ret; again: ret = waitpid(pid, &status, 0); if (ret == -1) { if (errno == EINTR) goto again; return -1; } if (ret != pid) goto again; return status; } ssize_t lxc_write_nointr(int fd, const void* buf, size_t count) { ssize_t ret; again: ret = write(fd, buf, count); if (ret < 0 && errno == EINTR) goto again; return ret; } ssize_t lxc_read_nointr(int fd, void* buf, size_t count) { ssize_t ret; again: ret = read(fd, buf, count); if (ret < 0 && errno == EINTR) goto again; return ret; } ssize_t lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf) { ssize_t ret; ret = lxc_read_nointr(fd, buf, count); if (ret <= 0) return ret; if ((size_t)ret != count) return -1; if (expected_buf && memcmp(buf, expected_buf, count) != 0) { errno = EINVAL; return -1; } return ret; } #if HAVE_LIBGNUTLS #include #include __attribute__((constructor)) static void gnutls_lxc_init(void) { gnutls_global_init(); } int sha1sum_file(char *fnam, unsigned char *digest) { char *buf; int ret; FILE *f; long flen; if (!fnam) return -1; f = fopen_cloexec(fnam, "r"); if (!f) { SYSERROR("Error opening template"); return -1; } if (fseek(f, 0, SEEK_END) < 0) { SYSERROR("Error seeking to end of template"); fclose(f); return -1; } if ((flen = ftell(f)) < 0) { SYSERROR("Error telling size of template"); fclose(f); return -1; } if (fseek(f, 0, SEEK_SET) < 0) { SYSERROR("Error seeking to start of template"); fclose(f); return -1; } if ((buf = malloc(flen+1)) == NULL) { SYSERROR("Out of memory"); fclose(f); return -1; } if (fread(buf, 1, flen, f) != flen) { SYSERROR("Failure reading template"); free(buf); fclose(f); return -1; } if (fclose(f) < 0) { SYSERROR("Failre closing template"); free(buf); return -1; } buf[flen] = '\0'; ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, buf, flen, (void *)digest); free(buf); return ret; } #endif char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup) { va_list ap2; size_t count = 1 + skip; char **result; /* first determine size of argument list, we don't want to reallocate * constantly... */ va_copy(ap2, ap); while (1) { char* arg = va_arg(ap2, char*); if (!arg) break; count++; } va_end(ap2); result = calloc(count, sizeof(char*)); if (!result) return NULL; count = skip; while (1) { char* arg = va_arg(ap, char*); if (!arg) break; arg = do_strdup ? strdup(arg) : arg; if (!arg) goto oom; result[count++] = arg; } /* calloc has already set last element to NULL*/ return result; oom: free(result); return NULL; } const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip) { return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0); } struct lxc_popen_FILE *lxc_popen(const char *command) { int ret; int pipe_fds[2]; pid_t child_pid; struct lxc_popen_FILE *fp = NULL; ret = pipe2(pipe_fds, O_CLOEXEC); if (ret < 0) return NULL; child_pid = fork(); if (child_pid < 0) goto on_error; if (!child_pid) { sigset_t mask; close(pipe_fds[0]); /* duplicate stdout */ if (pipe_fds[1] != STDOUT_FILENO) ret = dup2(pipe_fds[1], STDOUT_FILENO); else ret = fcntl(pipe_fds[1], F_SETFD, 0); if (ret < 0) { close(pipe_fds[1]); _exit(EXIT_FAILURE); } /* duplicate stderr */ if (pipe_fds[1] != STDERR_FILENO) ret = dup2(pipe_fds[1], STDERR_FILENO); else ret = fcntl(pipe_fds[1], F_SETFD, 0); close(pipe_fds[1]); if (ret < 0) _exit(EXIT_FAILURE); /* unblock all signals */ ret = sigfillset(&mask); if (ret < 0) _exit(EXIT_FAILURE); ret = pthread_sigmask(SIG_UNBLOCK, &mask, NULL); if (ret < 0) _exit(EXIT_FAILURE); /* check if /bin/sh exist, otherwise try Android location /system/bin/sh */ if (file_exists("/bin/sh")) execl("/bin/sh", "sh", "-c", command, (char *)NULL); else execl("/system/bin/sh", "sh", "-c", command, (char *)NULL); _exit(127); } close(pipe_fds[1]); pipe_fds[1] = -1; fp = malloc(sizeof(*fp)); if (!fp) goto on_error; memset(fp, 0, sizeof(*fp)); fp->child_pid = child_pid; fp->pipe = pipe_fds[0]; /* From now on, closing fp->f will also close fp->pipe. So only ever * call fclose(fp->f). */ fp->f = fdopen(pipe_fds[0], "r"); if (!fp->f) goto on_error; return fp; on_error: /* We can only close pipe_fds[0] if fdopen() didn't succeed or wasn't * called yet. Otherwise the fd belongs to the file opened by fdopen() * since it isn't dup()ed. */ if (fp && !fp->f && pipe_fds[0] >= 0) close(pipe_fds[0]); if (pipe_fds[1] >= 0) close(pipe_fds[1]); if (fp && fp->f) fclose(fp->f); if (fp) free(fp); return NULL; } int lxc_pclose(struct lxc_popen_FILE *fp) { pid_t wait_pid; int wstatus = 0; if (!fp) return -1; do { wait_pid = waitpid(fp->child_pid, &wstatus, 0); } while (wait_pid < 0 && errno == EINTR); fclose(fp->f); free(fp); if (wait_pid < 0) return -1; return wstatus; } char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack) { ssize_t len = -1, saved_len = -1; char *result = NULL; size_t replacement_len = strlen(replacement); size_t needle_len = strlen(needle); /* should be executed exactly twice */ while (len == -1 || result == NULL) { char *p; char *last_p; ssize_t part_len; if (len != -1) { result = calloc(1, len + 1); if (!result) return NULL; saved_len = len; } len = 0; for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) { part_len = (ssize_t)(p - last_p); if (result && part_len > 0) memcpy(&result[len], last_p, part_len); len += part_len; if (result && replacement_len > 0) memcpy(&result[len], replacement, replacement_len); len += replacement_len; p += needle_len; } part_len = strlen(last_p); if (result && part_len > 0) memcpy(&result[len], last_p, part_len); len += part_len; } /* make sure we did the same thing twice, * once for calculating length, the other * time for copying data */ if (saved_len != len) { free(result); return NULL; } /* make sure we didn't overwrite any buffer, * due to calloc the string should be 0-terminated */ if (result[len] != '\0') { free(result); return NULL; } return result; } bool lxc_string_in_array(const char *needle, const char **haystack) { for (; haystack && *haystack; haystack++) if (!strcmp(needle, *haystack)) return true; return false; } char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix) { char *result; char **p; size_t sep_len = strlen(sep); size_t result_len = use_as_prefix * sep_len; /* calculate new string length */ for (p = (char **)parts; *p; p++) result_len += (p > (char **)parts) * sep_len + strlen(*p); result = calloc(result_len + 1, 1); if (!result) return NULL; if (use_as_prefix) (void)strlcpy(result, sep, result_len + 1); for (p = (char **)parts; *p; p++) { if (p > (char **)parts) strcat(result, sep); strcat(result, *p); } return result; } char **lxc_normalize_path(const char *path) { char **components; char **p; size_t components_len = 0; size_t pos = 0; components = lxc_string_split(path, '/'); if (!components) return NULL; for (p = components; *p; p++) components_len++; /* resolve '.' and '..' */ for (pos = 0; pos < components_len; ) { if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) { /* eat this element */ free(components[pos]); memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos)); components_len--; } else if (!strcmp(components[pos], "..")) { /* eat this and the previous element */ free(components[pos - 1]); free(components[pos]); memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos)); components_len -= 2; pos--; } else { pos++; } } return components; } char *lxc_deslashify(const char *path) { char *dup, *p; char **parts = NULL; size_t n, len; dup = strdup(path); if (!dup) return NULL; parts = lxc_normalize_path(dup); if (!parts) { free(dup); return NULL; } /* We'll end up here if path == "///" or path == "". */ if (!*parts) { len = strlen(dup); if (!len) { lxc_free_array((void **)parts, free); return dup; } n = strcspn(dup, "/"); if (n == len) { free(dup); lxc_free_array((void **)parts, free); p = strdup("/"); if (!p) return NULL; return p; } } p = lxc_string_join("/", (const char **)parts, *dup == '/'); free(dup); lxc_free_array((void **)parts, free); return p; } char *lxc_append_paths(const char *first, const char *second) { int ret; size_t len; char *result = NULL; const char *pattern = "%s%s"; len = strlen(first) + strlen(second) + 1; if (second[0] != '/') { len += 1; pattern = "%s/%s"; } result = calloc(1, len); if (!result) return NULL; ret = snprintf(result, len, pattern, first, second); if (ret < 0 || (size_t)ret >= len) { free(result); return NULL; } return result; } bool lxc_string_in_list(const char *needle, const char *haystack, char _sep) { char *token, *str, *saveptr = NULL; char sep[2] = { _sep, '\0' }; size_t len; if (!haystack || !needle) return 0; len = strlen(haystack); str = alloca(len + 1); (void)strlcpy(str, haystack, len + 1); for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) { if (strcmp(needle, token) == 0) return 1; } return 0; } char **lxc_string_split(const char *string, char _sep) { char *token, *str, *saveptr = NULL; char sep[2] = {_sep, '\0'}; char **tmp = NULL, **result = NULL; size_t result_capacity = 0; size_t result_count = 0; int r, saved_errno; size_t len; if (!string) return calloc(1, sizeof(char *)); len = strlen(string); str = alloca(len + 1); (void)strlcpy(str, string, len + 1); for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) { r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); if (r < 0) goto error_out; result[result_count] = strdup(token); if (!result[result_count]) goto error_out; result_count++; } /* if we allocated too much, reduce it */ tmp = realloc(result, (result_count + 1) * sizeof(char *)); if (!tmp) goto error_out; result = tmp; /* Make sure we don't return uninitialized memory. */ if (result_count == 0) *result = NULL; return result; error_out: saved_errno = errno; lxc_free_array((void **)result, free); errno = saved_errno; return NULL; } char **lxc_string_split_and_trim(const char *string, char _sep) { char *token, *str, *saveptr = NULL; char sep[2] = { _sep, '\0' }; char **result = NULL; size_t result_capacity = 0; size_t result_count = 0; int r, saved_errno; size_t i = 0; size_t len; if (!string) return calloc(1, sizeof(char *)); len = strlen(string); str = alloca(len + 1); (void)strlcpy(str, string, len + 1); for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) { while (token[0] == ' ' || token[0] == '\t') token++; i = strlen(token); while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) { token[i - 1] = '\0'; i--; } r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); if (r < 0) goto error_out; result[result_count] = strdup(token); if (!result[result_count]) goto error_out; result_count++; } /* if we allocated too much, reduce it */ return realloc(result, (result_count + 1) * sizeof(char *)); error_out: saved_errno = errno; lxc_free_array((void **)result, free); errno = saved_errno; return NULL; } void lxc_free_array(void **array, lxc_free_fn element_free_fn) { void **p; for (p = array; p && *p; p++) element_free_fn(*p); free((void*)array); } int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment) { size_t new_capacity; void **new_array; /* first time around, catch some trivial mistakes of the user * only initializing one of these */ if (!*array || !*capacity) { *array = NULL; *capacity = 0; } new_capacity = *capacity; while (new_size + 1 > new_capacity) new_capacity += capacity_increment; if (new_capacity != *capacity) { /* we have to reallocate */ new_array = realloc(*array, new_capacity * sizeof(void *)); if (!new_array) return -1; memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *)); *array = new_array; *capacity = new_capacity; } /* array has sufficient elements */ return 0; } size_t lxc_array_len(void **array) { void **p; size_t result = 0; for (p = array; p && *p; p++) result++; return result; } int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool add_newline) { int fd, saved_errno; ssize_t ret; fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0666); if (fd < 0) return -1; ret = lxc_write_nointr(fd, buf, count); if (ret < 0) goto out_error; if ((size_t)ret != count) goto out_error; if (add_newline) { ret = lxc_write_nointr(fd, "\n", 1); if (ret != 1) goto out_error; } close(fd); return 0; out_error: saved_errno = errno; close(fd); errno = saved_errno; return -1; } int lxc_read_from_file(const char *filename, void* buf, size_t count) { int fd = -1, saved_errno; ssize_t ret; fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) return -1; if (!buf || !count) { char buf2[100]; size_t count2 = 0; while ((ret = read(fd, buf2, 100)) > 0) count2 += ret; if (ret >= 0) ret = count2; } else { memset(buf, 0, count); ret = read(fd, buf, count); } if (ret < 0) ERROR("read %s: %s", filename, strerror(errno)); saved_errno = errno; close(fd); errno = saved_errno; return ret; } void **lxc_append_null_to_array(void **array, size_t count) { void **temp; /* Append NULL to the array */ if (count) { temp = realloc(array, (count + 1) * sizeof(*array)); if (!temp) { size_t i; for (i = 0; i < count; i++) free(array[i]); free(array); return NULL; } array = temp; array[count] = NULL; } return array; } int randseed(bool srand_it) { /* srand pre-seed function based on /dev/urandom */ unsigned int seed = time(NULL) + getpid(); FILE *f; f = fopen("/dev/urandom", "r"); if (f) { int ret = fread(&seed, sizeof(seed), 1, f); if (ret != 1) DEBUG("unable to fread /dev/urandom, %s, fallback to time+pid rand seed", strerror(errno)); fclose(f); } if (srand_it) srand(seed); return seed; } uid_t get_ns_uid(uid_t orig) { char *line = NULL; size_t sz = 0; uid_t nsid, hostid, range; FILE *f = fopen("/proc/self/uid_map", "r"); if (!f) return 0; while (getline(&line, &sz, f) != -1) { if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3) continue; if (hostid <= orig && hostid + range > orig) { nsid += orig - hostid; goto found; } } nsid = 0; found: fclose(f); free(line); return nsid; } bool dir_exists(const char *path) { struct stat sb; int ret; ret = stat(path, &sb); if (ret < 0) // could be something other than eexist, just say no return false; return S_ISDIR(sb.st_mode); } /* Note we don't use SHA-1 here as we don't want to depend on HAVE_GNUTLS. * FNV has good anti collision properties and we're not worried * about pre-image resistance or one-way-ness, we're just trying to make * the name unique in the 108 bytes of space we have. */ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval) { unsigned char *bp; for(bp = buf; bp < (unsigned char *)buf + len; bp++) { /* xor the bottom with the current octet */ hval ^= (uint64_t)*bp; /* gcc optimised: * multiply by the 64 bit FNV magic prime mod 2^64 */ hval += (hval << 1) + (hval << 4) + (hval << 5) + (hval << 7) + (hval << 8) + (hval << 40); } return hval; } /* * Detect whether / is mounted MS_SHARED. The only way I know of to * check that is through /proc/self/mountinfo. * I'm only checking for /. If the container rootfs or mount location * is MS_SHARED, but not '/', then you're out of luck - figuring that * out would be too much work to be worth it. */ int detect_shared_rootfs(void) { char buf[LXC_LINELEN], *p; FILE *f; int i; char *p2; f = fopen("/proc/self/mountinfo", "r"); if (!f) return 0; while (fgets(buf, LXC_LINELEN, f)) { for (p = buf, i = 0; p && i < 4; i++) p = strchr(p + 1, ' '); if (!p) continue; p2 = strchr(p + 1, ' '); if (!p2) continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { // this is '/'. is it shared? p = strchr(p2 + 1, ' '); if (p && strstr(p, "shared:")) { fclose(f); return 1; } } } fclose(f); return 0; } bool switch_to_ns(pid_t pid, const char *ns) { int fd, ret; char nspath[MAXPATHLEN]; /* Switch to new ns */ ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns); if (ret < 0 || ret >= MAXPATHLEN) return false; fd = open(nspath, O_RDONLY); if (fd < 0) { SYSERROR("failed to open %s", nspath); return false; } ret = setns(fd, 0); if (ret) { SYSERROR("failed to set process %d to %s of %d.", pid, ns, fd); close(fd); return false; } close(fd); return true; } /* * looking at fs/proc_namespace.c, it appears we can * actually expect the rootfs entry to very specifically contain * " - rootfs rootfs " * IIUC, so long as we've chrooted so that rootfs is not our root, * the rootfs entry should always be skipped in mountinfo contents. */ bool detect_ramfs_rootfs(void) { FILE *f; char *p, *p2; char *line = NULL; size_t len = 0; int i; f = fopen("/proc/self/mountinfo", "r"); if (!f) return false; while (getline(&line, &len, f) != -1) { for (p = line, i = 0; p && i < 4; i++) p = strchr(p + 1, ' '); if (!p) continue; p2 = strchr(p + 1, ' '); if (!p2) continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { // this is '/'. is it the ramfs? p = strchr(p2 + 1, '-'); if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) { free(line); fclose(f); return true; } } } free(line); fclose(f); return false; } char *on_path(const char *cmd, const char *rootfs) { char *path = NULL; char *entry = NULL; char *saveptr = NULL; char cmdpath[MAXPATHLEN]; int ret; path = getenv("PATH"); if (!path) return NULL; path = strdup(path); if (!path) return NULL; entry = strtok_r(path, ":", &saveptr); while (entry) { if (rootfs) ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s/%s", rootfs, entry, cmd); else ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s", entry, cmd); if (ret < 0 || ret >= MAXPATHLEN) goto next_loop; if (access(cmdpath, X_OK) == 0) { free(path); return strdup(cmdpath); } next_loop: entry = strtok_r(NULL, ":", &saveptr); } free(path); return NULL; } bool file_exists(const char *f) { struct stat statbuf; return stat(f, &statbuf) == 0; } bool cgns_supported(void) { return file_exists("/proc/self/ns/cgroup"); } /* historically lxc-init has been under /usr/lib/lxc and under * /usr/lib/$ARCH/lxc. It now lives as $prefix/sbin/init.lxc. */ char *choose_init(const char *rootfs) { char *retv = NULL; const char *empty = "", *tmp; int ret, env_set = 0; if (!getenv("PATH")) { if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 0)) SYSERROR("Failed to setenv"); env_set = 1; } retv = on_path("init.lxc", rootfs); if (env_set) { if (unsetenv("PATH")) SYSERROR("Failed to unsetenv"); } if (retv) return retv; retv = malloc(PATH_MAX); if (!retv) return NULL; if (rootfs) tmp = rootfs; else tmp = empty; ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, SBINDIR, "/init.lxc"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, LXCINITDIR, "/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/usr/lib/lxc/lxc-init", tmp); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/sbin/lxc-init", tmp); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; /* * Last resort, look for the statically compiled init.lxc which we * hopefully bind-mounted in. * If we are called during container setup, and we get to this point, * then the init.lxc.static from the host will need to be bind-mounted * in. So we return NULL here to indicate that. */ if (rootfs) goto out1; ret = snprintf(retv, PATH_MAX, "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) { WARN("Nonsense - name /lxc.init.static too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; out1: free(retv); return NULL; } int print_to_file(const char *file, const char *content) { FILE *f; int ret = 0; f = fopen(file, "w"); if (!f) return -1; if (fprintf(f, "%s", content) != strlen(content)) ret = -1; fclose(f); return ret; } int is_dir(const char *path) { struct stat statbuf; int ret = stat(path, &statbuf); if (ret == 0 && S_ISDIR(statbuf.st_mode)) return 1; return 0; } /* * Given the '-t' template option to lxc-create, figure out what to * do. If the template is a full executable path, use that. If it * is something like 'sshd', then return $templatepath/lxc-sshd. * On success return the template, on error return NULL. */ char *get_template_path(const char *t) { int ret, len; char *tpath; if (t[0] == '/' && access(t, X_OK) == 0) { tpath = strdup(t); return tpath; } len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; tpath = malloc(len); if (!tpath) return NULL; ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); if (ret < 0 || ret >= len) { free(tpath); return NULL; } if (access(tpath, X_OK) < 0) { SYSERROR("bad template: %s", t); free(tpath); return NULL; } return tpath; } /* * @path: a pathname where / replaced with '\0'. * @offsetp: pointer to int showing which path segment was last seen. * Updated on return to reflect the next segment. * @fulllen: full original path length. * Returns a pointer to the next path segment, or NULL if done. */ static char *get_nextpath(char *path, int *offsetp, int fulllen) { int offset = *offsetp; if (offset >= fulllen) return NULL; while (path[offset] != '\0' && offset < fulllen) offset++; while (path[offset] == '\0' && offset < fulllen) offset++; *offsetp = offset; return (offset < fulllen) ? &path[offset] : NULL; } /* * Check that @subdir is a subdir of @dir. @len is the length of * @dir (to avoid having to recalculate it). */ static bool is_subdir(const char *subdir, const char *dir, size_t len) { size_t subdirlen = strlen(subdir); if (subdirlen < len) return false; if (strncmp(subdir, dir, len) != 0) return false; if (dir[len-1] == '/') return true; if (subdir[len] == '/' || subdirlen == len) return true; return false; } /* * Check if the open fd is a symlink. Return -ELOOP if it is. Return * -ENOENT if we couldn't fstat. Return 0 if the fd is ok. */ static int check_symlink(int fd) { struct stat sb; int ret = fstat(fd, &sb); if (ret < 0) return -ENOENT; if (S_ISLNK(sb.st_mode)) return -ELOOP; return 0; } /* * Open a file or directory, provided that it contains no symlinks. * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init */ static int open_if_safe(int dirfd, const char *nextpath) { int newfd = openat(dirfd, nextpath, O_RDONLY | O_NOFOLLOW); if (newfd >= 0) // was not a symlink, all good return newfd; if (errno == ELOOP) return newfd; if (errno == EPERM || errno == EACCES) { /* we're not root (cause we got EPERM) so try opening with O_PATH */ newfd = openat(dirfd, nextpath, O_PATH | O_NOFOLLOW); if (newfd >= 0) { /* O_PATH will return an fd for symlinks. We know * nextpath wasn't a symlink at last openat, so if fd * is now a link, then something * fishy is going on */ int ret = check_symlink(newfd); if (ret < 0) { close(newfd); newfd = ret; } } } return newfd; } /* * Open a path intending for mounting, ensuring that the final path * is inside the container's rootfs. * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init * * @target: path to be opened * @prefix_skip: a part of @target in which to ignore symbolic links. This * would be the container's rootfs. * * Return an open fd for the path, or <0 on error. */ static int open_without_symlink(const char *target, const char *prefix_skip) { int curlen = 0, dirfd, fulllen, i; char *dup = NULL; fulllen = strlen(target); /* make sure prefix-skip makes sense */ if (prefix_skip && strlen(prefix_skip) > 0) { curlen = strlen(prefix_skip); if (!is_subdir(target, prefix_skip, curlen)) { ERROR("WHOA there - target '%s' didn't start with prefix '%s'", target, prefix_skip); return -EINVAL; } /* * get_nextpath() expects the curlen argument to be * on a (turned into \0) / or before it, so decrement * curlen to make sure that happens */ if (curlen) curlen--; } else { prefix_skip = "/"; curlen = 0; } /* Make a copy of target which we can hack up, and tokenize it */ if ((dup = strdup(target)) == NULL) { SYSERROR("Out of memory checking for symbolic link"); return -ENOMEM; } for (i = 0; i < fulllen; i++) { if (dup[i] == '/') dup[i] = '\0'; } dirfd = open(prefix_skip, O_RDONLY); if (dirfd < 0) goto out; while (1) { int newfd, saved_errno; char *nextpath; if ((nextpath = get_nextpath(dup, &curlen, fulllen)) == NULL) goto out; newfd = open_if_safe(dirfd, nextpath); saved_errno = errno; close(dirfd); dirfd = newfd; if (newfd < 0) { errno = saved_errno; if (errno == ELOOP) SYSERROR("%s in %s was a symbolic link!", nextpath, target); goto out; } } out: free(dup); return dirfd; } /* * Safely mount a path into a container, ensuring that the mount target * is under the container's @rootfs. (If @rootfs is NULL, then the container * uses the host's /) * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init */ int safe_mount(const char *src, const char *dest, const char *fstype, unsigned long flags, const void *data, const char *rootfs) { int srcfd = -1, destfd, ret, saved_errno; char srcbuf[50], destbuf[50]; // only needs enough for /proc/self/fd/ const char *mntsrc = src; if (!rootfs) rootfs = ""; /* todo - allow symlinks for relative paths if 'allowsymlinks' option is passed */ if (flags & MS_BIND && src && src[0] != '/') { INFO("this is a relative bind mount"); srcfd = open_without_symlink(src, NULL); if (srcfd < 0) return srcfd; ret = snprintf(srcbuf, 50, "/proc/self/fd/%d", srcfd); if (ret < 0 || ret > 50) { close(srcfd); ERROR("Out of memory"); return -EINVAL; } mntsrc = srcbuf; } destfd = open_without_symlink(dest, rootfs); if (destfd < 0) { if (srcfd != -1) { saved_errno = errno; close(srcfd); errno = saved_errno; } return destfd; } ret = snprintf(destbuf, 50, "/proc/self/fd/%d", destfd); if (ret < 0 || ret > 50) { if (srcfd != -1) close(srcfd); close(destfd); ERROR("Out of memory"); return -EINVAL; } ret = mount(mntsrc, destbuf, fstype, flags, data); saved_errno = errno; if (srcfd != -1) close(srcfd); close(destfd); if (ret < 0) { errno = saved_errno; SYSERROR("Failed to mount %s onto %s", src ? src : "(null)", dest); return ret; } return 0; } /* * Mount a proc under @rootfs if proc self points to a pid other than * my own. This is needed to have a known-good proc mount for setting * up LSMs both at container startup and attach. * * @rootfs : the rootfs where proc should be mounted * * Returns < 0 on failure, 0 if the correct proc was already mounted * and 1 if a new proc was mounted. * * NOTE: not to be called from inside the container namespace! */ int lxc_mount_proc_if_needed(const char *rootfs) { char path[MAXPATHLEN]; int link_to_pid, linklen, mypid, ret; char link[LXC_NUMSTRLEN64] = {0}; ret = snprintf(path, MAXPATHLEN, "%s/proc/self", rootfs); if (ret < 0 || ret >= MAXPATHLEN) { SYSERROR("proc path name too long"); return -1; } linklen = readlink(path, link, LXC_NUMSTRLEN64); ret = snprintf(path, MAXPATHLEN, "%s/proc", rootfs); if (ret < 0 || ret >= MAXPATHLEN) { SYSERROR("proc path name too long"); return -1; } /* /proc not mounted */ if (linklen < 0) { if (mkdir(path, 0755) && errno != EEXIST) return -1; goto domount; } else if (linklen >= LXC_NUMSTRLEN64) { link[linklen - 1] = '\0'; ERROR("readlink returned truncated content: \"%s\"", link); return -1; } mypid = lxc_raw_getpid(); INFO("I am %d, /proc/self points to \"%s\"", mypid, link); if (lxc_safe_int(link, &link_to_pid) < 0) return -1; /* correct procfs is already mounted */ if (link_to_pid == mypid) return 0; ret = umount2(path, MNT_DETACH); if (ret < 0) WARN("failed to umount \"%s\" with MNT_DETACH", path); domount: /* rootfs is NULL */ if (!strcmp(rootfs, "")) ret = mount("proc", path, "proc", 0, NULL); else ret = safe_mount("proc", path, "proc", 0, NULL, rootfs); if (ret < 0) return -1; INFO("mounted /proc in container for security transition"); return 1; } int open_devnull(void) { int fd = open("/dev/null", O_RDWR); if (fd < 0) SYSERROR("Can't open /dev/null"); return fd; } int set_stdfds(int fd) { int ret; if (fd < 0) return -1; ret = dup2(fd, STDIN_FILENO); if (ret < 0) return -1; ret = dup2(fd, STDOUT_FILENO); if (ret < 0) return -1; ret = dup2(fd, STDERR_FILENO); if (ret < 0) return -1; return 0; } int null_stdfds(void) { int ret = -1; int fd = open_devnull(); if (fd >= 0) { ret = set_stdfds(fd); close(fd); } return ret; } /* * Return the number of lines in file @fn, or -1 on error */ int lxc_count_file_lines(const char *fn) { FILE *f; char *line = NULL; size_t sz = 0; int n = 0; f = fopen_cloexec(fn, "r"); if (!f) return -1; while (getline(&line, &sz, f) != -1) { n++; } free(line); fclose(f); return n; } void *lxc_strmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { void *tmp = NULL, *overlap = NULL; /* We establish an anonymous mapping that is one byte larger than the * underlying file. The pages handed to us are zero filled. */ tmp = mmap(addr, length + 1, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (tmp == MAP_FAILED) return tmp; /* Now we establish a fixed-address mapping starting at the address we * received from our anonymous mapping and replace all bytes excluding * the additional \0-byte with the file. This allows us to use normal * string-handling functions. */ overlap = mmap(tmp, length, prot, MAP_FIXED | flags, fd, offset); if (overlap == MAP_FAILED) munmap(tmp, length + 1); return overlap; } int lxc_strmunmap(void *addr, size_t length) { return munmap(addr, length + 1); } /* Check whether a signal is blocked by a process. */ /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ #define __PROC_STATUS_LEN (6 + (LXC_NUMSTRLEN64) + 7 + 1) bool task_blocks_signal(pid_t pid, int signal) { int ret; char status[__PROC_STATUS_LEN]; FILE *f; uint64_t sigblk = 0, one = 1; size_t n = 0; bool bret = false; char *line = NULL; ret = snprintf(status, __PROC_STATUS_LEN, "/proc/%d/status", pid); if (ret < 0 || ret >= __PROC_STATUS_LEN) return bret; f = fopen(status, "r"); if (!f) return bret; while (getline(&line, &n, f) != -1) { char *numstr; if (strncmp(line, "SigBlk:", 7)) continue; numstr = lxc_trim_whitespace_in_place(line + 7); ret = lxc_safe_uint64(numstr, &sigblk, 16); if (ret < 0) goto out; break; } if (sigblk & (one << (signal - 1))) bret = true; out: free(line); fclose(f); return bret; } static int lxc_append_null_to_list(void ***list) { int newentry = 0; void **tmp; if (*list) for (; (*list)[newentry]; newentry++) { ; } tmp = realloc(*list, (newentry + 2) * sizeof(void **)); if (!tmp) return -1; *list = tmp; (*list)[newentry + 1] = NULL; return newentry; } int lxc_append_string(char ***list, char *entry) { char *copy; int newentry; newentry = lxc_append_null_to_list((void ***)list); if (newentry < 0) return -1; copy = strdup(entry); if (!copy) return -1; (*list)[newentry] = copy; return 0; } int lxc_preserve_ns(const int pid, const char *ns) { int ret; /* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */ #define __NS_PATH_LEN 50 char path[__NS_PATH_LEN]; /* This way we can use this function to also check whether namespaces * are supported by the kernel by passing in the NULL or the empty * string. */ ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid, !ns || strcmp(ns, "") == 0 ? "" : "/", !ns || strcmp(ns, "") == 0 ? "" : ns); errno = EFBIG; if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) return -EFBIG; return open(path, O_RDONLY | O_CLOEXEC); } int lxc_safe_uint(const char *numstr, unsigned int *converted) { char *err = NULL; unsigned long int uli; while (isspace(*numstr)) numstr++; if (*numstr == '-') return -EINVAL; errno = 0; uli = strtoul(numstr, &err, 0); if (errno == ERANGE && uli == ULONG_MAX) return -ERANGE; if (err == numstr || *err != '\0') return -EINVAL; if (uli > UINT_MAX) return -ERANGE; *converted = (unsigned int)uli; return 0; } int lxc_safe_ulong(const char *numstr, unsigned long *converted) { char *err = NULL; unsigned long int uli; while (isspace(*numstr)) numstr++; if (*numstr == '-') return -EINVAL; errno = 0; uli = strtoul(numstr, &err, 0); if (errno == ERANGE && uli == ULONG_MAX) return -ERANGE; if (err == numstr || *err != '\0') return -EINVAL; *converted = uli; return 0; } int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base) { char *err = NULL; uint64_t u; while (isspace(*numstr)) numstr++; if (*numstr == '-') return -EINVAL; errno = 0; u = strtoull(numstr, &err, base); if (errno == ERANGE && u == ULLONG_MAX) return -ERANGE; if (err == numstr || *err != '\0') return -EINVAL; *converted = u; return 0; } int lxc_safe_int(const char *numstr, int *converted) { char *err = NULL; signed long int sli; errno = 0; sli = strtol(numstr, &err, 0); if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) return -ERANGE; if (errno != 0 && sli == 0) return -EINVAL; if (err == numstr || *err != '\0') return -EINVAL; if (sli > INT_MAX || sli < INT_MIN) return -ERANGE; *converted = (int)sli; return 0; } int lxc_safe_long(const char *numstr, long int *converted) { char *err = NULL; signed long int sli; errno = 0; sli = strtol(numstr, &err, 0); if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) return -ERANGE; if (errno != 0 && sli == 0) return -EINVAL; if (err == numstr || *err != '\0') return -EINVAL; *converted = sli; return 0; } int lxc_safe_long_long(const char *numstr, long long int *converted) { char *err = NULL; signed long long int sli; errno = 0; sli = strtoll(numstr, &err, 0); if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN)) return -ERANGE; if (errno != 0 && sli == 0) return -EINVAL; if (err == numstr || *err != '\0') return -EINVAL; *converted = sli; return 0; } int lxc_switch_uid_gid(uid_t uid, gid_t gid) { if (setgid(gid) < 0) { SYSERROR("Failed to switch to gid %d.", gid); return -errno; } NOTICE("Switched to gid %d.", gid); if (setuid(uid) < 0) { SYSERROR("Failed to switch to uid %d.", uid); return -errno; } NOTICE("Switched to uid %d.", uid); return 0; } /* Simple covenience function which enables uniform logging. */ int lxc_setgroups(int size, gid_t list[]) { if (setgroups(size, list) < 0) { SYSERROR("Failed to setgroups()."); return -errno; } NOTICE("Dropped additional groups."); return 0; } static int lxc_get_unused_loop_dev_legacy(char *loop_name) { struct dirent *dp; struct loop_info64 lo64; DIR *dir; int dfd = -1, fd = -1, ret = -1; dir = opendir("/dev"); if (!dir) return -1; while ((dp = readdir(dir))) { if (strncmp(dp->d_name, "loop", 4) != 0) continue; dfd = dirfd(dir); if (dfd < 0) continue; fd = openat(dfd, dp->d_name, O_RDWR); if (fd < 0) continue; ret = ioctl(fd, LOOP_GET_STATUS64, &lo64); if (ret < 0) { if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0 || errno != ENXIO) { close(fd); fd = -1; continue; } } ret = snprintf(loop_name, LO_NAME_SIZE, "/dev/%s", dp->d_name); if (ret < 0 || ret >= LO_NAME_SIZE) { close(fd); fd = -1; continue; } break; } closedir(dir); if (fd < 0) return -1; return fd; } static int lxc_get_unused_loop_dev(char *name_loop) { int loop_nr, ret; int fd_ctl = -1, fd_tmp = -1; fd_ctl = open("/dev/loop-control", O_RDWR | O_CLOEXEC); if (fd_ctl < 0) return -ENODEV; loop_nr = ioctl(fd_ctl, LOOP_CTL_GET_FREE); if (loop_nr < 0) goto on_error; ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/loop%d", loop_nr); if (ret < 0 || ret >= LO_NAME_SIZE) goto on_error; fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC); if (fd_tmp < 0) goto on_error; on_error: close(fd_ctl); return fd_tmp; } int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags) { int ret; struct loop_info64 lo64; int fd_img = -1, fret = -1, fd_loop = -1; fd_loop = lxc_get_unused_loop_dev(loop_dev); if (fd_loop < 0) { if (fd_loop == -ENODEV) fd_loop = lxc_get_unused_loop_dev_legacy(loop_dev); else goto on_error; } fd_img = open(source, O_RDWR | O_CLOEXEC); if (fd_img < 0) goto on_error; ret = ioctl(fd_loop, LOOP_SET_FD, fd_img); if (ret < 0) goto on_error; memset(&lo64, 0, sizeof(lo64)); lo64.lo_flags = flags; ret = ioctl(fd_loop, LOOP_SET_STATUS64, &lo64); if (ret < 0) goto on_error; fret = 0; on_error: if (fd_img >= 0) close(fd_img); if (fret < 0 && fd_loop >= 0) { close(fd_loop); fd_loop = -1; } return fd_loop; } int lxc_unstack_mountpoint(const char *path, bool lazy) { int ret; int umounts = 0; pop_stack: ret = umount2(path, lazy ? MNT_DETACH : 0); if (ret < 0) { /* We consider anything else than EINVAL deadly to prevent going * into an infinite loop. (The other alternative is constantly * parsing /proc/self/mountinfo which is yucky and probably * racy.) */ if (errno != EINVAL) return -errno; } else { /* Just stop counting when this happens. That'd just be so * stupid that we won't even bother trying to report back the * correct value anymore. */ if (umounts != INT_MAX) umounts++; /* We succeeded in umounting. Make sure that there's no other * mountpoint stacked underneath. */ goto pop_stack; } return umounts; } int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) { pid_t child; int ret, fret, pipefd[2]; ssize_t bytes; /* Make sure our callers do not receive uninitialized memory. */ if (buf_size > 0 && buf) buf[0] = '\0'; if (pipe(pipefd) < 0) { SYSERROR("failed to create pipe"); return -1; } child = lxc_raw_clone(0); if (child < 0) { close(pipefd[0]); close(pipefd[1]); SYSERROR("failed to create new process"); return -1; } if (child == 0) { /* Close the read-end of the pipe. */ close(pipefd[0]); /* Redirect std{err,out} to write-end of the * pipe. */ ret = dup2(pipefd[1], STDOUT_FILENO); if (ret >= 0) ret = dup2(pipefd[1], STDERR_FILENO); /* Close the write-end of the pipe. */ close(pipefd[1]); if (ret < 0) { SYSERROR("failed to duplicate std{err,out} file descriptor"); _exit(EXIT_FAILURE); } /* Does not return. */ child_fn(args); ERROR("failed to exec command"); _exit(EXIT_FAILURE); } /* close the write-end of the pipe */ close(pipefd[1]); if (buf && buf_size > 0) { bytes = read(pipefd[0], buf, buf_size - 1); if (bytes > 0) buf[bytes - 1] = '\0'; } fret = wait_for_pid(child); /* close the read-end of the pipe */ close(pipefd[0]); return fret; } char *must_make_path(const char *first, ...) { va_list args; char *cur, *dest; size_t full_len = strlen(first); dest = must_copy_string(first); va_start(args, first); while ((cur = va_arg(args, char *)) != NULL) { full_len += strlen(cur); if (cur[0] != '/') full_len++; dest = must_realloc(dest, full_len + 1); if (cur[0] != '/') strcat(dest, "/"); strcat(dest, cur); } va_end(args); return dest; } char *must_append_path(char *first, ...) { char *cur; size_t full_len; va_list args; char *dest = first; full_len = strlen(first); va_start(args, first); while ((cur = va_arg(args, char *)) != NULL) { full_len += strlen(cur); if (cur[0] != '/') full_len++; dest = must_realloc(dest, full_len + 1); if (cur[0] != '/') strcat(dest, "/"); strcat(dest, cur); } va_end(args); return dest; } char *must_copy_string(const char *entry) { char *ret; if (!entry) return NULL; do { ret = strdup(entry); } while (!ret); return ret; } void *must_realloc(void *orig, size_t sz) { void *ret; do { ret = realloc(orig, sz); } while (!ret); return ret; } bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) { return (fs->f_type == (fs_type_magic)magic_val); } bool has_fs_type(const char *path, fs_type_magic magic_val) { bool has_type; int ret; struct statfs sb; ret = statfs(path, &sb); if (ret < 0) return false; has_type = is_fs_type(&sb, magic_val); if (!has_type && magic_val == RAMFS_MAGIC) WARN("When the ramfs it a tmpfs statfs() might report tmpfs"); return has_type; } bool fhas_fs_type(int fd, fs_type_magic magic_val) { int ret; struct statfs sb; ret = fstatfs(fd, &sb); if (ret < 0) return false; return is_fs_type(&sb, magic_val); } bool lxc_nic_exists(char *nic) { #define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1 char path[__LXC_SYS_CLASS_NET_LEN]; int ret; struct stat sb; if (!strcmp(nic, "none")) return true; ret = snprintf(path, __LXC_SYS_CLASS_NET_LEN, "/sys/class/net/%s", nic); if (ret < 0 || (size_t)ret >= __LXC_SYS_CLASS_NET_LEN) return false; ret = stat(path, &sb); if (ret < 0) return false; return true; } int lxc_make_tmpfile(char *template, bool rm) { __do_close_prot_errno int fd = -EBADF; int ret; mode_t msk; msk = umask(0022); fd = mkstemp(template); umask(msk); if (fd < 0) return -1; if (lxc_set_cloexec(fd)) return -1; if (!rm) return move_fd(fd); ret = unlink(template); if (ret < 0) return -1; return move_fd(fd); } int parse_byte_size_string(const char *s, int64_t *converted) { int ret, suffix_len; long long int conv; int64_t mltpl, overflow; char *end; char dup[LXC_NUMSTRLEN64 + 2]; char suffix[3] = {0}; if (!s || !strcmp(s, "")) return -EINVAL; end = stpncpy(dup, s, sizeof(dup) - 1); if (*end != '\0') return -EINVAL; if (isdigit(*(end - 1))) suffix_len = 0; else if (isalpha(*(end - 1))) suffix_len = 1; else return -EINVAL; if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2))) return -EINVAL; if (suffix_len > 0 && isalpha(*(end - 2))) suffix_len++; if (suffix_len > 0) { memcpy(suffix, end - suffix_len, suffix_len); *(suffix + suffix_len) = '\0'; *(end - suffix_len) = '\0'; } dup[lxc_char_right_gc(dup, strlen(dup))] = '\0'; ret = lxc_safe_long_long(dup, &conv); if (ret < 0) return -ret; if (suffix_len != 2) { *converted = conv; return 0; } if (!strcmp(suffix, "kB")) mltpl = 1024; else if (!strcmp(suffix, "MB")) mltpl = 1024 * 1024; else if (!strcmp(suffix, "GB")) mltpl = 1024 * 1024 * 1024; else return -EINVAL; overflow = conv * mltpl; if (conv != 0 && (overflow / conv) != mltpl) return -ERANGE; *converted = overflow; return 0; } uint64_t lxc_find_next_power2(uint64_t n) { /* 0 is not valid input. We return 0 to the caller since 0 is not a * valid power of two. */ if (n == 0) return 0; if (!(n & (n - 1))) return n; while (n & (n - 1)) n = n & (n - 1); n = n << 1; return n; } lxc-2.0.11/src/lxc/initutils.c0000644061062106075000000002131213435013473013061 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "initutils.h" #include "log.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(lxc_initutils, lxc); static char *copy_global_config_value(char *p) { int len = strlen(p); char *retbuf; if (len < 1) return NULL; if (p[len-1] == '\n') { p[len-1] = '\0'; len--; } retbuf = malloc(len + 1); if (!retbuf) return NULL; (void)strlcpy(retbuf, p, len + 1); return retbuf; } const char *lxc_global_config_value(const char *option_name) { static const char * const options[][2] = { { "lxc.bdev.lvm.vg", DEFAULT_VG }, { "lxc.bdev.lvm.thin_pool", DEFAULT_THIN_POOL }, { "lxc.bdev.zfs.root", DEFAULT_ZFSROOT }, { "lxc.bdev.rbd.rbdpool", DEFAULT_RBDPOOL }, { "lxc.lxcpath", NULL }, { "lxc.default_config", NULL }, { "lxc.cgroup.pattern", NULL }, { "lxc.cgroup.use", NULL }, { NULL, NULL }, }; /* placed in the thread local storage pool for non-bionic targets */ #ifdef HAVE_TLS static __thread const char *values[sizeof(options) / sizeof(options[0])] = { 0 }; #else static const char *values[sizeof(options) / sizeof(options[0])] = { 0 }; #endif /* user_config_path is freed as soon as it is used */ char *user_config_path = NULL; /* * The following variables are freed at bottom unconditionally. * So NULL the value if it is to be returned to the caller */ char *user_default_config_path = NULL; char *user_lxc_path = NULL; char *user_cgroup_pattern = NULL; if (geteuid() > 0) { const char *user_home = getenv("HOME"); if (!user_home) user_home = "/"; user_config_path = malloc(sizeof(char) * (22 + strlen(user_home))); user_default_config_path = malloc(sizeof(char) * (26 + strlen(user_home))); user_lxc_path = malloc(sizeof(char) * (19 + strlen(user_home))); sprintf(user_config_path, "%s/.config/lxc/lxc.conf", user_home); sprintf(user_default_config_path, "%s/.config/lxc/default.conf", user_home); sprintf(user_lxc_path, "%s/.local/share/lxc/", user_home); user_cgroup_pattern = strdup("lxc/%n"); } else { user_config_path = strdup(LXC_GLOBAL_CONF); user_default_config_path = strdup(LXC_DEFAULT_CONFIG); user_lxc_path = strdup(LXCPATH); user_cgroup_pattern = strdup(DEFAULT_CGROUP_PATTERN); } const char * const (*ptr)[2]; size_t i; char buf[1024], *p, *p2; FILE *fin = NULL; for (i = 0, ptr = options; (*ptr)[0]; ptr++, i++) { if (!strcmp(option_name, (*ptr)[0])) break; } if (!(*ptr)[0]) { free(user_config_path); free(user_default_config_path); free(user_lxc_path); free(user_cgroup_pattern); errno = EINVAL; return NULL; } if (values[i]) { free(user_config_path); free(user_default_config_path); free(user_lxc_path); free(user_cgroup_pattern); return values[i]; } fin = fopen_cloexec(user_config_path, "r"); free(user_config_path); if (fin) { while (fgets(buf, 1024, fin)) { if (buf[0] == '#') continue; p = strstr(buf, option_name); if (!p) continue; /* see if there was just white space in front * of the option name */ for (p2 = buf; p2 < p; p2++) { if (*p2 != ' ' && *p2 != '\t') break; } if (p2 < p) continue; p = strchr(p, '='); if (!p) continue; /* see if there was just white space after * the option name */ for (p2 += strlen(option_name); p2 < p; p2++) { if (*p2 != ' ' && *p2 != '\t') break; } if (p2 < p) continue; p++; while (*p && (*p == ' ' || *p == '\t')) p++; if (!*p) continue; if (strcmp(option_name, "lxc.lxcpath") == 0) { free(user_lxc_path); user_lxc_path = copy_global_config_value(p); remove_trailing_slashes(user_lxc_path); values[i] = user_lxc_path; user_lxc_path = NULL; goto out; } values[i] = copy_global_config_value(p); goto out; } } /* could not find value, use default */ if (strcmp(option_name, "lxc.lxcpath") == 0) { remove_trailing_slashes(user_lxc_path); values[i] = user_lxc_path; user_lxc_path = NULL; } else if (strcmp(option_name, "lxc.default_config") == 0) { values[i] = user_default_config_path; user_default_config_path = NULL; } else if (strcmp(option_name, "lxc.cgroup.pattern") == 0) { values[i] = user_cgroup_pattern; user_cgroup_pattern = NULL; } else values[i] = (*ptr)[1]; /* special case: if default value is NULL, * and there is no config, don't view that * as an error... */ if (!values[i]) errno = 0; out: if (fin) fclose(fin); free(user_cgroup_pattern); free(user_default_config_path); free(user_lxc_path); return values[i]; } extern void remove_trailing_slashes(char *p) { int l = strlen(p); while (--l >= 0 && (p[l] == '/' || p[l] == '\n')) p[l] = '\0'; } FILE *fopen_cloexec(const char *path, const char *mode) { int open_mode = 0; int step = 0; int fd; int saved_errno = 0; FILE *ret; if (!strncmp(mode, "r+", 2)) { open_mode = O_RDWR; step = 2; } else if (!strncmp(mode, "r", 1)) { open_mode = O_RDONLY; step = 1; } else if (!strncmp(mode, "w+", 2)) { open_mode = O_RDWR | O_TRUNC | O_CREAT; step = 2; } else if (!strncmp(mode, "w", 1)) { open_mode = O_WRONLY | O_TRUNC | O_CREAT; step = 1; } else if (!strncmp(mode, "a+", 2)) { open_mode = O_RDWR | O_CREAT | O_APPEND; step = 2; } else if (!strncmp(mode, "a", 1)) { open_mode = O_WRONLY | O_CREAT | O_APPEND; step = 1; } for (; mode[step]; step++) if (mode[step] == 'x') open_mode |= O_EXCL; open_mode |= O_CLOEXEC; fd = open(path, open_mode, 0666); if (fd < 0) return NULL; ret = fdopen(fd, mode); saved_errno = errno; if (!ret) close(fd); errno = saved_errno; return ret; } /* * Sets the process title to the specified title. Note that this may fail if * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). */ int setproctitle(char *title) { static char *proctitle = NULL; char buf[2048], *tmp; FILE *f; int i, len, ret = 0; /* We don't really need to know all of this stuff, but unfortunately * PR_SET_MM_MAP requires us to set it all at once, so we have to * figure it out anyway. */ unsigned long start_data, end_data, start_brk, start_code, end_code, start_stack, arg_start, arg_end, env_start, env_end, brk_val; struct prctl_mm_map prctl_map; f = fopen_cloexec("/proc/self/stat", "r"); if (!f) { return -1; } tmp = fgets(buf, sizeof(buf), f); fclose(f); if (!tmp) { return -1; } /* Skip the first 25 fields, column 26-28 are start_code, end_code, * and start_stack */ tmp = strchr(buf, ' '); for (i = 0; i < 24; i++) { if (!tmp) return -1; tmp = strchr(tmp+1, ' '); } if (!tmp) return -1; i = sscanf(tmp, "%lu %lu %lu", &start_code, &end_code, &start_stack); if (i != 3) return -1; /* Skip the next 19 fields, column 45-51 are start_data to arg_end */ for (i = 0; i < 19; i++) { if (!tmp) return -1; tmp = strchr(tmp+1, ' '); } if (!tmp) return -1; i = sscanf(tmp, "%lu %lu %lu %*u %*u %lu %lu", &start_data, &end_data, &start_brk, &env_start, &env_end); if (i != 5) return -1; /* Include the null byte here, because in the calculations below we * want to have room for it. */ len = strlen(title) + 1; proctitle = realloc(proctitle, len); if (!proctitle) return -1; arg_start = (unsigned long) proctitle; arg_end = arg_start + len; brk_val = syscall(__NR_brk, 0); prctl_map = (struct prctl_mm_map) { .start_code = start_code, .end_code = end_code, .start_stack = start_stack, .start_data = start_data, .end_data = end_data, .start_brk = start_brk, .brk = brk_val, .arg_start = arg_start, .arg_end = arg_end, .env_start = env_start, .env_end = env_end, .auxv = NULL, .auxv_size = 0, .exe_fd = -1, }; ret = prctl(PR_SET_MM, PR_SET_MM_MAP, (long) &prctl_map, sizeof(prctl_map), 0); if (ret == 0) (void)strlcpy((char*)arg_start, title, len); else INFO("setting cmdline failed - %s", strerror(errno)); return ret; } lxc-2.0.11/src/lxc/rexec.c0000644061062106075000000001523513435013473012152 00000000000000/* liblxcapi * * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "macro.h" #include "memory_utils.h" #include "utils.h" #if IS_BIONIC #include "../include/fexecve.h" #endif #define LXC_MEMFD_REXEC_SEALS \ (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) static int push_vargs(char *data, int data_length, char ***output) { int num = 0; char *cur = data; if (!data || *output) return -1; *output = must_realloc(NULL, sizeof(**output)); while (cur < data + data_length) { num++; *output = must_realloc(*output, (num + 1) * sizeof(**output)); (*output)[num - 1] = cur; cur += strlen(cur) + 1; } (*output)[num] = NULL; return num; } static char *file_to_buf(char *path, size_t *length) { int fd; char buf[PATH_MAX]; char *copy = NULL; if (!length) return NULL; fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) return NULL; *length = 0; for (;;) { int n; char *old = copy; n = lxc_read_nointr(fd, buf, sizeof(buf)); if (n < 0) goto on_error; if (!n) break; copy = must_realloc(old, (*length + n) * sizeof(*old)); memcpy(copy + *length, buf, n); *length += n; } close(fd); return copy; on_error: close(fd); free(copy); return NULL; } static int parse_argv(char ***argv) { __do_free char *cmdline = NULL; int ret; size_t cmdline_size; cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size); if (!cmdline) return -1; ret = push_vargs(cmdline, cmdline_size, argv); if (ret <= 0) return -1; move_ptr(cmdline); return 0; } static int is_memfd(void) { __do_close_prot_errno int fd = -EBADF; int seals; fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (fd < 0) return -ENOTRECOVERABLE; seals = fcntl(fd, F_GET_SEALS); if (seals < 0) { struct stat s = {0}; if (fstat(fd, &s) == 0) return (s.st_nlink == 0); return -EINVAL; } return seals == LXC_MEMFD_REXEC_SEALS; } /* Maximum number of bytes sendfile() is able to send in one go. */ #define LXC_SENDFILE_MAX 0x7ffff000 static ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count) { ssize_t ret; again: ret = sendfile(out_fd, in_fd, offset, count); if (ret < 0) { if (errno == EINTR) goto again; return -1; } return ret; } static int fd_to_fd(int from, int to) { for (;;) { uint8_t buf[PATH_MAX]; uint8_t *p = buf; ssize_t bytes_to_write; ssize_t bytes_read; bytes_read = lxc_read_nointr(from, buf, sizeof buf); if (bytes_read < 0) return -1; if (bytes_read == 0) break; bytes_to_write = (size_t)bytes_read; do { ssize_t bytes_written; bytes_written = lxc_write_nointr(to, p, bytes_to_write); if (bytes_written < 0) return -1; bytes_to_write -= bytes_written; p += bytes_written; } while (bytes_to_write > 0); } return 0; } static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name) { __do_close_prot_errno int execfd = -EBADF, fd = -EBADF, memfd = -EBADF, tmpfd = -EBADF; int ret; ssize_t bytes_sent = 0; struct stat st = {0}; memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC); if (memfd < 0) { char template[PATH_MAX]; ret = snprintf(template, sizeof(template), P_tmpdir "/.%s_XXXXXX", memfd_name); if (ret < 0 || (size_t)ret >= sizeof(template)) return; tmpfd = lxc_make_tmpfile(template, true); if (tmpfd < 0) return; ret = fchmod(tmpfd, 0700); if (ret) return; } fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (fd < 0) return; /* sendfile() handles up to 2GB. */ ret = fstat(fd, &st); if (ret) return; while (bytes_sent < st.st_size) { ssize_t sent; sent = lxc_sendfile_nointr(memfd >= 0 ? memfd : tmpfd, fd, NULL, st.st_size - bytes_sent); if (sent < 0) { /* Fallback to shoveling data between kernel- and * userspace. */ lseek(fd, 0, SEEK_SET); if (fd_to_fd(fd, memfd >= 0 ? memfd : tmpfd)) break; return; } bytes_sent += sent; } close_prot_errno_disarm(fd); if (memfd >= 0) { if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) return; execfd = memfd; } else { char procfd[LXC_PROC_PID_FD_LEN]; ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd); if (ret < 0 || (size_t)ret >= sizeof(procfd)) return; execfd = open(procfd, O_PATH | O_CLOEXEC); close_prot_errno_disarm(tmpfd); } if (execfd < 0) return; fexecve(execfd, argv, envp); } /* * Get cheap access to the environment. This must be declared by the user as * mandated by POSIX. The definition is located in unistd.h. */ extern char **environ; int lxc_rexec(const char *memfd_name) { int ret; char **argv = NULL; ret = is_memfd(); if (ret < 0 && ret == -ENOTRECOVERABLE) { fprintf(stderr, "%s - Failed to determine whether this is a memfd\n", strerror(errno)); return -1; } else if (ret > 0) { return 0; } ret = parse_argv(&argv); if (ret < 0) { fprintf(stderr, "%s - Failed to parse command line parameters\n", strerror(errno)); return -1; } lxc_rexec_as_memfd(argv, environ, memfd_name); fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno)); return -1; } /** * This function will copy any binary that calls liblxc into a memory file and * will use the memfd to rexecute the binary. This is done to prevent attacks * through the /proc/self/exe symlink to corrupt the host binary when host and * container are in the same user namespace or have set up an identity id * mapping: CVE-2019-5736. */ __attribute__((constructor)) static void liblxc_rexec(void) { if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) { fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n"); _exit(EXIT_FAILURE); } } lxc-2.0.11/src/lxc/Makefile.in0000644061062106075000000052543513435013507012753 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @IS_BIONIC_TRUE@am__append_1 = \ @IS_BIONIC_TRUE@ ../include/fexecve.h \ @IS_BIONIC_TRUE@ ../include/getgrgid_r.h \ @IS_BIONIC_TRUE@ ../include/ifaddrs.h \ @IS_BIONIC_TRUE@ ../include/openpty.h \ @IS_BIONIC_TRUE@ ../include/lxcmntent.h @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_2 = ../include/getline.h @HAVE_GETSUBOPT_FALSE@am__append_3 = ../include/getsubopt.h @ENABLE_APPARMOR_TRUE@am__append_4 = lsm/apparmor.c @ENABLE_SELINUX_TRUE@am__append_5 = lsm/selinux.c @ENABLE_CGMANAGER_TRUE@am__append_6 = cgroups/cgmanager.c @IS_BIONIC_TRUE@am__append_7 = \ @IS_BIONIC_TRUE@ ../include/fexecve.c ../include/fexecve.h \ @IS_BIONIC_TRUE@ ../include/getgrgid_r.c ../include/getgrgid_r.h \ @IS_BIONIC_TRUE@ ../include/ifaddrs.c ../include/ifaddrs.h \ @IS_BIONIC_TRUE@ ../include/openpty.c ../include/openpty.h \ @IS_BIONIC_TRUE@ ../include/lxcmntent.c ../include/lxcmntent.h @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_8 = ../include/getline.c ../include/getline.h @HAVE_STRLCPY_FALSE@am__append_9 = ../include/strlcpy.c ../include/strlcpy.h @HAVE_STRLCAT_FALSE@am__append_10 = ../include/strlcat.c ../include/strlcat.h @ENFORCE_MEMFD_REXEC_TRUE@am__append_11 = rexec.c rexec.h @ENABLE_APPARMOR_TRUE@am__append_12 = -DHAVE_APPARMOR @ENABLE_CGMANAGER_TRUE@am__append_13 = -DHAVE_CGMANAGER @ENABLE_SELINUX_TRUE@am__append_14 = -DHAVE_SELINUX @USE_CONFIGPATH_LOGS_TRUE@am__append_15 = -DUSE_CONFIGPATH_LOGS @ENABLE_SECCOMP_TRUE@am__append_16 = -DHAVE_SECCOMP $(SECCOMP_CFLAGS) @ENABLE_SECCOMP_TRUE@am__append_17 = seccomp.c @ENABLE_CGMANAGER_TRUE@am__append_18 = $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) @ENABLE_CGMANAGER_TRUE@am__append_19 = $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) @ENABLE_DEPRECATED_TRUE@@ENABLE_PYTHON_TRUE@am__append_20 = tools/lxc-start-ephemeral bin_PROGRAMS = lxc-attach$(EXEEXT) lxc-autostart$(EXEEXT) \ lxc-cgroup$(EXEEXT) lxc-checkpoint$(EXEEXT) lxc-copy$(EXEEXT) \ lxc-config$(EXEEXT) lxc-console$(EXEEXT) lxc-create$(EXEEXT) \ lxc-destroy$(EXEEXT) lxc-device$(EXEEXT) lxc-execute$(EXEEXT) \ lxc-freeze$(EXEEXT) lxc-info$(EXEEXT) lxc-ls$(EXEEXT) \ lxc-monitor$(EXEEXT) lxc-snapshot$(EXEEXT) lxc-start$(EXEEXT) \ lxc-stop$(EXEEXT) lxc-top$(EXEEXT) lxc-unfreeze$(EXEEXT) \ lxc-unshare$(EXEEXT) lxc-usernsexec$(EXEEXT) lxc-wait$(EXEEXT) \ $(am__EXEEXT_1) @ENABLE_DEPRECATED_TRUE@am__append_21 = lxc-clone sbin_PROGRAMS = init.lxc$(EXEEXT) $(am__EXEEXT_2) pkglibexec_PROGRAMS = lxc-monitord$(EXEEXT) lxc-user-nic$(EXEEXT) @ENABLE_RPATH_TRUE@am__append_22 = -Wl,-rpath -Wl,$(libdir) @HAVE_GETSUBOPT_FALSE@am__append_23 = ../include/getsubopt.c ../include/getsubopt.h @HAVE_STATIC_LIBCAP_TRUE@am__append_24 = init.lxc.static @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__append_25 = ../include/getline.c @HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCPY_FALSE@am__append_26 = ../include/strlcpy.c ../include/strlcpy.h @HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCAT_FALSE@am__append_27 = ../include/strlcat.c ../include/strlcat.h subdir = src/lxc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/libtool.m4 \ $(top_srcdir)/config/ltoptions.m4 \ $(top_srcdir)/config/ltsugar.m4 \ $(top_srcdir)/config/ltversion.m4 \ $(top_srcdir)/config/lt~obsolete.m4 \ $(top_srcdir)/config/tls.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__noinst_HEADERS_DIST) \ $(pkginclude_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = lxc.functions version.h CONFIG_CLEAN_VPATH_FILES = @ENABLE_DEPRECATED_TRUE@am__EXEEXT_1 = lxc-clone$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgincludedir)" @HAVE_STATIC_LIBCAP_TRUE@am__EXEEXT_2 = init.lxc.static$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(pkglibexec_PROGRAMS) $(sbin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = @ENABLE_CGMANAGER_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ @ENABLE_CGMANAGER_TRUE@ $(am__DEPENDENCIES_1) \ @ENABLE_CGMANAGER_TRUE@ $(am__DEPENDENCIES_1) \ @ENABLE_CGMANAGER_TRUE@ $(am__DEPENDENCIES_1) liblxc_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am__liblxc_la_SOURCES_DIST = storage/storage.c storage/storage.h \ storage/aufs.c storage/aufs.h storage/btrfs.c storage/btrfs.h \ storage/dir.c storage/dir.h storage/loop.c storage/loop.h \ storage/lvm.c storage/lvm.h storage/nbd.c storage/nbd.h \ storage/overlay.c storage/overlay.h storage/rbd.c \ storage/rbd.h storage/rsync.c storage/rsync.h storage/zfs.c \ storage/zfs.h storage/storage_utils.c storage/storage_utils.h \ cgroups/cgfs.c cgroups/cgfsng.c cgroups/cgroup_utils.c \ cgroups/cgroup_utils.h cgroups/cgroup.c cgroups/cgroup.h \ commands.c commands.h commands_utils.c commands_utils.h \ start.c start.h execute.c monitor.c monitor.h console.c \ freezer.c error.h error.c parse.c parse.h lxc.h initutils.c \ initutils.h utils.c utils.h sync.c sync.h namespace.h \ namespace.c conf.c conf.h confile.c confile.h confile_utils.c \ confile_utils.h list.h state.c state.h log.c log.h attach.c \ attach.h criu.c criu.h network.c network.h nl.c nl.h rtnl.c \ rtnl.h caps.c caps.h lxcseccomp.h macro.h mainloop.c \ mainloop.h memory_utils.h af_unix.c af_unix.h lxcutmp.c \ lxcutmp.h lxclock.h lxclock.c lxccontainer.c lxccontainer.h \ version.h lsm/nop.c lsm/lsm.h lsm/lsm.c lsm/apparmor.c \ lsm/selinux.c cgroups/cgmanager.c ../include/fexecve.c \ ../include/fexecve.h ../include/getgrgid_r.c \ ../include/getgrgid_r.h ../include/ifaddrs.c \ ../include/ifaddrs.h ../include/openpty.c ../include/openpty.h \ ../include/lxcmntent.c ../include/lxcmntent.h \ ../include/getline.c ../include/getline.h ../include/strlcpy.c \ ../include/strlcpy.h ../include/strlcat.c ../include/strlcat.h \ rexec.c rexec.h seccomp.c am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_APPARMOR_TRUE@am__objects_1 = lsm/liblxc_la-apparmor.lo @ENABLE_SELINUX_TRUE@am__objects_2 = lsm/liblxc_la-selinux.lo am__objects_3 = lsm/liblxc_la-nop.lo lsm/liblxc_la-lsm.lo \ $(am__objects_1) $(am__objects_2) @ENABLE_CGMANAGER_TRUE@am__objects_4 = cgroups/liblxc_la-cgmanager.lo @IS_BIONIC_TRUE@am__objects_5 = ../include/liblxc_la-fexecve.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-getgrgid_r.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-ifaddrs.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-openpty.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-lxcmntent.lo @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__objects_6 = ../include/liblxc_la-getline.lo @HAVE_STRLCPY_FALSE@am__objects_7 = ../include/liblxc_la-strlcpy.lo @HAVE_STRLCAT_FALSE@am__objects_8 = ../include/liblxc_la-strlcat.lo @ENFORCE_MEMFD_REXEC_TRUE@am__objects_9 = liblxc_la-rexec.lo @ENABLE_SECCOMP_TRUE@am__objects_10 = liblxc_la-seccomp.lo am_liblxc_la_OBJECTS = storage/liblxc_la-storage.lo \ storage/liblxc_la-aufs.lo storage/liblxc_la-btrfs.lo \ storage/liblxc_la-dir.lo storage/liblxc_la-loop.lo \ storage/liblxc_la-lvm.lo storage/liblxc_la-nbd.lo \ storage/liblxc_la-overlay.lo storage/liblxc_la-rbd.lo \ storage/liblxc_la-rsync.lo storage/liblxc_la-zfs.lo \ storage/liblxc_la-storage_utils.lo cgroups/liblxc_la-cgfs.lo \ cgroups/liblxc_la-cgfsng.lo cgroups/liblxc_la-cgroup_utils.lo \ cgroups/liblxc_la-cgroup.lo liblxc_la-commands.lo \ liblxc_la-commands_utils.lo liblxc_la-start.lo \ liblxc_la-execute.lo liblxc_la-monitor.lo liblxc_la-console.lo \ liblxc_la-freezer.lo liblxc_la-error.lo liblxc_la-parse.lo \ liblxc_la-initutils.lo liblxc_la-utils.lo liblxc_la-sync.lo \ liblxc_la-namespace.lo liblxc_la-conf.lo liblxc_la-confile.lo \ liblxc_la-confile_utils.lo liblxc_la-state.lo liblxc_la-log.lo \ liblxc_la-attach.lo liblxc_la-criu.lo liblxc_la-network.lo \ liblxc_la-nl.lo liblxc_la-rtnl.lo liblxc_la-caps.lo \ liblxc_la-mainloop.lo liblxc_la-af_unix.lo \ liblxc_la-lxcutmp.lo liblxc_la-lxclock.lo \ liblxc_la-lxccontainer.lo $(am__objects_3) $(am__objects_4) \ $(am__objects_5) $(am__objects_6) $(am__objects_7) \ $(am__objects_8) $(am__objects_9) $(am__objects_10) liblxc_la_OBJECTS = $(am_liblxc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = liblxc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(liblxc_la_CFLAGS) \ $(CFLAGS) $(liblxc_la_LDFLAGS) $(LDFLAGS) -o $@ am_init_lxc_OBJECTS = lxc_init.$(OBJEXT) init_lxc_OBJECTS = $(am_init_lxc_OBJECTS) init_lxc_LDADD = $(LDADD) init_lxc_DEPENDENCIES = liblxc.la am__init_lxc_static_SOURCES_DIST = lxc_init.c error.c log.c \ initutils.c caps.c parse.c namespace.c ../include/getline.c \ ../include/strlcpy.c ../include/strlcpy.h ../include/strlcat.c \ ../include/strlcat.h @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__objects_11 = ../include/init_lxc_static-getline.$(OBJEXT) @HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCPY_FALSE@am__objects_12 = ../include/init_lxc_static-strlcpy.$(OBJEXT) @HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCAT_FALSE@am__objects_13 = ../include/init_lxc_static-strlcat.$(OBJEXT) @HAVE_STATIC_LIBCAP_TRUE@am_init_lxc_static_OBJECTS = \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-lxc_init.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-error.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-log.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-initutils.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-caps.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-parse.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-namespace.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_11) $(am__objects_12) \ @HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_13) init_lxc_static_OBJECTS = $(am_init_lxc_static_OBJECTS) init_lxc_static_DEPENDENCIES = init_lxc_static_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(init_lxc_static_CFLAGS) $(CFLAGS) $(init_lxc_static_LDFLAGS) \ $(LDFLAGS) -o $@ am_lxc_attach_OBJECTS = tools/lxc_attach.$(OBJEXT) \ tools/arguments.$(OBJEXT) rexec.$(OBJEXT) lxc_attach_OBJECTS = $(am_lxc_attach_OBJECTS) lxc_attach_LDADD = $(LDADD) lxc_attach_DEPENDENCIES = liblxc.la am_lxc_autostart_OBJECTS = tools/lxc_autostart.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_autostart_OBJECTS = $(am_lxc_autostart_OBJECTS) lxc_autostart_LDADD = $(LDADD) lxc_autostart_DEPENDENCIES = liblxc.la am_lxc_cgroup_OBJECTS = tools/lxc_cgroup.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_cgroup_OBJECTS = $(am_lxc_cgroup_OBJECTS) lxc_cgroup_LDADD = $(LDADD) lxc_cgroup_DEPENDENCIES = liblxc.la am_lxc_checkpoint_OBJECTS = tools/lxc_checkpoint.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_checkpoint_OBJECTS = $(am_lxc_checkpoint_OBJECTS) lxc_checkpoint_LDADD = $(LDADD) lxc_checkpoint_DEPENDENCIES = liblxc.la am__lxc_clone_SOURCES_DIST = tools/lxc_clone.c tools/arguments.c @ENABLE_DEPRECATED_TRUE@am_lxc_clone_OBJECTS = \ @ENABLE_DEPRECATED_TRUE@ tools/lxc_clone.$(OBJEXT) \ @ENABLE_DEPRECATED_TRUE@ tools/arguments.$(OBJEXT) lxc_clone_OBJECTS = $(am_lxc_clone_OBJECTS) lxc_clone_LDADD = $(LDADD) lxc_clone_DEPENDENCIES = liblxc.la am_lxc_config_OBJECTS = tools/lxc_config.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_config_OBJECTS = $(am_lxc_config_OBJECTS) lxc_config_LDADD = $(LDADD) lxc_config_DEPENDENCIES = liblxc.la am_lxc_console_OBJECTS = tools/lxc_console.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_console_OBJECTS = $(am_lxc_console_OBJECTS) lxc_console_LDADD = $(LDADD) lxc_console_DEPENDENCIES = liblxc.la am__lxc_copy_SOURCES_DIST = tools/lxc_copy.c tools/arguments.c \ ../include/getsubopt.c ../include/getsubopt.h @HAVE_GETSUBOPT_FALSE@am__objects_14 = ../include/getsubopt.$(OBJEXT) am_lxc_copy_OBJECTS = tools/lxc_copy.$(OBJEXT) \ tools/arguments.$(OBJEXT) $(am__objects_14) lxc_copy_OBJECTS = $(am_lxc_copy_OBJECTS) lxc_copy_LDADD = $(LDADD) lxc_copy_DEPENDENCIES = liblxc.la am_lxc_create_OBJECTS = tools/lxc_create.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_create_OBJECTS = $(am_lxc_create_OBJECTS) lxc_create_LDADD = $(LDADD) lxc_create_DEPENDENCIES = liblxc.la am_lxc_destroy_OBJECTS = tools/lxc_destroy.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_destroy_OBJECTS = $(am_lxc_destroy_OBJECTS) lxc_destroy_LDADD = $(LDADD) lxc_destroy_DEPENDENCIES = liblxc.la am_lxc_device_OBJECTS = tools/lxc_device.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_device_OBJECTS = $(am_lxc_device_OBJECTS) lxc_device_LDADD = $(LDADD) lxc_device_DEPENDENCIES = liblxc.la am_lxc_execute_OBJECTS = tools/lxc_execute.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_execute_OBJECTS = $(am_lxc_execute_OBJECTS) lxc_execute_LDADD = $(LDADD) lxc_execute_DEPENDENCIES = liblxc.la am_lxc_freeze_OBJECTS = tools/lxc_freeze.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_freeze_OBJECTS = $(am_lxc_freeze_OBJECTS) lxc_freeze_LDADD = $(LDADD) lxc_freeze_DEPENDENCIES = liblxc.la am_lxc_info_OBJECTS = tools/lxc_info.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_info_OBJECTS = $(am_lxc_info_OBJECTS) lxc_info_LDADD = $(LDADD) lxc_info_DEPENDENCIES = liblxc.la am_lxc_ls_OBJECTS = tools/lxc_ls.$(OBJEXT) tools/arguments.$(OBJEXT) lxc_ls_OBJECTS = $(am_lxc_ls_OBJECTS) lxc_ls_LDADD = $(LDADD) lxc_ls_DEPENDENCIES = liblxc.la am_lxc_monitor_OBJECTS = tools/lxc_monitor.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_monitor_OBJECTS = $(am_lxc_monitor_OBJECTS) lxc_monitor_LDADD = $(LDADD) lxc_monitor_DEPENDENCIES = liblxc.la am_lxc_monitord_OBJECTS = lxc_monitord.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_monitord_OBJECTS = $(am_lxc_monitord_OBJECTS) lxc_monitord_LDADD = $(LDADD) lxc_monitord_DEPENDENCIES = liblxc.la am_lxc_snapshot_OBJECTS = tools/lxc_snapshot.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_snapshot_OBJECTS = $(am_lxc_snapshot_OBJECTS) lxc_snapshot_LDADD = $(LDADD) lxc_snapshot_DEPENDENCIES = liblxc.la am_lxc_start_OBJECTS = tools/lxc_start.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_start_OBJECTS = $(am_lxc_start_OBJECTS) lxc_start_LDADD = $(LDADD) lxc_start_DEPENDENCIES = liblxc.la am_lxc_stop_OBJECTS = tools/lxc_stop.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_stop_OBJECTS = $(am_lxc_stop_OBJECTS) lxc_stop_LDADD = $(LDADD) lxc_stop_DEPENDENCIES = liblxc.la am_lxc_top_OBJECTS = tools/lxc_top.$(OBJEXT) tools/arguments.$(OBJEXT) lxc_top_OBJECTS = $(am_lxc_top_OBJECTS) lxc_top_LDADD = $(LDADD) lxc_top_DEPENDENCIES = liblxc.la am_lxc_unfreeze_OBJECTS = tools/lxc_unfreeze.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_unfreeze_OBJECTS = $(am_lxc_unfreeze_OBJECTS) lxc_unfreeze_LDADD = $(LDADD) lxc_unfreeze_DEPENDENCIES = liblxc.la am_lxc_unshare_OBJECTS = tools/lxc_unshare.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_unshare_OBJECTS = $(am_lxc_unshare_OBJECTS) lxc_unshare_LDADD = $(LDADD) lxc_unshare_DEPENDENCIES = liblxc.la am_lxc_user_nic_OBJECTS = lxc_user_nic.$(OBJEXT) namespace.$(OBJEXT) \ network.$(OBJEXT) tools/arguments.$(OBJEXT) lxc_user_nic_OBJECTS = $(am_lxc_user_nic_OBJECTS) lxc_user_nic_LDADD = $(LDADD) lxc_user_nic_DEPENDENCIES = liblxc.la am_lxc_usernsexec_OBJECTS = tools/lxc_usernsexec.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_usernsexec_OBJECTS = $(am_lxc_usernsexec_OBJECTS) lxc_usernsexec_LDADD = $(LDADD) lxc_usernsexec_DEPENDENCIES = liblxc.la am_lxc_wait_OBJECTS = tools/lxc_wait.$(OBJEXT) \ tools/arguments.$(OBJEXT) lxc_wait_OBJECTS = $(am_lxc_wait_OBJECTS) lxc_wait_LDADD = $(LDADD) lxc_wait_DEPENDENCIES = liblxc.la SCRIPTS = $(bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ../include/$(DEPDIR)/getsubopt.Po \ ../include/$(DEPDIR)/init_lxc_static-getline.Po \ ../include/$(DEPDIR)/init_lxc_static-strlcat.Po \ ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po \ ../include/$(DEPDIR)/liblxc_la-fexecve.Plo \ ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo \ ../include/$(DEPDIR)/liblxc_la-getline.Plo \ ../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo \ ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo \ ../include/$(DEPDIR)/liblxc_la-openpty.Plo \ ../include/$(DEPDIR)/liblxc_la-strlcat.Plo \ ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo \ ./$(DEPDIR)/init_lxc_static-caps.Po \ ./$(DEPDIR)/init_lxc_static-error.Po \ ./$(DEPDIR)/init_lxc_static-initutils.Po \ ./$(DEPDIR)/init_lxc_static-log.Po \ ./$(DEPDIR)/init_lxc_static-lxc_init.Po \ ./$(DEPDIR)/init_lxc_static-namespace.Po \ ./$(DEPDIR)/init_lxc_static-parse.Po \ ./$(DEPDIR)/liblxc_la-af_unix.Plo \ ./$(DEPDIR)/liblxc_la-attach.Plo \ ./$(DEPDIR)/liblxc_la-caps.Plo \ ./$(DEPDIR)/liblxc_la-commands.Plo \ ./$(DEPDIR)/liblxc_la-commands_utils.Plo \ ./$(DEPDIR)/liblxc_la-conf.Plo \ ./$(DEPDIR)/liblxc_la-confile.Plo \ ./$(DEPDIR)/liblxc_la-confile_utils.Plo \ ./$(DEPDIR)/liblxc_la-console.Plo \ ./$(DEPDIR)/liblxc_la-criu.Plo ./$(DEPDIR)/liblxc_la-error.Plo \ ./$(DEPDIR)/liblxc_la-execute.Plo \ ./$(DEPDIR)/liblxc_la-freezer.Plo \ ./$(DEPDIR)/liblxc_la-initutils.Plo \ ./$(DEPDIR)/liblxc_la-log.Plo \ ./$(DEPDIR)/liblxc_la-lxccontainer.Plo \ ./$(DEPDIR)/liblxc_la-lxclock.Plo \ ./$(DEPDIR)/liblxc_la-lxcutmp.Plo \ ./$(DEPDIR)/liblxc_la-mainloop.Plo \ ./$(DEPDIR)/liblxc_la-monitor.Plo \ ./$(DEPDIR)/liblxc_la-namespace.Plo \ ./$(DEPDIR)/liblxc_la-network.Plo ./$(DEPDIR)/liblxc_la-nl.Plo \ ./$(DEPDIR)/liblxc_la-parse.Plo \ ./$(DEPDIR)/liblxc_la-rexec.Plo ./$(DEPDIR)/liblxc_la-rtnl.Plo \ ./$(DEPDIR)/liblxc_la-seccomp.Plo \ ./$(DEPDIR)/liblxc_la-start.Plo \ ./$(DEPDIR)/liblxc_la-state.Plo ./$(DEPDIR)/liblxc_la-sync.Plo \ ./$(DEPDIR)/liblxc_la-utils.Plo ./$(DEPDIR)/lxc_init.Po \ ./$(DEPDIR)/lxc_monitord.Po ./$(DEPDIR)/lxc_user_nic.Po \ ./$(DEPDIR)/namespace.Po ./$(DEPDIR)/network.Po \ ./$(DEPDIR)/rexec.Po cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo \ lsm/$(DEPDIR)/liblxc_la-apparmor.Plo \ lsm/$(DEPDIR)/liblxc_la-lsm.Plo \ lsm/$(DEPDIR)/liblxc_la-nop.Plo \ lsm/$(DEPDIR)/liblxc_la-selinux.Plo \ storage/$(DEPDIR)/liblxc_la-aufs.Plo \ storage/$(DEPDIR)/liblxc_la-btrfs.Plo \ storage/$(DEPDIR)/liblxc_la-dir.Plo \ storage/$(DEPDIR)/liblxc_la-loop.Plo \ storage/$(DEPDIR)/liblxc_la-lvm.Plo \ storage/$(DEPDIR)/liblxc_la-nbd.Plo \ storage/$(DEPDIR)/liblxc_la-overlay.Plo \ storage/$(DEPDIR)/liblxc_la-rbd.Plo \ storage/$(DEPDIR)/liblxc_la-rsync.Plo \ storage/$(DEPDIR)/liblxc_la-storage.Plo \ storage/$(DEPDIR)/liblxc_la-storage_utils.Plo \ storage/$(DEPDIR)/liblxc_la-zfs.Plo \ tools/$(DEPDIR)/arguments.Po tools/$(DEPDIR)/lxc_attach.Po \ tools/$(DEPDIR)/lxc_autostart.Po tools/$(DEPDIR)/lxc_cgroup.Po \ tools/$(DEPDIR)/lxc_checkpoint.Po tools/$(DEPDIR)/lxc_clone.Po \ tools/$(DEPDIR)/lxc_config.Po tools/$(DEPDIR)/lxc_console.Po \ tools/$(DEPDIR)/lxc_copy.Po tools/$(DEPDIR)/lxc_create.Po \ tools/$(DEPDIR)/lxc_destroy.Po tools/$(DEPDIR)/lxc_device.Po \ tools/$(DEPDIR)/lxc_execute.Po tools/$(DEPDIR)/lxc_freeze.Po \ tools/$(DEPDIR)/lxc_info.Po tools/$(DEPDIR)/lxc_ls.Po \ tools/$(DEPDIR)/lxc_monitor.Po tools/$(DEPDIR)/lxc_snapshot.Po \ tools/$(DEPDIR)/lxc_start.Po tools/$(DEPDIR)/lxc_stop.Po \ tools/$(DEPDIR)/lxc_top.Po tools/$(DEPDIR)/lxc_unfreeze.Po \ tools/$(DEPDIR)/lxc_unshare.Po \ tools/$(DEPDIR)/lxc_usernsexec.Po tools/$(DEPDIR)/lxc_wait.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(liblxc_la_SOURCES) $(init_lxc_SOURCES) \ $(init_lxc_static_SOURCES) $(lxc_attach_SOURCES) \ $(lxc_autostart_SOURCES) $(lxc_cgroup_SOURCES) \ $(lxc_checkpoint_SOURCES) $(lxc_clone_SOURCES) \ $(lxc_config_SOURCES) $(lxc_console_SOURCES) \ $(lxc_copy_SOURCES) $(lxc_create_SOURCES) \ $(lxc_destroy_SOURCES) $(lxc_device_SOURCES) \ $(lxc_execute_SOURCES) $(lxc_freeze_SOURCES) \ $(lxc_info_SOURCES) $(lxc_ls_SOURCES) $(lxc_monitor_SOURCES) \ $(lxc_monitord_SOURCES) $(lxc_snapshot_SOURCES) \ $(lxc_start_SOURCES) $(lxc_stop_SOURCES) $(lxc_top_SOURCES) \ $(lxc_unfreeze_SOURCES) $(lxc_unshare_SOURCES) \ $(lxc_user_nic_SOURCES) $(lxc_usernsexec_SOURCES) \ $(lxc_wait_SOURCES) DIST_SOURCES = $(am__liblxc_la_SOURCES_DIST) $(init_lxc_SOURCES) \ $(am__init_lxc_static_SOURCES_DIST) $(lxc_attach_SOURCES) \ $(lxc_autostart_SOURCES) $(lxc_cgroup_SOURCES) \ $(lxc_checkpoint_SOURCES) $(am__lxc_clone_SOURCES_DIST) \ $(lxc_config_SOURCES) $(lxc_console_SOURCES) \ $(am__lxc_copy_SOURCES_DIST) $(lxc_create_SOURCES) \ $(lxc_destroy_SOURCES) $(lxc_device_SOURCES) \ $(lxc_execute_SOURCES) $(lxc_freeze_SOURCES) \ $(lxc_info_SOURCES) $(lxc_ls_SOURCES) $(lxc_monitor_SOURCES) \ $(lxc_monitord_SOURCES) $(lxc_snapshot_SOURCES) \ $(lxc_start_SOURCES) $(lxc_stop_SOURCES) $(lxc_top_SOURCES) \ $(lxc_unfreeze_SOURCES) $(lxc_unshare_SOURCES) \ $(lxc_user_nic_SOURCES) $(lxc_usernsexec_SOURCES) \ $(lxc_wait_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__noinst_HEADERS_DIST = tools/arguments.h attach.h storage/storage.h \ storage/aufs.h storage/btrfs.h storage/dir.h storage/loop.h \ storage/lvm.h storage/nbd.h storage/overlay.h storage/rbd.h \ storage/rsync.h storage/zfs.h storage/storage_utils.h \ cgroups/cgroup.h cgroups/cgroup_utils.h caps.h conf.h \ confile.h confile_utils.h console.h error.h initutils.h list.h \ log.h lxc.h lxclock.h macro.h memory_utils.h monitor.h \ namespace.h rexec.h start.h state.h utils.h criu.h \ ../tests/lxctest.h ../include/fexecve.h \ ../include/getgrgid_r.h ../include/ifaddrs.h \ ../include/openpty.h ../include/lxcmntent.h \ ../include/getline.h ../include/getsubopt.h HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/lxc.functions.in \ $(srcdir)/version.h.in $(top_srcdir)/config/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CAP_LIBS = @CAP_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CGMANAGER_CFLAGS = @CGMANAGER_CFLAGS@ CGMANAGER_LIBS = @CGMANAGER_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIR = @DATADIR@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ DEFAULT_CGROUP_PATTERN = @DEFAULT_CGROUP_PATTERN@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOCDIR = @DOCDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ HAVE_DOXYGEN = @HAVE_DOXYGEN@ INCLUDEDIR = @INCLUDEDIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBDIR = @LUA_LIBDIR@ LUA_LIBS = @LUA_LIBS@ LUA_SHAREDIR = @LUA_SHAREDIR@ LUA_VERSION = @LUA_VERSION@ LXCBINHOOKDIR = @LXCBINHOOKDIR@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_ABI = @LXC_ABI@ LXC_ABI_MAJOR = @LXC_ABI_MAJOR@ LXC_ABI_MICRO = @LXC_ABI_MICRO@ LXC_ABI_MINOR = @LXC_ABI_MINOR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ LXC_DEVEL = @LXC_DEVEL@ LXC_DISTRO_SYSCONF = @LXC_DISTRO_SYSCONF@ LXC_GENERATE_DATE = @LXC_GENERATE_DATE@ LXC_GLOBAL_CONF = @LXC_GLOBAL_CONF@ LXC_USERNIC_CONF = @LXC_USERNIC_CONF@ LXC_USERNIC_DB = @LXC_USERNIC_DB@ LXC_VERSION = @LXC_VERSION@ LXC_VERSION_BASE = @LXC_VERSION_BASE@ LXC_VERSION_BETA = @LXC_VERSION_BETA@ LXC_VERSION_MAJOR = @LXC_VERSION_MAJOR@ LXC_VERSION_MICRO = @LXC_VERSION_MICRO@ LXC_VERSION_MINOR = @LXC_VERSION_MINOR@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NIH_CFLAGS = @NIH_CFLAGS@ NIH_DBUS_CFLAGS = @NIH_DBUS_CFLAGS@ NIH_DBUS_LIBS = @NIH_DBUS_LIBS@ NIH_LIBS = @NIH_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PREFIX = @PREFIX@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHONDEV_CFLAGS = @PYTHONDEV_CFLAGS@ PYTHONDEV_LIBS = @PYTHONDEV_LIBS@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ RUNTIME_PATH = @RUNTIME_PATH@ SBINDIR = @SBINDIR@ SECCOMP_CFLAGS = @SECCOMP_CFLAGS@ SECCOMP_LIBS = @SECCOMP_LIBS@ SED = @SED@ SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSCONFDIR = @SYSCONFDIR@ SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bashcompdir = @bashcompdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ db2xman = @db2xman@ docdir = @docdir@ docdtd = @docdtd@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkginclude_HEADERS = \ attach_options.h \ lxccontainer.h \ version.h noinst_HEADERS = tools/arguments.h attach.h storage/storage.h \ storage/aufs.h storage/btrfs.h storage/dir.h storage/loop.h \ storage/lvm.h storage/nbd.h storage/overlay.h storage/rbd.h \ storage/rsync.h storage/zfs.h storage/storage_utils.h \ cgroups/cgroup.h cgroups/cgroup_utils.h caps.h conf.h \ confile.h confile_utils.h console.h error.h initutils.h list.h \ log.h lxc.h lxclock.h macro.h memory_utils.h monitor.h \ namespace.h rexec.h start.h state.h utils.h criu.h \ ../tests/lxctest.h $(am__append_1) $(am__append_2) \ $(am__append_3) sodir = $(libdir) LSM_SOURCES = lsm/nop.c lsm/lsm.h lsm/lsm.c $(am__append_4) \ $(am__append_5) lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = storage/storage.c storage/storage.h storage/aufs.c \ storage/aufs.h storage/btrfs.c storage/btrfs.h storage/dir.c \ storage/dir.h storage/loop.c storage/loop.h storage/lvm.c \ storage/lvm.h storage/nbd.c storage/nbd.h storage/overlay.c \ storage/overlay.h storage/rbd.c storage/rbd.h storage/rsync.c \ storage/rsync.h storage/zfs.c storage/zfs.h \ storage/storage_utils.c storage/storage_utils.h cgroups/cgfs.c \ cgroups/cgfsng.c cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ cgroups/cgroup.c cgroups/cgroup.h commands.c commands.h \ commands_utils.c commands_utils.h start.c start.h execute.c \ monitor.c monitor.h console.c freezer.c error.h error.c \ parse.c parse.h lxc.h initutils.c initutils.h utils.c utils.h \ sync.c sync.h namespace.h namespace.c conf.c conf.h confile.c \ confile.h confile_utils.c confile_utils.h list.h state.c \ state.h log.c log.h attach.c attach.h criu.c criu.h network.c \ network.h nl.c nl.h rtnl.c rtnl.h caps.c caps.h lxcseccomp.h \ macro.h mainloop.c mainloop.h memory_utils.h af_unix.c \ af_unix.h lxcutmp.c lxcutmp.h lxclock.h lxclock.c \ lxccontainer.c lxccontainer.h version.h $(LSM_SOURCES) \ $(am__append_6) $(am__append_7) $(am__append_8) \ $(am__append_9) $(am__append_10) $(am__append_11) \ $(am__append_17) AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ -DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" -DSBINDIR=\"$(SBINDIR)\" \ -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" -I \ $(top_srcdir)/src -I $(top_srcdir)/src/lxc -I \ $(top_srcdir)/src/lxc/storage -I $(top_srcdir)/src/lxc/cgroups \ $(am__append_12) $(am__append_13) $(am__append_14) \ $(am__append_15) $(am__append_16) liblxc_la_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) -pthread $(am__append_19) liblxc_la_LDFLAGS = \ -pthread \ -shared \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) $(SELINUX_LIBS) $(SECCOMP_LIBS) \ $(am__append_18) bin_SCRIPTS = tools/lxc-checkconfig $(am__append_20) EXTRA_DIST = \ tools/lxc-top.lua AM_LDFLAGS = -Wl,-E $(am__append_22) LDADD = liblxc.la @CAP_LIBS@ @SELINUX_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = tools/lxc_attach.c tools/arguments.c rexec.c rexec.h lxc_autostart_SOURCES = tools/lxc_autostart.c tools/arguments.c lxc_cgroup_SOURCES = tools/lxc_cgroup.c tools/arguments.c lxc_config_SOURCES = tools/lxc_config.c tools/arguments.c lxc_console_SOURCES = tools/lxc_console.c tools/arguments.c lxc_destroy_SOURCES = tools/lxc_destroy.c tools/arguments.c lxc_device_SOURCES = tools/lxc_device.c tools/arguments.c lxc_execute_SOURCES = tools/lxc_execute.c tools/arguments.c lxc_freeze_SOURCES = tools/lxc_freeze.c tools/arguments.c lxc_info_SOURCES = tools/lxc_info.c tools/arguments.c init_lxc_SOURCES = lxc_init.c lxc_monitor_SOURCES = tools/lxc_monitor.c tools/arguments.c lxc_ls_SOURCES = tools/lxc_ls.c tools/arguments.c lxc_copy_SOURCES = tools/lxc_copy.c tools/arguments.c $(am__append_23) lxc_start_SOURCES = tools/lxc_start.c tools/arguments.c lxc_stop_SOURCES = tools/lxc_stop.c tools/arguments.c lxc_top_SOURCES = tools/lxc_top.c tools/arguments.c lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c tools/arguments.c lxc_unshare_SOURCES = tools/lxc_unshare.c tools/arguments.c lxc_wait_SOURCES = tools/lxc_wait.c tools/arguments.c lxc_create_SOURCES = tools/lxc_create.c tools/arguments.c lxc_snapshot_SOURCES = tools/lxc_snapshot.c tools/arguments.c lxc_usernsexec_SOURCES = tools/lxc_usernsexec.c tools/arguments.c lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c tools/arguments.c lxc_user_nic_SOURCES = lxc_user_nic.c namespace.c network.c tools/arguments.c lxc_monitord_SOURCES = lxc_monitord.c tools/arguments.c @ENABLE_DEPRECATED_TRUE@lxc_clone_SOURCES = tools/lxc_clone.c tools/arguments.c @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_SOURCES = lxc_init.c error.c \ @HAVE_STATIC_LIBCAP_TRUE@ log.c initutils.c caps.c parse.c \ @HAVE_STATIC_LIBCAP_TRUE@ namespace.c $(am__append_25) \ @HAVE_STATIC_LIBCAP_TRUE@ $(am__append_26) $(am__append_27) @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDFLAGS = -all-static @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDADD = @CAP_LIBS@ @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/lxc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/lxc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): lxc.functions: $(top_builddir)/config.status $(srcdir)/lxc.functions.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ version.h: $(top_builddir)/config.status $(srcdir)/version.h.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } storage/$(am__dirstamp): @$(MKDIR_P) storage @: > storage/$(am__dirstamp) storage/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) storage/$(DEPDIR) @: > storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-storage.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-aufs.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-btrfs.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-dir.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-loop.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-lvm.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-nbd.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-overlay.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-rbd.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-rsync.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-zfs.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-storage_utils.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) cgroups/$(am__dirstamp): @$(MKDIR_P) cgroups @: > cgroups/$(am__dirstamp) cgroups/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) cgroups/$(DEPDIR) @: > cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgfs.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgfsng.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgroup_utils.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgroup.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) lsm/$(am__dirstamp): @$(MKDIR_P) lsm @: > lsm/$(am__dirstamp) lsm/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lsm/$(DEPDIR) @: > lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-nop.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-lsm.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-apparmor.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-selinux.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgmanager.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) ../include/$(am__dirstamp): @$(MKDIR_P) ../include @: > ../include/$(am__dirstamp) ../include/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../include/$(DEPDIR) @: > ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-fexecve.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-getgrgid_r.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-ifaddrs.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-openpty.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-lxcmntent.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-getline.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-strlcpy.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-strlcat.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) liblxc.la: $(liblxc_la_OBJECTS) $(liblxc_la_DEPENDENCIES) $(EXTRA_liblxc_la_DEPENDENCIES) $(AM_V_CCLD)$(liblxc_la_LINK) -rpath $(libdir) $(liblxc_la_OBJECTS) $(liblxc_la_LIBADD) $(LIBS) init.lxc$(EXEEXT): $(init_lxc_OBJECTS) $(init_lxc_DEPENDENCIES) $(EXTRA_init_lxc_DEPENDENCIES) @rm -f init.lxc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(init_lxc_OBJECTS) $(init_lxc_LDADD) $(LIBS) ../include/init_lxc_static-getline.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/init_lxc_static-strlcpy.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/init_lxc_static-strlcat.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) init.lxc.static$(EXEEXT): $(init_lxc_static_OBJECTS) $(init_lxc_static_DEPENDENCIES) $(EXTRA_init_lxc_static_DEPENDENCIES) @rm -f init.lxc.static$(EXEEXT) $(AM_V_CCLD)$(init_lxc_static_LINK) $(init_lxc_static_OBJECTS) $(init_lxc_static_LDADD) $(LIBS) tools/$(am__dirstamp): @$(MKDIR_P) tools @: > tools/$(am__dirstamp) tools/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/$(DEPDIR) @: > tools/$(DEPDIR)/$(am__dirstamp) tools/lxc_attach.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/arguments.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-attach$(EXEEXT): $(lxc_attach_OBJECTS) $(lxc_attach_DEPENDENCIES) $(EXTRA_lxc_attach_DEPENDENCIES) @rm -f lxc-attach$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_attach_OBJECTS) $(lxc_attach_LDADD) $(LIBS) tools/lxc_autostart.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-autostart$(EXEEXT): $(lxc_autostart_OBJECTS) $(lxc_autostart_DEPENDENCIES) $(EXTRA_lxc_autostart_DEPENDENCIES) @rm -f lxc-autostart$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_autostart_OBJECTS) $(lxc_autostart_LDADD) $(LIBS) tools/lxc_cgroup.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-cgroup$(EXEEXT): $(lxc_cgroup_OBJECTS) $(lxc_cgroup_DEPENDENCIES) $(EXTRA_lxc_cgroup_DEPENDENCIES) @rm -f lxc-cgroup$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_cgroup_OBJECTS) $(lxc_cgroup_LDADD) $(LIBS) tools/lxc_checkpoint.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-checkpoint$(EXEEXT): $(lxc_checkpoint_OBJECTS) $(lxc_checkpoint_DEPENDENCIES) $(EXTRA_lxc_checkpoint_DEPENDENCIES) @rm -f lxc-checkpoint$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_checkpoint_OBJECTS) $(lxc_checkpoint_LDADD) $(LIBS) tools/lxc_clone.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-clone$(EXEEXT): $(lxc_clone_OBJECTS) $(lxc_clone_DEPENDENCIES) $(EXTRA_lxc_clone_DEPENDENCIES) @rm -f lxc-clone$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_clone_OBJECTS) $(lxc_clone_LDADD) $(LIBS) tools/lxc_config.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-config$(EXEEXT): $(lxc_config_OBJECTS) $(lxc_config_DEPENDENCIES) $(EXTRA_lxc_config_DEPENDENCIES) @rm -f lxc-config$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_config_OBJECTS) $(lxc_config_LDADD) $(LIBS) tools/lxc_console.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-console$(EXEEXT): $(lxc_console_OBJECTS) $(lxc_console_DEPENDENCIES) $(EXTRA_lxc_console_DEPENDENCIES) @rm -f lxc-console$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_console_OBJECTS) $(lxc_console_LDADD) $(LIBS) tools/lxc_copy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) ../include/getsubopt.$(OBJEXT): ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) lxc-copy$(EXEEXT): $(lxc_copy_OBJECTS) $(lxc_copy_DEPENDENCIES) $(EXTRA_lxc_copy_DEPENDENCIES) @rm -f lxc-copy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_copy_OBJECTS) $(lxc_copy_LDADD) $(LIBS) tools/lxc_create.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-create$(EXEEXT): $(lxc_create_OBJECTS) $(lxc_create_DEPENDENCIES) $(EXTRA_lxc_create_DEPENDENCIES) @rm -f lxc-create$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_create_OBJECTS) $(lxc_create_LDADD) $(LIBS) tools/lxc_destroy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-destroy$(EXEEXT): $(lxc_destroy_OBJECTS) $(lxc_destroy_DEPENDENCIES) $(EXTRA_lxc_destroy_DEPENDENCIES) @rm -f lxc-destroy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_destroy_OBJECTS) $(lxc_destroy_LDADD) $(LIBS) tools/lxc_device.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-device$(EXEEXT): $(lxc_device_OBJECTS) $(lxc_device_DEPENDENCIES) $(EXTRA_lxc_device_DEPENDENCIES) @rm -f lxc-device$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_device_OBJECTS) $(lxc_device_LDADD) $(LIBS) tools/lxc_execute.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-execute$(EXEEXT): $(lxc_execute_OBJECTS) $(lxc_execute_DEPENDENCIES) $(EXTRA_lxc_execute_DEPENDENCIES) @rm -f lxc-execute$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_execute_OBJECTS) $(lxc_execute_LDADD) $(LIBS) tools/lxc_freeze.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-freeze$(EXEEXT): $(lxc_freeze_OBJECTS) $(lxc_freeze_DEPENDENCIES) $(EXTRA_lxc_freeze_DEPENDENCIES) @rm -f lxc-freeze$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_freeze_OBJECTS) $(lxc_freeze_LDADD) $(LIBS) tools/lxc_info.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-info$(EXEEXT): $(lxc_info_OBJECTS) $(lxc_info_DEPENDENCIES) $(EXTRA_lxc_info_DEPENDENCIES) @rm -f lxc-info$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_info_OBJECTS) $(lxc_info_LDADD) $(LIBS) tools/lxc_ls.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-ls$(EXEEXT): $(lxc_ls_OBJECTS) $(lxc_ls_DEPENDENCIES) $(EXTRA_lxc_ls_DEPENDENCIES) @rm -f lxc-ls$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_ls_OBJECTS) $(lxc_ls_LDADD) $(LIBS) tools/lxc_monitor.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-monitor$(EXEEXT): $(lxc_monitor_OBJECTS) $(lxc_monitor_DEPENDENCIES) $(EXTRA_lxc_monitor_DEPENDENCIES) @rm -f lxc-monitor$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_monitor_OBJECTS) $(lxc_monitor_LDADD) $(LIBS) lxc-monitord$(EXEEXT): $(lxc_monitord_OBJECTS) $(lxc_monitord_DEPENDENCIES) $(EXTRA_lxc_monitord_DEPENDENCIES) @rm -f lxc-monitord$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_monitord_OBJECTS) $(lxc_monitord_LDADD) $(LIBS) tools/lxc_snapshot.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-snapshot$(EXEEXT): $(lxc_snapshot_OBJECTS) $(lxc_snapshot_DEPENDENCIES) $(EXTRA_lxc_snapshot_DEPENDENCIES) @rm -f lxc-snapshot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_snapshot_OBJECTS) $(lxc_snapshot_LDADD) $(LIBS) tools/lxc_start.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-start$(EXEEXT): $(lxc_start_OBJECTS) $(lxc_start_DEPENDENCIES) $(EXTRA_lxc_start_DEPENDENCIES) @rm -f lxc-start$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_start_OBJECTS) $(lxc_start_LDADD) $(LIBS) tools/lxc_stop.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-stop$(EXEEXT): $(lxc_stop_OBJECTS) $(lxc_stop_DEPENDENCIES) $(EXTRA_lxc_stop_DEPENDENCIES) @rm -f lxc-stop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_stop_OBJECTS) $(lxc_stop_LDADD) $(LIBS) tools/lxc_top.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-top$(EXEEXT): $(lxc_top_OBJECTS) $(lxc_top_DEPENDENCIES) $(EXTRA_lxc_top_DEPENDENCIES) @rm -f lxc-top$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_top_OBJECTS) $(lxc_top_LDADD) $(LIBS) tools/lxc_unfreeze.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-unfreeze$(EXEEXT): $(lxc_unfreeze_OBJECTS) $(lxc_unfreeze_DEPENDENCIES) $(EXTRA_lxc_unfreeze_DEPENDENCIES) @rm -f lxc-unfreeze$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_unfreeze_OBJECTS) $(lxc_unfreeze_LDADD) $(LIBS) tools/lxc_unshare.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-unshare$(EXEEXT): $(lxc_unshare_OBJECTS) $(lxc_unshare_DEPENDENCIES) $(EXTRA_lxc_unshare_DEPENDENCIES) @rm -f lxc-unshare$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_unshare_OBJECTS) $(lxc_unshare_LDADD) $(LIBS) lxc-user-nic$(EXEEXT): $(lxc_user_nic_OBJECTS) $(lxc_user_nic_DEPENDENCIES) $(EXTRA_lxc_user_nic_DEPENDENCIES) @rm -f lxc-user-nic$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_user_nic_OBJECTS) $(lxc_user_nic_LDADD) $(LIBS) tools/lxc_usernsexec.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-usernsexec$(EXEEXT): $(lxc_usernsexec_OBJECTS) $(lxc_usernsexec_DEPENDENCIES) $(EXTRA_lxc_usernsexec_DEPENDENCIES) @rm -f lxc-usernsexec$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_usernsexec_OBJECTS) $(lxc_usernsexec_LDADD) $(LIBS) tools/lxc_wait.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-wait$(EXEEXT): $(lxc_wait_OBJECTS) $(lxc_wait_DEPENDENCIES) $(EXTRA_lxc_wait_DEPENDENCIES) @rm -f lxc-wait$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_wait_OBJECTS) $(lxc_wait_LDADD) $(LIBS) install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../include/*.$(OBJEXT) -rm -f ../include/*.lo -rm -f cgroups/*.$(OBJEXT) -rm -f cgroups/*.lo -rm -f lsm/*.$(OBJEXT) -rm -f lsm/*.lo -rm -f storage/*.$(OBJEXT) -rm -f storage/*.lo -rm -f tools/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/getsubopt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-getline.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-strlcat.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-strlcpy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-fexecve.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-getline.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-openpty.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-strlcat.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-strlcpy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-caps.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-error.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-initutils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-lxc_init.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-namespace.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-parse.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-af_unix.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-attach.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-caps.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-conf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-console.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-criu.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-error.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-execute.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-freezer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-initutils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxccontainer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxclock.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxcutmp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-monitor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-namespace.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-network.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-nl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-parse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-rexec.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-rtnl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-seccomp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-start.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-state.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-sync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc_init.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc_monitord.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc_user_nic.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/namespace.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rexec.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-apparmor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-lsm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-nop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-selinux.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-aufs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-btrfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-dir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-loop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-lvm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-nbd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-overlay.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rbd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rsync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-zfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/arguments.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_attach.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_autostart.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_cgroup.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_checkpoint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_clone.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_console.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_copy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_create.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_destroy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_device.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_execute.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_freeze.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_info.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_ls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_snapshot.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_start.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_stop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_top.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_unfreeze.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_unshare.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_usernsexec.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_wait.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< storage/liblxc_la-storage.lo: storage/storage.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-storage.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-storage.Tpo -c -o storage/liblxc_la-storage.lo `test -f 'storage/storage.c' || echo '$(srcdir)/'`storage/storage.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-storage.Tpo storage/$(DEPDIR)/liblxc_la-storage.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/storage.c' object='storage/liblxc_la-storage.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-storage.lo `test -f 'storage/storage.c' || echo '$(srcdir)/'`storage/storage.c storage/liblxc_la-aufs.lo: storage/aufs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-aufs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-aufs.Tpo -c -o storage/liblxc_la-aufs.lo `test -f 'storage/aufs.c' || echo '$(srcdir)/'`storage/aufs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-aufs.Tpo storage/$(DEPDIR)/liblxc_la-aufs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/aufs.c' object='storage/liblxc_la-aufs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-aufs.lo `test -f 'storage/aufs.c' || echo '$(srcdir)/'`storage/aufs.c storage/liblxc_la-btrfs.lo: storage/btrfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-btrfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-btrfs.Tpo -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-btrfs.Tpo storage/$(DEPDIR)/liblxc_la-btrfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/btrfs.c' object='storage/liblxc_la-btrfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c storage/liblxc_la-dir.lo: storage/dir.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-dir.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-dir.Tpo -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-dir.Tpo storage/$(DEPDIR)/liblxc_la-dir.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/dir.c' object='storage/liblxc_la-dir.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c storage/liblxc_la-loop.lo: storage/loop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-loop.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-loop.Tpo -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-loop.Tpo storage/$(DEPDIR)/liblxc_la-loop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/loop.c' object='storage/liblxc_la-loop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c storage/liblxc_la-lvm.lo: storage/lvm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-lvm.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-lvm.Tpo -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-lvm.Tpo storage/$(DEPDIR)/liblxc_la-lvm.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/lvm.c' object='storage/liblxc_la-lvm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c storage/liblxc_la-nbd.lo: storage/nbd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-nbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-nbd.Tpo -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-nbd.Tpo storage/$(DEPDIR)/liblxc_la-nbd.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/nbd.c' object='storage/liblxc_la-nbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c storage/liblxc_la-overlay.lo: storage/overlay.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-overlay.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-overlay.Tpo -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-overlay.Tpo storage/$(DEPDIR)/liblxc_la-overlay.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/overlay.c' object='storage/liblxc_la-overlay.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c storage/liblxc_la-rbd.lo: storage/rbd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rbd.Tpo -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rbd.Tpo storage/$(DEPDIR)/liblxc_la-rbd.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rbd.c' object='storage/liblxc_la-rbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c storage/liblxc_la-rsync.lo: storage/rsync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rsync.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rsync.Tpo -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rsync.Tpo storage/$(DEPDIR)/liblxc_la-rsync.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rsync.c' object='storage/liblxc_la-rsync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c storage/liblxc_la-zfs.lo: storage/zfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-zfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-zfs.Tpo -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-zfs.Tpo storage/$(DEPDIR)/liblxc_la-zfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/zfs.c' object='storage/liblxc_la-zfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c storage/liblxc_la-storage_utils.lo: storage/storage_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-storage_utils.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo storage/$(DEPDIR)/liblxc_la-storage_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/storage_utils.c' object='storage/liblxc_la-storage_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c cgroups/liblxc_la-cgfs.lo: cgroups/cgfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgfs.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgfs.Tpo -c -o cgroups/liblxc_la-cgfs.lo `test -f 'cgroups/cgfs.c' || echo '$(srcdir)/'`cgroups/cgfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgfs.Tpo cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgfs.c' object='cgroups/liblxc_la-cgfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgfs.lo `test -f 'cgroups/cgfs.c' || echo '$(srcdir)/'`cgroups/cgfs.c cgroups/liblxc_la-cgfsng.lo: cgroups/cgfsng.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgfsng.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgfsng.Tpo -c -o cgroups/liblxc_la-cgfsng.lo `test -f 'cgroups/cgfsng.c' || echo '$(srcdir)/'`cgroups/cgfsng.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgfsng.Tpo cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgfsng.c' object='cgroups/liblxc_la-cgfsng.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgfsng.lo `test -f 'cgroups/cgfsng.c' || echo '$(srcdir)/'`cgroups/cgfsng.c cgroups/liblxc_la-cgroup_utils.lo: cgroups/cgroup_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgroup_utils.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Tpo -c -o cgroups/liblxc_la-cgroup_utils.lo `test -f 'cgroups/cgroup_utils.c' || echo '$(srcdir)/'`cgroups/cgroup_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Tpo cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgroup_utils.c' object='cgroups/liblxc_la-cgroup_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgroup_utils.lo `test -f 'cgroups/cgroup_utils.c' || echo '$(srcdir)/'`cgroups/cgroup_utils.c cgroups/liblxc_la-cgroup.lo: cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgroup.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo -c -o cgroups/liblxc_la-cgroup.lo `test -f 'cgroups/cgroup.c' || echo '$(srcdir)/'`cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgroup.c' object='cgroups/liblxc_la-cgroup.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgroup.lo `test -f 'cgroups/cgroup.c' || echo '$(srcdir)/'`cgroups/cgroup.c liblxc_la-commands.lo: commands.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-commands.lo -MD -MP -MF $(DEPDIR)/liblxc_la-commands.Tpo -c -o liblxc_la-commands.lo `test -f 'commands.c' || echo '$(srcdir)/'`commands.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-commands.Tpo $(DEPDIR)/liblxc_la-commands.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='liblxc_la-commands.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands.lo `test -f 'commands.c' || echo '$(srcdir)/'`commands.c liblxc_la-commands_utils.lo: commands_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-commands_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-commands_utils.Tpo -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-commands_utils.Tpo $(DEPDIR)/liblxc_la-commands_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands_utils.c' object='liblxc_la-commands_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c liblxc_la-start.lo: start.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-start.lo -MD -MP -MF $(DEPDIR)/liblxc_la-start.Tpo -c -o liblxc_la-start.lo `test -f 'start.c' || echo '$(srcdir)/'`start.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-start.Tpo $(DEPDIR)/liblxc_la-start.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='start.c' object='liblxc_la-start.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-start.lo `test -f 'start.c' || echo '$(srcdir)/'`start.c liblxc_la-execute.lo: execute.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-execute.lo -MD -MP -MF $(DEPDIR)/liblxc_la-execute.Tpo -c -o liblxc_la-execute.lo `test -f 'execute.c' || echo '$(srcdir)/'`execute.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-execute.Tpo $(DEPDIR)/liblxc_la-execute.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='execute.c' object='liblxc_la-execute.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-execute.lo `test -f 'execute.c' || echo '$(srcdir)/'`execute.c liblxc_la-monitor.lo: monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-monitor.lo -MD -MP -MF $(DEPDIR)/liblxc_la-monitor.Tpo -c -o liblxc_la-monitor.lo `test -f 'monitor.c' || echo '$(srcdir)/'`monitor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-monitor.Tpo $(DEPDIR)/liblxc_la-monitor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='monitor.c' object='liblxc_la-monitor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-monitor.lo `test -f 'monitor.c' || echo '$(srcdir)/'`monitor.c liblxc_la-console.lo: console.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-console.lo -MD -MP -MF $(DEPDIR)/liblxc_la-console.Tpo -c -o liblxc_la-console.lo `test -f 'console.c' || echo '$(srcdir)/'`console.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-console.Tpo $(DEPDIR)/liblxc_la-console.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='console.c' object='liblxc_la-console.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-console.lo `test -f 'console.c' || echo '$(srcdir)/'`console.c liblxc_la-freezer.lo: freezer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-freezer.lo -MD -MP -MF $(DEPDIR)/liblxc_la-freezer.Tpo -c -o liblxc_la-freezer.lo `test -f 'freezer.c' || echo '$(srcdir)/'`freezer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-freezer.Tpo $(DEPDIR)/liblxc_la-freezer.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='freezer.c' object='liblxc_la-freezer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-freezer.lo `test -f 'freezer.c' || echo '$(srcdir)/'`freezer.c liblxc_la-error.lo: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-error.lo -MD -MP -MF $(DEPDIR)/liblxc_la-error.Tpo -c -o liblxc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-error.Tpo $(DEPDIR)/liblxc_la-error.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='liblxc_la-error.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c liblxc_la-parse.lo: parse.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-parse.lo -MD -MP -MF $(DEPDIR)/liblxc_la-parse.Tpo -c -o liblxc_la-parse.lo `test -f 'parse.c' || echo '$(srcdir)/'`parse.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-parse.Tpo $(DEPDIR)/liblxc_la-parse.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse.c' object='liblxc_la-parse.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-parse.lo `test -f 'parse.c' || echo '$(srcdir)/'`parse.c liblxc_la-initutils.lo: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-initutils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-initutils.Tpo -c -o liblxc_la-initutils.lo `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-initutils.Tpo $(DEPDIR)/liblxc_la-initutils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='liblxc_la-initutils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-initutils.lo `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c liblxc_la-utils.lo: utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-utils.Tpo -c -o liblxc_la-utils.lo `test -f 'utils.c' || echo '$(srcdir)/'`utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-utils.Tpo $(DEPDIR)/liblxc_la-utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='liblxc_la-utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-utils.lo `test -f 'utils.c' || echo '$(srcdir)/'`utils.c liblxc_la-sync.lo: sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-sync.lo -MD -MP -MF $(DEPDIR)/liblxc_la-sync.Tpo -c -o liblxc_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-sync.Tpo $(DEPDIR)/liblxc_la-sync.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sync.c' object='liblxc_la-sync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c liblxc_la-namespace.lo: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-namespace.lo -MD -MP -MF $(DEPDIR)/liblxc_la-namespace.Tpo -c -o liblxc_la-namespace.lo `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-namespace.Tpo $(DEPDIR)/liblxc_la-namespace.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='liblxc_la-namespace.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-namespace.lo `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c liblxc_la-conf.lo: conf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-conf.lo -MD -MP -MF $(DEPDIR)/liblxc_la-conf.Tpo -c -o liblxc_la-conf.lo `test -f 'conf.c' || echo '$(srcdir)/'`conf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-conf.Tpo $(DEPDIR)/liblxc_la-conf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='liblxc_la-conf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-conf.lo `test -f 'conf.c' || echo '$(srcdir)/'`conf.c liblxc_la-confile.lo: confile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile.Tpo -c -o liblxc_la-confile.lo `test -f 'confile.c' || echo '$(srcdir)/'`confile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile.Tpo $(DEPDIR)/liblxc_la-confile.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile.c' object='liblxc_la-confile.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile.lo `test -f 'confile.c' || echo '$(srcdir)/'`confile.c liblxc_la-confile_utils.lo: confile_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile_utils.Tpo -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile_utils.Tpo $(DEPDIR)/liblxc_la-confile_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile_utils.c' object='liblxc_la-confile_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c liblxc_la-state.lo: state.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-state.lo -MD -MP -MF $(DEPDIR)/liblxc_la-state.Tpo -c -o liblxc_la-state.lo `test -f 'state.c' || echo '$(srcdir)/'`state.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-state.Tpo $(DEPDIR)/liblxc_la-state.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='state.c' object='liblxc_la-state.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-state.lo `test -f 'state.c' || echo '$(srcdir)/'`state.c liblxc_la-log.lo: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-log.lo -MD -MP -MF $(DEPDIR)/liblxc_la-log.Tpo -c -o liblxc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-log.Tpo $(DEPDIR)/liblxc_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='liblxc_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c liblxc_la-attach.lo: attach.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-attach.lo -MD -MP -MF $(DEPDIR)/liblxc_la-attach.Tpo -c -o liblxc_la-attach.lo `test -f 'attach.c' || echo '$(srcdir)/'`attach.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-attach.Tpo $(DEPDIR)/liblxc_la-attach.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attach.c' object='liblxc_la-attach.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-attach.lo `test -f 'attach.c' || echo '$(srcdir)/'`attach.c liblxc_la-criu.lo: criu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-criu.lo -MD -MP -MF $(DEPDIR)/liblxc_la-criu.Tpo -c -o liblxc_la-criu.lo `test -f 'criu.c' || echo '$(srcdir)/'`criu.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-criu.Tpo $(DEPDIR)/liblxc_la-criu.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='criu.c' object='liblxc_la-criu.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-criu.lo `test -f 'criu.c' || echo '$(srcdir)/'`criu.c liblxc_la-network.lo: network.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-network.lo -MD -MP -MF $(DEPDIR)/liblxc_la-network.Tpo -c -o liblxc_la-network.lo `test -f 'network.c' || echo '$(srcdir)/'`network.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-network.Tpo $(DEPDIR)/liblxc_la-network.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network.c' object='liblxc_la-network.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-network.lo `test -f 'network.c' || echo '$(srcdir)/'`network.c liblxc_la-nl.lo: nl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-nl.lo -MD -MP -MF $(DEPDIR)/liblxc_la-nl.Tpo -c -o liblxc_la-nl.lo `test -f 'nl.c' || echo '$(srcdir)/'`nl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-nl.Tpo $(DEPDIR)/liblxc_la-nl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nl.c' object='liblxc_la-nl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-nl.lo `test -f 'nl.c' || echo '$(srcdir)/'`nl.c liblxc_la-rtnl.lo: rtnl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-rtnl.lo -MD -MP -MF $(DEPDIR)/liblxc_la-rtnl.Tpo -c -o liblxc_la-rtnl.lo `test -f 'rtnl.c' || echo '$(srcdir)/'`rtnl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-rtnl.Tpo $(DEPDIR)/liblxc_la-rtnl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtnl.c' object='liblxc_la-rtnl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-rtnl.lo `test -f 'rtnl.c' || echo '$(srcdir)/'`rtnl.c liblxc_la-caps.lo: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-caps.lo -MD -MP -MF $(DEPDIR)/liblxc_la-caps.Tpo -c -o liblxc_la-caps.lo `test -f 'caps.c' || echo '$(srcdir)/'`caps.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-caps.Tpo $(DEPDIR)/liblxc_la-caps.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='liblxc_la-caps.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-caps.lo `test -f 'caps.c' || echo '$(srcdir)/'`caps.c liblxc_la-mainloop.lo: mainloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-mainloop.lo -MD -MP -MF $(DEPDIR)/liblxc_la-mainloop.Tpo -c -o liblxc_la-mainloop.lo `test -f 'mainloop.c' || echo '$(srcdir)/'`mainloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-mainloop.Tpo $(DEPDIR)/liblxc_la-mainloop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mainloop.c' object='liblxc_la-mainloop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-mainloop.lo `test -f 'mainloop.c' || echo '$(srcdir)/'`mainloop.c liblxc_la-af_unix.lo: af_unix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-af_unix.lo -MD -MP -MF $(DEPDIR)/liblxc_la-af_unix.Tpo -c -o liblxc_la-af_unix.lo `test -f 'af_unix.c' || echo '$(srcdir)/'`af_unix.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-af_unix.Tpo $(DEPDIR)/liblxc_la-af_unix.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='af_unix.c' object='liblxc_la-af_unix.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-af_unix.lo `test -f 'af_unix.c' || echo '$(srcdir)/'`af_unix.c liblxc_la-lxcutmp.lo: lxcutmp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxcutmp.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxcutmp.Tpo -c -o liblxc_la-lxcutmp.lo `test -f 'lxcutmp.c' || echo '$(srcdir)/'`lxcutmp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxcutmp.Tpo $(DEPDIR)/liblxc_la-lxcutmp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxcutmp.c' object='liblxc_la-lxcutmp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxcutmp.lo `test -f 'lxcutmp.c' || echo '$(srcdir)/'`lxcutmp.c liblxc_la-lxclock.lo: lxclock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxclock.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxclock.Tpo -c -o liblxc_la-lxclock.lo `test -f 'lxclock.c' || echo '$(srcdir)/'`lxclock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxclock.Tpo $(DEPDIR)/liblxc_la-lxclock.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxclock.c' object='liblxc_la-lxclock.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxclock.lo `test -f 'lxclock.c' || echo '$(srcdir)/'`lxclock.c liblxc_la-lxccontainer.lo: lxccontainer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxccontainer.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxccontainer.Tpo -c -o liblxc_la-lxccontainer.lo `test -f 'lxccontainer.c' || echo '$(srcdir)/'`lxccontainer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxccontainer.Tpo $(DEPDIR)/liblxc_la-lxccontainer.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxccontainer.c' object='liblxc_la-lxccontainer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxccontainer.lo `test -f 'lxccontainer.c' || echo '$(srcdir)/'`lxccontainer.c lsm/liblxc_la-nop.lo: lsm/nop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-nop.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-nop.Tpo -c -o lsm/liblxc_la-nop.lo `test -f 'lsm/nop.c' || echo '$(srcdir)/'`lsm/nop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-nop.Tpo lsm/$(DEPDIR)/liblxc_la-nop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/nop.c' object='lsm/liblxc_la-nop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-nop.lo `test -f 'lsm/nop.c' || echo '$(srcdir)/'`lsm/nop.c lsm/liblxc_la-lsm.lo: lsm/lsm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-lsm.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-lsm.Tpo -c -o lsm/liblxc_la-lsm.lo `test -f 'lsm/lsm.c' || echo '$(srcdir)/'`lsm/lsm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-lsm.Tpo lsm/$(DEPDIR)/liblxc_la-lsm.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/lsm.c' object='lsm/liblxc_la-lsm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-lsm.lo `test -f 'lsm/lsm.c' || echo '$(srcdir)/'`lsm/lsm.c lsm/liblxc_la-apparmor.lo: lsm/apparmor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-apparmor.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-apparmor.Tpo -c -o lsm/liblxc_la-apparmor.lo `test -f 'lsm/apparmor.c' || echo '$(srcdir)/'`lsm/apparmor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-apparmor.Tpo lsm/$(DEPDIR)/liblxc_la-apparmor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/apparmor.c' object='lsm/liblxc_la-apparmor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-apparmor.lo `test -f 'lsm/apparmor.c' || echo '$(srcdir)/'`lsm/apparmor.c lsm/liblxc_la-selinux.lo: lsm/selinux.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-selinux.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-selinux.Tpo -c -o lsm/liblxc_la-selinux.lo `test -f 'lsm/selinux.c' || echo '$(srcdir)/'`lsm/selinux.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-selinux.Tpo lsm/$(DEPDIR)/liblxc_la-selinux.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/selinux.c' object='lsm/liblxc_la-selinux.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-selinux.lo `test -f 'lsm/selinux.c' || echo '$(srcdir)/'`lsm/selinux.c cgroups/liblxc_la-cgmanager.lo: cgroups/cgmanager.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgmanager.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgmanager.Tpo -c -o cgroups/liblxc_la-cgmanager.lo `test -f 'cgroups/cgmanager.c' || echo '$(srcdir)/'`cgroups/cgmanager.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgmanager.Tpo cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgmanager.c' object='cgroups/liblxc_la-cgmanager.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgmanager.lo `test -f 'cgroups/cgmanager.c' || echo '$(srcdir)/'`cgroups/cgmanager.c ../include/liblxc_la-fexecve.lo: ../include/fexecve.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-fexecve.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-fexecve.Tpo -c -o ../include/liblxc_la-fexecve.lo `test -f '../include/fexecve.c' || echo '$(srcdir)/'`../include/fexecve.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-fexecve.Tpo ../include/$(DEPDIR)/liblxc_la-fexecve.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/fexecve.c' object='../include/liblxc_la-fexecve.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-fexecve.lo `test -f '../include/fexecve.c' || echo '$(srcdir)/'`../include/fexecve.c ../include/liblxc_la-getgrgid_r.lo: ../include/getgrgid_r.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-getgrgid_r.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Tpo -c -o ../include/liblxc_la-getgrgid_r.lo `test -f '../include/getgrgid_r.c' || echo '$(srcdir)/'`../include/getgrgid_r.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Tpo ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getgrgid_r.c' object='../include/liblxc_la-getgrgid_r.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-getgrgid_r.lo `test -f '../include/getgrgid_r.c' || echo '$(srcdir)/'`../include/getgrgid_r.c ../include/liblxc_la-ifaddrs.lo: ../include/ifaddrs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-ifaddrs.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-ifaddrs.Tpo -c -o ../include/liblxc_la-ifaddrs.lo `test -f '../include/ifaddrs.c' || echo '$(srcdir)/'`../include/ifaddrs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-ifaddrs.Tpo ../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/ifaddrs.c' object='../include/liblxc_la-ifaddrs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-ifaddrs.lo `test -f '../include/ifaddrs.c' || echo '$(srcdir)/'`../include/ifaddrs.c ../include/liblxc_la-openpty.lo: ../include/openpty.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-openpty.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-openpty.Tpo -c -o ../include/liblxc_la-openpty.lo `test -f '../include/openpty.c' || echo '$(srcdir)/'`../include/openpty.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-openpty.Tpo ../include/$(DEPDIR)/liblxc_la-openpty.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/openpty.c' object='../include/liblxc_la-openpty.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-openpty.lo `test -f '../include/openpty.c' || echo '$(srcdir)/'`../include/openpty.c ../include/liblxc_la-lxcmntent.lo: ../include/lxcmntent.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-lxcmntent.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-lxcmntent.Tpo -c -o ../include/liblxc_la-lxcmntent.lo `test -f '../include/lxcmntent.c' || echo '$(srcdir)/'`../include/lxcmntent.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-lxcmntent.Tpo ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/lxcmntent.c' object='../include/liblxc_la-lxcmntent.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-lxcmntent.lo `test -f '../include/lxcmntent.c' || echo '$(srcdir)/'`../include/lxcmntent.c ../include/liblxc_la-getline.lo: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-getline.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-getline.Tpo -c -o ../include/liblxc_la-getline.lo `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-getline.Tpo ../include/$(DEPDIR)/liblxc_la-getline.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/liblxc_la-getline.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-getline.lo `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c ../include/liblxc_la-strlcpy.lo: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-strlcpy.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-strlcpy.Tpo -c -o ../include/liblxc_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-strlcpy.Tpo ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/liblxc_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c ../include/liblxc_la-strlcat.lo: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-strlcat.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-strlcat.Tpo -c -o ../include/liblxc_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-strlcat.Tpo ../include/$(DEPDIR)/liblxc_la-strlcat.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/liblxc_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c liblxc_la-rexec.lo: rexec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-rexec.lo -MD -MP -MF $(DEPDIR)/liblxc_la-rexec.Tpo -c -o liblxc_la-rexec.lo `test -f 'rexec.c' || echo '$(srcdir)/'`rexec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-rexec.Tpo $(DEPDIR)/liblxc_la-rexec.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rexec.c' object='liblxc_la-rexec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-rexec.lo `test -f 'rexec.c' || echo '$(srcdir)/'`rexec.c liblxc_la-seccomp.lo: seccomp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-seccomp.lo -MD -MP -MF $(DEPDIR)/liblxc_la-seccomp.Tpo -c -o liblxc_la-seccomp.lo `test -f 'seccomp.c' || echo '$(srcdir)/'`seccomp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-seccomp.Tpo $(DEPDIR)/liblxc_la-seccomp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='seccomp.c' object='liblxc_la-seccomp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-seccomp.lo `test -f 'seccomp.c' || echo '$(srcdir)/'`seccomp.c init_lxc_static-lxc_init.o: lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-lxc_init.o -MD -MP -MF $(DEPDIR)/init_lxc_static-lxc_init.Tpo -c -o init_lxc_static-lxc_init.o `test -f 'lxc_init.c' || echo '$(srcdir)/'`lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-lxc_init.Tpo $(DEPDIR)/init_lxc_static-lxc_init.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxc_init.c' object='init_lxc_static-lxc_init.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-lxc_init.o `test -f 'lxc_init.c' || echo '$(srcdir)/'`lxc_init.c init_lxc_static-lxc_init.obj: lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-lxc_init.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-lxc_init.Tpo -c -o init_lxc_static-lxc_init.obj `if test -f 'lxc_init.c'; then $(CYGPATH_W) 'lxc_init.c'; else $(CYGPATH_W) '$(srcdir)/lxc_init.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-lxc_init.Tpo $(DEPDIR)/init_lxc_static-lxc_init.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxc_init.c' object='init_lxc_static-lxc_init.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-lxc_init.obj `if test -f 'lxc_init.c'; then $(CYGPATH_W) 'lxc_init.c'; else $(CYGPATH_W) '$(srcdir)/lxc_init.c'; fi` init_lxc_static-error.o: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-error.o -MD -MP -MF $(DEPDIR)/init_lxc_static-error.Tpo -c -o init_lxc_static-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-error.Tpo $(DEPDIR)/init_lxc_static-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='init_lxc_static-error.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c init_lxc_static-error.obj: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-error.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-error.Tpo -c -o init_lxc_static-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-error.Tpo $(DEPDIR)/init_lxc_static-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='init_lxc_static-error.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` init_lxc_static-log.o: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-log.o -MD -MP -MF $(DEPDIR)/init_lxc_static-log.Tpo -c -o init_lxc_static-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-log.Tpo $(DEPDIR)/init_lxc_static-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='init_lxc_static-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c init_lxc_static-log.obj: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-log.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-log.Tpo -c -o init_lxc_static-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-log.Tpo $(DEPDIR)/init_lxc_static-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='init_lxc_static-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` init_lxc_static-initutils.o: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-initutils.o -MD -MP -MF $(DEPDIR)/init_lxc_static-initutils.Tpo -c -o init_lxc_static-initutils.o `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-initutils.Tpo $(DEPDIR)/init_lxc_static-initutils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='init_lxc_static-initutils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-initutils.o `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c init_lxc_static-initutils.obj: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-initutils.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-initutils.Tpo -c -o init_lxc_static-initutils.obj `if test -f 'initutils.c'; then $(CYGPATH_W) 'initutils.c'; else $(CYGPATH_W) '$(srcdir)/initutils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-initutils.Tpo $(DEPDIR)/init_lxc_static-initutils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='init_lxc_static-initutils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-initutils.obj `if test -f 'initutils.c'; then $(CYGPATH_W) 'initutils.c'; else $(CYGPATH_W) '$(srcdir)/initutils.c'; fi` init_lxc_static-caps.o: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-caps.o -MD -MP -MF $(DEPDIR)/init_lxc_static-caps.Tpo -c -o init_lxc_static-caps.o `test -f 'caps.c' || echo '$(srcdir)/'`caps.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-caps.Tpo $(DEPDIR)/init_lxc_static-caps.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='init_lxc_static-caps.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-caps.o `test -f 'caps.c' || echo '$(srcdir)/'`caps.c init_lxc_static-caps.obj: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-caps.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-caps.Tpo -c -o init_lxc_static-caps.obj `if test -f 'caps.c'; then $(CYGPATH_W) 'caps.c'; else $(CYGPATH_W) '$(srcdir)/caps.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-caps.Tpo $(DEPDIR)/init_lxc_static-caps.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='init_lxc_static-caps.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-caps.obj `if test -f 'caps.c'; then $(CYGPATH_W) 'caps.c'; else $(CYGPATH_W) '$(srcdir)/caps.c'; fi` init_lxc_static-parse.o: parse.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-parse.o -MD -MP -MF $(DEPDIR)/init_lxc_static-parse.Tpo -c -o init_lxc_static-parse.o `test -f 'parse.c' || echo '$(srcdir)/'`parse.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-parse.Tpo $(DEPDIR)/init_lxc_static-parse.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse.c' object='init_lxc_static-parse.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-parse.o `test -f 'parse.c' || echo '$(srcdir)/'`parse.c init_lxc_static-parse.obj: parse.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-parse.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-parse.Tpo -c -o init_lxc_static-parse.obj `if test -f 'parse.c'; then $(CYGPATH_W) 'parse.c'; else $(CYGPATH_W) '$(srcdir)/parse.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-parse.Tpo $(DEPDIR)/init_lxc_static-parse.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse.c' object='init_lxc_static-parse.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-parse.obj `if test -f 'parse.c'; then $(CYGPATH_W) 'parse.c'; else $(CYGPATH_W) '$(srcdir)/parse.c'; fi` init_lxc_static-namespace.o: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-namespace.o -MD -MP -MF $(DEPDIR)/init_lxc_static-namespace.Tpo -c -o init_lxc_static-namespace.o `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-namespace.Tpo $(DEPDIR)/init_lxc_static-namespace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='init_lxc_static-namespace.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-namespace.o `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c init_lxc_static-namespace.obj: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-namespace.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-namespace.Tpo -c -o init_lxc_static-namespace.obj `if test -f 'namespace.c'; then $(CYGPATH_W) 'namespace.c'; else $(CYGPATH_W) '$(srcdir)/namespace.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-namespace.Tpo $(DEPDIR)/init_lxc_static-namespace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='init_lxc_static-namespace.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-namespace.obj `if test -f 'namespace.c'; then $(CYGPATH_W) 'namespace.c'; else $(CYGPATH_W) '$(srcdir)/namespace.c'; fi` ../include/init_lxc_static-getline.o: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-getline.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-getline.Tpo -c -o ../include/init_lxc_static-getline.o `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-getline.Tpo ../include/$(DEPDIR)/init_lxc_static-getline.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/init_lxc_static-getline.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-getline.o `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c ../include/init_lxc_static-getline.obj: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-getline.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-getline.Tpo -c -o ../include/init_lxc_static-getline.obj `if test -f '../include/getline.c'; then $(CYGPATH_W) '../include/getline.c'; else $(CYGPATH_W) '$(srcdir)/../include/getline.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-getline.Tpo ../include/$(DEPDIR)/init_lxc_static-getline.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/init_lxc_static-getline.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-getline.obj `if test -f '../include/getline.c'; then $(CYGPATH_W) '../include/getline.c'; else $(CYGPATH_W) '$(srcdir)/../include/getline.c'; fi` ../include/init_lxc_static-strlcpy.o: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcpy.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo -c -o ../include/init_lxc_static-strlcpy.o `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/init_lxc_static-strlcpy.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcpy.o `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c ../include/init_lxc_static-strlcpy.obj: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcpy.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo -c -o ../include/init_lxc_static-strlcpy.obj `if test -f '../include/strlcpy.c'; then $(CYGPATH_W) '../include/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcpy.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/init_lxc_static-strlcpy.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcpy.obj `if test -f '../include/strlcpy.c'; then $(CYGPATH_W) '../include/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcpy.c'; fi` ../include/init_lxc_static-strlcat.o: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcat.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo -c -o ../include/init_lxc_static-strlcat.o `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/init_lxc_static-strlcat.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcat.o `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c ../include/init_lxc_static-strlcat.obj: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcat.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo -c -o ../include/init_lxc_static-strlcat.obj `if test -f '../include/strlcat.c'; then $(CYGPATH_W) '../include/strlcat.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcat.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/init_lxc_static-strlcat.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcat.obj `if test -f '../include/strlcat.c'; then $(CYGPATH_W) '../include/strlcat.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcat.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf ../include/.libs ../include/_libs -rm -rf cgroups/.libs cgroups/_libs -rm -rf lsm/.libs lsm/_libs -rm -rf storage/.libs storage/_libs install-pkgincludeHEADERS: $(pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../include/$(DEPDIR)/$(am__dirstamp) -rm -f ../include/$(am__dirstamp) -rm -f cgroups/$(DEPDIR)/$(am__dirstamp) -rm -f cgroups/$(am__dirstamp) -rm -f lsm/$(DEPDIR)/$(am__dirstamp) -rm -f lsm/$(am__dirstamp) -rm -f storage/$(DEPDIR)/$(am__dirstamp) -rm -f storage/$(am__dirstamp) -rm -f tools/$(DEPDIR)/$(am__dirstamp) -rm -f tools/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-pkglibexecPROGRAMS clean-sbinPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -f ../include/$(DEPDIR)/getsubopt.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-getline.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcat.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po -rm -f ../include/$(DEPDIR)/liblxc_la-fexecve.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getline.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-openpty.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo -rm -f ./$(DEPDIR)/init_lxc_static-caps.Po -rm -f ./$(DEPDIR)/init_lxc_static-error.Po -rm -f ./$(DEPDIR)/init_lxc_static-initutils.Po -rm -f ./$(DEPDIR)/init_lxc_static-log.Po -rm -f ./$(DEPDIR)/init_lxc_static-lxc_init.Po -rm -f ./$(DEPDIR)/init_lxc_static-namespace.Po -rm -f ./$(DEPDIR)/init_lxc_static-parse.Po -rm -f ./$(DEPDIR)/liblxc_la-af_unix.Plo -rm -f ./$(DEPDIR)/liblxc_la-attach.Plo -rm -f ./$(DEPDIR)/liblxc_la-caps.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-conf.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-console.Plo -rm -f ./$(DEPDIR)/liblxc_la-criu.Plo -rm -f ./$(DEPDIR)/liblxc_la-error.Plo -rm -f ./$(DEPDIR)/liblxc_la-execute.Plo -rm -f ./$(DEPDIR)/liblxc_la-freezer.Plo -rm -f ./$(DEPDIR)/liblxc_la-initutils.Plo -rm -f ./$(DEPDIR)/liblxc_la-log.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxccontainer.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxclock.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxcutmp.Plo -rm -f ./$(DEPDIR)/liblxc_la-mainloop.Plo -rm -f ./$(DEPDIR)/liblxc_la-monitor.Plo -rm -f ./$(DEPDIR)/liblxc_la-namespace.Plo -rm -f ./$(DEPDIR)/liblxc_la-network.Plo -rm -f ./$(DEPDIR)/liblxc_la-nl.Plo -rm -f ./$(DEPDIR)/liblxc_la-parse.Plo -rm -f ./$(DEPDIR)/liblxc_la-rexec.Plo -rm -f ./$(DEPDIR)/liblxc_la-rtnl.Plo -rm -f ./$(DEPDIR)/liblxc_la-seccomp.Plo -rm -f ./$(DEPDIR)/liblxc_la-start.Plo -rm -f ./$(DEPDIR)/liblxc_la-state.Plo -rm -f ./$(DEPDIR)/liblxc_la-sync.Plo -rm -f ./$(DEPDIR)/liblxc_la-utils.Plo -rm -f ./$(DEPDIR)/lxc_init.Po -rm -f ./$(DEPDIR)/lxc_monitord.Po -rm -f ./$(DEPDIR)/lxc_user_nic.Po -rm -f ./$(DEPDIR)/namespace.Po -rm -f ./$(DEPDIR)/network.Po -rm -f ./$(DEPDIR)/rexec.Po -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-apparmor.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-lsm.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-nop.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-selinux.Plo -rm -f storage/$(DEPDIR)/liblxc_la-aufs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-btrfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-dir.Plo -rm -f storage/$(DEPDIR)/liblxc_la-loop.Plo -rm -f storage/$(DEPDIR)/liblxc_la-lvm.Plo -rm -f storage/$(DEPDIR)/liblxc_la-nbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-overlay.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rsync.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage_utils.Plo -rm -f storage/$(DEPDIR)/liblxc_la-zfs.Plo -rm -f tools/$(DEPDIR)/arguments.Po -rm -f tools/$(DEPDIR)/lxc_attach.Po -rm -f tools/$(DEPDIR)/lxc_autostart.Po -rm -f tools/$(DEPDIR)/lxc_cgroup.Po -rm -f tools/$(DEPDIR)/lxc_checkpoint.Po -rm -f tools/$(DEPDIR)/lxc_clone.Po -rm -f tools/$(DEPDIR)/lxc_config.Po -rm -f tools/$(DEPDIR)/lxc_console.Po -rm -f tools/$(DEPDIR)/lxc_copy.Po -rm -f tools/$(DEPDIR)/lxc_create.Po -rm -f tools/$(DEPDIR)/lxc_destroy.Po -rm -f tools/$(DEPDIR)/lxc_device.Po -rm -f tools/$(DEPDIR)/lxc_execute.Po -rm -f tools/$(DEPDIR)/lxc_freeze.Po -rm -f tools/$(DEPDIR)/lxc_info.Po -rm -f tools/$(DEPDIR)/lxc_ls.Po -rm -f tools/$(DEPDIR)/lxc_monitor.Po -rm -f tools/$(DEPDIR)/lxc_snapshot.Po -rm -f tools/$(DEPDIR)/lxc_start.Po -rm -f tools/$(DEPDIR)/lxc_stop.Po -rm -f tools/$(DEPDIR)/lxc_top.Po -rm -f tools/$(DEPDIR)/lxc_unfreeze.Po -rm -f tools/$(DEPDIR)/lxc_unshare.Po -rm -f tools/$(DEPDIR)/lxc_usernsexec.Po -rm -f tools/$(DEPDIR)/lxc_wait.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkgincludeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-binSCRIPTS \ install-exec-local install-libLTLIBRARIES \ install-pkglibexecPROGRAMS install-sbinPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ../include/$(DEPDIR)/getsubopt.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-getline.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcat.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po -rm -f ../include/$(DEPDIR)/liblxc_la-fexecve.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getline.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-openpty.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo -rm -f ./$(DEPDIR)/init_lxc_static-caps.Po -rm -f ./$(DEPDIR)/init_lxc_static-error.Po -rm -f ./$(DEPDIR)/init_lxc_static-initutils.Po -rm -f ./$(DEPDIR)/init_lxc_static-log.Po -rm -f ./$(DEPDIR)/init_lxc_static-lxc_init.Po -rm -f ./$(DEPDIR)/init_lxc_static-namespace.Po -rm -f ./$(DEPDIR)/init_lxc_static-parse.Po -rm -f ./$(DEPDIR)/liblxc_la-af_unix.Plo -rm -f ./$(DEPDIR)/liblxc_la-attach.Plo -rm -f ./$(DEPDIR)/liblxc_la-caps.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-conf.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-console.Plo -rm -f ./$(DEPDIR)/liblxc_la-criu.Plo -rm -f ./$(DEPDIR)/liblxc_la-error.Plo -rm -f ./$(DEPDIR)/liblxc_la-execute.Plo -rm -f ./$(DEPDIR)/liblxc_la-freezer.Plo -rm -f ./$(DEPDIR)/liblxc_la-initutils.Plo -rm -f ./$(DEPDIR)/liblxc_la-log.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxccontainer.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxclock.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxcutmp.Plo -rm -f ./$(DEPDIR)/liblxc_la-mainloop.Plo -rm -f ./$(DEPDIR)/liblxc_la-monitor.Plo -rm -f ./$(DEPDIR)/liblxc_la-namespace.Plo -rm -f ./$(DEPDIR)/liblxc_la-network.Plo -rm -f ./$(DEPDIR)/liblxc_la-nl.Plo -rm -f ./$(DEPDIR)/liblxc_la-parse.Plo -rm -f ./$(DEPDIR)/liblxc_la-rexec.Plo -rm -f ./$(DEPDIR)/liblxc_la-rtnl.Plo -rm -f ./$(DEPDIR)/liblxc_la-seccomp.Plo -rm -f ./$(DEPDIR)/liblxc_la-start.Plo -rm -f ./$(DEPDIR)/liblxc_la-state.Plo -rm -f ./$(DEPDIR)/liblxc_la-sync.Plo -rm -f ./$(DEPDIR)/liblxc_la-utils.Plo -rm -f ./$(DEPDIR)/lxc_init.Po -rm -f ./$(DEPDIR)/lxc_monitord.Po -rm -f ./$(DEPDIR)/lxc_user_nic.Po -rm -f ./$(DEPDIR)/namespace.Po -rm -f ./$(DEPDIR)/network.Po -rm -f ./$(DEPDIR)/rexec.Po -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-apparmor.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-lsm.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-nop.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-selinux.Plo -rm -f storage/$(DEPDIR)/liblxc_la-aufs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-btrfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-dir.Plo -rm -f storage/$(DEPDIR)/liblxc_la-loop.Plo -rm -f storage/$(DEPDIR)/liblxc_la-lvm.Plo -rm -f storage/$(DEPDIR)/liblxc_la-nbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-overlay.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rsync.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage_utils.Plo -rm -f storage/$(DEPDIR)/liblxc_la-zfs.Plo -rm -f tools/$(DEPDIR)/arguments.Po -rm -f tools/$(DEPDIR)/lxc_attach.Po -rm -f tools/$(DEPDIR)/lxc_autostart.Po -rm -f tools/$(DEPDIR)/lxc_cgroup.Po -rm -f tools/$(DEPDIR)/lxc_checkpoint.Po -rm -f tools/$(DEPDIR)/lxc_clone.Po -rm -f tools/$(DEPDIR)/lxc_config.Po -rm -f tools/$(DEPDIR)/lxc_console.Po -rm -f tools/$(DEPDIR)/lxc_copy.Po -rm -f tools/$(DEPDIR)/lxc_create.Po -rm -f tools/$(DEPDIR)/lxc_destroy.Po -rm -f tools/$(DEPDIR)/lxc_device.Po -rm -f tools/$(DEPDIR)/lxc_execute.Po -rm -f tools/$(DEPDIR)/lxc_freeze.Po -rm -f tools/$(DEPDIR)/lxc_info.Po -rm -f tools/$(DEPDIR)/lxc_ls.Po -rm -f tools/$(DEPDIR)/lxc_monitor.Po -rm -f tools/$(DEPDIR)/lxc_snapshot.Po -rm -f tools/$(DEPDIR)/lxc_start.Po -rm -f tools/$(DEPDIR)/lxc_stop.Po -rm -f tools/$(DEPDIR)/lxc_top.Po -rm -f tools/$(DEPDIR)/lxc_unfreeze.Po -rm -f tools/$(DEPDIR)/lxc_unshare.Po -rm -f tools/$(DEPDIR)/lxc_usernsexec.Po -rm -f tools/$(DEPDIR)/lxc_wait.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-libLTLIBRARIES uninstall-local \ uninstall-pkgincludeHEADERS uninstall-pkglibexecPROGRAMS \ uninstall-sbinPROGRAMS .MAKE: install-am install-exec-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-pkglibexecPROGRAMS clean-sbinPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-binSCRIPTS install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-exec-hook install-exec-local \ install-html install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-pkgincludeHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-sbinPROGRAMS install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-binSCRIPTS uninstall-libLTLIBRARIES uninstall-local \ uninstall-pkgincludeHEADERS uninstall-pkglibexecPROGRAMS \ uninstall-sbinPROGRAMS .PRECIOUS: Makefile install-exec-local: install-libLTLIBRARIES mkdir -p $(DESTDIR)$(datadir)/lxc install -c -m 644 lxc.functions $(DESTDIR)$(datadir)/lxc mv $(shell readlink -f $(DESTDIR)$(libdir)/liblxc.so) $(DESTDIR)$(libdir)/liblxc.so.@LXC_ABI@ rm -f $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.1 cd $(DESTDIR)$(libdir); \ ln -sf liblxc.so.@LXC_ABI@ liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)); \ ln -sf liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) liblxc.so install-exec-hook: chmod u+s $(DESTDIR)$(libexecdir)/lxc/lxc-user-nic uninstall-local: $(RM) $(DESTDIR)$(libdir)/liblxc.so* # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: lxc-2.0.11/src/lxc/version.h0000644061062106075000000000212213435013521012517 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_VERSION_H #define __LXC_VERSION_H #define LXC_DEVEL 0 #define LXC_VERSION_MAJOR 2 #define LXC_VERSION_MINOR 0 #define LXC_VERSION_MICRO 11 #define LXC_VERSION_ABI "1.2.0" #define LXC_VERSION "2.0.11" #endif lxc-2.0.11/src/lxc/lxccontainer.c0000644061062106075000000033447513435013473013547 00000000000000/* liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "attach.h" #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "config.h" #include "confile.h" #include "console.h" #include "criu.h" #include "initutils.h" #include "log.h" #include "lxc.h" #include "lxccontainer.h" #include "lxclock.h" #include "monitor.h" #include "namespace.h" #include "network.h" #include "start.h" #include "state.h" #include "storage.h" #include "storage/btrfs.h" #include "storage/overlay.h" #include "storage_utils.h" #include "sync.h" #include "utils.h" #include "version.h" /* major()/minor() */ #ifdef MAJOR_IN_MKDEV # include #endif #if HAVE_IFADDRS_H #include #else #include <../include/ifaddrs.h> #endif #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #define MAX_BUFFER 4096 #define NOT_SUPPORTED_ERROR "the requested function %s is not currently supported with unprivileged containers" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif /* Define faccessat() if missing from the C library */ #ifndef HAVE_FACCESSAT static int faccessat(int __fd, const char *__file, int __type, int __flag) { #ifdef __NR_faccessat return syscall(__NR_faccessat, __fd, __file, __type, __flag); #else errno = ENOSYS; return -1; #endif } #endif lxc_log_define(lxc_container, lxc); static bool do_lxcapi_destroy(struct lxc_container *c); static const char *lxcapi_get_config_path(struct lxc_container *c); #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c) static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); static bool container_destroy(struct lxc_container *c); static bool get_snappath_dir(struct lxc_container *c, char *snappath); static bool lxcapi_snapshot_destroy_all(struct lxc_container *c); static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file); static bool config_file_exists(const char *lxcpath, const char *cname) { int ret; size_t len; char *fname; /* $lxcpath + '/' + $cname + '/config' + \0 */ len = strlen(lxcpath) + strlen(cname) + 9; fname = alloca(len); ret = snprintf(fname, len, "%s/%s/config", lxcpath, cname); if (ret < 0 || (size_t)ret >= len) return false; return file_exists(fname); } /* A few functions to help detect when a container creation failed. If a * container creation was killed partway through, then trying to actually start * that container could harm the host. We detect this by creating a 'partial' * file under the container directory, and keeping an advisory lock. When * container creation completes, we remove that file. When we load or try to * start a container, if we find that file, without a flock, we remove the * container. */ static int ongoing_create(struct lxc_container *c) { int fd, ret; size_t len; char *path; struct flock lk = {0}; len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return -1; if (!file_exists(path)) return 0; fd = open(path, O_RDWR); if (fd < 0) return 0; lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_pid = -1; ret = fcntl(fd, F_OFD_GETLK, &lk); if (ret < 0 && errno == EINVAL) ret = flock(fd, LOCK_EX | LOCK_NB); close(fd); if (ret == 0 && lk.l_pid != -1) { /* create is still ongoing */ return 1; } return 2; } static int create_partial(struct lxc_container *c) { int fd, ret; size_t len; char *path; struct flock lk = {0}; /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return -1; fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0755); if (fd < 0) return -1; lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) { ret = flock(fd, LOCK_EX); if (ret == 0) return fd; } SYSERROR("Failed to lock partial file %s", path); close(fd); return -1; } return fd; } static void remove_partial(struct lxc_container *c, int fd) { int ret; size_t len; char *path; close(fd); /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return; ret = unlink(path); if (ret < 0) SYSERROR("Failed to remove partial file %s", path); } /* LOCKING * 1. container_mem_lock(c) protects the struct lxc_container from multiple threads. * 2. container_disk_lock(c) protects the on-disk container data - in particular the * container configuration file. * The container_disk_lock also takes the container_mem_lock. * 3. thread_mutex protects process data (ex: fd table) from multiple threads. * NOTHING mutexes two independent programs with their own struct * lxc_container for the same c->name, between API calls. For instance, * c->config_read(); c->start(); Between those calls, data on disk * could change (which shouldn't bother the caller unless for instance * the rootfs get moved). c->config_read(); update; c->config_write(); * Two such updaters could race. The callers should therefore check their * results. Trying to prevent that would necessarily expose us to deadlocks * due to hung callers. So I prefer to keep the locks only within our own * functions, not across functions. * * If you're going to clone while holding a lxccontainer, increment * c->numthreads (under privlock) before forking. When deleting, * decrement numthreads under privlock, then if it hits 0 you can delete. * Do not ever use a lxccontainer whose numthreads you did not bump. */ static void lxc_container_free(struct lxc_container *c) { if (!c) return; free(c->configfile); c->configfile = NULL; free(c->error_string); c->error_string = NULL; if (c->slock) { lxc_putlock(c->slock); c->slock = NULL; } if (c->privlock) { lxc_putlock(c->privlock); c->privlock = NULL; } free(c->name); c->name = NULL; if (c->lxc_conf) { lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } free(c->config_path); c->config_path = NULL; free(c); } /* Consider the following case: * * |====================================================================| * | freer | racing get()er | * |====================================================================| * | lxc_container_put() | lxc_container_get() | * | \ lxclock(c->privlock) | c->numthreads < 1? (no) | * | \ c->numthreads = 0 | \ lxclock(c->privlock) -> waits | * | \ lxcunlock() | \ | * | \ lxc_container_free() | \ lxclock() returns | * | | \ c->numthreads < 1 -> return 0 | * | \ \ (free stuff) | | * | \ \ sem_destroy(privlock) | | * |_______________________________|____________________________________| * * When the get()er checks numthreads the first time, one of the following * is true: * 1. freer has set numthreads = 0. get() returns 0 * 2. freer is between lxclock and setting numthreads to 0. get()er will * sem_wait on privlock, get lxclock after freer() drops it, then see * numthreads is 0 and exit without touching lxclock again.. * 3. freer has not yet locked privlock. If get()er runs first, then put()er * will see --numthreads = 1 and not call lxc_container_free(). */ int lxc_container_get(struct lxc_container *c) { if (!c) return 0; // if someone else has already started freeing the container, don't // try to take the lock, which may be invalid if (c->numthreads < 1) return 0; if (container_mem_lock(c)) return 0; if (c->numthreads < 1) { // bail without trying to unlock, bc the privlock is now probably // in freed memory return 0; } c->numthreads++; container_mem_unlock(c); return 1; } int lxc_container_put(struct lxc_container *c) { if (!c) return -1; if (container_mem_lock(c)) return -1; c->numthreads--; if (c->numthreads < 1) { container_mem_unlock(c); lxc_container_free(c); return 1; } container_mem_unlock(c); return 0; } static bool do_lxcapi_is_defined(struct lxc_container *c) { int statret; struct stat statbuf; bool ret = false; if (!c) return false; if (container_mem_lock(c)) return false; if (!c->configfile) goto on_error; statret = stat(c->configfile, &statbuf); if (statret != 0) goto on_error; ret = true; on_error: container_mem_unlock(c); return ret; } #define WRAP_API(rettype, fnname) \ static rettype fnname(struct lxc_container *c) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_1(rettype, fnname, t1) \ static rettype fnname(struct lxc_container *c, t1 a1) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_2(rettype, fnname, t1, t2) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_3(rettype, fnname, t1, t2, t3) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2, a3); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } WRAP_API(bool, lxcapi_is_defined) static const char *do_lxcapi_state(struct lxc_container *c) { lxc_state_t s; if (!c) return NULL; s = lxc_getstate(c->name, c->config_path); return lxc_state2str(s); } WRAP_API(const char *, lxcapi_state) static bool is_stopped(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); return (s == STOPPED); } static bool do_lxcapi_is_running(struct lxc_container *c) { if (!c) return false; return !is_stopped(c); } WRAP_API(bool, lxcapi_is_running) static bool do_lxcapi_freeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_freeze(c->name, c->config_path); if (ret < 0) return false; return true; } WRAP_API(bool, lxcapi_freeze) static bool do_lxcapi_unfreeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_unfreeze(c->name, c->config_path); if (ret < 0) return false; return true; } WRAP_API(bool, lxcapi_unfreeze) static int do_lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { if (!c) return -1; return lxc_console_getfd(c, ttynum, masterfd); } WRAP_API_2(int, lxcapi_console_getfd, int *, int *) static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape); current_config = NULL; return ret; } static pid_t do_lxcapi_init_pid(struct lxc_container *c) { if (!c) return -1; return lxc_cmd_get_init_pid(c->name, c->config_path); } WRAP_API(pid_t, lxcapi_init_pid) static bool load_config_locked(struct lxc_container *c, const char *fname) { if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; if (lxc_config_read(fname, c->lxc_conf, false) != 0) return false; return true; } static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file) { int lret; const char *fname; bool need_disklock = false, ret = false; if (!c) return false; fname = c->configfile; if (alt_file) fname = alt_file; if (!fname) return false; /* If we're reading something other than the container's config, we only * need to lock the in-memory container. If loading the container's * config file, take the disk lock. */ if (strcmp(fname, c->configfile) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; ret = load_config_locked(c, fname); if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_load_config, const char *) static bool do_lxcapi_want_daemonize(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->daemonize = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_daemonize, bool) static bool do_lxcapi_want_close_all_fds(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->lxc_conf->close_all_fds = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_close_all_fds, bool) static bool do_lxcapi_wait(struct lxc_container *c, const char *state, int timeout) { int ret; if (!c) return false; ret = lxc_wait(c->name, state, timeout, c->config_path); return ret == 0; } WRAP_API_2(bool, lxcapi_wait, const char *, int) static bool am_single_threaded(void) { DIR *dir; struct dirent *direntp; int count = 0; dir = opendir("/proc/self/task"); if (!dir) return false; while ((direntp = readdir(dir))) { if (strcmp(direntp->d_name, ".") == 0) continue; if (strcmp(direntp->d_name, "..") == 0) continue; count++; if (count > 1) break; } closedir(dir); return count == 1; } static void push_arg(char ***argp, char *arg, int *nargs) { char *copy; char **argv; copy = must_copy_string(arg); do { argv = realloc(*argp, (*nargs + 2) * sizeof(char *)); } while (!argv); *argp = argv; argv[*nargs] = copy; (*nargs)++; argv[*nargs] = NULL; } static char **split_init_cmd(const char *incmd) { size_t len, retlen; char *copy, *p; char **argv; int nargs = 0; char *saveptr = NULL; if (!incmd) return NULL; len = strlen(incmd) + 1; copy = alloca(len); retlen = strlcpy(copy, incmd, len); if (retlen >= len) { return NULL; } do { argv = malloc(sizeof(char *)); } while (!argv); argv[0] = NULL; for (; (p = strtok_r(copy, " ", &saveptr)); copy = NULL) push_arg(&argv, p, &nargs); if (nargs == 0) { free(argv); return NULL; } return argv; } static void free_init_cmd(char **argv) { int i = 0; if (!argv) return; while (argv[i]) free(argv[i++]); free(argv); } static int lxc_rcv_status(int state_socket) { int ret; int state = -1; again: /* Receive container state. */ ret = lxc_abstract_unix_rcv_credential(state_socket, &state, sizeof(int)); if (ret <= 0) { if (errno != EINTR) return -1; TRACE("Caught EINTR; retrying"); goto again; } return state; } static bool wait_on_daemonized_start(struct lxc_handler *handler, int pid) { int ret, state; /* Close write end of the socket pair. */ close(handler->state_socket_pair[1]); handler->state_socket_pair[1] = -1; state = lxc_rcv_status(handler->state_socket_pair[0]); /* Close read end of the socket pair. */ close(handler->state_socket_pair[0]); handler->state_socket_pair[0] = -1; /* The first child is going to fork() again and then exits. So we reap * the first child here. */ ret = wait_for_pid(pid); if (ret < 0) DEBUG("Failed waiting on first child %d", pid); else DEBUG("First child %d exited", pid); if (state < 0) { SYSERROR("Failed to receive the container state"); return false; } /* If we receive anything else then running we know that the container * failed to start. */ if (state != RUNNING) { ERROR("Received container state \"%s\" instead of \"RUNNING\"", lxc_state2str(state)); return false; } TRACE("Container is in \"RUNNING\" state"); return true; } static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) { int ret; struct lxc_handler *handler; struct lxc_conf *conf; bool daemonize = false; FILE *pid_fp = NULL; char *default_args[] = { "/sbin/init", NULL, }; char **init_cmd = NULL; int keepfds[3] = {-1, -1, -1}; /* container does exist */ if (!c) return false; /* If anything fails before we set error_num, we want an error in there. */ c->error_num = 1; /* Container has not been setup. */ if (!c->lxc_conf) return false; ret = ongoing_create(c); if (ret < 0) { ERROR("Failed checking for incomplete container creation"); return false; } else if (ret == 1) { ERROR("Ongoing container creation detected"); return false; } else if (ret == 2) { ERROR("Failed to create container"); do_lxcapi_destroy(c); return false; } if (container_mem_lock(c)) return false; conf = c->lxc_conf; daemonize = c->daemonize; /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, daemonize); container_mem_unlock(c); if (!handler) return false; if (!argv) argv = init_cmd = split_init_cmd(conf->init_cmd); /* ... otherwise use default_args. */ if (!argv) { if (useinit) { ERROR("No valid init detected"); lxc_free_handler(handler); return false; } argv = default_args; } /* I'm not sure what locks we want here.Any? Is liblxc's locking enough * here to protect the on disk container? We don't want to exclude * things like lxc_info while the container is running. */ if (daemonize) { bool started; char title[2048]; pid_t pid; pid = fork(); if (pid < 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } /* first parent */ if (pid != 0) { /* Set to NULL because we don't want father unlink * the PID file, child will do the free and unlink. */ c->pidfile = NULL; /* Wait for container to tell us whether it started * successfully. */ started = wait_on_daemonized_start(handler, pid); free_init_cmd(init_cmd); lxc_free_handler(handler); return started; } /* first child */ /* We don't really care if this doesn't print all the * characters. All that it means is that the proctitle will be * ugly. Similarly, we also don't care if setproctitle() fails. * */ (void)snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); INFO("Attempting to set proc title to %s", title); (void)setproctitle(title); /* We fork() a second time to be reparented to init. Like * POSIX's daemon() function we change to "/" and redirect * std{in,out,err} to /dev/null. */ pid = fork(); if (pid < 0) { SYSERROR("Failed to fork first child process"); _exit(EXIT_FAILURE); } /* second parent */ if (pid != 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); _exit(EXIT_SUCCESS); } /* second child */ /* change to / directory */ ret = chdir("/"); if (ret < 0) { SYSERROR("Failed to change to \"/\" directory"); _exit(EXIT_FAILURE); } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, true, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) _exit(EXIT_FAILURE); /* redirect std{in,out,err} to /dev/null */ ret = null_stdfds(); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to /dev/null"); _exit(EXIT_FAILURE); } /* become session leader */ ret = setsid(); if (ret < 0) TRACE("Process %d is already process group leader", lxc_raw_getpid()); } else { if (!am_single_threaded()) { ERROR("Cannot start non-daemonized container when threaded"); free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } } /* We need to write PID file after daemonize, so we always * write the right PID. */ if (c->pidfile) { pid_fp = fopen(c->pidfile, "w"); if (pid_fp == NULL) { SYSERROR("Failed to create pidfile '%s' for '%s'", c->pidfile, c->name); free_init_cmd(init_cmd); lxc_free_handler(handler); if (daemonize) _exit(EXIT_FAILURE); return false; } if (fprintf(pid_fp, "%d\n", lxc_raw_getpid()) < 0) { SYSERROR("Failed to write '%s'", c->pidfile); fclose(pid_fp); pid_fp = NULL; free_init_cmd(init_cmd); lxc_free_handler(handler); if (daemonize) _exit(EXIT_FAILURE); return false; } fclose(pid_fp); pid_fp = NULL; } conf->reboot = 0; /* Unshare the mount namespace if requested */ if (conf->monitor_unshare) { ret = unshare(CLONE_NEWNS); if (ret < 0) { SYSERROR("failed to unshare mount namespace"); lxc_free_handler(handler); ret = 1; goto on_error; } ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to make / rslave at startup"); lxc_free_handler(handler); ret = 1; goto on_error; } } reboot: if (conf->reboot == 2) { /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, daemonize); if (!handler) { ret = 1; goto on_error; } } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, daemonize, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) { lxc_free_handler(handler); ret = 1; goto on_error; } if (useinit) ret = lxc_execute(c->name, argv, 1, handler, c->config_path, daemonize, &c->error_num); else ret = lxc_start(c->name, argv, handler, c->config_path, daemonize, &c->error_num); if (conf->reboot == 1) { INFO("Container requested reboot"); conf->reboot = 2; goto reboot; } on_error: if (c->pidfile) { unlink(c->pidfile); free(c->pidfile); c->pidfile = NULL; } free_init_cmd(init_cmd); if (daemonize && ret != 0) _exit(EXIT_FAILURE); else if (daemonize) _exit(EXIT_SUCCESS); if (ret != 0) return false; return true; } static bool lxcapi_start(struct lxc_container *c, int useinit, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_start(c, useinit, argv); current_config = NULL; return ret; } /* Note, there MUST be an ending NULL. */ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) { va_list ap; char **inargs = NULL; bool bret = false; /* container exists */ if (!c) return false; current_config = c->lxc_conf; va_start(ap, useinit); inargs = lxc_va_arg_list_to_argv(ap, 0, 1); va_end(ap); if (!inargs) goto on_error; /* pass NULL if no arguments were supplied */ bret = do_lxcapi_start(c, useinit, *inargs ? inargs : NULL); on_error: if (inargs) { char **arg; for (arg = inargs; *arg; arg++) free(*arg); free(inargs); } current_config = NULL; return bret; } static bool do_lxcapi_stop(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_cmd_stop(c->name, c->config_path); return ret == 0; } WRAP_API(bool, lxcapi_stop) static int do_create_container_dir(const char *path, struct lxc_conf *conf) { int lasterr; size_t len; char *p; int ret = -1; mode_t mask = umask(0002); ret = mkdir(path, 0770); lasterr = errno; umask(mask); errno = lasterr; if (ret) { if (errno != EEXIST) return -1; ret = 0; } len = strlen(path); p = alloca(len + 1); (void)strlcpy(p, path, len + 1); if (!lxc_list_empty(&conf->id_map)) { ret = chown_mapped_root(p, conf); if (ret < 0) ret = -1; } return ret; } /* Create the standard expected container dir. */ static bool create_container_dir(struct lxc_container *c) { int ret; size_t len; char *s; len = strlen(c->config_path) + strlen(c->name) + 2; s = malloc(len); if (!s) return false; ret = snprintf(s, len, "%s/%s", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) { free(s); return false; } ret = do_create_container_dir(s, c->lxc_conf); free(s); return ret == 0; } /* do_storage_create: thin wrapper around storage_create(). Like * storage_create(), it returns a mounted bdev on success, NULL on error. */ static struct lxc_storage *do_storage_create(struct lxc_container *c, const char *type, struct bdev_specs *specs) { int ret; size_t len; char *dest; struct lxc_storage *bdev; /* rootfs.path or lxcpath/lxcname/rootfs */ if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) { const char *rpath = c->lxc_conf->rootfs.path; len = strlen(rpath) + 1; dest = alloca(len); ret = snprintf(dest, len, "%s", rpath); } else { const char *lxcpath = do_lxcapi_get_config_path(c); len = strlen(c->name) + strlen(lxcpath) + 9; dest = alloca(len); ret = snprintf(dest, len, "%s/%s/rootfs", lxcpath, c->name); } if (ret < 0 || (size_t)ret >= len) return NULL; bdev = storage_create(dest, type, c->name, specs); if (!bdev) { ERROR("Failed to create \"%s\" storage", type); return NULL; } do_lxcapi_set_config_item(c, "lxc.rootfs", bdev->src); do_lxcapi_set_config_item(c, "lxc.rootfs.backend", bdev->type); /* If we are not root, chown the rootfs dir to root in the target user * namespace. */ ret = geteuid(); if (ret != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) { ret = chown_mapped_root(bdev->dest, c->lxc_conf); if (ret < 0) { ERROR("Error chowning \"%s\" to container root", bdev->dest); suggest_default_idmap(); storage_put(bdev); return NULL; } } return bdev; } static char *lxcbasename(char *path) { char *p = path + strlen(path) - 1; while (*p != '/' && p > path) p--; return p; } static bool create_run_template(struct lxc_container *c, char *tpath, bool need_null_stdfds, char *const argv[]) { pid_t pid; if (!tpath) return true; pid = fork(); if (pid < 0) { SYSERROR("failed to fork task for container creation template"); return false; } if (pid == 0) { // child char *patharg, *namearg, *rootfsarg; struct lxc_storage *bdev = NULL; int i; int ret, len, nargs = 0; char **newargv; struct lxc_conf *conf = c->lxc_conf; if (need_null_stdfds && null_stdfds() < 0) { exit(1); } bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Error opening rootfs"); exit(1); } if (geteuid() == 0) { if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); exit(1); } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave to run template"); ERROR("Continuing..."); } } } if (strcmp(bdev->type, "dir") && strcmp(bdev->type, "btrfs")) { if (geteuid() != 0) { ERROR("non-root users can only create btrfs and directory-backed containers"); exit(1); } if (bdev->ops->mount(bdev) < 0) { ERROR("Error mounting rootfs"); exit(1); } } else { // TODO come up with a better way here! free(bdev->dest); bdev->dest = strdup(bdev->src); } /* * create our new array, pre-pend the template name and * base args */ if (argv) for (nargs = 0; argv[nargs]; nargs++) ; nargs += 4; // template, path, rootfs and name args newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) exit(1); newargv[0] = lxcbasename(tpath); len = strlen(c->config_path) + strlen(c->name) + strlen("--path=") + 2; patharg = malloc(len); if (!patharg) exit(1); ret = snprintf(patharg, len, "--path=%s/%s", c->config_path, c->name); if (ret < 0 || ret >= len) exit(1); newargv[1] = patharg; len = strlen("--name=") + strlen(c->name) + 1; namearg = malloc(len); if (!namearg) exit(1); ret = snprintf(namearg, len, "--name=%s", c->name); if (ret < 0 || ret >= len) exit(1); newargv[2] = namearg; len = strlen("--rootfs=") + 1 + strlen(bdev->dest); rootfsarg = malloc(len); if (!rootfsarg) exit(1); ret = snprintf(rootfsarg, len, "--rootfs=%s", bdev->dest); if (ret < 0 || ret >= len) exit(1); newargv[3] = rootfsarg; /* add passed-in args */ if (argv) for (i = 4; i < nargs; i++) newargv[i] = argv[i-4]; /* add trailing NULL */ nargs++; newargv = realloc(newargv, nargs * sizeof(*newargv)); if (!newargv) exit(1); newargv[nargs - 1] = NULL; /* * If we're running the template in a mapped userns, then * we prepend the template command with: * lxc-usernsexec <-m map1> ... <-m mapn> -- * and we append "--mapped-uid x", where x is the mapped uid * for our geteuid() */ if (!lxc_list_empty(&conf->id_map)) { int n2args = 1; char txtuid[20]; char txtgid[20]; char **n2 = malloc(n2args * sizeof(*n2)); struct lxc_list *it; struct id_map *map; if (!n2) { SYSERROR("out of memory"); exit(1); } newargv[0] = tpath; tpath = "lxc-usernsexec"; n2[0] = "lxc-usernsexec"; lxc_list_for_each(it, &conf->id_map) { map = it->elem; n2args += 2; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) exit(1); n2[n2args-2] = "-m"; n2[n2args-1] = malloc(200); if (!n2[n2args-1]) exit(1); ret = snprintf(n2[n2args-1], 200, "%c:%lu:%lu:%lu", map->idtype == ID_TYPE_UID ? 'u' : 'g', map->nsid, map->hostid, map->range); if (ret < 0 || ret >= 200) exit(1); } int hostid_mapped = mapped_hostid(geteuid(), conf, ID_TYPE_UID); int extraargs = hostid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) exit(1); if (hostid_mapped < 0) { hostid_mapped = find_unmapped_nsid(conf, ID_TYPE_UID); n2[n2args++] = "-m"; if (hostid_mapped < 0) { ERROR("Could not find free uid to map"); exit(1); } n2[n2args++] = malloc(200); if (!n2[n2args-1]) { SYSERROR("out of memory"); exit(1); } ret = snprintf(n2[n2args-1], 200, "u:%d:%d:1", hostid_mapped, geteuid()); if (ret < 0 || ret >= 200) { ERROR("string too long"); exit(1); } } int hostgid_mapped = mapped_hostid(getegid(), conf, ID_TYPE_GID); extraargs = hostgid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) exit(1); if (hostgid_mapped < 0) { hostgid_mapped = find_unmapped_nsid(conf, ID_TYPE_GID); n2[n2args++] = "-m"; if (hostgid_mapped < 0) { ERROR("Could not find free uid to map"); exit(1); } n2[n2args++] = malloc(200); if (!n2[n2args-1]) { SYSERROR("out of memory"); exit(1); } ret = snprintf(n2[n2args-1], 200, "g:%d:%d:1", hostgid_mapped, getegid()); if (ret < 0 || ret >= 200) { ERROR("string too long"); exit(1); } } n2[n2args++] = "--"; for (i = 0; i < nargs; i++) n2[i + n2args] = newargv[i]; n2args += nargs; // Finally add "--mapped-uid $uid" to tell template what to chown // cached images to n2args += 4; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) { SYSERROR("out of memory"); exit(1); } // note n2[n2args-1] is NULL n2[n2args-5] = "--mapped-uid"; snprintf(txtuid, 20, "%d", hostid_mapped); n2[n2args-4] = txtuid; n2[n2args-3] = "--mapped-gid"; snprintf(txtgid, 20, "%d", hostgid_mapped); n2[n2args-2] = txtgid; n2[n2args-1] = NULL; free(newargv); newargv = n2; } /* execute */ execvp(tpath, newargv); SYSERROR("failed to execute template %s", tpath); exit(1); } if (wait_for_pid(pid) != 0) { ERROR("container creation template for %s failed", c->name); return false; } return true; } static bool prepend_lxc_header(char *path, const char *t, char *const argv[]) { long flen; size_t len; char *contents; FILE *f; int ret = -1; #if HAVE_LIBGNUTLS int i; unsigned char md_value[SHA_DIGEST_LENGTH]; char *tpath; #endif f = fopen(path, "r"); if (f == NULL) return false; ret = fseek(f, 0, SEEK_END); if (ret < 0) goto out_error; ret = -1; flen = ftell(f); if (flen < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = -1; contents = malloc(flen + 1); if (!contents) goto out_error; len = fread(contents, 1, flen, f); if (len != flen) goto out_free_contents; contents[flen] = '\0'; ret = fclose(f); f = NULL; if (ret < 0) goto out_free_contents; #if HAVE_LIBGNUTLS tpath = get_template_path(t); if (!tpath) { ERROR("Invalid template \"%s\" specified", t); goto out_free_contents; } ret = sha1sum_file(tpath, md_value); if (ret < 0) { ERROR("Failed to get sha1sum of %s", tpath); free(tpath); goto out_free_contents; } free(tpath); #endif f = fopen(path, "w"); if (f == NULL) { SYSERROR("Reopening config for writing"); free(contents); return false; } fprintf(f, "# Template used to create this container: %s\n", t); if (argv) { fprintf(f, "# Parameters passed to the template:"); while (*argv) { fprintf(f, " %s", *argv); argv++; } fprintf(f, "\n"); } #if HAVE_LIBGNUTLS fprintf(f, "# Template script checksum (SHA-1): "); for (i=0; ilxc_conf) return; lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } #define do_lxcapi_clear_config(c) lxcapi_clear_config(c) /* * lxcapi_create: * create a container with the given parameters. * @c: container to be created. It has the lxcpath, name, and a starting * configuration already set * @t: the template to execute to instantiate the root filesystem and * adjust the configuration. * @bdevtype: backing store type to use. If NULL, dir will be used. * @specs: additional parameters for the backing store, i.e. LVM vg to * use. * * @argv: the arguments to pass to the template, terminated by NULL. If no * arguments, you can just pass NULL. */ static bool do_lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { int partial_fd; mode_t mask; pid_t pid; bool ret = false; char *tpath = NULL; if (!c) return false; if (t) { tpath = get_template_path(t); if (!tpath) { ERROR("Unknown template \"%s\"", t); goto out; } } /* If a template is passed in, and the rootfs already is defined in the * container config and exists, then the caller is trying to create an * existing container. Return an error, but do NOT delete the container. */ if (do_lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0 && tpath) { ERROR("Container \"%s\" already exists in \"%s\"", c->name, c->config_path); goto free_tpath; } if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s", lxc_global_config_value("lxc.default_config")); goto free_tpath; } } if (!create_container_dir(c)) goto free_tpath; /* If both template and rootfs.path are set, template is setup as * rootfs.path. The container is already created if we have a config and * rootfs.path is accessible */ if (!c->lxc_conf->rootfs.path && !tpath) { /* No template passed in and rootfs does not exist. */ if (!c->save_config(c, NULL)) { ERROR("Failed to save initial config for \"%s\"", c->name); goto out; } ret = true; goto out; } /* Rootfs passed into configuration, but does not exist. */ if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) != 0) goto out; if (do_lxcapi_is_defined(c) && c->lxc_conf->rootfs.path && !tpath) { /* Rootfs already existed, user just wanted to save the loaded * configuration. */ if (!c->save_config(c, NULL)) ERROR("Failed to save initial config for \"%s\"", c->name); ret = true; goto out; } /* Mark that this container is being created */ partial_fd = create_partial(c); if (partial_fd < 0) goto out; /* No need to get disk lock bc we have the partial lock. */ mask = umask(0022); /* Create the storage. * Note we can't do this in the same task as we use to execute the * template because of the way zfs works. * After you 'zfs create', zfs mounts the fs only in the initial * namespace. */ pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task for container creation template"); goto out_unlock; } if (pid == 0) { // child struct lxc_storage *bdev = NULL; bdev = do_storage_create(c, bdevtype, specs); if (!bdev) { ERROR("Failed to create %s storage for %s", bdevtype ? bdevtype : "(none)", c->name); _exit(EXIT_FAILURE); } /* Save config file again to store the new rootfs location. */ if (!do_lxcapi_save_config(c, NULL)) { ERROR("Failed to save initial config for %s", c->name); /* Parent task won't see the storage driver in the * config so we delete it. */ bdev->ops->umount(bdev); bdev->ops->destroy(bdev); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } if (wait_for_pid(pid) != 0) goto out_unlock; /* Reload config to get the rootfs. */ lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; if (!load_config_locked(c, c->configfile)) goto out_unlock; if (!create_run_template(c, tpath, !!(flags & LXC_CREATE_QUIET), argv)) goto out_unlock; // now clear out the lxc_conf we have, reload from the created // container do_lxcapi_clear_config(c); if (t) { if (!prepend_lxc_header(c->configfile, tpath, argv)) { ERROR("Failed to prepend header to config file"); goto out_unlock; } } ret = load_config_locked(c, c->configfile); out_unlock: umask(mask); if (partial_fd >= 0) remove_partial(c, partial_fd); out: if (!ret) container_destroy(c); free_tpath: free(tpath); return ret; } static bool lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_create(c, t, bdevtype, specs, flags, argv); current_config = NULL; return ret; } static bool do_lxcapi_reboot(struct lxc_container *c) { int ret; pid_t pid; int rebootsignal = SIGINT; if (!c) return false; if (!do_lxcapi_is_running(c)) return false; pid = do_lxcapi_init_pid(c); if (pid <= 0) return false; if (c->lxc_conf && c->lxc_conf->rebootsignal) rebootsignal = c->lxc_conf->rebootsignal; ret = kill(pid, rebootsignal); if (ret < 0) { WARN("Failed to send signal %d to pid %d", rebootsignal, pid); return false; } return true; } WRAP_API(bool, lxcapi_reboot) static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) { int killret, ret; pid_t pid; int haltsignal = SIGPWR, state_client_fd = -EBADF; lxc_state_t states[MAX_STATE] = {0}; if (!c) return false; if (!do_lxcapi_is_running(c)) return true; pid = do_lxcapi_init_pid(c); if (pid <= 0) return true; /* Detect whether we should send SIGRTMIN + 3 (e.g. systemd). */ if (c->lxc_conf && c->lxc_conf->haltsignal) haltsignal = c->lxc_conf->haltsignal; else if (task_blocks_signal(pid, (SIGRTMIN + 3))) haltsignal = (SIGRTMIN + 3); /* Add a new state client before sending the shutdown signal so that we * don't miss a state. */ if (timeout != 0) { states[STOPPED] = 1; ret = lxc_cmd_add_state_client(c->name, c->config_path, states, &state_client_fd); if (ret < 0) return false; if (state_client_fd < 0) return false; if (ret == STOPPED) return true; if (ret < MAX_STATE) return false; } /* Send shutdown signal to container. */ killret = kill(pid, haltsignal); if (killret < 0) { if (state_client_fd >= 0) close(state_client_fd); WARN("Failed to send signal %d to pid %d", haltsignal, pid); return false; } TRACE("Sent signal %d to pid %d", haltsignal, pid); if (timeout == 0) return true; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); close(state_client_fd); if (ret < 0) return false; TRACE("Received state \"%s\"", lxc_state2str(ret)); if (ret != STOPPED) return false; return true; } WRAP_API_1(bool, lxcapi_shutdown, int) static bool lxcapi_createl(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, ...) { bool bret = false; char **args = NULL; va_list ap; if (!c) return false; current_config = c->lxc_conf; /* * since we're going to wait for create to finish, I don't think we * need to get a copy of the arguments. */ va_start(ap, flags); args = lxc_va_arg_list_to_argv(ap, 0, 0); va_end(ap); if (!args) { ERROR("Failed to allocate memory"); goto out; } bret = do_lxcapi_create(c, t, bdevtype, specs, flags, args); out: free(args); current_config = NULL; return bret; } static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key) { if (strcmp(key, "lxc.cgroup") == 0) clear_unexp_config_line(conf, key, true); else if (strcmp(key, "lxc.network") == 0) clear_unexp_config_line(conf, key, true); else if (strcmp(key, "lxc.hook") == 0) clear_unexp_config_line(conf, key, true); else clear_unexp_config_line(conf, key, false); if (!do_append_unexp_config_line(conf, key, "")) WARN("Error clearing configuration for %s", key); } static bool do_lxcapi_clear_config_item(struct lxc_container *c, const char *key) { int ret = 1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; config = lxc_getconfig(key); /* Verify that the config key exists and that it has a callback * implemented. */ if (config && config->clr) ret = config->clr(key, c->lxc_conf, NULL); if (!ret) do_clear_unexp_config_line(c->lxc_conf, key); container_mem_unlock(c); return ret == 0; } WRAP_API_1(bool, lxcapi_clear_config_item, const char *) static inline bool enter_net_ns(struct lxc_container *c) { pid_t pid = do_lxcapi_init_pid(c); if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) { if (!switch_to_ns(pid, "user")) return false; } return switch_to_ns(pid, "net"); } // used by qsort and bsearch functions for comparing names static inline int string_cmp(char **first, char **second) { return strcmp(*first, *second); } // used by qsort and bsearch functions for comparing container names static inline int container_cmp(struct lxc_container **first, struct lxc_container **second) { return strcmp((*first)->name, (*second)->name); } static bool add_to_array(char ***names, char *cname, int pos) { char **newnames = realloc(*names, (pos+1) * sizeof(char *)); if (!newnames) { ERROR("Out of memory"); return false; } *names = newnames; newnames[pos] = strdup(cname); if (!newnames[pos]) return false; // sort the arrray as we will use binary search on it qsort(newnames, pos + 1, sizeof(char *), (int (*)(const void *,const void *))string_cmp); return true; } static bool add_to_clist(struct lxc_container ***list, struct lxc_container *c, int pos, bool sort) { struct lxc_container **newlist = realloc(*list, (pos+1) * sizeof(struct lxc_container *)); if (!newlist) { ERROR("Out of memory"); return false; } *list = newlist; newlist[pos] = c; // sort the arrray as we will use binary search on it if (sort) qsort(newlist, pos + 1, sizeof(struct lxc_container *), (int (*)(const void *,const void *))container_cmp); return true; } static char** get_from_array(char ***names, char *cname, int size) { return (char **)bsearch(&cname, *names, size, sizeof(char *), (int (*)(const void *, const void *))string_cmp); } static bool array_contains(char ***names, char *cname, int size) { if(get_from_array(names, cname, size) != NULL) return true; return false; } static bool remove_from_array(char ***names, char *cname, int size) { char **result = get_from_array(names, cname, size); if (result != NULL) { free(result); return true; } return false; } static char **do_lxcapi_get_interfaces(struct lxc_container *c) { pid_t pid; int i, count = 0, pipefd[2]; char **interfaces = NULL; char interface[IFNAMSIZ]; if (pipe2(pipefd, O_CLOEXEC) < 0) return NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task to get interfaces information"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { // child int ret = 1, nbytes; struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to enter network namespace"); goto out; } /* Grab the list of interfaces */ if (getifaddrs(&interfaceArray)) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { nbytes = lxc_write_nointr(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ); if (nbytes < 0) goto out; count++; } ret = 0; out: if (interfaceArray) freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &interface, IFNAMSIZ) == IFNAMSIZ) { interface[IFNAMSIZ - 1] = '\0'; if (array_contains(&interfaces, interface, count)) continue; if (!add_to_array(&interfaces, interface, count)) ERROR("Failed to add \"%s\" to array", interface); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(interfaces[i]); free(interfaces); interfaces = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (interfaces) interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count); return interfaces; } WRAP_API(char **, lxcapi_get_interfaces) static char **do_lxcapi_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope) { int i, ret; pid_t pid; int pipefd[2]; char address[INET6_ADDRSTRLEN]; int count = 0; char **addresses = NULL; ret = pipe2(pipefd, O_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create pipe"); return NULL; } pid = fork(); if (pid < 0) { SYSERROR("Failed to create new process"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { ssize_t nbytes; char addressOutputBuffer[INET6_ADDRSTRLEN]; int ret = 1; char *address = NULL; void *tempAddrPtr = NULL; struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to attach to network namespace"); goto out; } /* Grab the list of interfaces */ if (getifaddrs(&interfaceArray)) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr; tempIfAddr = tempIfAddr->ifa_next) { if (tempIfAddr->ifa_addr == NULL) continue; if (tempIfAddr->ifa_addr->sa_family == AF_INET) { if (family && strcmp(family, "inet")) continue; tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; } else { if (family && strcmp(family, "inet6")) continue; if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope) continue; tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; } if (interface && strcmp(interface, tempIfAddr->ifa_name)) continue; else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) continue; address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, tempAddrPtr, addressOutputBuffer, sizeof(addressOutputBuffer)); if (!address) continue; nbytes = lxc_write_nointr(pipefd[1], address, INET6_ADDRSTRLEN); if (nbytes != INET6_ADDRSTRLEN) { SYSERROR("Failed to send ipv6 address \"%s\"", address); goto out; } count++; } ret = 0; out: if (interfaceArray) freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &address, INET6_ADDRSTRLEN) == INET6_ADDRSTRLEN) { address[INET6_ADDRSTRLEN - 1] = '\0'; if (!add_to_array(&addresses, address, count)) ERROR("PARENT: add_to_array failed"); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(addresses[i]); free(addresses); addresses = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (addresses) addresses = (char **)lxc_append_null_to_array((void **)addresses, count); return addresses; } WRAP_API_3(char **, lxcapi_get_ips, const char *, const char *, int) static int do_lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret = -1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; config = lxc_getconfig(key); /* Verify that the config key exists and that it has a callback * implemented. */ if (config && config->get) ret = config->get(key, retv, inlen, c->lxc_conf); container_mem_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_config_item, const char *, char *, int) static char* do_lxcapi_get_running_config_item(struct lxc_container *c, const char *key) { char *ret; if (!c || !c->lxc_conf) return NULL; if (container_mem_lock(c)) return NULL; ret = lxc_cmd_get_config_item(c->name, key, do_lxcapi_get_config_path(c)); container_mem_unlock(c); return ret; } WRAP_API_1(char *, lxcapi_get_running_config_item, const char *) static int do_lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen) { if (!key) return lxc_list_config_items(retv, inlen); /* * Support 'lxc.network.', i.e. 'lxc.network.0' * This is an intelligent result to show which keys are valid given * the type of nic it is */ if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; int ret = -1; if (strncmp(key, "lxc.network.", 12) == 0) ret = lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen); container_mem_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_keys, const char *, char *, int) static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file) { FILE *fout; bool ret = false, need_disklock = false; int lret; if (!alt_file) alt_file = c->configfile; if (!alt_file) return false; // should we write to stdout if no file is specified? // If we haven't yet loaded a config, load the stock config if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s while saving %s", lxc_global_config_value("lxc.default_config"), c->name); return false; } } if (!create_container_dir(c)) return false; /* * If we're writing to the container's config file, take the * disk lock. Otherwise just take the memlock to protect the * struct lxc_container while we're traversing it. */ if (strcmp(c->configfile, alt_file) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; fout = fopen(alt_file, "w"); if (!fout) goto out; write_config(fout, c->lxc_conf); fclose(fout); ret = true; out: if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_save_config, const char *) static bool mod_rdep(struct lxc_container *c0, struct lxc_container *c, bool inc) { FILE *f1; struct stat fbuf; void *buf = NULL; char *del = NULL; char path[MAXPATHLEN]; char newpath[MAXPATHLEN]; int fd, ret, n = 0, v = 0; bool bret = false; size_t len = 0, bytes = 0; if (container_disk_lock(c0)) return false; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c0->config_path, c0->name); if (ret < 0 || ret > MAXPATHLEN) goto out; ret = snprintf(newpath, MAXPATHLEN, "%s\n%s\n", c->config_path, c->name); if (ret < 0 || ret > MAXPATHLEN) goto out; /* If we find an lxc-snapshot file using the old format only listing the * number of snapshots we will keep using it. */ f1 = fopen(path, "r"); if (f1) { n = fscanf(f1, "%d", &v); fclose(f1); if (n == 1 && v == 0) { ret = remove(path); if (ret < 0) ERROR("%s - Failed to remove \"%s\"", strerror(errno), path); n = 0; } } if (n == 1) { v += inc ? 1 : -1; f1 = fopen(path, "w"); if (!f1) goto out; if (fprintf(f1, "%d\n", v) < 0) { ERROR("Error writing new snapshots value"); fclose(f1); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else { /* Here we know that we have or can use an lxc-snapshot file * using the new format. */ if (inc) { f1 = fopen(path, "a"); if (!f1) goto out; if (fprintf(f1, "%s", newpath) < 0) { ERROR("Error writing new snapshots entry"); ret = fclose(f1); if (ret != 0) SYSERROR("Error writing to or closing snapshots file"); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else if (!inc) { if ((fd = open(path, O_RDWR | O_CLOEXEC)) < 0) goto out; if (fstat(fd, &fbuf) < 0) { close(fd); goto out; } if (fbuf.st_size != 0) { buf = lxc_strmmap(NULL, fbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { SYSERROR("Failed to create mapping %s", path); close(fd); goto out; } len = strlen(newpath); while ((del = strstr((char *)buf, newpath))) { memmove(del, del + len, strlen(del) - len + 1); bytes += len; } lxc_strmunmap(buf, fbuf.st_size); if (ftruncate(fd, fbuf.st_size - bytes) < 0) { SYSERROR("Failed to truncate file %s", path); close(fd); goto out; } } close(fd); } /* If the lxc-snapshot file is empty, remove it. */ if (stat(path, &fbuf) < 0) goto out; if (!fbuf.st_size) { ret = remove(path); if (ret < 0) SYSERROR("Failed to remove \"%s\"", path); } } bret = true; out: container_disk_unlock(c0); return bret; } static void strip_newline(char *p) { size_t len = strlen(p); if (len < 1) return; if (p[len-1] == '\n') p[len-1] = '\0'; } void mod_all_rdeps(struct lxc_container *c, bool inc) { struct lxc_container *p; char *lxcpath = NULL, *lxcname = NULL, path[MAXPATHLEN]; size_t pathlen = 0, namelen = 0; FILE *f; int ret; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("Path name too long"); return; } f = fopen(path, "r"); if (f == NULL) return; while (getline(&lxcpath, &pathlen, f) != -1) { if (getline(&lxcname, &namelen, f) == -1) { ERROR("badly formatted file %s", path); goto out; } strip_newline(lxcpath); strip_newline(lxcname); if ((p = lxc_container_new(lxcname, lxcpath)) == NULL) { ERROR("Unable to find dependent container %s:%s", lxcpath, lxcname); continue; } if (!mod_rdep(p, c, inc)) ERROR("Failed to update snapshots file for %s:%s", lxcpath, lxcname); lxc_container_put(p); } out: free(lxcpath); free(lxcname); fclose(f); } static bool has_fs_snapshots(struct lxc_container *c) { FILE *f; char path[MAXPATHLEN]; int ret, v; struct stat fbuf; bool bret = false; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret > MAXPATHLEN) goto out; /* If the file doesn't exist there are no snapshots. */ if (stat(path, &fbuf) < 0) goto out; v = fbuf.st_size; if (v != 0) { f = fopen(path, "r"); if (!f) goto out; ret = fscanf(f, "%d", &v); fclose(f); // TODO: Figure out what to do with the return value of fscanf. if (ret != 1) INFO("Container uses new lxc-snapshots format %s", path); } bret = v != 0; out: return bret; } static bool has_snapshots(struct lxc_container *c) { char path[MAXPATHLEN]; struct dirent *direntp; int count=0; DIR *dir; if (!get_snappath_dir(c, path)) return false; dir = opendir(path); if (!dir) return false; while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; count++; break; } closedir(dir); return count > 0; } static bool do_destroy_container(struct lxc_conf *conf) { int ret; if (am_guest_unpriv()) { ret = userns_exec_full(conf, storage_destroy_wrapper, conf, "storage_destroy_wrapper"); if (ret < 0) return false; return true; } return storage_destroy(conf); } static int lxc_rmdir_onedev_wrapper(void *data) { char *arg = (char *) data; return lxc_rmdir_onedev(arg, "snaps"); } static bool container_destroy(struct lxc_container *c) { bool bret = false; int ret = 0; struct lxc_conf *conf; if (!c || !do_lxcapi_is_defined(c)) return false; conf = c->lxc_conf; if (container_disk_lock(c)) return false; if (!is_stopped(c)) { // we should queue some sort of error - in c->error_string? ERROR("container %s is not stopped", c->name); goto out; } if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) { /* Start of environment variable setup for hooks */ if (c->name && setenv("LXC_NAME", c->name, 1)) { SYSERROR("failed to set environment variable for container name"); } if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) { SYSERROR("failed to set environment variable for config path"); } if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) { SYSERROR("failed to set environment variable for rootfs mount"); } if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) { SYSERROR("failed to set environment variable for rootfs mount"); } if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) { SYSERROR("failed to set environment variable for console path"); } if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) { SYSERROR("failed to set environment variable for console log"); } /* End of environment variable setup for hooks */ if (run_lxc_hooks(c->name, "destroy", conf, c->get_config_path(c), NULL)) { ERROR("Error executing clone hook for %s", c->name); goto out; } } if (current_config && conf == current_config) { current_config = NULL; if (conf->logfd != -1) { close(conf->logfd); conf->logfd = -1; } } if (conf && conf->rootfs.path && conf->rootfs.mount) { if (!do_destroy_container(conf)) { ERROR("Error destroying rootfs for %s", c->name); goto out; } INFO("Destroyed rootfs for %s", c->name); } mod_all_rdeps(c, false); const char *p1 = do_lxcapi_get_config_path(c); char *path = alloca(strlen(p1) + strlen(c->name) + 2); sprintf(path, "%s/%s", p1, c->name); if (am_guest_unpriv()) ret = userns_exec_full(conf, lxc_rmdir_onedev_wrapper, path, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(path, "snaps"); if (ret < 0) { ERROR("Error destroying container directory for %s", c->name); goto out; } INFO("Destroyed directory for %s", c->name); bret = true; out: container_disk_unlock(c); return bret; } static bool do_lxcapi_destroy(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (has_snapshots(c)) { ERROR("Container %s has snapshots; not removing", c->name); return false; } if (has_fs_snapshots(c)) { ERROR("container %s has snapshots on its rootfs", c->name); return false; } return container_destroy(c); } WRAP_API(bool, lxcapi_destroy) static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (!lxcapi_snapshot_destroy_all(c)) { ERROR("Error deleting all snapshots"); return false; } return lxcapi_destroy(c); } WRAP_API(bool, lxcapi_destroy_with_snapshots) static bool set_config_item_locked(struct lxc_container *c, const char *key, const char *v) { struct lxc_config_t *config; if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; config = lxc_getconfig(key); if (!config) return false; if (config->set(key, v, c->lxc_conf, NULL) != 0) return false; return do_append_unexp_config_line(c->lxc_conf, key, v); } static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) { bool b = false; if (!c) return false; if (container_mem_lock(c)) return false; b = set_config_item_locked(c, key, v); container_mem_unlock(c); return b; } WRAP_API_2(bool, lxcapi_set_config_item, const char *, const char *) static char *lxcapi_config_file_name(struct lxc_container *c) { if (!c || !c->configfile) return NULL; return strdup(c->configfile); } static const char *lxcapi_get_config_path(struct lxc_container *c) { if (!c || !c->config_path) return NULL; return (const char *)(c->config_path); } /* * not for export * Just recalculate the c->configfile based on the * c->config_path, which must be set. * The lxc_container must be locked or not yet public. */ static bool set_config_filename(struct lxc_container *c) { char *newpath; int len, ret; if (!c->config_path) return false; /* $lxc_path + "/" + c->name + "/" + "config" + '\0' */ len = strlen(c->config_path) + strlen(c->name) + strlen("config") + 3; newpath = malloc(len); if (!newpath) return false; ret = snprintf(newpath, len, "%s/%s/config", c->config_path, c->name); if (ret < 0 || ret >= len) { fprintf(stderr, "Error printing out config file name\n"); free(newpath); return false; } free(c->configfile); c->configfile = newpath; return true; } static bool do_lxcapi_set_config_path(struct lxc_container *c, const char *path) { char *p; bool b = false; char *oldpath = NULL; if (!c) return b; if (container_mem_lock(c)) return b; p = strdup(path); if (!p) { ERROR("Out of memory setting new lxc path"); goto err; } b = true; if (c->config_path) oldpath = c->config_path; c->config_path = p; /* Since we've changed the config path, we have to change the * config file name too */ if (!set_config_filename(c)) { ERROR("Out of memory setting new config filename"); b = false; free(c->config_path); c->config_path = oldpath; oldpath = NULL; } err: free(oldpath); container_mem_unlock(c); return b; } WRAP_API_1(bool, lxcapi_set_config_path, const char *) static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) { int ret; if (!c) return false; if (is_stopped(c)) return false; if (container_disk_lock(c)) return false; ret = lxc_cgroup_set(subsys, value, c->name, c->config_path); container_disk_unlock(c); return ret == 0; } WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *) static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) { int ret; if (!c) return -1; if (is_stopped(c)) return -1; if (container_disk_lock(c)) return -1; ret = lxc_cgroup_get(subsys, retv, inlen, c->name, c->config_path); container_disk_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_cgroup_item, const char *, char *, int) const char *lxc_get_global_config_item(const char *key) { return lxc_global_config_value(key); } const char *lxc_get_version(void) { return LXC_VERSION; } static int copy_file(const char *old, const char *new) { int in, out; ssize_t len, ret; char buf[8096]; struct stat sbuf; if (file_exists(new)) { ERROR("copy destination %s exists", new); return -1; } ret = stat(old, &sbuf); if (ret < 0) { INFO("Error stat'ing %s", old); return -1; } in = open(old, O_RDONLY); if (in < 0) { SYSERROR("Error opening original file %s", old); return -1; } out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644); if (out < 0) { SYSERROR("Error opening new file %s", new); close(in); return -1; } while (1) { len = lxc_read_nointr(in, buf, 8096); if (len < 0) { SYSERROR("Error reading old file %s", old); goto err; } if (len == 0) break; ret = lxc_write_nointr(out, buf, len); if (ret < len) { /* should we retry? */ SYSERROR("Error: write to new file %s was interrupted", new); goto err; } } close(in); close(out); // we set mode, but not owner/group ret = chmod(new, sbuf.st_mode); if (ret) { SYSERROR("Error setting mode on %s", new); return -1; } return 0; err: close(in); close(out); return -1; } static int copyhooks(struct lxc_container *oldc, struct lxc_container *c) { int i, len, ret; struct lxc_list *it; char *cpath; len = strlen(oldc->config_path) + strlen(oldc->name) + 3; cpath = alloca(len); ret = snprintf(cpath, len, "%s/%s/", oldc->config_path, oldc->name); if (ret < 0 || ret >= len) return -1; for (i=0; ilxc_conf->hooks[i]) { char *hookname = it->elem; char *fname = strrchr(hookname, '/'); char tmppath[MAXPATHLEN]; if (!fname) // relative path - we don't support, but maybe we should return 0; if (strncmp(hookname, cpath, len - 1) != 0) { // this hook is public - ignore continue; } // copy the script, and change the entry in confile ret = snprintf(tmppath, MAXPATHLEN, "%s/%s/%s", c->config_path, c->name, fname+1); if (ret < 0 || ret >= MAXPATHLEN) return -1; ret = copy_file(it->elem, tmppath); if (ret < 0) return -1; free(it->elem); it->elem = strdup(tmppath); if (!it->elem) { ERROR("out of memory copying hook path"); return -1; } } } if (!clone_update_unexp_hooks(c->lxc_conf, oldc->config_path, c->config_path, oldc->name, c->name)) { ERROR("Error saving new hooks in clone"); return -1; } do_lxcapi_save_config(c, NULL); return 0; } static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c) { char newpath[MAXPATHLEN]; char *oldpath = oldc->lxc_conf->fstab; int ret; if (!oldpath) return 0; clear_unexp_config_line(c->lxc_conf, "lxc.mount", false); char *p = strrchr(oldpath, '/'); if (!p) return -1; ret = snprintf(newpath, MAXPATHLEN, "%s/%s%s", c->config_path, c->name, p); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("error printing new path for %s", oldpath); return -1; } if (file_exists(newpath)) { ERROR("error: fstab file %s exists", newpath); return -1; } if (copy_file(oldpath, newpath) < 0) { ERROR("error: copying %s to %s", oldpath, newpath); return -1; } free(c->lxc_conf->fstab); c->lxc_conf->fstab = strdup(newpath); if (!c->lxc_conf->fstab) { ERROR("error: allocating pathname"); return -1; } if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount", newpath)) { ERROR("error saving new lxctab"); return -1; } return 0; } static void copy_rdepends(struct lxc_container *c, struct lxc_container *c0) { char path0[MAXPATHLEN], path1[MAXPATHLEN]; int ret; ret = snprintf(path0, MAXPATHLEN, "%s/%s/lxc_rdepends", c0->config_path, c0->name); if (ret < 0 || ret >= MAXPATHLEN) { WARN("Error copying reverse dependencies"); return; } ret = snprintf(path1, MAXPATHLEN, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) { WARN("Error copying reverse dependencies"); return; } if (copy_file(path0, path1) < 0) { INFO("Error copying reverse dependencies"); return; } } static bool add_rdepends(struct lxc_container *c, struct lxc_container *c0) { int ret; char path[MAXPATHLEN]; FILE *f; bool bret; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; f = fopen(path, "a"); if (!f) return false; bret = true; // if anything goes wrong, just return an error if (fprintf(f, "%s\n%s\n", c0->config_path, c0->name) < 0) bret = false; if (fclose(f) != 0) bret = false; return bret; } /* * If the fs natively supports snapshot clones with no penalty, * then default to those even if not requested. * Currently we only do this for btrfs. */ bool should_default_to_snapshot(struct lxc_container *c0, struct lxc_container *c1) { int ret; size_t l0 = strlen(c0->config_path) + strlen(c0->name) + 2; size_t l1 = strlen(c1->config_path) + strlen(c1->name) + 2; char *p0 = alloca(l0 + 1); char *p1 = alloca(l1 + 1); char *rootfs = c0->lxc_conf->rootfs.path; ret = snprintf(p0, l0, "%s/%s", c0->config_path, c0->name); if (ret < 0 || ret >= l0) return false; ret = snprintf(p1, l1, "%s/%s", c1->config_path, c1->name); if (ret < 0 || ret >= l1) return false; if (!is_btrfs_fs(p0) || !is_btrfs_fs(p1)) return false; if (is_btrfs_subvol(rootfs) <= 0) return false; return btrfs_same_fs(p0, p1) == 0; } static int copy_storage(struct lxc_container *c0, struct lxc_container *c, const char *newtype, int flags, const char *bdevdata, uint64_t newsize) { struct lxc_storage *bdev; int need_rdep; if (should_default_to_snapshot(c0, c)) flags |= LXC_CLONE_SNAPSHOT; bdev = storage_copy(c0, c->name, c->config_path, newtype, flags, bdevdata, newsize, &need_rdep); if (!bdev) { ERROR("Error copying storage."); return -1; } /* Set new rootfs. */ free(c->lxc_conf->rootfs.path); c->lxc_conf->rootfs.path = strdup(bdev->src); /* Set new bdev type. */ free(c->lxc_conf->rootfs.bdev_type); c->lxc_conf->rootfs.bdev_type = strdup(bdev->type); storage_put(bdev); if (!c->lxc_conf->rootfs.path) { ERROR("Out of memory while setting storage path."); return -1; } if (!c->lxc_conf->rootfs.bdev_type) { ERROR("Out of memory while setting rootfs backend."); return -1; } /* Append a new lxc.rootfs entry to the unexpanded config. */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false); if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs", c->lxc_conf->rootfs.path)) { ERROR("Error saving new rootfs to cloned config."); return -1; } /* Append a new lxc.rootfs.backend entry to the unexpanded config. */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.backend", false); if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs.backend", c->lxc_conf->rootfs.bdev_type)) { ERROR("Error saving new rootfs backend to cloned config."); return -1; } if (flags & LXC_CLONE_SNAPSHOT) copy_rdepends(c, c0); if (need_rdep) { if (!add_rdepends(c, c0)) WARN("Error adding reverse dependency from %s to %s", c->name, c0->name); } mod_all_rdeps(c, true); return 0; } struct clone_update_data { struct lxc_container *c0; struct lxc_container *c1; int flags; char **hookargs; }; static int clone_update_rootfs(struct clone_update_data *data) { struct lxc_container *c0 = data->c0; struct lxc_container *c = data->c1; int flags = data->flags; char **hookargs = data->hookargs; int ret = -1; char path[MAXPATHLEN]; struct lxc_storage *bdev; FILE *fout; struct lxc_conf *conf = c->lxc_conf; /* update hostname in rootfs */ /* we're going to mount, so run in a clean namespace to simplify cleanup */ if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (unshare(CLONE_NEWNS) < 0) return -1; bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) return -1; if (strcmp(bdev->type, "dir") != 0) { if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); storage_put(bdev); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } if (bdev->ops->mount(bdev) < 0) { storage_put(bdev); return -1; } } else { // TODO come up with a better way free(bdev->dest); bdev->dest = strdup(bdev->src); } if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) { /* Start of environment variable setup for hooks */ if (c0->name && setenv("LXC_SRC_NAME", c0->name, 1)) { SYSERROR("failed to set environment variable for source container name"); } if (c->name && setenv("LXC_NAME", c->name, 1)) { SYSERROR("failed to set environment variable for container name"); } if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) { SYSERROR("failed to set environment variable for config path"); } if (bdev->dest && setenv("LXC_ROOTFS_MOUNT", bdev->dest, 1)) { SYSERROR("failed to set environment variable for rootfs mount"); } if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) { SYSERROR("failed to set environment variable for rootfs mount"); } if (run_lxc_hooks(c->name, "clone", conf, c->get_config_path(c), hookargs)) { ERROR("Error executing clone hook for %s", c->name); storage_put(bdev); return -1; } } if (!(flags & LXC_CLONE_KEEPNAME)) { ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); storage_put(bdev); if (ret < 0 || ret >= MAXPATHLEN) return -1; if (!file_exists(path)) return 0; if (!(fout = fopen(path, "w"))) { SYSERROR("unable to open %s: ignoring", path); return 0; } if (fprintf(fout, "%s", c->name) < 0) { fclose(fout); return -1; } if (fclose(fout) < 0) return -1; } else { storage_put(bdev); } return 0; } static int clone_update_rootfs_wrapper(void *data) { struct clone_update_data *arg = (struct clone_update_data *) data; return clone_update_rootfs(arg); } /* * We want to support: sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \ -p|--lvprefix lvprefix -t|--fstype fstype -B backingstore -s [ implies overlayfs] -s -B overlayfs -s -B aufs only rootfs gets converted (copied/snapshotted) on clone. */ static int create_file_dirname(char *path, struct lxc_conf *conf) { char *p = strrchr(path, '/'); int ret = -1; if (!p) return -1; *p = '\0'; ret = do_create_container_dir(path, conf); *p = '/'; return ret; } static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { struct lxc_container *c2 = NULL; char newpath[MAXPATHLEN]; int ret, storage_copied = 0; char *origroot = NULL, *saved_unexp_conf = NULL; struct clone_update_data data; size_t saved_unexp_len; FILE *fout; pid_t pid; if (!c || !do_lxcapi_is_defined(c)) return NULL; if (container_mem_lock(c)) return NULL; if (!is_stopped(c)) { ERROR("error: Original container (%s) is running", c->name); goto out; } // Make sure the container doesn't yet exist. if (!newname) newname = c->name; if (!lxcpath) lxcpath = do_lxcapi_get_config_path(c); ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", lxcpath, newname); if (ret < 0 || ret >= MAXPATHLEN) { SYSERROR("clone: failed making config pathname"); goto out; } if (file_exists(newpath)) { ERROR("error: clone: %s exists", newpath); goto out; } ret = create_file_dirname(newpath, c->lxc_conf); if (ret < 0 && errno != EEXIST) { ERROR("Error creating container dir for %s", newpath); goto out; } // copy the configuration, tweak it as needed, if (c->lxc_conf->rootfs.path) { origroot = c->lxc_conf->rootfs.path; c->lxc_conf->rootfs.path = NULL; } fout = fopen(newpath, "w"); if (!fout) { SYSERROR("open %s", newpath); goto out; } saved_unexp_conf = c->lxc_conf->unexpanded_config; saved_unexp_len = c->lxc_conf->unexpanded_len; c->lxc_conf->unexpanded_config = strdup(saved_unexp_conf); if (!c->lxc_conf->unexpanded_config) { ERROR("Out of memory"); fclose(fout); goto out; } clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false); write_config(fout, c->lxc_conf); fclose(fout); c->lxc_conf->rootfs.path = origroot; free(c->lxc_conf->unexpanded_config); c->lxc_conf->unexpanded_config = saved_unexp_conf; saved_unexp_conf = NULL; c->lxc_conf->unexpanded_len = saved_unexp_len; ret = snprintf(newpath, MAXPATHLEN, "%s/%s/rootfs", lxcpath, newname); if (ret < 0 || ret >= MAXPATHLEN) { SYSERROR("clone: failed making rootfs pathname"); goto out; } if (mkdir(newpath, 0755) < 0) { SYSERROR("error creating %s", newpath); goto out; } if (am_guest_unpriv()) { if (chown_mapped_root(newpath, c->lxc_conf) < 0) { ERROR("Error chowning %s to container root", newpath); goto out; } } c2 = lxc_container_new(newname, lxcpath); if (!c2) { ERROR("clone: failed to create new container (%s %s)", newname, lxcpath); goto out; } // copy/snapshot rootfs's ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); if (ret < 0) goto out; // update utsname if (!(flags & LXC_CLONE_KEEPNAME)) { clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false); if (!set_config_item_locked(c2, "lxc.utsname", newname)) { ERROR("Error setting new hostname"); goto out; } } // copy hooks ret = copyhooks(c, c2); if (ret < 0) { ERROR("error copying hooks"); goto out; } if (copy_fstab(c, c2) < 0) { ERROR("error copying fstab"); goto out; } // update macaddrs if (!(flags & LXC_CLONE_KEEPMACADDR)) { if (!network_new_hwaddrs(c2->lxc_conf)) { ERROR("Error updating mac addresses"); goto out; } } // update absolute paths for overlay mount directories if (ovl_update_abs_paths(c2->lxc_conf, c->config_path, c->name, lxcpath, newname) < 0) goto out; // We've now successfully created c2's storage, so clear it out if we // fail after this storage_copied = 1; if (!c2->save_config(c2, NULL)) goto out; if ((pid = fork()) < 0) { SYSERROR("fork"); goto out; } if (pid > 0) { ret = wait_for_pid(pid); if (ret) goto out; container_mem_unlock(c); return c2; } data.c0 = c; data.c1 = c2; data.flags = flags; data.hookargs = hookargs; if (am_guest_unpriv()) ret = userns_exec_full(c->lxc_conf, clone_update_rootfs_wrapper, &data, "clone_update_rootfs_wrapper"); else ret = clone_update_rootfs(&data); if (ret < 0) _exit(EXIT_FAILURE); container_mem_unlock(c); _exit(EXIT_SUCCESS); out: container_mem_unlock(c); if (c2) { if (!storage_copied) c2->lxc_conf->rootfs.path = NULL; c2->destroy(c2); lxc_container_put(c2); } return NULL; } static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { struct lxc_container * ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_clone(c, newname, lxcpath, flags, bdevtype, bdevdata, newsize, hookargs); current_config = NULL; return ret; } static bool do_lxcapi_rename(struct lxc_container *c, const char *newname) { struct lxc_storage *bdev; struct lxc_container *newc; if (!c || !c->name || !c->config_path || !c->lxc_conf) return false; if (has_fs_snapshots(c) || has_snapshots(c)) { ERROR("Renaming a container with snapshots is not supported"); return false; } bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } newc = lxcapi_clone(c, newname, c->config_path, LXC_CLONE_KEEPMACADDR, NULL, bdev->type, 0, NULL); storage_put(bdev); if (!newc) { lxc_container_put(newc); return false; } if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); if (!container_destroy(c)) { ERROR("Could not destroy existing container %s", c->name); return false; } return true; } WRAP_API_1(bool, lxcapi_rename, const char *) static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process); current_config = NULL; return ret; } static int do_lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]) { lxc_attach_command_t command; pid_t pid; int r; if (!c) return -1; command.program = (char*)program; command.argv = (char**)argv; r = lxc_attach(c->name, c->config_path, lxc_attach_run_command, &command, options, &pid); if (r < 0) { ERROR("ups"); return r; } return lxc_wait_for_pid_status(pid); } static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]) { int ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_attach_run_wait(c, options, program, argv); current_config = NULL; return ret; } static int get_next_index(const char *lxcpath, char *cname) { char *fname; struct stat sb; int i = 0, ret; fname = alloca(strlen(lxcpath) + 20); while (1) { sprintf(fname, "%s/snap%d", lxcpath, i); ret = stat(fname, &sb); if (ret != 0) return i; i++; } } static bool get_snappath_dir(struct lxc_container *c, char *snappath) { int ret; /* * If the old style snapshot path exists, use it * /var/lib/lxc -> /var/lib/lxcsnaps */ ret = snprintf(snappath, MAXPATHLEN, "%ssnaps", c->config_path); if (ret < 0 || ret >= MAXPATHLEN) return false; if (dir_exists(snappath)) { ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; return true; } /* * Use the new style path * /var/lib/lxc -> /var/lib/lxc + c->name + /snaps + \0 */ ret = snprintf(snappath, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) return false; return true; } static int do_lxcapi_snapshot(struct lxc_container *c, const char *commentfile) { int i, flags, ret; struct lxc_container *c2; char snappath[MAXPATHLEN], newname[20]; if (!c || !lxcapi_is_defined(c)) return -1; if (!storage_can_backup(c->lxc_conf)) { ERROR("%s's backing store cannot be backed up.", c->name); ERROR("Your container must use another backing store type."); return -1; } if (!get_snappath_dir(c, snappath)) return -1; i = get_next_index(snappath, c->name); if (mkdir_p(snappath, 0755) < 0) { ERROR("Failed to create snapshot directory %s", snappath); return -1; } ret = snprintf(newname, 20, "snap%d", i); if (ret < 0 || ret >= 20) return -1; /* * We pass LXC_CLONE_SNAPSHOT to make sure that a rdepends file entry is * created in the original container */ flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME | LXC_CLONE_KEEPBDEVTYPE | LXC_CLONE_MAYBE_SNAPSHOT; if (storage_is_dir(c->lxc_conf, c->lxc_conf->rootfs.path)) { ERROR("Snapshot of directory-backed container requested."); ERROR("Making a copy-clone. If you do want snapshots, then"); ERROR("please create an aufs or overlayfs clone first, snapshot that"); ERROR("and keep the original container pristine."); flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; } c2 = do_lxcapi_clone(c, newname, snappath, flags, NULL, NULL, 0, NULL); if (!c2) { ERROR("clone of %s:%s failed", c->config_path, c->name); return -1; } lxc_container_put(c2); // Now write down the creation time time_t timer; char buffer[25]; struct tm* tm_info; FILE *f; time(&timer); tm_info = localtime(&timer); strftime(buffer, 25, "%Y:%m:%d %H:%M:%S", tm_info); char *dfnam = alloca(strlen(snappath) + strlen(newname) + 5); sprintf(dfnam, "%s/%s/ts", snappath, newname); f = fopen(dfnam, "w"); if (!f) { ERROR("Failed to open %s", dfnam); return -1; } if (fprintf(f, "%s", buffer) < 0) { SYSERROR("Writing timestamp"); fclose(f); return -1; } ret = fclose(f); if (ret != 0) { SYSERROR("Writing timestamp"); return -1; } if (commentfile) { // $p / $name / comment \0 int len = strlen(snappath) + strlen(newname) + 10; char *path = alloca(len); sprintf(path, "%s/%s/comment", snappath, newname); return copy_file(commentfile, path) < 0 ? -1 : i; } return i; } WRAP_API_1(int, lxcapi_snapshot, const char *) static void lxcsnap_free(struct lxc_snapshot *s) { free(s->name); free(s->comment_pathname); free(s->timestamp); free(s->lxcpath); } static char *get_snapcomment_path(char* snappath, char *name) { // $snappath/$name/comment int ret, len = strlen(snappath) + strlen(name) + 10; char *s = malloc(len); if (s) { ret = snprintf(s, len, "%s/%s/comment", snappath, name); if (ret < 0 || ret >= len) { free(s); s = NULL; } } return s; } static char *get_timestamp(char* snappath, char *name) { char path[MAXPATHLEN], *s = NULL; int ret, len; FILE *fin; ret = snprintf(path, MAXPATHLEN, "%s/%s/ts", snappath, name); if (ret < 0 || ret >= MAXPATHLEN) return NULL; fin = fopen(path, "r"); if (!fin) return NULL; (void) fseek(fin, 0, SEEK_END); len = ftell(fin); (void) fseek(fin, 0, SEEK_SET); if (len > 0) { s = malloc(len+1); if (s) { s[len] = '\0'; if (fread(s, 1, len, fin) != len) { SYSERROR("reading timestamp"); free(s); s = NULL; } } } fclose(fin); return s; } static int do_lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps) { char snappath[MAXPATHLEN], path2[MAXPATHLEN]; int count = 0, ret; struct dirent *direntp; struct lxc_snapshot *snaps =NULL, *nsnaps; DIR *dir; if (!c || !lxcapi_is_defined(c)) return -1; if (!get_snappath_dir(c, snappath)) { ERROR("path name too long"); return -1; } dir = opendir(snappath); if (!dir) { INFO("failed to open %s - assuming no snapshots", snappath); return 0; } while ((direntp = readdir(dir))) { if (!direntp) break; if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; ret = snprintf(path2, MAXPATHLEN, "%s/%s/config", snappath, direntp->d_name); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); goto out_free; } if (!file_exists(path2)) continue; nsnaps = realloc(snaps, (count + 1)*sizeof(*snaps)); if (!nsnaps) { SYSERROR("Out of memory"); goto out_free; } snaps = nsnaps; snaps[count].free = lxcsnap_free; snaps[count].name = strdup(direntp->d_name); if (!snaps[count].name) goto out_free; snaps[count].lxcpath = strdup(snappath); if (!snaps[count].lxcpath) { free(snaps[count].name); goto out_free; } snaps[count].comment_pathname = get_snapcomment_path(snappath, direntp->d_name); snaps[count].timestamp = get_timestamp(snappath, direntp->d_name); count++; } if (closedir(dir)) WARN("failed to close directory"); *ret_snaps = snaps; return count; out_free: if (snaps) { int i; for (i=0; iname || !c->config_path) return false; if (has_fs_snapshots(c)) { ERROR("container rootfs has dependent snapshots"); return false; } bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } if (!newname) newname = c->name; if (!get_snappath_dir(c, clonelxcpath)) { storage_put(bdev); return false; } // how should we lock this? snap = lxc_container_new(snapname, clonelxcpath); if (!snap || !lxcapi_is_defined(snap)) { ERROR("Could not open snapshot %s", snapname); if (snap) lxc_container_put(snap); storage_put(bdev); return false; } if (strcmp(c->name, newname) == 0) { if (!container_destroy(c)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); storage_put(bdev); return false; } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0) flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; rest = lxcapi_clone(snap, newname, c->config_path, flags, bdev->type, NULL, 0, NULL); storage_put(bdev); if (rest && lxcapi_is_defined(rest)) b = true; if (rest) lxc_container_put(rest); lxc_container_put(snap); return b; } WRAP_API_2(bool, lxcapi_snapshot_restore, const char *, const char *) static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath) { struct lxc_container *snap = NULL; bool bret = false; snap = lxc_container_new(snapname, clonelxcpath); if (!snap) { ERROR("Could not find snapshot %s", snapname); goto err; } if (!do_lxcapi_destroy(snap)) { ERROR("Could not destroy snapshot %s", snapname); goto err; } bret = true; err: if (snap) lxc_container_put(snap); return bret; } static bool remove_all_snapshots(const char *path) { DIR *dir; struct dirent *direntp; bool bret = true; dir = opendir(path); if (!dir) { SYSERROR("opendir on snapshot path %s", path); return false; } while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; if (!do_snapshot_destroy(direntp->d_name, path)) { bret = false; continue; } } closedir(dir); if (rmdir(path)) SYSERROR("Error removing directory %s", path); return bret; } static bool do_lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname) { char clonelxcpath[MAXPATHLEN]; if (!c || !c->name || !c->config_path || !snapname) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return do_snapshot_destroy(snapname, clonelxcpath); } WRAP_API_1(bool, lxcapi_snapshot_destroy, const char *) static bool do_lxcapi_snapshot_destroy_all(struct lxc_container *c) { char clonelxcpath[MAXPATHLEN]; if (!c || !c->name || !c->config_path) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return remove_all_snapshots(clonelxcpath); } WRAP_API(bool, lxcapi_snapshot_destroy_all) static bool do_lxcapi_may_control(struct lxc_container *c) { return lxc_try_cmd(c->name, c->config_path) == 0; } WRAP_API(bool, lxcapi_may_control) static bool do_add_remove_node(pid_t init_pid, const char *path, bool add, struct stat *st) { int ret; char *tmp; pid_t pid; char chrootpath[MAXPATHLEN]; char *directory_path = NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork()"); return false; } if (pid) { ret = wait_for_pid(pid); if (ret != 0) { ERROR("Failed to create device node"); return false; } return true; } /* prepare the path */ ret = snprintf(chrootpath, MAXPATHLEN, "/proc/%d/root", init_pid); if (ret < 0 || ret >= MAXPATHLEN) return false; ret = chroot(chrootpath); if (ret < 0) _exit(EXIT_FAILURE); ret = chdir("/"); if (ret < 0) _exit(EXIT_FAILURE); /* remove path if it exists */ ret = faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW); if(ret == 0) { ret = unlink(path); if (ret < 0) { ERROR("%s - Failed to remove \"%s\"", strerror(errno), path); _exit(EXIT_FAILURE); } } if (!add) _exit(EXIT_SUCCESS); /* create any missing directories */ tmp = strdup(path); if (!tmp) _exit(EXIT_FAILURE); directory_path = dirname(tmp); ret = mkdir_p(directory_path, 0755); if (ret < 0 && errno != EEXIST) { ERROR("%s - Failed to create path \"%s\"", strerror(errno), directory_path); free(tmp); _exit(EXIT_FAILURE); } /* create the device node */ ret = mknod(path, st->st_mode, st->st_rdev); free(tmp); if (ret < 0) { ERROR("%s - Failed to create device node at \"%s\"", strerror(errno), path); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } static bool add_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path, bool add) { int ret; struct stat st; char value[MAX_BUFFER]; const char *p; /* make sure container is running */ if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); return false; } /* use src_path if dest_path is NULL otherwise use dest_path */ p = dest_path ? dest_path : src_path; /* make sure we can access p */ if(access(p, F_OK) < 0 || stat(p, &st) < 0) return false; /* continue if path is character device or block device */ if (S_ISCHR(st.st_mode)) ret = snprintf(value, MAX_BUFFER, "c %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else if (S_ISBLK(st.st_mode)) ret = snprintf(value, MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else return false; /* check snprintf return code */ if (ret < 0 || ret >= MAX_BUFFER) return false; if (!do_add_remove_node(do_lxcapi_init_pid(c), p, add, &st)) return false; /* add or remove device to/from cgroup access list */ if (add) { if (!do_lxcapi_set_cgroup_item(c, "devices.allow", value)) { ERROR("set_cgroup_item failed while adding the device node"); return false; } } else { if (!do_lxcapi_set_cgroup_item(c, "devices.deny", value)) { ERROR("set_cgroup_item failed while removing the device node"); return false; } } return true; } static bool do_lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_host_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, true); } WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *) static bool do_lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_host_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, false); } WRAP_API_2(bool, lxcapi_remove_device_node, const char *, const char *) static bool do_lxcapi_attach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { pid_t init_pid; int ret = 0; if (am_guest_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } ret = lxc_netdev_isup(ifname); if (ret > 0) { /* netdev of ifname is up. */ ret = lxc_netdev_down(ifname); if (ret) goto err; } init_pid = do_lxcapi_init_pid(c); ret = lxc_netdev_move_by_name(ifname, init_pid, dst_ifname); if (ret) goto err; INFO("Moved network device \"%s\" to network namespace of %d", ifname, init_pid); return true; err: return false; } WRAP_API_2(bool, lxcapi_attach_interface, const char *, const char *) static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { int ret; pid_t pid, pid_outside; if (am_guest_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } pid_outside = lxc_raw_getpid(); pid = fork(); if (pid < 0) { ERROR("Failed to fork"); return false; } if (pid == 0) { /* child */ pid_t init_pid; init_pid = do_lxcapi_init_pid(c); if (!switch_to_ns(init_pid, "net")) { ERROR("Failed to enter network namespace"); _exit(EXIT_FAILURE); } ret = lxc_netdev_isup(ifname); if (ret < 0) { ERROR("Failed to determine whether network device \"%s\" is up", ifname); _exit(EXIT_FAILURE); } /* netdev of ifname is up. */ if (ret) { ret = lxc_netdev_down(ifname); if (ret) { ERROR("Failed to set network device \"%s\" down", ifname); _exit(EXIT_FAILURE); } } ret = lxc_netdev_move_by_name(ifname, pid_outside, dst_ifname); /* -EINVAL means there is no netdev named as ifname. */ if (ret < 0) { if (ret == -EINVAL) ERROR("Network device \"%s\" not found", ifname); else ERROR("Failed to remove network device \"%s\"", ifname); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } ret = wait_for_pid(pid); if (ret != 0) return false; INFO("Moved network device \"%s\" to network namespace of %d", ifname, pid_outside); return true; } WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *) static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size) { int ret = -1; struct migrate_opts *valid_opts = opts; /* If the caller has a bigger (newer) struct migrate_opts, let's make * sure that the stuff on the end is zero, i.e. that they didn't ask us * to do anything special. */ if (size > sizeof(*opts)) { unsigned char *addr; unsigned char *end; addr = (void *)opts + sizeof(*opts); end = (void *)opts + size; for (; addr < end; addr++) { if (*addr) { return -E2BIG; } } } /* If the caller has a smaller struct, let's zero out the end for them * so we don't accidentally use bits of it that they didn't know about * to initialize. */ if (size < sizeof(*opts)) { valid_opts = malloc(sizeof(*opts)); if (!valid_opts) return -ENOMEM; memset(valid_opts, 0, sizeof(*opts)); memcpy(valid_opts, opts, size); } switch (cmd) { case MIGRATE_PRE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_pre_dump(c, valid_opts); break; case MIGRATE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_dump(c, valid_opts); break; case MIGRATE_RESTORE: if (do_lxcapi_is_running(c)) { ERROR("container is already running"); goto on_error; } ret = !__criu_restore(c, valid_opts); break; default: ERROR("invalid migrate command %u", cmd); ret = -EINVAL; } on_error: if (size < sizeof(*opts)) free(valid_opts); return ret; } WRAP_API_3(int, lxcapi_migrate, unsigned int, struct migrate_opts *, unsigned int) static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.stop = stop; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_DUMP, &opts, sizeof(opts)); } WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool) static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_RESTORE, &opts, sizeof(opts)); } WRAP_API_2(bool, lxcapi_restore, char *, bool) static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...) { va_list ap; const char **argv; int ret; if (!c) return -1; current_config = c->lxc_conf; va_start(ap, arg); argv = lxc_va_arg_list_to_argv_const(ap, 1); va_end(ap); if (!argv) { ERROR("Memory allocation error."); ret = -1; goto out; } argv[0] = arg; ret = do_lxcapi_attach_run_wait(c, options, program, (const char * const *)argv); free((void*)argv); out: current_config = NULL; return ret; } struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; size_t len; if (!name) return NULL; c = malloc(sizeof(*c)); if (!c) { fprintf(stderr, "Failed to allocate memory for %s\n", name); return NULL; } memset(c, 0, sizeof(*c)); if (configpath) c->config_path = strdup(configpath); else c->config_path = strdup(lxc_global_config_value("lxc.lxcpath")); if (!c->config_path) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } remove_trailing_slashes(c->config_path); len = strlen(name); c->name = malloc(len + 1); if (!c->name) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } (void)strlcpy(c->name, name, len + 1); c->numthreads = 1; c->slock = lxc_newlock(c->config_path, name); if (!c->slock) { fprintf(stderr, "Failed to create lock for %s\n", name); goto err; } c->privlock = lxc_newlock(NULL, NULL); if (!c->privlock) { fprintf(stderr, "Failed to create private lock for %s\n", name); goto err; } if (!set_config_filename(c)) { fprintf(stderr, "Failed to create config file name for %s\n", name); goto err; } if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) { fprintf(stderr, "Failed to load config for %s\n", name); goto err; } if (ongoing_create(c) == 2) { ERROR("Failed to complete container creation for %s", c->name); container_destroy(c); lxcapi_clear_config(c); } c->daemonize = true; c->pidfile = NULL; // assign the member functions c->is_defined = lxcapi_is_defined; c->state = lxcapi_state; c->is_running = lxcapi_is_running; c->freeze = lxcapi_freeze; c->unfreeze = lxcapi_unfreeze; c->console = lxcapi_console; c->console_getfd = lxcapi_console_getfd; c->init_pid = lxcapi_init_pid; c->load_config = lxcapi_load_config; c->want_daemonize = lxcapi_want_daemonize; c->want_close_all_fds = lxcapi_want_close_all_fds; c->start = lxcapi_start; c->startl = lxcapi_startl; c->stop = lxcapi_stop; c->config_file_name = lxcapi_config_file_name; c->wait = lxcapi_wait; c->set_config_item = lxcapi_set_config_item; c->destroy = lxcapi_destroy; c->destroy_with_snapshots = lxcapi_destroy_with_snapshots; c->rename = lxcapi_rename; c->save_config = lxcapi_save_config; c->get_keys = lxcapi_get_keys; c->create = lxcapi_create; c->createl = lxcapi_createl; c->shutdown = lxcapi_shutdown; c->reboot = lxcapi_reboot; c->clear_config = lxcapi_clear_config; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; c->get_running_config_item = lxcapi_get_running_config_item; c->get_cgroup_item = lxcapi_get_cgroup_item; c->set_cgroup_item = lxcapi_set_cgroup_item; c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; c->clone = lxcapi_clone; c->get_interfaces = lxcapi_get_interfaces; c->get_ips = lxcapi_get_ips; c->attach = lxcapi_attach; c->attach_run_wait = lxcapi_attach_run_wait; c->attach_run_waitl = lxcapi_attach_run_waitl; c->snapshot = lxcapi_snapshot; c->snapshot_list = lxcapi_snapshot_list; c->snapshot_restore = lxcapi_snapshot_restore; c->snapshot_destroy = lxcapi_snapshot_destroy; c->snapshot_destroy_all = lxcapi_snapshot_destroy_all; c->may_control = lxcapi_may_control; c->add_device_node = lxcapi_add_device_node; c->remove_device_node = lxcapi_remove_device_node; c->attach_interface = lxcapi_attach_interface; c->detach_interface = lxcapi_detach_interface; c->checkpoint = lxcapi_checkpoint; c->restore = lxcapi_restore; c->migrate = lxcapi_migrate; return c; err: lxc_container_free(c); return NULL; } int lxc_get_wait_states(const char **states) { int i; if (states) for (i=0; i