lxc-1.0.10/0000755061062106075000000000000013105114547007371 500000000000000lxc-1.0.10/templates/0000755061062106075000000000000013105114547011367 500000000000000lxc-1.0.10/templates/lxc-cirros.in0000644061062106075000000002437713105114536013737 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 if [ $(id -u) -eq 0 ]; then CACHE_D="@LOCALSTATEDIR@/cache/lxc/cirros" else CACHE_D="$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 echo "lxc.mount.auto = cgroup:mixed proc:mixed sys:ro" >> $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-1.0.10/templates/lxc-ubuntu-cloud.in0000644061062106075000000002657213105114536015063 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 vivid wily xenial" 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 ## Create the fstab (empty by default) touch $path/fstab ## 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.mount = $path/fstab 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:,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;; -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 cache="$STATE_DIR/cache/lxc/cloud-$release" if [ $in_userns -eq 1 ]; then STATE_DIR="$HOME/.cache/lxc/" cache="$HOME/.cache/lxc/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-1.0.10/templates/lxc-archlinux.in0000755061062106075000000002512413105114536014425 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) lxc_network_type="veth" lxc_network_link="br0" default_path="@LXCPATH@" default_locale="en-US.UTF-8" default_timezone="UTC" pacman_config="/etc/pacman.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/vconsole.conf /etc/locale.conf /etc/locale.gen \ "${rootfs_path}/etc/" else echo "LANG=${default_locale}" > "${rootfs_path}/etc/locale.conf" echo "KEYMAP=us" > "${rootfs_path}/etc/vconsole.conf" cat > "${rootfs_path}/etc/adjtime" << EOF 0.0 0.0 0.0 0 LOCAL EOF 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" while read r; do [ "${r#nameserver}" = "$r" ] || echo "$r" done < /etc/resolv.conf > "${rootfs_path}/etc/resolv.conf" # 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 ln -s /usr/share/zoneinfo/${default_timezone} /etc/localtime # disable services unavailable for container for i in systemd-udevd.service \ systemd-udevd-control.socket \ systemd-udevd-kernel.socket \ proc-sys-fs-binfmt_misc.automount; do ln -s /dev/null /etc/systemd/system/\$i done # set default systemd target ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target # enable sigpwr signal handling in systemd as otherwise lxc-stop won't work ln -s /usr/lib/systemd/system/poweroff.target /etc/systemd/system/sigpwr.target # 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 return 0 } # write container configuration files copy_configuration() { mkdir -p "${config_path}" cat > "${config_path}/config" << EOF lxc.utsname=${name} lxc.autodev=1 lxc.tty=1 lxc.pts=1024 lxc.mount=${config_path}/fstab lxc.cap.drop=sys_module mac_admin mac_override sys_time lxc.kmsg=0 lxc.stopsignal=SIGRTMIN+4 #networking lxc.network.type=${lxc_network_type} lxc.network.link=${lxc_network_link} lxc.network.flags=up lxc.network.name=eth0 lxc.network.mtu=1500 #cgroups lxc.cgroup.devices.deny = a lxc.cgroup.devices.allow = c *:* m lxc.cgroup.devices.allow = b *:* m lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm lxc.cgroup.devices.allow = c 1:7 rwm lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 4:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:2 rwm lxc.cgroup.devices.allow = c 136:* rwm EOF grep -q "^lxc.rootfs" ${config_path}/config 2>/dev/null || echo "lxc.rootfs = ${path}/rootfs" >> ${config_path}/config cat > "${config_path}/fstab" << EOF sysfs sys sysfs defaults 0 0 proc proc proc nodev,noexec,nosuid 0 0 EOF 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|--packages=] [-p|--path=] [-t|--network_type=] [-l|--network_link=] [-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 ${default_path}/rootfs. The container config will go under ${default_path} in that case -P,--packages preinstall additional packages, comma-separated list -e,--enable_units Enable additional systemd units, comma-separated list -c,--config use specified pacman config when installing container packages -a,--arch use specified architecture instead of host's architecture -t,--network_type set container network interface type (${lxc_network_type}) -l,--network_link set network link device (${lxc_network_link}) -r,--root_passwd set container root password -h,--help print this help EOF return 0 } options=$(getopt -o hp:P:e:n:c:a:l:t:r: -l help,rootfs:,path:,packages:,enable_units:,name:,config:,arch:,network_type:,network_link:,root_passwd: -- "${@}") 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;; -c|--config) pacman_config=${2}; shift 2;; -a|--arch) arch=${2}; shift 2;; -t|--network_type) lxc_network_type=${2}; shift 2;; -l|--network_link) lxc_network_link=${2}; shift 2;; -r|--root_passwd) root_passwd=${2}; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ -z "${name}" ]; then echo "missing required 'name' parameter" exit 1 fi if [ ! -e /sys/class/net/${lxc_network_link} ]; then echo "network link interface, ${lxc_network_link}, does not exist" 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="${default_path}/${name}" 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}" = *'.'* ] || unit="${unit}.service" ln -s /usr/lib/systemd/system/"${unit}" \ "${rootfs_path}"/etc/systemd/system/multi-user.target.wants done fi if [ -n "${root_passwd}" ]; then echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd fi cat << EOF ArchLinux container ${name} is successfully created! The configuration is stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for information about configuring ArchLinux. EOF lxc-1.0.10/templates/lxc-busybox.in0000644061062106075000000002271513105114536014123 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= # 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 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 "/dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/fstab 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 # mount points cat <> $rootfs/etc/fstab shm /dev/shm tmpfs defaults 0 0 EOF # writable and readable for other chmod 644 $rootfs/etc/fstab || 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 } 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 # setting passwd for root CHPASSWD_FILE=$rootfs/root/chpasswd.sh cat <$CHPASSWD_FILE echo "setting root password to \"root\"" mount -n --bind /lib $rootfs/lib if [ \$? -ne 0 ]; then echo "Failed bind-mounting /lib at $rootfs/lib" exit 1 fi chroot $rootfs chpasswd </dev/null root:root EOFF if [ \$? -ne 0 ]; then echo "Failed to change root password" exit 1 fi umount $rootfs/lib EOF lxc-unshare -s MOUNT -- /bin/sh < $CHPASSWD_FILE rm $CHPASSWD_FILE # add ssh functionality if dropbear package available on host which dropbear >/dev/null 2>&1 if [ $? -eq 0 ]; then # copy dropbear binary cp $(which dropbear) $rootfs/usr/sbin if [ $? -ne 0 ]; then echo "Failed to copy dropbear in the rootfs" return 1 fi # 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" fi 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.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 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 echo "lxc.mount.auto = proc:mixed sys" >>$path/config if [ -f "$path/fstab" ]; then echo "lxc.mount = $path/fstab" >>$path/config fi } remap_userns() { path=$1 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then chown $LXC_MAPPED_UID $path/config $path/fstab >/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 $path/fstab >/dev/null 2>&1 chgrp -R root $path/rootfs >/dev/null 2>&1 fi } usage() { cat < EOF return 0 } options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid: -- "$@") 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;; --) 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 lxc-1.0.10/templates/lxc-altlinux.in0000644061062106075000000003240113105114536014261 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 root_password=rooter 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/veth0 cat < ${rootfs_path}/etc/net/ifaces/veth0/options BOOTPROTO=${BOOTPROTO} ONBOOT=yes NM_CONTROLLED=no TYPE=eth EOF if [ ${BOOTPROTO} != "dhcp" ]; then # ip address cat < ${rootfs_path}/etc/net/ifaces/veth0/ipv4address ${ipv4} EOF cat < ${rootfs_path}/etc/net/ifaces/veth0/ipv4route ${gw} EOF cat < ${rootfs_path}/etc/net/ifaces/veth0/resolv.conf nameserver ${dns} EOF cat < ${rootfs_path}/etc/net/ifaces/veth0/ipv6address ${ipv6} EOF cat < ${rootfs_path}/etc/net/ifaces/veth0/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 do chroot ${rootfs_path} chkconfig $service --list &>/dev/null && chroot ${rootfs_path} chkconfig $service on || 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 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 # touch file for fastboot touch ${rootfs_path}/fastboot chattr +i ${rootfs_path}/fastboot 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 echo "setting root passwd to $root_password" echo "root:$root_password" | chroot $rootfs_path chpasswd 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")" # PKG_LIST="basesystem apt apt-conf-sisyphus etcnet openssh-server passwd sysklogd net-tools e2fsprogs" mkdir -p $INSTALL_ROOT/var/lib/rpm rpm --root $INSTALL_ROOT --initdb $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 -Ha $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.mount = $config_path/fstab 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 EOF cat < $config_path/fstab proc proc proc nodev,noexec,nosuid 0 0 sysfs sys sysfs defaults 0 0 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 if [ -f $config_path/config ]; then echo "A container with that name exists, chose a different name" exit 1 fi 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-1.0.10/templates/lxc-download.in0000644061062106075000000004322113105114536014232 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=1 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" 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 -n " 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 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/zero 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 line; do fields=$(echo $line | awk '{ print $1 " " $2 " " $3 }') [ "$fields" = "0 0 4294967295" ] && { echo no; return; } || true echo $fields | grep -q " 0 1$" && { echo userns-root; return; } || true 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 } 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: -- "$@") if [ $? -ne 0 ]; 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 ! type $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 ! type 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 if ! type mktemp >/dev/null 2>&1; then DOWNLOAD_TEMP=/tmp/lxc-download.$$ mkdir -p $DOWNLOAD_TEMP else 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 read line; do # Basic CSV parser OLD_IFS=$IFS IFS=";" set -- $line IFS=$OLD_IFS [ -n "$DOWNLOAD_DIST" ] && [ "$1" != "$DOWNLOAD_DIST" ] && continue [ -n "$DOWNLOAD_RELEASE" ] && [ "$2" != "$DOWNLOAD_RELEASE" ] && continue [ -n "$DOWNLOAD_ARCH" ] && [ "$3" != "$DOWNLOAD_ARCH" ] && continue [ -n "$DOWNLOAD_VARIANT" ] && [ "$4" != "$DOWNLOAD_VARIANT" ] && continue [ -z "$5" ] || [ -z "$6" ] && continue printf "$1\t$2\t$3\t$4\t$5\n" done < ${DOWNLOAD_TEMP}/index echo "---" if [ "$DOWNLOAD_LIST_IMAGES" = "true" ]; then exit 1 fi # Interactive mode echo "" if [ -z "$DOWNLOAD_DIST" ]; then echo -n "Distribution: " read DOWNLOAD_DIST fi if [ -z "$DOWNLOAD_RELEASE" ]; then echo -n "Release: " read DOWNLOAD_RELEASE fi if [ -z "$DOWNLOAD_ARCH" ]; then echo -n "Architecture: " read 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 LXC_CACHE_PATH="$LXC_CACHE_BASE/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 read line; do # Basic CSV parser OLD_IFS=$IFS IFS=";" set -- $line IFS=$OLD_IFS if [ "$1" != "$DOWNLOAD_DIST" ] || \ [ "$2" != "$DOWNLOAD_RELEASE" ] || \ [ "$3" != "$DOWNLOAD_ARCH" ] || \ [ "$4" != "$DOWNLOAD_VARIANT" ] || \ [ -z "$6" ]; then continue fi DOWNLOAD_BUILD=$5 DOWNLOAD_URL=$6 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 chown -R $LXC_MAPPED_UID $LXC_CACHE_BASE >/dev/null 2>&1 || true fi if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then chgrp -R $LXC_MAPPED_GID $LXC_CACHE_BASE >/dev/null 2>&1 || true 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 line; do EXCLUDES="$EXCLUDES --exclude=$line" done < $excludelist fi 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 line; do fullpath=${LXC_ROOTFS}/$line [ ! -e "$fullpath" ] && continue TEMPLATE_FILES="$TEMPLATE_FILES $fullpath" done < $(relevant_file templates) fi # Replace variables in all templates 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 # 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 || true 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 || true fi if [ -e "$(relevant_file create-message)" ]; then echo "" echo "---" cat "$(relevant_file create-message)" fi exit 0 lxc-1.0.10/templates/lxc-plamo.in0000644061062106075000000002776113105114536013546 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:-"www.ring.gr.jp"} 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() { # create /dev chmod 666 $rootfs/dev/null mknod -m 666 $rootfs/dev/zero c 1 5 chmod 666 $rootfs/dev/random mknod -m 666 $rootfs/dev/urandom c 1 9 mkdir -m 755 $rootfs/dev/pts mkdir -m 755 $rootfs/dev/shm chmod 666 $rootfs/dev/tty chmod 600 $rootfs/dev/console mknod -m 666 $rootfs/dev/tty0 c 4 0 mknod -m 666 $rootfs/dev/tty1 c 4 1 mknod -m 666 $rootfs/dev/tty2 c 4 2 mknod -m 666 $rootfs/dev/tty3 c 4 3 mknod -m 666 $rootfs/dev/tty4 c 4 4 mknod -m 666 $rootfs/dev/full c 1 7 mknod -m 600 $rootfs/dev/initctl p mknod -m 666 $rootfs/dev/ptmx c 5 2 # 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 # root password echo "Setting root password to 'root'..." echo "root:root" | chroot $rootfs chpasswd echo "Please change root password!" # /etc/rc.d/rc.S ed - $rootfs/etc/rc.d/rc.S <<- "EOF" /^mount -w -n -t proc/;/^# ln -s \/bin\/true/-1d /^mknod \/dev\/unikey/;/^# Clean \/etc\/mtab/-2d /^# copy the rules/;/^# Set the hostname/-1d /^# Check the integrity/;/^# Clean up temporary/-1d w EOF # /etc/rc.d/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 # /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.mount = $path/fstab 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 # create the fstab (empty by default) touch $path/fstab || let ret++ 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 "$h $rootfs/$h none bind 0 0" >> $path/fstab # 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:-5.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=@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-1.0.10/templates/lxc-gentoo.in0000644061062106075000000006717413105114536013733 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" wget -O - "${stage3_baseurl}/${stage3_latest_tarball}" \ | tar -xjpf - --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" else store_user_message "${user} has the default password 'toor', please change it ASAP" 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" password="toor" 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;; --tty) [[ $2 -lt 6 ]] && tty=$2; shift 2;; --autologin) autologin=1; shift 1;; --) shift 1; break ;; *) break ;; esac done cacheroot="@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-1.0.10/templates/lxc-openmandriva.in0000644061062106075000000003325513105114536015114 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) cache_base=@LOCALSTATEDIR@/cache/lxc/openmandriva/$arch default_path=@LXCPATH@ default_profile=default root_password=root 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 -Ha $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.autodev = 1 lxc.tty = 4 lxc.pts = 1024 lxc.mount = $config_path/fstab 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 = 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 cat < $config_path/fstab proc $rootfs_path/proc proc nodev,noexec,nosuid 0 0 sysfs $rootfs_path/sys sysfs defaults 0 0 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-1.0.10/templates/lxc-ubuntu.in0000644061062106075000000005666113105114536013761 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@" if [ -r /etc/default/lxc ]; then . /etc/default/lxc fi configure_ubuntu() { rootfs=$1 hostname=$2 release=$3 user=$4 password=$5 # configure the network using the dhcp 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 # 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 rm -f $rootfs/etc/ssh/ssh_host_*key* mv $rootfs/etc/init/ssh.conf $rootfs/etc/init/ssh.conf.disabled DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure mv $rootfs/etc/init/ssh.conf.disabled $rootfs/etc/init/ssh.conf sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub 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() { rm -rf $cache/partial-$arch rm -rf $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:-"ssh,vim"} # 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')" echo "Installing packages in template: ${packages_template}" trap cleanup EXIT SIGHUP SIGINT SIGTERM # check the mini ubuntu was not already downloaded mkdir -p "$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 --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR else debootstrap --verbose --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 ..." mkdir -p $rootfs rsync -Ha $cache/rootfs-$arch/ $rootfs/ || return 1 return 0 } install_ubuntu() { rootfs=$1 release=$2 flushcache=$3 cache="$LOCALSTATEDIR/cache/lxc/$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..." rm -rf "$cache/partial-$arch" rm -rf "$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 ## Create the fstab (empty by default) touch $path/fstab ## 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.mount = $path/fstab 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 "$h $h2 none bind 0 0" >> $path/fstab # 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 ] [ -S | --auth-key ] [--rootfs ] [--packages ] [-u|--user ] [--password ] [--mirror ] [--security-mirror ] release: the ubuntu release (e.g. precise): defaults to host release on ubuntu, otherwise uses latest LTS 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:n:FS:du: -l arch:,bindhome:,help,path:,release:,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=precise # 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;; --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 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-1.0.10/templates/lxc-alpine.in0000644061062106075000000002452013105114536013674 00000000000000#!/bin/sh # 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 PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin export PATH key_sha256sums="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" get_static_apk () { wget="wget -q -O -" pkglist=alpine-keys:apk-tools-static auto_repo_dir= if [ -z "$repository" ]; then url=http://wiki.alpinelinux.org/cgi-bin/dl.cgi yaml_path="latest-stable/releases/$apk_arch/latest-releases.yaml" if [ -z "$release" ]; then echo -n "Determining the latest release... " release=$($wget $url/$yaml_path | \ awk '$1 == "branch:" {print $2; exit 0}') if [ -z "$release" ]; then release=$($wget $url/.latest.$apk_arch.txt | \ cut -d " " -f 3 | cut -d / -f 1 | uniq) fi if [ -z "$release" ]; then echo failed return 1 fi echo $release fi auto_repo_dir=$release/main repository=$url/$auto_repo_dir pkglist=$pkglist:alpine-mirrors fi rootfs="$1" echo "Using static apk from $repository/$apk_arch" wget="$wget $repository/$apk_arch" # parse APKINDEX to find the current versions static_pkgs=$($wget/APKINDEX.tar.gz | \ tar -Oxz APKINDEX | \ awk -F: -v pkglist=$pkglist ' BEGIN { split(pkglist,pkg) } $0 != "" { f[$1] = $2 } $0 == "" { for (i in pkg) if (pkg[i] == f["P"]) print(f["P"] "-" f["V"] ".apk") }') [ "$static_pkgs" ] || return 1 mkdir -p "$rootfs" || return 1 for pkg in $static_pkgs; do echo "Downloading $pkg" $wget/$pkg | tar -xz -C "$rootfs" done # clean up .apk meta files rm -f "$rootfs"/.[A-Z]* # verify checksum of the key keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//') checksum=$(echo "$key_sha256sums" | grep -w "$keyname") if [ -z "$checksum" ]; then echo "ERROR: checksum is missing for $keyname" return 1 fi (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1 # verify the static apk binary signature APK=$rootfs/sbin/apk.static openssl dgst -sha1 -verify $rootfs/etc/apk/keys/$keyname \ -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1 if [ "$auto_repo_dir" ]; then mirror_list=$rootfs/usr/share/alpine-mirrors/MIRRORS.txt mirror_count=$(wc -l $mirror_list | cut -d " " -f 1) random=$(hexdump -n 2 -e '/2 "%u"' /dev/urandom) repository=$(sed $(expr $random % $mirror_count + 1)\!d \ $mirror_list)$auto_repo_dir echo "Selecting mirror $repository" fi } install_alpine() { rootfs="$1" shift mkdir -p "$rootfs"/etc/apk || return 1 : ${keys_dir:=/etc/apk/keys} if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then cp -r "$keys_dir" "$rootfs"/etc/apk/keys fi if [ -n "$repository" ]; then echo "$repository" > "$rootfs"/etc/apk/repositories else cp /etc/apk/repositories "$rootfs"/etc/apk/repositories || return 1 if [ -n "$release" ]; then sed -E -i "s:/[^/]+/([^/]+)$:/$release/\\1:" \ "$rootfs"/etc/apk/repositories fi fi opt_arch= if [ -n "$apk_arch" ]; then opt_arch="--arch $apk_arch" fi $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base } configure_alpine() { rootfs="$1" echo "Setting up /etc/inittab" cat >"$rootfs"/etc/inittab< "$rootfs/etc/resolv.conf" # configure the network using the dhcp cat < $rootfs/etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp hostname \$(hostname) EOF # set the hostname echo $hostname > $rootfs/etc/hostname # missing device nodes echo "Setting up device nodes" mkdir -p -m 755 "$rootfs/dev/pts" mkdir -p -m 1777 "$rootfs/dev/shm" mknod -m 666 "$rootfs/dev/zero" c 1 5 mknod -m 666 "$rootfs/dev/full" c 1 7 mknod -m 666 "$rootfs/dev/random" c 1 8 mknod -m 666 "$rootfs/dev/urandom" c 1 9 mknod -m 666 "$rootfs/dev/tty0" c 4 0 mknod -m 666 "$rootfs/dev/tty1" c 4 1 mknod -m 666 "$rootfs/dev/tty2" c 4 2 mknod -m 666 "$rootfs/dev/tty3" c 4 3 mknod -m 666 "$rootfs/dev/tty4" c 4 4 # mknod -m 600 "$rootfs/dev/initctl" p mknod -m 666 "$rootfs/dev/tty" c 5 0 mknod -m 666 "$rootfs/dev/console" c 5 1 mknod -m 666 "$rootfs/dev/ptmx" c 5 2 # start services ln -s /etc/init.d/bootmisc "$rootfs"/etc/runlevels/boot/bootmisc ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/boot/syslog return 0 } copy_configuration() { path=$1 rootfs=$2 hostname=$3 grep -q "^lxc.rootfs" $path/config 2>/dev/null \ || echo "lxc.rootfs = $rootfs" >> $path/config if [ -n "$lxc_arch" ]; then echo "lxc.arch = $lxc_arch" >> $path/config fi lxc_network_link_line="# lxc.network.link = br0" for br in lxcbr0 virbr0 br0; do if [ -d /sys/class/net/$br/bridge ]; then lxc_network_link_line="lxc.network.link = $br" break fi done if ! grep -q "^lxc.network.type" $path/config 2>/dev/null; then cat <> $path/config lxc.network.type = veth $lxc_network_link_line lxc.network.flags = up EOF fi # if there is exactly one veth or macvlan network entry, make sure # it has an associated mac address. nics=$(awk -F '[ \t]*=[ \t]*' \ '$1=="lxc.network.type" && ($2=="veth" || $2=="macvlan") {print $2}' \ $path/config | wc -l) if [ "$nics" -eq 1 ] && ! grep -q "^lxc.network.hwaddr" $path/config; then # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 hwaddr="fe:$(dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n 1 |awk '{print $2}' | cut -c1-10 |\ sed 's/\(..\)/\1:/g; s/.$//')" echo "lxc.network.hwaddr = $hwaddr" >> $path/config fi cat <> $path/config lxc.tty = 4 lxc.pts = 1024 lxc.utsname = $hostname lxc.cap.drop = sys_module mac_admin mac_override sys_time sys_admin # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined # devices lxc.cgroup.devices.deny = a # /dev/null, zero and full lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm lxc.cgroup.devices.allow = c 1:7 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 254:0 rm # mounts point lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry=run run tmpfs nodev,noexec,nosuid,relatime,size=1m,mode=0755 0 0 lxc.mount.entry=none dev/pts devpts gid=5,mode=620 0 0 lxc.mount.entry=shm dev/shm tmpfs nodev,nosuid,noexec,mode=1777,create=dir 0 0 EOF return 0 } die() { echo "$@" >&2 exit 1 } usage() { cat >&2 <] [-R|--release ] [-a|--arch ] [--rootfs ] -p|--path -n|--name [PKG...] EOF } usage_err() { usage exit 1 } default_path=@LXCPATH@ release= arch=$(uname -m) # template mknods, requires root if [ $(id -u) -ne 0 ]; then echo "$(basename $0): must be run as root" >&2 exit 1 fi options=$(getopt -o hn:p:r:R:a: -l help,name:,rootfs:,path:,repository:,release:,arch: -- "$@") [ $? -eq 0 ] || usage_err eval set -- "$options" while [ $# -gt 0 ]; do case "$1" in -h|--help) usage exit 0 ;; -n|--name) name=$2 ;; --rootfs) rootfs=$2 ;; -p|--path) path=$2 ;; -r|--repository) repository=$2 ;; -R|--release) release=$2 ;; -a|--arch) arch=$2 ;; --) shift break;; esac shift 2 done [ -z "$name" ] && usage_err if [ -z "${path}" ]; then path="${default_path}/${name}" fi if [ -z "$rootfs" ]; then rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` if [ -z "$rootfs" ]; then rootfs="${path}/rootfs" fi fi lxc_arch=$arch apk_arch=$arch case "$arch" in i[3-6]86) apk_arch=x86 lxc_arch=x86 ;; x86) lxc_arch=i686 ;; x86_64|"") ;; arm*) apk_arch=armhf ;; *) die "unsupported architecture: $arch" ;; esac : ${APK:=apk} if ! which $APK >/dev/null; then get_static_apk "$rootfs" || die "Failed to download a valid static apk" fi install_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name" configure_alpine "$rootfs" "$name" || die "Failed to configure $name" copy_configuration "$path" "$rootfs" "$name" lxc-1.0.10/templates/lxc-centos.in0000644061062106075000000007150213105114536013721 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 tunning 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. # # 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'} # 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. 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 # Also set this password as expired to force the user to change it! chroot $rootfs_path passwd -e root # This will need to be enhanced for CentOS 7 when systemd # comes into play... /\/\|=mhw=|\/\/ return 0 } configure_centos_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 if [ "$release" = "6" ]; then chroot ${rootfs_path} chkconfig udev-post off fi chroot ${rootfs_path} chkconfig network on 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" # use temporary repository definition 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 else 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 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 $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 -a $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 lxc.autodev = $auto_dev # 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 cache_base=@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 # CentOS 7 and above should run systemd. We need autodev enabled to keep # systemd from causing problems. # # There is some ambiguity here due to the differnce between versioning # of point specific releases such as 6.5 and the rolling release 6. We # only want the major number here if it's a point release... mrelease=$(expr $release : '\([0-9]*\)') if [ $mrelease -gt 6 ]; then auto_dev="1" else auto_dev="0" 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 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 lxc-1.0.10/templates/Makefile.in0000644061062106075000000004600113105114540013346 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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/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-sshd lxc-ubuntu lxc-ubuntu-cloud 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-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@ APPARMOR_LIBS = @APPARMOR_LIBS@ 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@ DOCDIR = @DOCDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ 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@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBDIR = @LUA_LIBDIR@ LUA_LIBS = @LUA_LIBS@ LUA_SHAREDIR = @LUA_SHAREDIR@ LUA_VERSION = @LUA_VERSION@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ 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@ MKDIR_P = @MKDIR_P@ NIH_CFLAGS = @NIH_CFLAGS@ NIH_DBUS_CFLAGS = @NIH_DBUS_CFLAGS@ NIH_DBUS_LIBS = @NIH_DBUS_LIBS@ NIH_LIBS = @NIH_LIBS@ OBJEXT = @OBJEXT@ 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@ 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@ 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@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ 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-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud 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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ 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-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)/$@ 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) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(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 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 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 cscopelist-am \ ctags-am distclean distclean-generic 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 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-1.0.10/templates/lxc-fedora.in0000644061062106075000000014047513105114536013674 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 tunning 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. # # 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'} # 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 # Also set this password as expired to force the user to change it! chroot $rootfs_path passwd -e root # 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 -Ha $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 if [ "x$have_systemd" = "x1" ]; then cat <> $config_path/config lxc.autodev = 1 lxc.kmsg = 0 EOF else cat <> $config_path/config lxc.autodev = 0 EOF 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 cache_base=@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 # Fedora 15 and above run systemd.We need autodev enabled to keep # systemd from causing problems. # Also, kmsg must not be mapped to prevent a 100% cpu loop # in systemd-journald. if [ $release -gt 14 ]; then have_systemd="1" 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 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 lxc-1.0.10/templates/Makefile.am0000644061062106075000000000046413105114536013345 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-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud lxc-1.0.10/templates/lxc-opensuse.in0000644061062106075000000003301213105114536014261 00000000000000#!/bin/bash # # template script for generating suse container for LXC # # # lxc: linux Container library # Authors: # Daniel Lezcano # Frederic Crozat # 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 DISTRO=13.1 configure_opensuse() { rootfs=$1 hostname=$2 # set network as static, but everything is done by LXC outside the container cat < $rootfs/etc/sysconfig/network/ifcfg-eth0 STARTMODE='auto' BOOTPROTO='none' 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 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 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 !" echo "root:root" | chpasswd -R $rootfs return 0 } download_opensuse() { cache=$1 arch=$2 if [ ! -x /usr/bin/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" zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/distribution/$DISTRO/repo/oss/ repo-oss || return 1 zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1 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 libgcc_s1 libncurses5 pam Preinstall: permissions libreadline6 rpm sed tar libz1 libselinux1 Preinstall: liblzma5 libcap2 libacl1 libattr1 Preinstall: libpopt0 libelf1 liblua5_1 Preinstall: libpcre1 RunScripts: aaa_base Support: zypper Support: patterns-openSUSE-base Support: lxc Support: ncurses-utils Support: iputils Support: udev Support: netcfg Support: dhcpcd hwinfo insserv-compat module-init-tools openSUSE-release openssh Support: pwdutils rpcbind sysconfig Ignore: rpm:suse-build-key,build-key Ignore: systemd:systemd-presets-branding EOF 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 CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:/usr/lib/build" /usr/lib/build/init_buildsystem --clean --configdir /usr/lib/build/configs --cachedir $cache/partial-$arch-cache --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 --repository $cache/partial-$arch-packages/var/cache/zypp/packages/update/$arch --repository $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch || return 1 chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/distribution/$DISTRO/repo/oss repo-oss || return 1 chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1 # 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 -Ha $cache/rootfs-$arch/ $rootfs/ || return 1 return 0 } install_opensuse() { cache="@LOCALSTATEDIR@/cache/lxc/opensuse" rootfs=$1 mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 9 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi arch=$(uname -m) 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 $? } copy_configuration() { path=$1 rootfs=$2 name=$3 if grep -q "^lxc.network.type" $path/config; then TYPE=$(sed '/^#/d; /lxc.network.type/!d; s/.*=[ \t]*//' $path/config) grep -q "^lxc.network.ipv4" $path/config IPV4_NOT_CONFIGURED=$? if ! grep -q "^lxc.network.*.gateway" $path/config; then [ $IPV4_NOT_CONFIGURED -eq 0 ] && IPV4=$(sed '/^#/d; /lxc.network.ipv4/!d; /gateway/d; s/.*=[ \t]*//; s/\([[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+\).*/\1/' $path/config) if [ "$TYPE" = "veth" -o "$TYPE" = "macvlan" ]; then if [ $IPV4_NOT_CONFIGURED -eq 0 -a "$IPV4" != "0.0.0.0" ]; then # set default route IP=$(/sbin/ip route | awk '/default/ { print $3 }') echo "lxc.network.ipv4.gateway = $IP " >> $path/config else # set network as dhcp sed -i -e 's/BOOTPROTO=.*/BOOTPROTO=dhcp/' $rootfs/etc/sysconfig/network/ifcfg-eth0 fi fi fi if [ "$TYPE" != "empty" ]; then echo "#remove next line if host DNS configuration should not be available to container" >> $path/config echo "lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind,ro 0 0" >> $path/config fi else echo 'lxc.network.type = empty' >> $path/config fi grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.autodev=1 lxc.tty = 4 lxc.pts = 1024 lxc.mount = $path/fstab lxc.cap.drop = sys_module mac_admin mac_override mknod sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined 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 254:0 rm EOF cat < $path/fstab proc proc proc nodev,noexec,nosuid 0 0 sysfs sys sysfs defaults 0 0 tmpfs run tmpfs mode=0755,nodev,nosuid 0 0 EOF if [ $? -ne 0 ]; then echo "Failed to add configuration" return 1 fi return 0 } clean() { cache="@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 < --clean EOF return 0 } options=$(getopt -o hp:n:c -l help,rootfs:,path:,name:,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;; -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 [ "$(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-1.0.10/templates/lxc-debian.in0000644061062106075000000004355513105114536013657 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://httpredir.debian.org/debian} SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.debian.org/} LOCALSTATEDIR="@LOCALSTATEDIR@" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" configure_debian() { rootfs=$1 hostname=$2 # squeeze only has /dev/tty and /dev/tty0 by default, # therefore creating missing device nodes for tty1-4. for tty in $(seq 1 4); 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 c1:12345:respawn:/sbin/getty 38400 tty1 linux c2:12345:respawn:/sbin/getty 38400 tty2 linux c3:12345:respawn:/sbin/getty 38400 tty3 linux c4:12345:respawn:/sbin/getty 38400 tty4 linux 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" ]; 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 echo "root:root" | chroot $rootfs chpasswd echo "Root password is 'root', please change !" 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 cat >> "${rootfs}/etc/apt/sources.list" << EOF ${prefix} $MIRROR ${release} main contrib non-free EOF if [ "$release" != "unstable" -a "$release" != "sid" ]; then cat >> "${rootfs}/etc/apt/sources.list" << EOF ${prefix} $SECURITY_MIRROR ${release}/updates main contrib non-free EOF fi } configure_debian_systemd() { path=$1 rootfs=$2 init="$(chroot ${rootfs} dpkg-query --search /sbin/init | cut -d : -f 1)" if [ "$init" = "systemd-sysv" ]; then # only appropriate when systemd is PID 1 echo 'lxc.autodev = 1' >> "$path/config" echo 'lxc.kmsg = 0' >> "$path/config" fi # this only works if we have getty@.service to manipulate if [ -f ${rootfs}/lib/systemd/system/getty\@.service ]; then sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ -e 's/After=dev-%i.device/After=/' \ < ${rootfs}/lib/systemd/system/getty\@.service \ > ${rootfs}/etc/systemd/system/getty\@.service fi # just in case systemd is not installed mkdir -p ${rootfs}/{lib,etc}/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 sed 's/ getty@tty[5-9].service//g' $rootfs/lib/systemd/system/getty-static.service | sed 's/\(tty2-tty\)[5-9]/\14/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 touch ${rootfs}/etc/fstab 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 # Make systemd honor SIGPWR chroot ${rootfs} ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target # Setup getty service on the 4 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 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${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 ;; *) init=init ;; 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 -Ha "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 return 0 } install_debian() { cache="$LOCALSTATEDIR/cache/lxc/debian" rootfs=$1 release=$2 arch=$3 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 # Generate the configuration file ## Create the fstab (empty by default) touch $path/fstab # 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 ## 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.mount = $path/fstab 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 # 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 # Re-enable service startup rm ${rootfs}/usr/sbin/policy-rc.d # end } clean() { cache="$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=] 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, 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). -c, --clean only clean up the cache and terminate 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,mirror:,name:,path:,release:,rootfs:,security-mirror: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 fi eval set -- "$options" arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then arch="amd64" elif [ "$arch" = "armv7l" ]; then arch="armhf" elif [ "$arch" = "ppc" ]; then arch="powerpc" elif [ "$arch" = "ppc64le" ]; then arch="ppc64el" fi hostarch=$arch 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;; --mirror) MIRROR=$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;; --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' 'sid') 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 =/{ print $2 }' $config) else rootfs=$path/rootfs fi fi install_debian $rootfs $release $arch if [ $? -ne 0 ]; then echo "failed to install debian" exit 1 fi configure_debian $rootfs $name if [ $? -ne 0 ]; then echo "failed to configure debian for a container" exit 1 fi copy_configuration $path $rootfs $name $arch if [ $? -ne 0 ]; then echo "failed write configuration file" exit 1 fi configure_debian_systemd $path $rootfs post_process ${rootfs} ${release} ${arch} ${hostarch} if [ ! -z "$clean" ]; then clean || exit 1 exit 0 fi lxc-1.0.10/templates/lxc-sshd.in0000644061062106075000000001527313105114536013372 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/run/sshd \ $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/shm \ $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 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 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 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 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.kmsg = 0 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 var/run/sshd tmpfs mode=0644 0 0 lxc.mount.entry = @LXCTEMPLATEDIR@/lxc-sshd sbin/init none ro,bind 0 0 lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry = sysfs sys sysfs ro 0 0 lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0 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-1.0.10/templates/lxc-oracle.in0000644061062106075000000010534413105114536013675 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, 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 # use virbr0 that is setup by default by libvirtd lxc_network_type=veth lxc_network_link=virbr0 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 = "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=initscripts,iptables,selinux-policy,readahead,udev,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 # 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 # 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 # 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 } 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 # 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 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 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 # 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 else 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 # add oracle user, set root password chroot $container_rootfs useradd -m -s /bin/bash oracle echo "oracle:oracle" | chroot $container_rootfs chpasswd echo "root:root" | chroot $container_rootfs chpasswd printf "Added container user:\033[1moracle\033[0m password:\033[1moracle\033[0m\n" printf "Added container user:\033[1mroot\033[0m password:\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 # generate a hwaddr for the container with a high mac address # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="fe:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n 1 |awk '{print $2}' | cut -c1-10 |\ sed 's/\(..\)/\1:/g; s/.$//'`" 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 echo "# Networking" >>$cfg_dir/config # see if the 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" -a \ \( $host_distribution = "OracleServer" -o \ $host_distribution = "Fedora" \) ]; then echo "lxc.network.type = veth" >>$cfg_dir/config echo "lxc.network.flags = up" >>$cfg_dir/config echo "lxc.network.link = virbr0" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.hwaddr = $hwaddr 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_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/lxc ( flock -x 9 if [ $? -ne 0 ]; then die "The template is busy." fi echo "Downloading release $container_release_major.$container_release_minor for $basearch" # get yum repo file if [ -n "$repourl" ]; then yum_url=$repourl else yum_url=http://public-yum.oracle.com fi 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 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 "Failed to download repo file $yum_url/$repofile" 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 if [ $container_release_major = "4" -o $container_release_major = "5" ]; then repo="el"$container_release_major"_"$container_release_minor else repo="ol"$container_release_major"_"$container_release_minor fi 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 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 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}'` 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 } 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. local yum mirror) -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" EOF return 0 } options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch: -- "$@") 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) 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;; --) 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 "$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-1.0.10/src/0000755061062106075000000000000013105114547010160 500000000000000lxc-1.0.10/src/Makefile.in0000644061062106075000000004734113105114537012155 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.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 = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = 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 = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # 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 DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_LIBS = @APPARMOR_LIBS@ 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@ DOCDIR = @DOCDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ 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@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBDIR = @LUA_LIBDIR@ LUA_LIBS = @LUA_LIBS@ LUA_SHAREDIR = @LUA_SHAREDIR@ LUA_VERSION = @LUA_VERSION@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ 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@ MKDIR_P = @MKDIR_P@ NIH_CFLAGS = @NIH_CFLAGS@ NIH_DBUS_CFLAGS = @NIH_DBUS_CFLAGS@ NIH_DBUS_LIBS = @NIH_DBUS_LIBS@ NIH_LIBS = @NIH_LIBS@ OBJEXT = @OBJEXT@ 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@ 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@ 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@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ 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@ SUBDIRS = lxc tests python-lxc lua-lxc all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .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 src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ 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): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(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-recursive 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-recursive 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: $(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 @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile config.h installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive 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-recursive clean-am: clean-generic mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic cscopelist-am ctags ctags-am \ distclean distclean-generic distclean-hdr distclean-tags \ 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 installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ tags-am uninstall uninstall-am .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-1.0.10/src/lxc/0000755061062106075000000000000013105114547010746 500000000000000lxc-1.0.10/src/lxc/lxc_config.c0000644061062106075000000000361413105114536013147 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 = 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(1); } static void list_config_items(void) { struct lxc_config_items *i; for (i = &items[0]; i->name; i++) printf("%s\n", i->name); exit(0); } int main(int argc, char *argv[]) { struct lxc_config_items *i; if (argc < 2) 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) { printf("%s\n", lxc_get_global_config_item(i->name)); exit(0); } } printf("Unknown configuration item: %s\n", argv[1]); exit(1); } lxc-1.0.10/src/lxc/conf.h0000644061062106075000000002730113105114536011765 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 "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" 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; 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; int prefix; }; struct lxc_route6 { struct in6_addr addr; }; struct ifla_veth { char *pair; /* pair name */ char veth1[IFNAMSIZ]; /* needed for deconf */ }; struct ifla_vlan { unsigned int flags; unsigned int fmask; unsigned short vid; unsigned short pad; }; struct ifla_macvlan { int mode; /* private, vepa, bridge */ }; union netdev_p { struct ifla_veth veth_attr; struct ifla_vlan vlan_attr; struct ifla_macvlan macvlan_attr; }; /* * Defines a structure to configure a network device * @link : lxc.network.link, name of bridge or host iface to attach if any * @name : lxc.network.name, name of iface on the container side * @flags : flag of the network device (IFF_UP, ... ) * @ipv4 : a list of ipv4 addresses to be set on the network device * @ipv6 : a list of ipv6 addresses to be set on the network device * @upscript : a script filename to be executed during interface configuration * @downscript : a script filename to be executed during interface destruction */ struct lxc_netdev { int type; int flags; int ifindex; char *link; char *name; char *hwaddr; char *mtu; union netdev_p priv; struct lxc_list ipv4; struct lxc_list ipv6; struct in_addr *ipv4_gateway; bool ipv4_gateway_auto; struct in6_addr *ipv6_gateway; bool ipv6_gateway_auto; char *upscript; char *downscript; }; /* * 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 * @rootfs : a path to the rootfs * @pivot_root : a path to a pivot_root location to be used */ struct lxc_rootfs { char *path; char *mount; char *pivot; char *options; }; /* * 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_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_MASK = 0x0F0, LXC_AUTO_ALL_MASK = 0x0FF, /* 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_POSTSTOP, LXCHOOK_CLONE, NUM_LXC_HOOKS}; extern char *lxchook_names[NUM_LXC_HOOKS]; struct saved_nic { int ifindex; char *orig_name; }; struct lxc_conf { int is_execute; char *fstab; int tty; int pts; int reboot; int need_utmp_watch; signed long personality; struct utsname *utsname; struct lxc_list cgroup; struct lxc_list id_map; struct lxc_list network; struct saved_nic *saved_nics; int num_savednics; int auto_mounts; struct lxc_list mount_list; struct lxc_list caps; struct lxc_list keepcaps; struct lxc_tty_info tty_info; 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; 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; int autodev; // if 1, mount and fill a /dev at start int haltsignal; // signal used to halt container int stopsignal; // signal used to hard stop container 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 inherit_ns_fd[LXC_NS_MAX]; int start_auto; int start_delay; int start_order; struct lxc_list groups; int nbd_idx; /* set to true when rootfs has been setup */ bool rootfs_setup; }; 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_requests_empty_network(struct lxc_handler *handler); extern int lxc_create_network(struct lxc_handler *handler); extern void lxc_delete_network(struct lxc_handler *handler); extern int lxc_assign_network(struct lxc_list *networks, pid_t pid); extern int lxc_map_ids(struct lxc_list *idmap, pid_t pid); extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_create_tty(const char *name, struct lxc_conf *conf); extern void lxc_delete_tty(struct lxc_tty_info *tty_info); extern int lxc_clear_config_network(struct lxc_conf *c); extern int lxc_clear_nic(struct lxc_conf *c, const char *key); extern int lxc_clear_config_caps(struct lxc_conf *c); extern int lxc_clear_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_delete_autodev(struct lxc_handler *handler); 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 void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf); extern int find_unmapped_nsuid(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 ttys_shift_ids(struct lxc_conf *c); extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data); 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); struct lxc_list *sort_cgroup_settings(struct lxc_list* cgroup_settings); #endif lxc-1.0.10/src/lxc/network.c0000644061062106075000000007231713105114536012533 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nl.h" #include "network.h" #include "conf.h" #if HAVE_IFADDRS_H #include #else #include <../include/ifaddrs.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 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; } int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char* newname) { int index; if (!ifname) return -EINVAL; index = if_nametoindex(ifname); if (!index) return -EINVAL; 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; 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; } 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 (write(fd, value, strlen(value)) < 0) err = -errno; close(fd); return err; } static int ip_forward_set(const char *ifname, int family, int flag) { char path[MAXPATHLEN]; int rc; if (family != AF_INET && family != AF_INET6) return -EINVAL; rc = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/forwarding", family == AF_INET?"ipv4":"ipv6" , ifname); if (rc >= MAXPATHLEN) return -E2BIG; return proc_sys_net_write(path, flag?"1":"0"); } int lxc_ip_forward_on(const char *ifname, int family) { return ip_forward_set(ifname, family, 1); } int lxc_ip_forward_off(const char *ifname, int family) { return ip_forward_set(ifname, family, 0); } static int neigh_proxy_set(const char *ifname, int family, int flag) { char path[MAXPATHLEN]; int ret; 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)) { val = 0; 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); } /* * There is a lxc_bridge_attach, but no need of a bridge detach * as automatically done by kernel when a netdev is deleted. */ int lxc_bridge_attach(const char *bridge, const char *ifname) { int fd, index, err; struct ifreq ifr; if (strlen(ifname) >= IFNAMSIZ) return -EINVAL; index = if_nametoindex(ifname); if (!index) return -EINVAL; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) return -errno; strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); ifr.ifr_name[IFNAMSIZ-1] = '\0'; 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) { char *name = NULL; int i = 0; FILE *urandom; unsigned int seed; struct ifaddrs *ifaddr, *ifa; int ifexists = 0; /* Get all the network interfaces */ getifaddrs(&ifaddr); /* 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(1) { ifexists = 0; name = strdup(template); if (name == NULL) return NULL; 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) == 0) { ifexists = 1; break; } } if (ifexists == 0) break; free(name); } freeifaddrs(ifaddr); return name; } 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; snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1); 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; } lxc-1.0.10/src/lxc/attach_options.h0000644061062106075000000001230713105114536014057 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, 1, 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-1.0.10/src/lxc/monitor.h0000644061062106075000000000615113105114536012527 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 #include "conf.h" 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-1.0.10/src/lxc/lxc-ls0000644061062106075000000003736713105114545012031 00000000000000#!/usr/bin/env python3 # # lxc-ls: List containers # # This python implementation is based on the work done in the original # shell implementation done by Serge Hallyn in Ubuntu (and other contributors) # # (C) Copyright Canonical Ltd. 2012 # # Authors: # Stéphane Graber # # 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 json import lxc import os import re import shutil import tempfile import sys _ = gettext.gettext gettext.textdomain("lxc-ls") # Required for containers without python import encodings.ascii assert encodings.ascii # Constants LXCPATH = "/usr/local/var/lib/lxc" RUNTIME_PATH = "/run" # Functions used later on def batch(iterable, cols=1): import math length = len(iterable) lines = math.ceil(length / cols) for line in range(lines): fields = [] for col in range(cols): index = line + (col * lines) if index < length: fields.append(iterable[index]) yield fields def get_terminal_size(): import os env = os.environ def ioctl_GWINSZ(fd): try: import fcntl import termios import struct cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) return cr except: return cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: fd = os.open(os.ctermid(), os.O_RDONLY) cr = ioctl_GWINSZ(fd) os.close(fd) except: pass if not cr: cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) return int(cr[1]), int(cr[0]) def get_root_path(path): lxc_path = LXCPATH global_conf = "%s/etc/lxc/lxc.conf" % path if os.path.exists(global_conf): with open(global_conf, "r") as fd: for line in fd: if line.startswith("lxc.lxcpath"): lxc_path = line.split("=")[-1].strip() break return lxc_path # Constants FIELDS = ("name", "state", "ipv4", "ipv6", "autostart", "pid", "memory", "ram", "swap") DEFAULT_FIELDS = ("name", "state", "ipv4", "ipv6", "autostart") # Begin parsing the command line parser = argparse.ArgumentParser(description=_("LXC: List containers"), formatter_class=argparse.RawTextHelpFormatter, epilog=_("""Valid fancy-format fields: %s Default fancy-format fields: %s\n""" % (", ".join(FIELDS), ", ".join(DEFAULT_FIELDS)))) parser.add_argument("-1", dest="one", action="store_true", help=_("list one container per line (default when piped)")) parser.add_argument("-P", "--lxcpath", dest="lxcpath", metavar="PATH", help=_("Use specified container path"), default=lxc.default_config_path) parser.add_argument("--active", action="store_true", help=_("list only active containers")) parser.add_argument("--frozen", dest="state", action="append_const", const="FROZEN", help=_("list only frozen containers")) parser.add_argument("--running", dest="state", action="append_const", const="RUNNING", help=_("list only running containers")) parser.add_argument("--stopped", dest="state", action="append_const", const="STOPPED", help=_("list only stopped containers")) parser.add_argument("-f", "--fancy", action="store_true", help=_("use fancy output")) parser.add_argument("-F", "--fancy-format", type=str, default=",".join(DEFAULT_FIELDS), help=_("comma separated list of fields to show")) parser.add_argument("--nesting", dest="nesting", action="store_true", help=_("show nested containers")) parser.add_argument("filter", metavar='FILTER', type=str, nargs="?", help=_("regexp to be applied on the container list")) parser.add_argument("--version", action="version", version=lxc.version) args = parser.parse_args() # --active is the same as --running --frozen if args.active: if not args.state: args.state = [] args.state += ["RUNNING", "FROZEN", "UNKNOWN"] # If the output is piped, default to --one if not sys.stdout.isatty(): args.one = True # Turn args.fancy_format into a list args.fancy_format = args.fancy_format.strip().split(",") if set(args.fancy_format) - set(FIELDS): parser.error(_("Invalid field(s): %s" % ", ".join(list(set(args.fancy_format) - set(FIELDS))))) # Basic checks ## Check for setns SUPPORT_SETNS = os.path.exists("/proc/self/ns") SUPPORT_SETNS_NET = False SUPPORT_SETNS_PID = False if SUPPORT_SETNS: SUPPORT_SETNS_NET = os.path.exists("/proc/self/ns/net") SUPPORT_SETNS_PID = os.path.exists("/proc/self/ns/pid") ## Nesting requires setns to pid and net ns if args.nesting: if not SUPPORT_SETNS: parser.error(_("Showing nested containers requires setns support " "which your kernel doesn't support.")) if not SUPPORT_SETNS_NET: parser.error(_("Showing nested containers requires setns to the " "network namespace which your kernel doesn't support.")) if not SUPPORT_SETNS_PID: parser.error(_("Showing nested containers requires setns to the " "PID namespace which your kernel doesn't support.")) # Set the actual lxcpath value if not args.lxcpath: args.lxcpath = lxc.default_config_path # List of containers, stored as dictionaries def get_containers(fd=None, base="/", root=False): containers = [] paths = [args.lxcpath] if not root: paths.append(get_root_path(base)) # Generate a unique list of valid paths paths = set([os.path.normpath("%s/%s" % (base, path)) for path in paths]) for path in paths: if not os.access(path, os.R_OK): continue for container_name in lxc.list_containers(config_path=path): entry = {} entry['name'] = container_name # Apply filter if root and args.filter and \ not re.match(args.filter, container_name): continue # Return before grabbing the object (non-root) if not args.state and not args.fancy and not args.nesting: containers.append(entry) continue try: container = lxc.Container(container_name, path) except: continue if container.controllable: state = container.state else: state = 'UNKNOWN' # Filter by status if args.state and state not in args.state: continue # Nothing more is needed if we're not printing some fancy output if not args.fancy and not args.nesting: containers.append(entry) continue # Some extra field we may want if 'state' in args.fancy_format: entry['state'] = state if 'pid' in args.fancy_format: entry['pid'] = "-" if state == 'UNKNOWN': entry['pid'] = state elif container.init_pid != -1: entry['pid'] = str(container.init_pid) if 'autostart' in args.fancy_format: entry['autostart'] = "NO" try: if container.get_config_item("lxc.start.auto") == "1": entry['autostart'] = "YES" groups = container.get_config_item("lxc.group") if len(groups) > 0: entry['autostart'] = "YES (%s)" % ", ".join(groups) except KeyError: pass if 'memory' in args.fancy_format or \ 'ram' in args.fancy_format or \ 'swap' in args.fancy_format: if container.running: try: memory_ram = int(container.get_cgroup_item( "memory.usage_in_bytes")) except: memory_ram = 0 try: memory_stat = container.get_cgroup_item("memory.stat") beg = memory_stat.find("\nswap ") + 6 end = memory_stat[beg:].find("\n") + len(memory_stat[:beg]) memory_swap = int(memory_stat[beg:end]) except: memory_swap = 0 else: memory_ram = 0 memory_swap = 0 memory_total = memory_ram + memory_swap if 'memory' in args.fancy_format: if container.running: entry['memory'] = "%sMB" % round(memory_total / 1048576, 2) else: entry['memory'] = "-" if 'ram' in args.fancy_format: if container.running: entry['ram'] = "%sMB" % round(memory_ram / 1048576, 2) else: entry['ram'] = "-" if 'swap' in args.fancy_format: if container.running: entry['swap'] = "%sMB" % round(memory_swap / 1048576, 2) else: entry['swap'] = "-" # Get the IPs for family, protocol in {'inet': 'ipv4', 'inet6': 'ipv6'}.items(): if protocol in args.fancy_format: entry[protocol] = "-" if state == 'UNKNOWN': entry[protocol] = state continue if container.running: if not SUPPORT_SETNS_NET: entry[protocol] = 'UNKNOWN' continue ips = container.get_ips(family=family) if ips: entry[protocol] = ", ".join(ips) # Nested containers if args.nesting: if container.running: # Recursive call in container namespace temp_fd, temp_file = tempfile.mkstemp() os.remove(temp_file) container.attach_wait(get_containers, temp_fd) json_file = os.fdopen(temp_fd, "r") json_file.seek(0) try: sub_containers = json.loads(json_file.read()) except: sub_containers = [] json_file.close() else: def clear_lock(): try: lock_path = "%s/lxc/lock/%s/%s" % (RUNTIME_PATH, path, entry['name']) if os.path.exists(lock_path): if os.path.isdir(lock_path): shutil.rmtree(lock_path) else: os.remove(lock_path) except: pass clear_lock() # Recursive call using container rootfs sub_containers = get_containers( base="%s/%s" % ( base, container.get_config_item("lxc.rootfs"))) clear_lock() for sub in sub_containers: if 'nesting_parent' not in sub: sub['nesting_parent'] = [] sub['nesting_parent'].insert(0, entry['name']) sub['nesting_real_name'] = sub.get('nesting_real_name', sub['name']) sub['name'] = "%s/%s" % (entry['name'], sub['name']) containers.append(sub) # Append the container containers.append(entry) if fd: json_file = os.fdopen(fd, "w+") json_file.write(json.dumps(containers)) return return containers containers = get_containers(root=True) # Print the list ## Standard list with one entry per line if not args.fancy and args.one: for container in sorted(containers, key=lambda container: container['name']): print(container['name']) sys.exit(0) ## Standard list with multiple entries per line if not args.fancy and not args.one: # Get the longest name and extra simple list field_maxlength = 0 container_names = [] for container in containers: if len(container['name']) > field_maxlength: field_maxlength = len(container['name']) container_names.append(container['name']) # Figure out how many we can put per line width = get_terminal_size()[0] entries = int(width / (field_maxlength + 2)) if entries == 0: entries = 1 for line in batch(sorted(container_names), entries): line_format = "" for index in range(len(line)): line_format += "{line[%s]:%s}" % (index, field_maxlength + 2) print(line_format.format(line=line)) ## Fancy listing if args.fancy: field_maxlength = {} # Get the maximum length per field for field in args.fancy_format: field_maxlength[field] = len(field) for container in containers: for field in args.fancy_format: if field == 'name' and 'nesting_real_name' in container: fieldlen = len(" " * ((len(container['nesting_parent']) - 1) * 4) + " \_ " + container['nesting_real_name']) if fieldlen > field_maxlength[field]: field_maxlength[field] = fieldlen elif len(container[field]) > field_maxlength[field]: field_maxlength[field] = len(container[field]) # Generate the line format string based on the maximum length and # a 2 character padding line_format = "" index = 0 for field in args.fancy_format: line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + 2) index += 1 # Get the line length minus the padding of the last field line_length = -2 for field in field_maxlength: line_length += field_maxlength[field] + 2 # Print header print(line_format.format(fields=[header.upper() for header in args.fancy_format])) print("-" * line_length) # Print the entries for container in sorted(containers, key=lambda container: container['name']): fields = [] for field in args.fancy_format: if field == 'name' and 'nesting_real_name' in container: prefix = " " * ((len(container['nesting_parent']) - 1) * 4) fields.append(prefix + " \_ " + container['nesting_real_name']) else: fields.append(container[field]) print(line_format.format(fields=fields)) lxc-1.0.10/src/lxc/lxc_monitor.c0000644061062106075000000001071113105114536013365 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" lxc_log_define(lxc_monitor_ui, lxc); 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; rc_main = 0; if (lxc_arguments_parse(&my_args, argc, argv)) return 1; if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return 1; 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) { ERROR("Unable to open monitor on path: %s", my_args.lxcpath[i]); ret = EXIT_FAILURE; continue; } if (write(fd, "quit", 4) < 0) { SYSERROR("Unable to close monitor on path: %s", my_args.lxcpath[i]); ret = EXIT_FAILURE; close(fd); continue; } close(fd); } return ret; } len = strlen(my_args.name) + 3; regexp = malloc(len + 3); if (!regexp) { ERROR("failed to allocate memory"); return 1; } rc_snp = snprintf(regexp, len, "^%s$", my_args.name); if (rc_snp < 0 || rc_snp >= len) { ERROR("Name too long"); rc_main = 1; goto error; } if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) { ERROR("failed to compile the regex '%s'", my_args.name); rc_main = 1; goto error; } fds = malloc(my_args.lxcpath_cnt * sizeof(struct pollfd)); if (!fds) { SYSERROR("out of memory"); rc_main = -1; 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); rc_main = 1; 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) { rc_main = 1; 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; } } close_and_clean: close_fds(fds, nfds); cleanup: regfree(&preg); free(fds); error: free(regexp); return rc_main; } lxc-1.0.10/src/lxc/arguments.h0000644061062106075000000000621013105114536013041 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 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 */ 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 *zfsroot, *lowerdir, *dir; /* auto-start */ int all; int list; char *groups; /* 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'}, \ {0, 0, 0, 0} /* option keys for long only options */ #define OPT_USAGE 0x1000 #define OPT_VERSION OPT_USAGE-1 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-1.0.10/src/lxc/error.c0000644061062106075000000000321313105114536012160 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); } return ret; } lxc-1.0.10/src/lxc/lxc-device0000644061062106075000000000644613105114536012644 00000000000000#!/usr/bin/python3 # # lxc-device: Add devices to a running container # # This python implementation is based on the work done in the original # shell implementation done by Serge Hallyn in Ubuntu (and other contributors) # # (C) Copyright Canonical Ltd. 2012 # # Authors: # Stéphane Graber # # 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 os _ = gettext.gettext gettext.textdomain("lxc-device") # Begin parsing the command line parser = argparse.ArgumentParser(description=_("LXC: Manage devices"), formatter_class=argparse.RawTextHelpFormatter) # Global arguments parser.add_argument("-n", dest="container", metavar="CONTAINER", help=_("Name of the container to add the device to"), required=True) parser.add_argument("-P", "--lxcpath", dest="lxcpath", metavar="PATH", help=_("Use specified container path"), default=None) parser.add_argument("--version", action="version", version=lxc.version) # Commands subparsers = parser.add_subparsers() subparser_add = subparsers.add_parser('add', help=_('Add a device')) subparser_add.set_defaults(action="add") subparser_add.add_argument(dest="device", metavar="DEVICE", help=_("Add a device " "(path to a node or interface name)")) subparser_add.add_argument(dest="name", metavar="NAME", nargs="?", help=_("Use an alternative path or name " "in the container")) args = parser.parse_args() # Some basic checks ## Check for valid action if not hasattr(args, "action"): parser.error(_("You must specify an action.")) ## Don't rename if no alternative name if not args.name: args.name = args.device ## Check that the container is ready container = lxc.Container(args.container, args.lxcpath) ## Check that we have control over the container if not container.controllable: parser.error("Insufficent privileges to control: %s" % container.name) ## Check that the container is running if not container.running: parser.error("The container must be running.") # Do the work if args.action == "add": if os.path.exists("/sys/class/net/%s/" % args.device): ret = container.add_device_net(args.device, args.name) else: ret = container.add_device_node(args.device, args.name) if ret: print("Added '%s' to '%s' as '%s'." % (args.device, container.name, args.name)) else: print("Failed to add '%s' to '%s' as '%s'." % (args.device, container.name, args.name)) lxc-1.0.10/src/lxc/lxc_info.c0000644061062106075000000002261013105114536012632 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" lxc_log_define(lxc_info_ui, lxc); 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", .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", (int)(val >> 30), (int)(val & ((1 << 30) - 1)) / 10737419); } else if (val > 1 << 20) { 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) { 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", (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) { 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) { 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 (!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; if (lxc_arguments_parse(&my_args, argc, argv)) return ret; if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return ret; lxc_log_options_no_override(); if (print_info(my_args.name, my_args.lxcpath[0]) == 0) ret = EXIT_SUCCESS; return ret; } lxc-1.0.10/src/lxc/execute.c0000644061062106075000000001047013105114536012474 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 "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; }; /* historically lxc-init has been under /usr/lib/lxc and under * /usr/lib/$ARCH/lxc. It now lives as $prefix/sbin/init.lxc. */ static char *choose_init(void) { char *retv = NULL; int ret, env_set = 0; struct stat mystat; 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"); if (env_set) { if (unsetenv("PATH")) SYSERROR("Failed to unsetenv"); } if (retv) return retv; retv = malloc(PATH_MAX); if (!retv) return NULL; ret = snprintf(retv, PATH_MAX, SBINDIR "/init.lxc"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } ret = stat(retv, &mystat); if (ret == 0) return retv; ret = snprintf(retv, PATH_MAX, LXCINITDIR "/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } ret = stat(retv, &mystat); if (ret == 0) return retv; ret = snprintf(retv, PATH_MAX, "/usr/lib/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } ret = stat(retv, &mystat); if (ret == 0) return retv; ret = snprintf(retv, PATH_MAX, "/sbin/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out1; } ret = stat(retv, &mystat); if (ret == 0) return retv; out1: free(retv); return NULL; } static int execute_start(struct lxc_handler *handler, void* data) { int j, i = 0; struct execute_args *my_args = data; char **argv; int argc = 0, argc_add; char *initpath; while (my_args->argv[argc++]); argc_add = 4; if (my_args->quiet) argc_add++; if (!handler->conf->rootfs.path) { argc_add += 4; if (lxc_log_has_valid_level()) argc_add += 2; } argv = malloc((argc + argc_add) * sizeof(*argv)); if (!argv) goto out1; initpath = choose_init(); if (!initpath) { ERROR("Failed to find an lxc-init or init.lxc"); goto out2; } argv[i++] = initpath; if (my_args->quiet) argv[i++] = "--quiet"; if (!handler->conf->rootfs.path) { argv[i++] = "--name"; argv[i++] = (char *)handler->name; argv[i++] = "--lxcpath"; argv[i++] = (char *)handler->lxcpath; if (lxc_log_has_valid_level()) { argv[i++] = "--logpriority"; argv[i++] = (char *) lxc_log_priority_to_string(lxc_log_get_level()); } } 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); 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_conf *conf, const char *lxcpath) { struct execute_args args = { .argv = argv, .quiet = quiet }; if (lxc_check_inherited(conf, -1)) return -1; conf->is_execute = 1; return __lxc_start(name, conf, &execute_start_ops, &args, lxcpath); } lxc-1.0.10/src/lxc/cgfs.c0000644061062106075000000020362513105114536011762 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 "error.h" #include "commands.h" #include "list.h" #include "conf.h" #include "utils.h" #include "bdev.h" #include "log.h" #include "cgroup.h" #include "start.h" #include "state.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); 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 count_lines(const char *fn); 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); 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("%s: failed to open %s", __func__, 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("%s: failed to stat %s", __func__, 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("%s: failed to delete %s", __func__, dirname); if (!saved_errno) saved_errno = errno; failed=1; } ret = closedir(dir); if (ret) { SYSERROR("%s: failed to close directory %s", __func__, dirname); if (!saved_errno) saved_errno = errno; failed=1; } errno = saved_errno; return failed ? -1 : 0; } 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; 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); 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) 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; } } k = lxc_array_len((void **)h->all_mount_points); 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)) : false; 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(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/%ld", 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; } /* 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++) { r = remove_cgroup(info_ptr->designated_mount_point, info_ptr->created_paths[info_ptr->created_paths_count - 1], false); 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) { SYSERROR("Could not create cgroup '%s' in '%s'.", current_entire_path, info_ptr->designated_mount_point->mount_point); goto cleanup_from_error; } 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; } /* 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); 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) { 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 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); } 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); } 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; cgfs_d = hdata; 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) { 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) { 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 (recurse) r = cgroup_rmdir(buf); else r = rmdir(buf); } else r = mkdir(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); } static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse) { return create_or_remove_cgroup(true, mp, path, recurse); } 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 = count_lines(sub_path); if (r >= 0) n += r; } free(sub_path); } closedir(d); return n; } static int count_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; } 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("%s: was not expecting 0 bufsize", __func__); 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(const char *name) { struct cgfs_data *d; d = malloc(sizeof(*d)); if (!d) return NULL; memset(d, 0, sizeof(*d)); d->name = strdup(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 cgfs_data *d = hdata; if (!d) return; free(d->name); lxc_cgroup_process_info_free_and_remove(d->info); 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_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_list *cgroup_conf, bool with_devices) { struct cgfs_data *d = hdata; if (!d) return false; return do_setup_cgroup_limits(d, cgroup_conf, 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; } 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, .get = lxc_cgroupfs_get, .set = lxc_cgroupfs_set, .unfreeze = cgfs_unfreeze, .setup_limits = cgroupfs_setup_limits, .name = "cgroupfs", .attach = lxc_cgroupfs_attach, .chown = NULL, .mount_cgroup = cgroupfs_mount_cgroup, .nrtasks = cgfs_nrtasks, }; lxc-1.0.10/src/lxc/lxc_destroy.c0000644061062106075000000000516513105114536013376 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 "lxc.h" #include "log.h" #include "arguments.h" #include "utils.h" lxc_log_define(lxc_destroy_ui, lxc); static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'f': args->force = 1; break; } return 0; } static const struct option my_longopts[] = { {"force", no_argument, 0, 'f'}, 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 for name of the container\n\ -f, --force wait for the container to shut down\n", .options = my_longopts, .parser = my_parser, .checker = NULL, }; int main(int argc, char *argv[]) { struct lxc_container *c; if (lxc_arguments_parse(&my_args, argc, argv)) exit(1); if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) exit(1); 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(1); } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(1); } if (!c->is_defined(c)) { fprintf(stderr, "Container is not defined\n"); lxc_container_put(c); exit(1); } if (c->is_running(c)) { if (!my_args.force) { fprintf(stderr, "%s is running\n", my_args.name); lxc_container_put(c); exit(1); } c->stop(c); } if (!c->destroy(c)) { fprintf(stderr, "Destroying %s failed\n", my_args.name); lxc_container_put(c); exit(1); } lxc_container_put(c); exit(0); } lxc-1.0.10/src/lxc/lxc_stop.c0000644061062106075000000001316513105114536012671 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 lxc_log_define(lxc_stop_ui, lxc); 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': args->timeout = atoi(arg); 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", .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; bool s; int ret = 1; if (lxc_arguments_parse(&my_args, argc, argv)) return 1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return 1; 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"); return 1; } if (my_args.hardstop && my_args.nokill) { fprintf(stderr, "-k can't be used with --nokill\n"); return 1; } if (my_args.hardstop && my_args.reboot) { fprintf(stderr, "-k can't be used with -r\n"); return 1; } if (my_args.hardstop && my_args.timeout) { fprintf(stderr, "-k doesn't allow timeouts\n"); return 1; } if (my_args.nolock && !my_args.hardstop) { fprintf(stderr, "--nolock may only be used with -k\n"); return 1; } /* shortcut - if locking is bogus, we should be able to kill * containers at least */ if (my_args.nolock) return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "Error opening container\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) ? 0 : 1; goto out; } /* reboot */ if (my_args.reboot) { ret = do_reboot_and_check(&my_args, c); goto out; } /* shutdown */ s = c->shutdown(c, my_args.timeout); if (!s) { if (my_args.timeout == 0) ret = 0; else if (my_args.nokill) ret = 1; else ret = c->stop(c) ? 0 : 1; } else ret = 0; out: lxc_container_put(c); if (ret < 0) return 1; return ret; } lxc-1.0.10/src/lxc/console.h0000644061062106075000000001762513105114536012512 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 "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 SIGWINCH signals. If set to -1 no * SIGWINCH 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_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_conf *); /* * 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_sigwinch_init: install SIGWINCH 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 SIGWINCH. * * Must be called with process_lock held to protect the lxc_ttys list, or * from a non-threaded context. * * Note that SIGWINCH 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_sigwinch_fd()). * * This function allocates memory. It is up to the caller to free it. */ extern struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd); /* * Handler for SIGWINCH 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_sigwinch_fd(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /* * lxc_console_sigwinch_fini: uninstall SIGWINCH handler * * @ts : the lxc_tty_state returned by lxc_console_sigwinch_init * * Restore the saved signal handler that was in effect at the time * lxc_console_sigwinch_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_sigwinch_fini(struct lxc_tty_state *ts); #endif lxc-1.0.10/src/lxc/cgroup.h0000644061062106075000000000540313105114536012336 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; struct cgroup_ops { const char *name; void *(*init)(const char *name); void (*destroy)(void *hdata); 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); 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_list *cgroup_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); }; 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_unfreeze(struct lxc_handler *handler); extern void cgroup_disconnect(void); extern void prune_init_scope(char *cg); #endif lxc-1.0.10/src/lxc/lxc.functions.in0000644061062106075000000000210013105114536014002 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-1.0.10/src/lxc/lxclock.h0000644061062106075000000001072613105114536012502 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 /* For O_* constants */ #include /* For mode constants */ #include #include #include #include #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 $lxcpath/$lxcname/locks/$name. * 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)). * * \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 flock(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. */ extern void container_disk_unlock(struct lxc_container *c); #endif lxc-1.0.10/src/lxc/nl.c0000644061062106075000000001554013105114536011446 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; 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-1.0.10/src/lxc/lxc-start-ephemeral.in0000644061062106075000000003312213105114536015076 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 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") # 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, help=_("directory to bind 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")) # 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)] # 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) 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 if args.bdir: if not os.path.exists(args.bdir): print(_("Path '%s' doesn't exist, won't be bind-mounted.") % args.bdir) else: src_path = os.path.abspath(args.bdir) dst_path = "%s/rootfs/%s" % (dest_path, os.path.abspath(args.bdir)) fd.write("mkdir -p %s\nmount -n --bind %s %s\n" % ( dst_path, src_path, dst_path)) fd.write(""" [ -e $LXC_DIR/configured ] && exit 0 for file in $LXC_DIR/rootfs/etc/hostname \\ $LXC_DIR/rootfs/etc/hosts \\ $LXC_DIR/rootfs/etc/sysconfig/network \\ $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file done touch $LXC_DIR/configured """) dest.set_config_item("lxc.hook.pre-mount", os.path.join(dest_path, "pre-mount")) # Generate post-stop script if not args.keep_data: with open(os.path.join(dest_path, "post-stop"), "w+") as fd: os.fchmod(fd.fileno(), 0o755) fd.write("""#!/bin/sh [ -d "%s" ] && rm -Rf "%s" """ % (dest_path, dest_path)) dest.set_config_item("lxc.hook.post-stop", os.path.join(dest_path, "post-stop")) dest.save_config() # Start the container if not dest.start() or not dest.wait("RUNNING", timeout=5): print(_("The container '%s' failed to start.") % dest.name) dest.stop() if dest.defined: dest.destroy() sys.exit(1) # Deal with the case where we just attach to the container's console if not args.command and not args.daemon: dest.console() 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-1.0.10/src/lxc/lxc_console.c0000644061062106075000000000644313105114536013347 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 #include #include #include #include #include #include #include #include #include "error.h" #include "lxc.h" #include "log.h" #include "mainloop.h" #include "arguments.h" #include "commands.h" lxc_log_define(lxc_console_ui, lxc); 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': args->ttynum = atoi(arg); 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", .options = my_longopts, .parser = my_parser, .checker = NULL, .ttynum = -1, .escape = 1, }; int main(int argc, char *argv[]) { int ret; struct lxc_container *c; ret = lxc_arguments_parse(&my_args, argc, argv); if (ret) return EXIT_FAILURE; if (!my_args.log_file) my_args.log_file = "none"; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0]); 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 (!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); return EXIT_SUCCESS; } lxc-1.0.10/src/lxc/state.c0000644061062106075000000001021713105114536012151 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 #include "lxc.h" #include "log.h" #include "start.h" #include "cgroup.h" #include "monitor.h" #include "commands.h" #include "config.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, int *states) { char *token, *saveptr = NULL; char *strstates_dup = strdup(strstates); int state; if (!strstates_dup) return -1; token = strtok_r(strstates_dup, "|", &saveptr); while (token) { state = lxc_str2state(token); if (state < 0) { free(strstates_dup); return -1; } states[state] = 1; token = strtok_r(NULL, "|", &saveptr); } free(strstates_dup); return 0; } extern int lxc_wait(const char *lxcname, const char *states, int timeout, const char *lxcpath) { struct lxc_msg msg; int state, ret; int s[MAX_STATE] = { }, fd; if (fillwaitedstates(states, s)) return -1; if (lxc_monitord_spawn(lxcpath)) return -1; fd = lxc_monitor_open(lxcpath); if (fd < 0) return -1; /* * if container present, * then check if already in requested state */ ret = -1; state = lxc_getstate(lxcname, lxcpath); if (state < 0) { goto out_close; } else if ((state >= 0) && (s[state])) { ret = 0; goto out_close; } for (;;) { int elapsed_time, curtime = 0; struct timeval tv; int stop = 0; int retval; if (timeout != -1) { retval = gettimeofday(&tv, NULL); if (retval) goto out_close; curtime = tv.tv_sec; } if (lxc_monitor_read_timeout(fd, &msg, timeout) < 0) { /* try again if select interrupted by signal */ if (errno != EINTR) goto out_close; } if (timeout != -1) { retval = gettimeofday(&tv, NULL); if (retval) goto out_close; elapsed_time = tv.tv_sec - curtime; if (timeout - elapsed_time <= 0) stop = 1; timeout -= elapsed_time; } if (strcmp(lxcname, msg.name)) { if (stop) { ret = -2; goto out_close; } continue; } switch (msg.type) { case lxc_msg_state: if (msg.value < 0 || msg.value >= MAX_STATE) { ERROR("Receive an invalid state number '%d'", msg.value); goto out_close; } if (s[msg.value]) { ret = 0; goto out_close; } break; default: if (stop) { ret = -2; goto out_close; } /* just ignore garbage */ break; } } out_close: lxc_monitor_close(fd); return ret; } lxc-1.0.10/src/lxc/af_unix.h0000644061062106075000000000263713105114536012476 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 extern int lxc_abstract_unix_open(const char *path, int type, int flags); extern int lxc_abstract_unix_close(int fd); extern int lxc_abstract_unix_connect(const char *path); extern int lxc_abstract_unix_send_fd(int fd, int sendfd, void *data, size_t size); extern int lxc_abstract_unix_recv_fd(int fd, int *recvfd, 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-1.0.10/src/lxc/attach.c0000644061062106075000000010406713105114536012304 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 #if !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 "log.h" #include "lxclock.h" #include "lxcseccomp.h" #include "namespace.h" #include "utils.h" #include "lsm/lsm.h" #include #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 lxc_log_define(lxc_attach, lxc); /* /proc/pid-to-str/current\0 = (5 + 21 + 7 + 1) */ #define __LSMATTRLEN (5 + 21 + 7 + 1) static int lsm_openat(int procfd, pid_t pid, int on_exec) { int ret = -1; int labelfd = -1; const char *name; char path[__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, __LSMATTRLEN, "%d/attr/exec", pid); else ret = snprintf(path, __LSMATTRLEN, "%d/attr/current", pid); if (ret < 0 || ret >= __LSMATTRLEN) return -1; labelfd = openat(procfd, path, O_RDWR); if (labelfd < 0) { SYSERROR("Unable to open file descriptor to set LSM label."); return -1; } return labelfd; } static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label) { int fret = -1; const char* name; char *command = NULL; 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 (strcmp(name, "AppArmor") == 0) { int size; command = malloc(strlen(lsm_label) + strlen("changeprofile ") + 1); if (!command) { SYSERROR("Failed to write apparmor profile"); goto out; } size = sprintf(command, "changeprofile %s", lsm_label); if (size < 0) { SYSERROR("Failed to write apparmor profile"); goto out; } if (write(lsm_labelfd, command, size + 1) < 0) { SYSERROR("Unable to set LSM label: %s.", command); goto out; } INFO("Set LSM label to: %s.", command); } else if (strcmp(name, "SELinux") == 0) { if (write(lsm_labelfd, lsm_label, strlen(lsm_label) + 1) < 0) { SYSERROR("Unable to set LSM label"); goto out; } INFO("Set LSM label to: %s.", lsm_label); } else { ERROR("Unable to restore label for unknown LSM: %s", name); goto out; } fret = 0; out: free(command); if (lsm_labelfd != -1) close(lsm_labelfd); return fret; } static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { struct lxc_proc_context_info *info = calloc(1, sizeof(*info)); FILE *proc_file; char proc_fn[MAXPATHLEN]; char *line = NULL; size_t line_bufsz = 0; int ret, found; if (!info) { SYSERROR("Could not allocate memory."); return NULL; } /* read capabilities */ snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", pid); proc_file = fopen(proc_fn, "r"); if (!proc_file) { SYSERROR("Could not open %s", proc_fn); goto out_error; } found = 0; while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret > 0) { found = 1; break; } } free(line); fclose(proc_file); if (!found) { SYSERROR("Could not read capability bounding set from %s", proc_fn); errno = ENOENT; goto out_error; } info->lsm_label = lsm_process_label_get(pid); return info; out_error: free(info); return NULL; } static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx) { free(ctx->lsm_label); if (ctx->container) lxc_container_put(ctx->container); free(ctx); } static int lxc_attach_to_ns(pid_t pid, int which) { char path[MAXPATHLEN]; /* according to , * the file for user namepsaces in /proc/$pid/ns will be called * 'user' once the kernel supports it */ static char *ns[] = { "user", "mnt", "pid", "uts", "ipc", "net" }; static int flags[] = { CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWNET }; static const int size = sizeof(ns) / sizeof(char *); int fd[size]; int i, j, saved_errno; snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid); if (access(path, X_OK)) { ERROR("Does this kernel version support 'attach' ?"); return -1; } for (i = 0; i < size; i++) { /* ignore if we are not supposed to attach to that * namespace */ if (which != -1 && !(which & flags[i])) { fd[i] = -1; continue; } snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]); fd[i] = open(path, O_RDONLY | O_CLOEXEC); if (fd[i] < 0) { saved_errno = errno; /* close all already opened file descriptors before * we return an error, so we don't leak them */ for (j = 0; j < i; j++) close(fd[j]); errno = saved_errno; SYSERROR("failed to open '%s'", path); return -1; } } for (i = 0; i < size; i++) { if (fd[i] >= 0 && setns(fd[i], 0) != 0) { saved_errno = errno; for (j = i; j < size; j++) close(fd[j]); errno = saved_errno; SYSERROR("failed to set namespace '%s'", ns[i]); return -1; } close(fd[i]); } 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 last_cap = lxc_caps_last_cap(); int 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 remove capability id %d", cap); return -1; } } return 0; } static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char** extra_env, char** extra_keep) { if (policy == LXC_ATTACH_CLEAR_ENV) { char **extra_keep_store = NULL; int path_kept = 0; 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) { SYSERROR("failed to allocate memory for storing current " "environment variable values that will be kept"); 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]) { SYSERROR("failed to allocate memory for storing current " "environment variable values that will be kept"); while (i > 0) free(extra_keep_store[--i]); free(extra_keep_store); return -1; } if (strcmp(extra_keep[i], "PATH") == 0) path_kept = 1; } /* calloc sets entire array to zero, so we don't * need an else */ } } if (clearenv()) { char **p; SYSERROR("failed to clear environment"); if (extra_keep_store) { for (p = extra_keep_store; *p; p++) free(*p); free(extra_keep_store); } return -1; } if (extra_keep_store) { size_t i; for (i = 0; extra_keep[i]; i++) { if (extra_keep_store[i]) { if (setenv(extra_keep[i], extra_keep_store[i], 1) < 0) SYSERROR("Unable to set environment variable"); } 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) setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); } if (putenv("container=lxc")) { SYSERROR("failed to set environment variable"); return -1; } /* set extra environment variables */ if (extra_env) { for (; *extra_env; extra_env++) { /* duplicate the string, just to be on * the safe side, because putenv does not * do it for us */ char *p = strdup(*extra_env); /* we just assume the user knows what they * are doing, so we don't do any checks */ if (!p) { SYSERROR("failed to allocate memory for additional environment " "variables"); return -1; } putenv(p); } } return 0; } static char *lxc_attach_getpwshell(uid_t uid) { /* local variables */ pid_t pid; int pipes[2]; int ret; int fd; char *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 = pipe(pipes); if (ret < 0) return NULL; pid = fork(); if (pid < 0) { close(pipes[0]); close(pipes[1]); return NULL; } if (pid) { /* parent process */ FILE *pipe_f; char *line = NULL; size_t line_bufsz = 0; int found = 0; int status; close(pipes[1]); pipe_f = fdopen(pipes[0], "r"); while (getline(&line, &line_bufsz, pipe_f) != -1) { char *token; char *saveptr = NULL; long value; char *endptr = NULL; int i; /* 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; /* 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) break; } 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 = 1; } free(line); fclose(pipe_f); again: if (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) goto again; return NULL; } /* some sanity checks: if anything even hinted at going * wrong: we can't be sure we have a valid result, so * we assume we don't */ if (!WIFEXITED(status)) return NULL; if (WEXITSTATUS(status) != 0) return NULL; if (!found) return NULL; return result; } else { /* child process */ char uid_buf[32]; char *arguments[] = { "getent", "passwd", uid_buf, NULL }; close(pipes[0]); /* we want to capture stdout */ dup2(pipes[1], 1); close(pipes[1]); /* get rid of stdin/stderr, so we try to associate it * with /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) { close(0); close(2); } else { dup2(fd, 0); dup2(fd, 2); close(fd); } /* finish argument list */ ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid); if (ret <= 0) exit(-1); /* try to run getent program */ (void) execvp("getent", arguments); exit(-1); } } static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) { FILE *proc_file; char proc_fn[MAXPATHLEN]; char *line = NULL; size_t line_bufsz = 0; int ret; long value = -1; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; /* read capabilities */ snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", 1); 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 > 0) { uid = (uid_t) value; } else { ret = sscanf(line, "Gid: %ld", &value); if (ret != EOF && ret > 0) 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 */ } struct attach_clone_payload { int ipc_socket; lxc_attach_options_t* options; struct lxc_proc_context_info* init_ctx; lxc_attach_exec_t exec_function; void* exec_payload; }; static int attach_child_main(void* data); /* 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(const char *name, const char *lxcpath, struct lxc_proc_context_info *i, lxc_attach_options_t *options) { struct lxc_container *c; char *path; if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) return true; c = lxc_container_new(name, lxcpath); if (!c) return false; i->container = c; /* Initialize an empty lxc_conf */ 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) { 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; } return true; } static signed long get_personality(const char *name, const char *lxcpath) { char *p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); signed long ret; if (!p) return -1; ret = lxc_config_parse_arch(p); free(p); return ret; } 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 ret, status; pid_t init_pid, pid, attached_pid, expected; struct lxc_proc_context_info *init_ctx; char* cwd; char* new_cwd; int ipc_sockets[2]; signed long personality; if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { ERROR("failed to get the init pid"); return -1; } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("failed to get context of the init process, pid = %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; if (!fetch_seccomp(name, lxcpath, init_ctx, 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 unshared"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } } /* 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, three reasons: * 1. runs all pthread_atfork handlers and the * child will no longer be threaded * (we can't properly setns() in a threaded process) * 2. we can't setns() in the child itself, since * we want to make sure we are properly attached to * the pidns * 3. 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 procfd = -1; pid_t to_cleanup_pid = pid; /* initial thread, we close the socket that is for the * subprocesses */ close(ipc_sockets[1]); free(cwd); /* attach to cgroup, if requested */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (!cgroup_attach(name, lxcpath, pid)) goto cleanup_error; } /* Open /proc before setns() to the containers namespace so we * don't rely on any information from inside the container. */ procfd = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (procfd < 0) { SYSERROR("Unable to open /proc."); goto cleanup_error; } /* Let the child process know to go ahead */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("error using IPC to notify attached process for initialization (0)"); goto cleanup_error; } /* get pid from intermediate process */ ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL); if (ret <= 0) { if (ret != 0) ERROR("error using IPC to receive notification " "from attached process (1)"); goto cleanup_error; } /* 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 cleanup_error; /* we will always have to reap the grandchild now */ to_cleanup_pid = attached_pid; /* tell attached process it may start initializing */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("error using IPC to notify attached process for initialization (0)"); goto cleanup_error; } /* wait for the attached process to finish initializing */ expected = 1; ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); if (ret <= 0) { if (ret != 0) ERROR("error using IPC to receive notification from attached process (1)"); goto cleanup_error; } /* tell attached process we're done */ status = 2; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("Error using IPC to notify attached process for " "initialization (2): %s.", strerror(errno)); goto cleanup_error; } /* Wait for the (grand)child to tell us that it's ready to set * up its LSM labels. */ expected = 3; ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Error using IPC for the child to tell us to open LSM fd (3): %s.", strerror(errno)); goto cleanup_error; } /* Open LSM fd and send it to child. */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec, saved_errno; int labelfd = -1; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; /* Open fd for the LSM security module. */ labelfd = lsm_openat(procfd, attached_pid, on_exec); if (labelfd < 0) goto cleanup_error; /* Send child fd of the LSM security module to write to. */ ret = lxc_abstract_unix_send_fd(ipc_sockets[0], labelfd, NULL, 0); saved_errno = errno; close(labelfd); if (ret <= 0) { ERROR("Intended to send file descriptor %d: %s.", labelfd, strerror(saved_errno)); goto cleanup_error; } } if (procfd >= 0) close(procfd); /* now shut down communication with child, we're done */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); lxc_proc_put_context_info(init_ctx); /* 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; return 0; cleanup_error: /* first shut down the socket, then wait for the pid, * otherwise the pid we're waiting for may never exit */ if (procfd >= 0) close(procfd); shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); if (to_cleanup_pid) (void) wait_for_pid(to_cleanup_pid); lxc_proc_put_context_info(init_ctx); return -1; } /* first subprocess begins here, we close the socket that is for the * initial thread */ close(ipc_sockets[0]); /* Wait for the parent to have setup cgroups */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected); if (ret <= 0) { ERROR("error communicating with child process"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* 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, options->namespaces); if (ret < 0) { ERROR("failed to enter the namespace"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* attach succeeded, try to cwd */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; ret = chdir(new_cwd); if (ret < 0) WARN("could not change directory to '%s'", new_cwd); free(cwd); /* now create the real child process */ { struct attach_clone_payload payload = { .ipc_socket = ipc_sockets[1], .options = options, .init_ctx = init_ctx, .exec_function = exec_function, .exec_payload = exec_payload }; /* We use clone_parent here to make this subprocess a direct child of * the initial process. Then this intermediate process can exit and * the parent can directly track the attached process. */ pid = lxc_clone(attach_child_main, &payload, CLONE_PARENT); } /* shouldn't happen, clone() should always return positive pid */ if (pid <= 0) { SYSERROR("failed to create subprocess"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* 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 */ ERROR("error using IPC to notify main process of pid of the attached process"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* the rest is in the hands of the initial and the attached process */ rexit(0); } static int attach_child_main(void* data) { struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int ipc_socket = payload->ipc_socket; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; #if HAVE_SYS_PERSONALITY_H long new_personality; #endif int ret; int status; int expected; long flags; int fd; int lsm_labelfd; uid_t new_uid; gid_t new_gid; /* wait for the initial thread to signal us that it's ready * for us to start initializing */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Error using IPC to receive notification from initial process (0): %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* 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) { shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* now perform additional attachments*/ #if HAVE_SYS_PERSONALITY_H if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { ret = personality(new_personality); if (ret < 0) { SYSERROR("could not ensure correct architecture"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) { ERROR("could not drop privileges"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* 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(options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) { ERROR("could not set initial environment for attached process"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* set user / group 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; /* setup the control tty */ if (options->stdin_fd && isatty(options->stdin_fd)) { if (setsid() < 0) { SYSERROR("unable to setsid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) { SYSERROR("unable to TIOCSTTY"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* try to set the uid/gid combination */ if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) { if (setgid(new_gid) || setgroups(0, NULL)) { SYSERROR("switching to container gid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { SYSERROR("switching to container uid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* tell initial process it may now put us into the cgroups */ status = 1; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret != sizeof(status)) { ERROR("Error using IPC to notify initial process for initialization (1): %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* wait for the initial thread to signal us that it has done * everything for us when it comes to cgroups etc. */ expected = 2; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Error using IPC to receive message from initial process " "that it is done pre-initializing (2): %s", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Tell the (grand)parent to send us LSM label fd. */ status = 3; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret <= 0) { ERROR("Error using IPC to tell parent to set up LSM labels (3): %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec; /* Receive fd for LSM security module. */ ret = lxc_abstract_unix_recv_fd(ipc_socket, &lsm_labelfd, NULL, 0); if (ret <= 0) { ERROR("Error using IPC for parent to tell us LSM label fd (4): %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) { SYSERROR("Failed to set LSM label."); shutdown(ipc_socket, SHUT_RDWR); close(lsm_labelfd); rexit(-1); } close(lsm_labelfd); } if (init_ctx->container && init_ctx->container->lxc_conf && lxc_seccomp_load(init_ctx->container->lxc_conf) != 0) { ERROR("Loading seccomp policy"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } shutdown(ipc_socket, SHUT_RDWR); close(ipc_socket); lxc_proc_put_context_info(init_ctx); /* 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 != 0) dup2(options->stdin_fd, 0); if (options->stdout_fd >= 0 && options->stdout_fd != 1) dup2(options->stdout_fd, 1); if (options->stderr_fd >= 0 && options->stderr_fd != 2) dup2(options->stderr_fd, 2); /* close the old fds */ if (options->stdin_fd > 2) close(options->stdin_fd); if (options->stdout_fd > 2) close(options->stdout_fd); if (options->stderr_fd > 2) close(options->stderr_fd); /* try to remove CLOEXEC flag from stdin/stdout/stderr, * but also here, ignore errors */ for (fd = 0; fd <= 2; fd++) { flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if (flags & FD_CLOEXEC) { if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0) { SYSERROR("Unable to clear CLOEXEC from fd"); } } } /* we're done, so we can now do whatever the user intended us to do */ rexit(payload->exec_function(payload->exec_payload)); } int lxc_attach_run_command(void* payload) { lxc_attach_command_t* cmd = (lxc_attach_command_t*)payload; execvp(cmd->program, cmd->argv); SYSERROR("failed to exec '%s'", cmd->program); return -1; } int lxc_attach_run_shell(void* payload) { uid_t uid; struct passwd *passwd; char *user_shell; /* ignore payload parameter */ (void)payload; uid = getuid(); passwd = getpwuid(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 (!passwd) user_shell = lxc_attach_getpwshell(uid); else user_shell = passwd->pw_shell; if (user_shell) execlp(user_shell, user_shell, 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", NULL); SYSERROR("failed to exec shell"); return -1; } lxc-1.0.10/src/lxc/lxc-checkconfig.in0000644061062106075000000001374313105114536014253 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=$? if [ $RES -eq 0 ]; then $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL else if [ ! -z "$mandatory" ] && [ "$mandatory" = yes ]; then $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL else $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL fi 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 echo "--- Namespaces ---" echo -n "Namespaces: " && is_enabled CONFIG_NAMESPACES yes echo -n "Utsname namespace: " && is_enabled CONFIG_UTS_NS echo -n "Ipc namespace: " && is_enabled CONFIG_IPC_NS yes echo -n "Pid namespace: " && is_enabled CONFIG_PID_NS yes echo -n "User namespace: " && is_enabled CONFIG_USER_NS if is_set CONFIG_USER_NS; then if type newuidmap > /dev/null 2>&1; then f=`type -P newuidmap` if [ ! -u "${f}" ]; then echo "Warning: newuidmap is not setuid-root" fi else echo "newuidmap is not installed" fi if type newgidmap > /dev/null 2>&1; then f=`type -P 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 -n "Multiple /dev/pts instances: " && is_enabled DEVPTS_MULTIPLE_INSTANCES echo echo "--- Control groups ---" print_cgroups() { # print all mountpoints for cgroup filesystems awk '$1 !~ /#/ && $3 == mp { print $2; } ; END { exit(0); } ' "mp=$1" "$2" ; } CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -n 1` 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 -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then echo -n "Cgroup clone_children flag: " && $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL else echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes fi echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT echo -n "Cgroup memory controller: " if ([ $KVER_MAJOR -ge 3 ] && [ $KVER_MINOR -ge 6 ]) || ([ $KVER_MAJOR -gt 3 ]); then is_enabled CONFIG_MEMCG else is_enabled CONFIG_CGROUP_MEM_RES_CTLR fi is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS echo echo "--- Misc ---" echo -n "Veth pair device: " && is_enabled CONFIG_VETH echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q echo -n "Bridges: " && is_enabled CONFIG_BRIDGE echo -n "Advanced netfilter: " && is_enabled CONFIG_NETFILTER_ADVANCED echo -n "CONFIG_NF_NAT_IPV4: " && is_enabled CONFIG_NF_NAT_IPV4 echo -n "CONFIG_NF_NAT_IPV6: " && is_enabled CONFIG_NF_NAT_IPV6 echo -n "CONFIG_IP_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP_NF_TARGET_MASQUERADE echo -n "CONFIG_IP6_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP6_NF_TARGET_MASQUERADE echo -n "CONFIG_NETFILTER_XT_TARGET_CHECKSUM: " && is_enabled CONFIG_NETFILTER_XT_TARGET_CHECKSUM echo echo "--- Checkpoint/Restore ---" echo -n "checkpoint restore: " && is_enabled CONFIG_CHECKPOINT_RESTORE echo -n "CONFIG_FHANDLE: " && is_enabled CONFIG_FHANDLE echo -n "CONFIG_EVENTFD: " && is_enabled CONFIG_EVENTFD echo -n "CONFIG_EPOLL: " && is_enabled CONFIG_EPOLL echo -n "CONFIG_UNIX_DIAG: " && is_enabled CONFIG_UNIX_DIAG echo -n "CONFIG_INET_DIAG: " && is_enabled CONFIG_INET_DIAG echo -n "CONFIG_PACKET_DIAG: " && is_enabled CONFIG_PACKET_DIAG echo -n "CONFIG_NETLINK_DIAG: " && is_enabled CONFIG_NETLINK_DIAG echo -n "File capabilities: " && \ ( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \ is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || \ ( ( [ "${KVER_MAJOR}" = "2" ] && [ ${KVER_MINOR} -gt 32 ] ) || \ [ ${KVER_MAJOR} -gt 2 ] && $SETCOLOR_SUCCESS && \ echo "enabled" && $SETCOLOR_NORMAL ) echo echo "Note : Before booting a new kernel, you can check its configuration" echo "usage : CONFIG=/path/to/config $0" echo lxc-1.0.10/src/lxc/version.h0000644061062106075000000000203613105114545012523 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_VERSION_MAJOR 1 #define LXC_VERSION_MINOR 0 #define LXC_VERSION_MICRO 10 #define LXC_VERSION "1.0.10" #endif lxc-1.0.10/src/lxc/lxccontainer.h0000644061062106075000000006745713105114536013551 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; /*! * 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, the value written * to \p retv will be truncated. */ 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/lxcsnaps/\/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/lxcsnaps/c1/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); }; /*! * \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 */ }; /*! * \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-1.0.10/src/lxc/rtnl.h0000644061062106075000000000622413105114536012020 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 */ 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 */ 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 */ 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 */ int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); struct genlmsg *genlmsg_alloc(size_t size); 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 */ int rtnetlink_transaction(struct rtnl_handler *handler, struct rtnlmsg *request, struct rtnlmsg *answer); #endif lxc-1.0.10/src/lxc/start.h0000644061062106075000000000367013105114536012200 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_START_H #define __LXC_START_H #include #include #include "config.h" #include "state.h" #include "namespace.h" struct lxc_conf; struct lxc_handler; struct lxc_operations { int (*start)(struct lxc_handler *, void *); int (*post_start)(struct lxc_handler *, void *); }; struct cgroup_desc; enum { LXC_NS_MNT, LXC_NS_PID, LXC_NS_UTS, LXC_NS_IPC, LXC_NS_USER, LXC_NS_NET, LXC_NS_MAX }; struct ns_info { const char *proc_name; int clone_flag; }; extern const struct ns_info ns_info[LXC_NS_MAX]; struct lxc_handler { pid_t pid; char *name; lxc_state_t state; int clone_flags; int sigfd; sigset_t oldmask; struct lxc_conf *conf; struct lxc_operations *ops; void *data; int sv[2]; int pinfd; const char *lxcpath; void *cgroup_data; int nsfd[LXC_NS_MAX]; }; extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const char *); extern int lxc_check_inherited(struct lxc_conf *conf, int fd_to_ignore); int __lxc_start(const char *, struct lxc_conf *, struct lxc_operations *, void *, const char *); #endif lxc-1.0.10/src/lxc/lxc_snapshot.c0000644061062106075000000001201613105114536013535 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 "config.h" #include #include #include #include #include #include #include #include "lxc.h" #include "log.h" #include "bdev.h" #include "arguments.h" #include "utils.h" lxc_log_define(lxc_snapshot_ui, lxc); static char *newname; static char *snapshot; #define DO_SNAP 0 #define DO_LIST 1 #define DO_RESTORE 2 #define DO_DESTROY 3 static int action; static int print_comments; static char *commentfile; static int do_snapshot(struct lxc_container *c) { int ret; ret = c->snapshot(c, commentfile); if (ret < 0) { ERROR("Error creating a snapshot"); return -1; } INFO("Created snapshot snap%d", ret); 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); } static int do_list_snapshots(struct lxc_container *c) { struct lxc_snapshot *s; int i, n; n = c->snapshot_list(c, &s); if (n < 0) { ERROR("Error listing snapshots"); return -1; } if (n == 0) { printf("No snapshots\n"); return 0; } for (i=0; isnapshot_restore(c, snapshot, newname)) return 0; ERROR("Error restoring snapshot %s", snapshot); return -1; } static int do_destroy_snapshots(struct lxc_container *c) { if (c->snapshot_destroy(c, snapshot)) return 0; ERROR("Error destroying snapshot %s", snapshot); return -1; } static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 'L': action = DO_LIST; break; case 'r': snapshot = arg; action = DO_RESTORE; break; case 'd': snapshot = arg; action = DO_DESTROY; break; case 'c': commentfile = arg; break; case 'C': print_comments = true; break; } return 0; } static const struct option my_longopts[] = { {"list", no_argument, 0, 'L'}, {"restore", required_argument, 0, 'r'}, {"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 [newname]]\n\ \n\ lxc-snapshot snapshots a container\n\ \n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ -L, --list list snapshots\n\ -C, --showcomments show snapshot comments in list\n\ -c, --comment=file add file as a comment\n\ -r, --restore=name restore snapshot name, i.e. 'snap0'\n\ -d, --destroy=name destroy snapshot name, i.e. 'snap0'\n", .options = my_longopts, .parser = my_parser, .checker = NULL, }; /* * lxc-snapshot -P lxcpath -n container * lxc-snapshot -P lxcpath -n container -l * lxc-snapshot -P lxcpath -n container -r snap3 recovered_1 */ int main(int argc, char *argv[]) { struct lxc_container *c; int ret = 0; if (lxc_arguments_parse(&my_args, argc, argv)) exit(1); if (!my_args.log_file) my_args.log_file = "none"; if (my_args.argc > 1) { ERROR("Too many arguments"); exit(1); } if (my_args.argc == 1) newname = my_args.argv[0]; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) exit(1); lxc_log_options_no_override(); if (geteuid()) { if (access(my_args.lxcpath[0], O_RDWR) < 0) { fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]); exit(1); } } c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { fprintf(stderr, "System error loading container\n"); exit(1); } if (!c->may_control(c)) { fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); lxc_container_put(c); exit(1); } switch(action) { case DO_SNAP: ret = do_snapshot(c); break; case DO_LIST: ret = do_list_snapshots(c); break; case DO_RESTORE: ret = do_restore_snapshots(c); break; case DO_DESTROY: ret = do_destroy_snapshots(c); break; } lxc_container_put(c); if (ret == 0) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } lxc-1.0.10/src/lxc/caps.c0000644061062106075000000001000513105114536011752 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" lxc_log_define(lxc_caps, lxc); #if HAVE_SYS_CAPABILITY_H #include #ifndef PR_CAPBSET_READ #define PR_CAPBSET_READ 23 #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: %m"); return -1; } ret = cap_clear_flag(caps, CAP_EFFECTIVE); if (ret) { ERROR("failed to cap_clear_flag: %m"); goto out; } ret = cap_set_proc(caps); if (ret) { ERROR("failed to cap_set_proc: %m"); 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: %m"); 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: %m"); goto out; } } ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); if (ret) { ERROR("failed to cap_set_flag: %m"); goto out; } } ret = cap_set_proc(caps); if (ret) { ERROR("failed to cap_set_proc: %m"); 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': %m"); return -1; } if (setresgid(gid, gid, gid)) { ERROR("failed to change gid to '%d': %m", gid); return -1; } if (setresuid(uid, uid, uid)) { ERROR("failed to change uid to '%d': %m", uid); return -1; } if (lxc_caps_up()) { ERROR("failed to restore capabilities: %m"); 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; if ((n = read(fd, buf, 31)) >= 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; } #endif lxc-1.0.10/src/lxc/utils.c0000644061062106075000000007170313105114536012200 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 "utils.h" #include "log.h" #include "lxclock.h" #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, bool onedev) { struct dirent *direntp; DIR *dir; int ret, failed=0; char pathname[MAXPATHLEN]; dir = opendir(dirname); if (!dir) { ERROR("%s: failed to open %s", __func__, 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; continue; } ret = lstat(pathname, &mystat); if (ret) { ERROR("%s: failed to stat %s", __func__, 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, onedev) < 0) failed=1; } else { if (unlink(pathname) < 0) { SYSERROR("%s: failed to delete %s", __func__, pathname); failed=1; } } } if (rmdir(dirname) < 0 && !btrfs_try_remove_subvol(dirname)) { ERROR("%s: failed to delete %s", __func__, dirname); failed=1; } ret = closedir(dir); if (ret) { ERROR("%s: failed to close directory %s", __func__, dirname); failed=1; } return failed ? -1 : 0; } /* we have two different magic values for overlayfs, yay */ #define OVERLAYFS_SUPER_MAGIC 0x794c764f #define OVERLAY_SUPER_MAGIC 0x794c7630 /* * 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) { struct statfs sb; if (statfs(path, &sb) < 0) return false; if (sb.f_type == OVERLAYFS_SUPER_MAGIC || sb.f_type == OVERLAY_SUPER_MAGIC) return true; return false; } /* returns 0 on success, -1 if there were any failures */ extern int lxc_rmdir_onedev(char *path) { struct stat mystat; bool onedev = true; if (is_native_overlayfs(path)) { onedev = false; } if (lstat(path, &mystat) < 0) { if (errno == ENOENT) return 0; ERROR("%s: failed to stat %s", __func__, path); return -1; } return _recursive_rmdir(path, mystat.st_dev, 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; if (geteuid() == 0) { 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); } extern struct lxc_popen_FILE *lxc_popen(const char *command) { struct lxc_popen_FILE *fp = NULL; int parent_end = -1, child_end = -1; int pipe_fds[2]; pid_t child_pid; int r = pipe2(pipe_fds, O_CLOEXEC); if (r < 0) { ERROR("pipe2 failure"); return NULL; } parent_end = pipe_fds[0]; child_end = pipe_fds[1]; child_pid = fork(); if (child_pid == 0) { /* child */ int child_std_end = STDOUT_FILENO; if (child_end != child_std_end) { /* dup2() doesn't dup close-on-exec flag */ dup2(child_end, child_std_end); /* it's safe not to close child_end here * as it's marked close-on-exec anyway */ } else { /* * The descriptor is already the one we will use. * But it must not be marked close-on-exec. * Undo the effects. */ if (fcntl(child_end, F_SETFD, 0) != 0) { SYSERROR("Failed to remove FD_CLOEXEC from fd."); exit(127); } } /* * Unblock signals. * This is the main/only reason * why we do our lousy popen() emulation. */ { sigset_t mask; sigfillset(&mask); sigprocmask(SIG_UNBLOCK, &mask, NULL); } execl("/bin/sh", "sh", "-c", command, (char *) NULL); exit(127); } /* parent */ close(child_end); child_end = -1; if (child_pid < 0) { ERROR("fork failure"); goto error; } fp = calloc(1, sizeof(*fp)); if (!fp) { ERROR("failed to allocate memory"); goto error; } fp->f = fdopen(parent_end, "r"); if (!fp->f) { ERROR("fdopen failure"); goto error; } fp->child_pid = child_pid; return fp; error: if (fp) { if (fp->f) { fclose(fp->f); parent_end = -1; /* so we do not close it second time */ } free(fp); } if (parent_end != -1) close(parent_end); return NULL; } extern int lxc_pclose(struct lxc_popen_FILE *fp) { FILE *f = NULL; pid_t child_pid = 0; int wstatus = 0; pid_t wait_pid; if (fp) { f = fp->f; child_pid = fp->child_pid; /* free memory (we still need to close file stream) */ free(fp); fp = NULL; } if (!f || fclose(f)) { ERROR("fclose failure"); return -1; } do { wait_pid = waitpid(child_pid, &wstatus, 0); } while (wait_pid == -1 && errno == EINTR); if (wait_pid == -1) { ERROR("waitpid failure"); 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 */ assert(saved_len == len); /* make sure we didn't overwrite any buffer, * due to calloc the string should be 0-terminated */ assert(result[len] == '\0'); 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) strcpy(result, sep); 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_append_paths(const char *first, const char *second) { size_t len = strlen(first) + strlen(second) + 1; const char *pattern = "%s%s"; char *result = NULL; if (second[0] != '/') { len += 1; pattern = "%s/%s"; } result = calloc(1, len); if (!result) return NULL; snprintf(result, len, pattern, first, second); 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' }; if (!haystack || !needle) return 0; str = alloca(strlen(haystack)+1); strcpy(str, haystack); 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; if (!string) return calloc(1, sizeof(char *)); str = alloca(strlen(string) + 1); strcpy(str, string); 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; if (!string) return calloc(1, sizeof(char *)); str = alloca(strlen(string)+1); strcpy(str, string); 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) { int 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. */ #define LINELEN 4096 int detect_shared_rootfs(void) { char buf[LINELEN], *p; FILE *f; int i; char *p2; f = fopen("/proc/self/mountinfo", "r"); if (!f) return 0; while (fgets(buf, 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; } /* * 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. */ int detect_ramfs_rootfs(void) { char buf[LINELEN], *p; FILE *f; int i; char *p2; f = fopen("/proc/self/mountinfo", "r"); if (!f) return 0; while (fgets(buf, 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 the ramfs? p = strchr(p2+1, '-'); if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) { fclose(f); return 1; } } } fclose(f); return 0; } char *on_path(char *cmd) { 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) { 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; } /* * 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; } int null_stdfds(void) { int fd, ret = -1; fd = open("/dev/null", O_RDWR); if (fd < 0) return -1; if (dup2(fd, 0) < 0) goto err; if (dup2(fd, 1) < 0) goto err; if (dup2(fd, 2) < 0) goto err; ret = 0; err: close(fd); return ret; } /* * @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, dest); return ret; } return 0; } lxc-1.0.10/src/lxc/lxc-top0000755061062106075000000001643513105114536012211 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-1.0.10/src/lxc/cgmanager.c0000644061062106075000000010727713105114536012772 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 "conf.h" #include "utils.h" #include "bdev.h" #include "log.h" #include "cgroup.h" #include "start.h" #include "state.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, "pthread_mutex_unlock returned:%d %s\n", 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_supports_multiple_controllers; /* * if cgm_all_controllers_same is true, then cgm_supports_multiple_controllers * is 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_supports_multiple_controllers = false; cgm_all_controllers_same = false; if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS) { cgm_supports_multiple_controllers = false; return; } cgm_supports_multiple_controllers = true; 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 lxc_cgmanager_escape(void) { bool ret = true; pid_t me = getpid(); char **slist = subsystems; int i; 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; } } return ret; } 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; 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; } if (send_creds(sv[0], getpid(), getuid(), getgid())) { SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); 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], getpid(), newuid, 0)) { SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); 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_supports_multiple_controllers) 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) < 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_supports_multiple_controllers) 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(const char *name) { struct cgm_data *d; if (!cgm_dbus_connect()) { ERROR("Error connecting to cgroup manager"); return NULL; } check_supports_multiple_controllers(-1); d = malloc(sizeof(*d)); if (!d) { cgm_dbus_disconnect(); return NULL; } memset(d, 0, sizeof(*d)); d->name = strdup(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 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_supports_multiple_controllers) 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_supports_multiple_controllers) 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_supports_multiple_controllers) 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(); ERROR("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(getpid(), controller, cgroup, abs_cgroup_supported())) { ERROR("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(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--; } } 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; } 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 bool collect_subsytems(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 out_good; 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; } out_good: return true; 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) { if (!collect_subsytems()) return NULL; if (!cgm_dbus_connect()) goto err1; // root; try to escape to root cgroup if (geteuid() == 0 && !lxc_cgmanager_escape()) goto err2; cgm_dbus_disconnect(); return &cgmanager_ops; err2: cgm_dbus_disconnect(); err1: free_subsystems(); return NULL; } /* 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_list *cgroup_settings, bool do_devices) { struct cgm_data *d = hdata; struct lxc_list *iterator, *sorted_cgroup_settings, *next; struct lxc_cgroup *cg; 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.id_map") * 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, .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, }; #endif lxc-1.0.10/src/lxc/seccomp.c0000644061062106075000000005202113105114536012461 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 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; } } #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 n-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.", ret, line, nr, 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 n-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.", line, 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.", line, 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.", line, 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.", line, 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' * the directives must include 'whitelist' (only type of policy currently * supported) and can include 'debug' (though debug is not yet supported). */ static int parse_config(FILE *f, struct lxc_conf *conf) { char line[1024]; int ret, version; ret = fscanf(f, "%d\n", &version); if (ret != 1 || (version != 1 && 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 n-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; } 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-1.0.10/src/lxc/mainloop.c0000644061062106075000000000676713105114536012666 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; 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 = (struct mainloop_handler *) events[i].data.ptr; /* If the handler returns a positive value, exit the mainloop */ if (handler->callback(handler->fd, events[i].events, handler->data, descr) > 0) return 0; } if (nfds == 0 && timeout_ms != 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_create(2); if (descr->epfd < 0) return -1; if (fcntl(descr->epfd, F_SETFD, FD_CLOEXEC)) { close(descr->epfd); 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; } return close(descr->epfd); } lxc-1.0.10/src/lxc/lxc_init.c0000644061062106075000000001353013105114536012643 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 #define _GNU_SOURCE #include #include "log.h" #include "caps.h" #include "error.h" #include "initutils.h" lxc_log_define(lxc_init, lxc); static int quiet; static const struct option options[] = { { "name", required_argument, NULL, 'n' }, { "logpriority", required_argument, NULL, 'l' }, { "quiet", no_argument, NULL, 'q' }, { "lxcpath", required_argument, NULL, 'P' }, { 0, 0, 0, 0 }, }; static sig_atomic_t was_interrupted = 0; static void interrupt_handler(int sig) { if (!was_interrupted) was_interrupted = sig; } static void usage(void) { fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n" "Common options :\n" " -n, --name=NAME NAME of the container\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" "\n" "Mandatory or optional arguments to long options are also mandatory or optional\n" "for any corresponding short options.\n" "\n" "NOTE: lxc-init is intended for use by lxc internally\n" " and does not need to be run by hand\n\n"); } int main(int argc, char *argv[]) { pid_t pid; int err; char **aargv; sigset_t mask, omask; int i, have_status = 0, shutdown = 0; int opt; char *lxcpath = NULL, *name = NULL, *logpriority = NULL; while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) { switch(opt) { case 'n': name = optarg; break; case 'l': logpriority = optarg; break; case 'q': quiet = 1; break; case 'P': lxcpath = optarg; break; default: /* '?' */ usage(); exit(EXIT_FAILURE); } } err = lxc_log_init(name, name ? NULL : "none", logpriority, basename(argv[0]), quiet, lxcpath); if (err < 0) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (!argv[optind]) { ERROR("missing command to launch"); exit(EXIT_FAILURE); } aargv = &argv[optind]; /* * mask all the signals so we are safe to install a * signal handler and to fork */ if (sigfillset(&mask) || sigdelset(&mask, SIGILL) || sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGBUS) || sigprocmask(SIG_SETMASK, &mask, &omask)) { SYSERROR("failed to set signal mask"); exit(EXIT_FAILURE); } for (i = 1; i < NSIG; i++) { struct sigaction act; /* 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. */ if (i == SIGILL || i == SIGSEGV || i == SIGBUS || i == SIGSTOP || i == SIGKILL || i == 32 || i == 33) continue; if (sigfillset(&act.sa_mask) || sigdelset(&act.sa_mask, SIGILL) || sigdelset(&act.sa_mask, SIGSEGV) || sigdelset(&act.sa_mask, SIGBUS) || sigdelset(&act.sa_mask, SIGSTOP) || sigdelset(&act.sa_mask, SIGKILL)) { ERROR("failed to set signal"); exit(EXIT_FAILURE); } act.sa_flags = 0; act.sa_handler = interrupt_handler; if (sigaction(i, &act, NULL) && errno != EINVAL) { SYSERROR("failed to sigaction"); exit(EXIT_FAILURE); } } lxc_setup_fs(); pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (!pid) { /* restore default signal handlers */ for (i = 1; i < NSIG; i++) signal(i, SIG_DFL); if (sigprocmask(SIG_SETMASK, &omask, NULL)) { SYSERROR("failed to set signal mask"); exit(EXIT_FAILURE); } NOTICE("about to exec '%s'", aargv[0]); execvp(aargv[0], aargv); ERROR("failed to exec: '%s' : %m", aargv[0]); exit(err); } /* let's process the signals now */ if (sigdelset(&omask, SIGALRM) || sigprocmask(SIG_SETMASK, &omask, NULL)) { SYSERROR("failed to set signal mask"); exit(EXIT_FAILURE); } /* no need of other inherited fds but stderr */ close(fileno(stdin)); close(fileno(stdout)); err = EXIT_SUCCESS; for (;;) { int status; pid_t waited_pid; switch (was_interrupted) { case 0: break; case SIGPWR: case SIGTERM: if (!shutdown) { shutdown = 1; kill(-1, SIGTERM); alarm(1); } break; case SIGALRM: kill(-1, SIGKILL); break; default: kill(pid, was_interrupted); break; } was_interrupted = 0; waited_pid = wait(&status); if (waited_pid < 0) { if (errno == ECHILD) goto out; if (errno == EINTR) continue; ERROR("failed to wait child : %s", strerror(errno)); goto out; } /* reset timer each time a process exited */ if (shutdown) alarm(1); /* * keep the exit code of started application * (not wrapped pid) and continue to wait for * the end of the orphan group. */ if (waited_pid == pid && !have_status) { err = lxc_error_set_and_log(waited_pid, status); have_status = 1; } } out: if (err < 0) exit(EXIT_FAILURE); exit(err); } lxc-1.0.10/src/lxc/namespace.h0000644061062106075000000000373413105114536013000 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 "config.h" #ifndef CLONE_FS # define CLONE_FS 0x00000200 #endif #ifndef CLONE_NEWNS # define CLONE_NEWNS 0x00020000 #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 #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 extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags); extern int lxc_namespace_2_cloneflag(char *namespace); extern int lxc_fill_namespace_flags(char *flaglist, int *flags); #endif lxc-1.0.10/src/lxc/lxc_freeze.c0000644061062106075000000000437213105114536013164 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" lxc_log_define(lxc_freeze_ui, lxc); 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", .options = my_longopts, .parser = NULL, .checker = NULL, }; int main(int argc, char *argv[]) { struct lxc_container *c; if (lxc_arguments_parse(&my_args, argc, argv)) exit(1); if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) exit(1); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name); exit(1); } if (!c->may_control(c)) { ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(1); } if (!c->freeze(c)) { ERROR("Failed to freeze %s:%s", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(1); } lxc_container_put(c); exit(0); } lxc-1.0.10/src/lxc/lxc_cgroup.c0000644061062106075000000000604013105114536013175 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" lxc_log_define(lxc_cgroup_ui, lxc); 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", .options = my_longopts, .parser = NULL, .checker = my_checker, }; int main(int argc, char *argv[]) { char *state_object = NULL, *value = NULL; struct lxc_container *c; if (lxc_arguments_parse(&my_args, argc, argv)) return 1; if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return 1; lxc_log_options_no_override(); state_object = my_args.argv[0]; c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) return 1; if (!c->may_control(c)) { ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name); lxc_container_put(c); return 1; } if (!c->is_running(c)) { ERROR("'%s:%s' is not running", my_args.lxcpath[0], my_args.name); lxc_container_put(c); return 1; } if ((my_args.argc) > 1) { value = my_args.argv[1]; if (!c->set_cgroup_item(c, state_object, value)) { ERROR("failed to assign '%s' value to '%s' for '%s'", value, state_object, my_args.name); lxc_container_put(c); return 1; } } else { int len = 4096; char buffer[len]; int ret = c->get_cgroup_item(c, state_object, buffer, len); if (ret < 0) { ERROR("failed to retrieve value of '%s' for '%s:%s'", state_object, my_args.lxcpath[0], my_args.name); lxc_container_put(c); return 1; } printf("%*s", ret, buffer); } lxc_container_put(c); return 0; } lxc-1.0.10/src/lxc/genl.c0000644061062106075000000000742713105114536011767 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 "genl.h" static int genetlink_resolve_family(const char *family) { struct nl_handler handler; struct nlattr *attr; struct genlmsg *request, *reply; struct genlmsghdr *genlmsghdr; int len, ret; request = genlmsg_alloc(GENLMSG_GOOD_SIZE); if (!request) return -ENOMEM; reply = genlmsg_alloc(GENLMSG_GOOD_SIZE); if (!reply) { genlmsg_free(request); return -ENOMEM; } request->nlmsghdr.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); request->nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; request->nlmsghdr.nlmsg_type = GENL_ID_CTRL; genlmsghdr = NLMSG_DATA(&request->nlmsghdr); genlmsghdr->cmd = CTRL_CMD_GETFAMILY; ret = netlink_open(&handler, NETLINK_GENERIC); if (ret) goto out; ret = nla_put_string((struct nlmsg *)&request->nlmsghdr, CTRL_ATTR_FAMILY_NAME, family); if (ret) goto out_close; ret = netlink_transaction(&handler, (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&reply->nlmsghdr); if (ret < 0) goto out_close; genlmsghdr = NLMSG_DATA(&reply->nlmsghdr); len = reply->nlmsghdr.nlmsg_len; ret = -ENOMSG; if (reply->nlmsghdr.nlmsg_type != GENL_ID_CTRL) goto out_close; if (genlmsghdr->cmd != CTRL_CMD_NEWFAMILY) goto out_close; ret = -EMSGSIZE; len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < 0) goto out_close; attr = (struct nlattr *)GENLMSG_DATA(reply); attr = (struct nlattr *)((char *)attr + NLA_ALIGN(attr->nla_len)); ret = -ENOMSG; if (attr->nla_type != CTRL_ATTR_FAMILY_ID) goto out_close; ret = *(__u16 *) NLA_DATA(attr); out_close: netlink_close(&handler); out: genlmsg_free(request); genlmsg_free(reply); return ret; } extern int genetlink_open(struct genl_handler *handler, const char *family) { int ret; handler->family = genetlink_resolve_family(family); if (handler->family < 0) return handler->family; ret = netlink_open(&handler->nlh, NETLINK_GENERIC); return ret; } extern int genetlink_close(struct genl_handler *handler) { return netlink_close(&handler->nlh); } extern int genetlink_rcv(struct genl_handler *handler, struct genlmsg *genlmsg) { return netlink_rcv(&handler->nlh, (struct nlmsg *)&genlmsg->nlmsghdr); } extern int genetlink_send(struct genl_handler *handler, struct genlmsg *genlmsg) { return netlink_send(&handler->nlh, (struct nlmsg *)&genlmsg->nlmsghdr); } extern int genetlink_transaction(struct genl_handler *handler, struct genlmsg *request, struct genlmsg *answer) { return netlink_transaction(&handler->nlh, (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&answer->nlmsghdr); } extern struct genlmsg *genlmsg_alloc(size_t size) { size_t len = NLMSG_LENGTH(GENL_HDRLEN) + size; return (struct genlmsg *)nlmsg_alloc(len); } extern void genlmsg_free(struct genlmsg *genlmsg) { free(genlmsg); } lxc-1.0.10/src/lxc/confile.h0000644061062106075000000000366013105114536012461 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 struct lxc_conf; struct lxc_list; typedef int (*config_cb)(const char *, const char *, struct lxc_conf *); struct lxc_config_t { char *name; config_cb cb; }; extern struct lxc_config_t *lxc_getconfig(const char *key); extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, char *retv, int inlen); extern int lxc_listconfigs(char *retv, int inlen); extern int lxc_config_read(const char *file, struct lxc_conf *conf); extern int lxc_config_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_get_config_item(struct lxc_conf *c, const char *key, char *retv, int inlen); extern int lxc_clear_config_item(struct lxc_conf *c, const char *key); extern void write_config(FILE *fout, struct lxc_conf *c); #endif lxc-1.0.10/src/lxc/sync.h0000644061062106075000000000323613105114536012015 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_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-1.0.10/src/lxc/lxc_start.c0000644061062106075000000002140713105114536013037 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" #define OPT_SHARE_NET OPT_USAGE+1 #define OPT_SHARE_IPC OPT_USAGE+2 #define OPT_SHARE_UTS OPT_USAGE+3 lxc_log_define(lxc_start_ui, lxc); 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) { SYSERROR("failed to create '%s'", path); goto err; } if (fd >= 0) close(fd); } fullpath = realpath(path, NULL); if (!fullpath) { SYSERROR("failed to get the real path of '%s'", path); goto err; } *confpath = strdup(fullpath); if (!*confpath) { ERROR("failed to dup string '%s'", fullpath); goto err; } } err = 0; err: free(fullpath); 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) { SYSERROR("'%s' is not a valid pid nor a container name", lxcname_or_pid); return -1; } if (!s->may_control(s)) { SYSERROR("Insufficient privileges to control container '%s'", s->name); lxc_container_put(s); return -1; } pid = s->init_pid(s); if (pid < 1) { SYSERROR("Is container '%s' running?", s->name); lxc_container_put(s); return -1; } lxc_container_put(s); } if (kill(pid, 0) < 0) { SYSERROR("Can't send signal to pid %d", 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) { SYSERROR("failed to open %s", 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\n\ -F, --foreground Start with the current tty attached to /dev/console (default)\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 = 0, .pidfile = NULL, }; int main(int argc, char *argv[]) { int err = 1; struct lxc_conf *conf; 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()) return err; if (lxc_arguments_parse(&my_args, argc, argv)) return err; if (!my_args.argc) args = default_args; else args = my_args.argv; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return err; lxc_log_options_no_override(); 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) { ERROR("Failed to create lxc_container"); return err; } c->clear_config(c); if (!c->load_config(c, rcfile)) { ERROR("Failed to load rcfile"); lxc_container_put(c); return err; } } else { int rc; rc = asprintf(&rcfile, "%s/%s/config", lxcpath, my_args.name); if (rc == -1) { SYSERROR("failed to allocate memory"); return err; } INFO("using rcfile %s", rcfile); /* container configuration does not exist */ if (access(rcfile, F_OK)) { free(rcfile); rcfile = NULL; } c = lxc_container_new(my_args.name, lxcpath); if (!c) { ERROR("Failed to create lxc_container"); return err; } } if (c->is_running(c)) { ERROR("Container is already running."); err = 0; 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])) { ERROR("Executing '/sbin/init' with no configuration file may crash the host"); goto out; } if (ensure_path(&conf->console.path, my_args.console) < 0) { ERROR("failed to ensure console path '%s'", my_args.console); goto out; } if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) { ERROR("failed to ensure console log '%s'", my_args.console_log); goto out; } if (my_args.pidfile != NULL) { if (ensure_path(&c->pidfile, my_args.pidfile) < 0) { ERROR("failed to ensure pidfile '%s'", my_args.pidfile); goto out; } } int i; 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); err = c->start(c, 0, args) ? 0 : 1; if (err) { ERROR("The container failed to start."); if (my_args.daemonize) ERROR("To get more details, run the container in foreground mode."); ERROR("Additional information can be obtained by setting the " "--logfile and --logpriority options."); err = c->error_num; lxc_container_put(c); return err; } out: lxc_container_put(c); return err; } lxc-1.0.10/src/lxc/commands.c0000644061062106075000000006346013105114536012642 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 #include #include "log.h" #include "lxc.h" #include "conf.h" #include "start.h" /* for struct lxc_handler */ #include "utils.h" #include "cgroup.h" #include "commands.h" #include "console.h" #include "confile.h" #include "mainloop.h" #include "af_unix.h" #include "config.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 int fill_sock_name(char *path, int len, const char *lxcname, const char *lxcpath, const char *hashed_sock_name) { const char *name; char *tmppath; size_t tmplen; uint64_t hash; int ret; name = lxcname; if (!name) name = ""; if (hashed_sock_name != NULL) { ret = snprintf(path, len, "lxc/%s/command", hashed_sock_name); if (ret < 0 || ret >= len) { ERROR("Error writing to command sock path"); return -1; } return 0; } if (!lxcpath) { lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) { ERROR("Out of memory getting lxcpath"); return -1; } } ret = snprintf(path, len, "%s/%s/command", lxcpath, name); if (ret < 0) { ERROR("Error writing to command sock path"); 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 || ret >= tmplen) { ERROR("memory error"); return -1; } hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT); ret = snprintf(path, len, "lxc/%016" PRIx64 "/command", hash); if (ret < 0 || ret >= len) { ERROR("Command socket name too long"); return -1; } return 0; } static const char *lxc_cmd_str(lxc_cmd_t cmd) { static const char * const cmdname[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = "console", [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", }; if (cmd >= LXC_CMD_MAX) return "Unknown cmd"; 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_fd(sock, &rspfd, rsp, sizeof(*rsp)); if (ret < 0) { WARN("Command %s failed to receive response: %s.", lxc_cmd_str(cmd->req.cmd), strerror(errno)); return -1; } 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) { ERROR("Command %s couldn't allocate response buffer.", 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("command %s response data length is 0", lxc_cmd_str(cmd->req.cmd)); return ret; } if (rsp->datalen > LXC_CMD_DATA_MAX) { ERROR("Command %s response data %d too long.", lxc_cmd_str(cmd->req.cmd), rsp->datalen); errno = EFBIG; return -1; } rsp->data = malloc(rsp->datalen); if (!rsp->data) { ERROR("Command %s was unable to allocate response buffer.", lxc_cmd_str(cmd->req.cmd)); return -1; } ret = recv(sock, rsp->data, rsp->datalen, 0); if (ret != rsp->datalen) { ERROR("Command %s failed to receive response data: %s.", lxc_cmd_str(cmd->req.cmd), strerror(errno)); if (ret >= 0) ret = -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) { int ret; ret = send(fd, rsp, sizeof(*rsp), 0); if (ret != sizeof(*rsp)) { ERROR("Failed to send command response %d: %s.", ret, strerror(errno)); return -1; } if (rsp->datalen > 0) { ret = send(fd, rsp->data, rsp->datalen, 0); if (ret != rsp->datalen) { WARN("Failed to send command response data %d: %s.", ret, strerror(errno)); return -1; } } return 0; } /* * 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 reqest 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 sock, ret = -1; char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; char *offset = &path[1]; size_t len; int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE; *stopped = 0; /* -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 = sizeof(path)-2; if (fill_sock_name(offset, len, name, lxcpath, hashed_sock_name)) return -1; sock = lxc_abstract_unix_connect(path); if (sock < 0) { if (errno == ECONNREFUSED) *stopped = 1; else SYSERROR("Command %s failed to connect to \"@%s\".", lxc_cmd_str(cmd->req.cmd), offset); return -1; } ret = lxc_abstract_unix_send_credential(sock, &cmd->req, sizeof(cmd->req)); if (ret != sizeof(cmd->req)) { if (errno == EPIPE) goto epipe; SYSERROR("Command %s failed to send req to \"@%s\" %d.", lxc_cmd_str(cmd->req.cmd), offset, ret); if (ret >=0) ret = -1; goto out; } if (cmd->req.datalen > 0) { ret = send(sock, cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL); if (ret != cmd->req.datalen) { if (errno == EPIPE) goto epipe; SYSERROR("Command %s failed to send request data to \"@%s\" %d.", lxc_cmd_str(cmd->req.cmd), offset, ret); if (ret >=0) ret = -1; goto out; } } ret = lxc_cmd_rsp_recv(sock, cmd); out: if (!stay_connected || ret <= 0) close(sock); if (stay_connected && ret > 0) cmd->rsp.ret = sock; return ret; epipe: close(sock); *stopped = 1; return 0; } 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; } /* Implentations 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; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_INIT_PID }, }; 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_init_pid_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->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, .datalen = strlen(subsystem)+1, .data = subsystem, }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return NULL; if (!ret) { WARN("Container \"%s\" has stopped before sending its state.", name); return NULL; } if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) { ERROR("Command %s failed for container \"%s\": %s.", lxc_cmd_str(cmd.req.cmd), name, strerror(-cmd.rsp.ret)); return NULL; } return cmd.rsp.data; } static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; const char *path; if (req->datalen < 1) return -1; path = cgroup_get_cgroup(handler, req->data); if (!path) return -1; rsp.datalen = strlen(path) + 1, rsp.data = (char *)path; rsp.ret = 0; 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; struct lxc_cmd_rsp rsp; char *cidata; memset(&rsp, 0, sizeof(rsp)); cilen = lxc_get_config_item(handler->conf, req->data, NULL, 0); if (cilen <= 0) goto err1; cidata = alloca(cilen + 1); if (lxc_get_config_item(handler->conf, req->data, cidata, cilen + 1) != 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 */ lxc_state_t 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) { ERROR("Failed to stop container \"%s\": %s.", name, strerror(-cmd.rsp.ret)); 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) { ERROR("Console access denied: %s.", strerror(-cmd.rsp.ret)); ret = -1; goto out; } if (ret == 0) { ERROR("Console %d invalid, busy or all consoles 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; /* sock fd */ *fd = rspdata->masterfd; *ttynum = rspdata->ttynum; INFO("tty %d allocated fd %d sock %d.", rspdata->ttynum, *fd, 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 ttynum = PTR_TO_INT(req->data); int masterfd; struct lxc_cmd_rsp rsp; 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); if (lxc_abstract_unix_send_fd(fd, masterfd, &rsp, sizeof(rsp)) < 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 = 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.data = (char *)handler->lxcpath; rsp.datalen = strlen(handler->lxcpath) + 1; rsp.ret = 0; return lxc_cmd_rsp_send(fd, &rsp); } 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, }; if (req->cmd >= LXC_CMD_MAX) { ERROR("Undefined command id %d received.", 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) { lxc_console_free(handler->conf, fd); lxc_mainloop_del_handler(descr, fd); close(fd); } 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 == -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) { SYSERROR("Failed to receive data on command socket."); goto out_close; } if (!ret) { DEBUG("Peer has disconnected."); goto out_close; } if (ret != sizeof(req)) { WARN("Failed to receive full command request. Ignoring request."); ret = -1; goto out_close; } if (req.datalen > LXC_CMD_DATA_MAX) { ERROR("Received command data length %d is too large.", req.datalen); ret = -1; 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."); 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); goto out; } static int lxc_cmd_accept(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int opt = 1, ret = -1, connection; connection = accept(fd, NULL, 0); if (connection < 0) { SYSERROR("Failed to accept connection to run command."); return -1; } if (fcntl(connection, F_SETFD, FD_CLOEXEC)) { SYSERROR("Failed to set close-on-exec on incoming command connection."); goto out_close; } if (setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt))) { 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, struct lxc_handler *handler, const char *lxcpath) { int fd; char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; char *offset = &path[1]; int len; /* -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 = sizeof(path) - 2; if (fill_sock_name(offset, len, name, lxcpath, NULL)) return -1; fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); if (fd < 0) { ERROR("Failed to create the command service point %s: %s.", offset, strerror(errno)); if (errno == EADDRINUSE) ERROR("Container \"%s\" appears to be already running!", name); return -1; } if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { SYSERROR("Failed to set FD_CLOEXEC on signal file descriptor."); close(fd); return -1; } handler->conf->maincmd_fd = fd; return 0; } int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler) { int ret, fd = handler->conf->maincmd_fd; ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); if (ret) { ERROR("Failed to add handler for command socket."); close(fd); } return ret; } lxc-1.0.10/src/lxc/lxc_unfreeze.c0000644061062106075000000000436013105114536013524 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" lxc_log_define(lxc_unfreeze_ui, lxc); 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", .options = my_longopts, .parser = NULL, .checker = NULL, }; int main(int argc, char *argv[]) { struct lxc_container *c; if (lxc_arguments_parse(&my_args, argc, argv)) exit(1); if (!my_args.log_file) my_args.log_file = "none"; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) exit(1); lxc_log_options_no_override(); c = lxc_container_new(my_args.name, my_args.lxcpath[0]); if (!c) { ERROR("No such container: %s:%s", my_args.lxcpath[0], my_args.name); exit(1); } if (!c->may_control(c)) { ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(1); } if (!c->unfreeze(c)) { ERROR("Failed to unfreeze %s:%s", my_args.lxcpath[0], my_args.name); lxc_container_put(c); exit(1); } lxc_container_put(c); exit(0); } lxc-1.0.10/src/lxc/parse.c0000644061062106075000000000444113105114536012145 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) { int 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; } 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-1.0.10/src/lxc/lxc_autostart.c0000644061062106075000000003045013105114536013726 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" lxc_log_define(lxc_autostart_ui, lxc); 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 'g': cmd_groups_list = accumulate_list( arg, ",", cmd_groups_list); break; case 't': args->timeout = atoi(arg); 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'}, {"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\ -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; } ret = atoi(value); 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) * -1; } 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; char *const default_start_args[] = { "/sbin/init", NULL, }; if (lxc_arguments_parse(&my_args, argc, argv)) return 1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, my_args.progname, my_args.quiet, my_args.lxcpath[0])) return 1; lxc_log_options_no_override(); count = list_defined_containers(my_args.lxcpath[0], NULL, &containers); if (count < 0) return 1; 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 (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, default_start_args)) { 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); return 0; } lxc-1.0.10/src/lxc/initutils.h0000644061062106075000000000301213105114536013055 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" extern void lxc_setup_fs(void); extern const char *lxc_global_config_value(const char *option_name); /* open a file with O_CLOEXEC */ extern void remove_trailing_slashes(char *p); FILE *fopen_cloexec(const char *path, const char *mode); #endif /* __LXC_INITUTILS_H */ lxc-1.0.10/src/lxc/lxcutmp.h0000644061062106075000000000211213105114536012525 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-1.0.10/src/lxc/bdev.c0000644061062106075000000022710513105114536011757 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 */ /* * this is all just a first shot for experiment. If we go this route, much * should change. bdev should be a directory with per-bdev file. Things which * I'm doing by calling out to userspace should sometimes be done through * libraries like liblvm2 */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lxc.h" #include "config.h" #include "conf.h" #include "bdev.h" #include "log.h" #include "error.h" #include "utils.h" #include "namespace.h" #include "parse.h" #include "lxclock.h" #include "lxc-btrfs.h" /* makedev() */ #ifdef MAJOR_IN_MKDEV # include #endif #ifdef MAJOR_IN_SYSMACROS # include #endif #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif #ifndef LO_FLAGS_AUTOCLEAR #define LO_FLAGS_AUTOCLEAR 4 #endif #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif #define DEFAULT_FS_SIZE 1073741824 #define DEFAULT_FSTYPE "ext3" lxc_log_define(bdev, lxc); struct ovl_rsync_data { struct bdev *orig; struct bdev *new; }; struct rsync_data_char { char *src; char *dest; }; static 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); } /* * return block size of dev->src in units of bytes */ static int blk_getsize(struct bdev *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; } /* * 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 */ static 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; } static int mount_unknown_fs(const char *rootfs, const char *target, const char *options) { int i; 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++) { int ret; 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; } static int do_mkfs(const char *path, const char *fstype) { pid_t pid; if ((pid = fork()) < 0) { ERROR("error forking"); return -1; } if (pid > 0) return wait_for_pid(pid); // If the file is not a block device, we don't want mkfs to ask // us about whether to proceed. if (null_stdfds() < 0) exit(1); execlp("mkfs", "mkfs", "-t", fstype, path, NULL); exit(1); } static 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; } /* * Given a bdev (presumably blockdev-based), detect the fstype * by trying mounting (in a private mntns) it. * @bdev: 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 */ static int detect_fs(struct bdev *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); } struct bdev_type { const char *name; const struct bdev_ops *ops; }; static 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; } static 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; } // // XXXXXXX plain directory bind mount ops // static int dir_mount(struct bdev *bdev) { unsigned long mntflags; char *mntdata; int ret; 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); free(mntdata); return ret; } static int dir_umount(struct bdev *bdev) { if (strcmp(bdev->type, "dir")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } /* the bulk of this needs to become a common helper */ static 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) { strncpy(p, src, p2-src); // copy text up to oldname p += p2-src; // move target pointer (p) p += sprintf(p, "%s", name); // print new name in place of oldname src = p2 + l2; // move src to end of oldname } sprintf(p, "%s", src); // copy the rest of src return ret; } /* * for a simple directory bind mount, we substitute the old container * name and paths for the new */ static int dir_clonepaths(struct bdev *orig, struct bdev *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; } static int dir_destroy(struct bdev *orig) { if (lxc_rmdir_onedev(orig->src) < 0) return -1; return 0; } static int dir_create(struct bdev *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; } static const struct bdev_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, }; // // XXXXXXX 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. // static 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; } static int zfs_detect(const char *path) { char *output = malloc(LXC_LOG_BUFFER_SIZE); int found; if (!output) { ERROR("out of memory"); return 0; } found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); free(output); return found; } static int zfs_mount(struct bdev *bdev) { unsigned long mntflags; char *mntdata; int ret; if (strcmp(bdev->type, "zfs")) 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; } static int zfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "zfs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } static 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], *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(1); execlp("zfs", "zfs", "create", option, dev, NULL); exit(1); } 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, NULL); exit(1); } // 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, NULL); exit(1); } 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, NULL); exit(1); } return wait_for_pid(pid); } } static int zfs_clonepaths(struct bdev *orig, struct bdev *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. */ static int zfs_destroy(struct bdev *orig) { pid_t pid; char output[MAXPATHLEN], *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, NULL); exit(1); } static int zfs_create(struct bdev *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(1); execlp("zfs", "zfs", "create", option, dev, NULL); exit(1); } static const struct bdev_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, }; // // LVM ops // /* * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM * prefix "LVM-", then this is an lvm2 LV */ static 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; } static int lvm_mount(struct bdev *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); } static int lvm_umount(struct bdev *bdev) { if (strcmp(bdev->type, "lvm")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } static 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; } static int lvm_is_thin_volume(const char *path) { return lvm_compare_lv_attr(path, 6, 't'); } static int lvm_is_thin_pool(const char *path) { return lvm_compare_lv_attr(path, 0, 't'); } /* * 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(1); pathdup = strdup(path); if (!pathdup) exit(1); lv = strrchr(pathdup, '/'); if (!lv) exit(1); *lv = '\0'; lv++; vg = strrchr(pathdup, '/'); if (!vg) exit(1); 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(1); ret = lvm_is_thin_pool(tp); INFO("got %d for thin pool at path: %s", ret, tp); if (ret < 0) exit(1); if (!ret) tp = NULL; } 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(1); } static 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(1); pathdup = strdup(path); if (!pathdup) exit(1); lv = strrchr(pathdup, '/'); if (!lv) { free(pathdup); exit(1); } *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; } 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(1); } // this will return 1 for physical disks, qemu-nbd, loop, etc // right now only lvm is a block device static int is_blktype(struct bdev *b) { if (strcmp(b->type, "lvm") == 0) return 1; return 0; } static int lvm_clonepaths(struct bdev *orig, struct bdev *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; 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; } if (do_mkfs(new->src, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, new->src); return -1; } } return 0; } static int lvm_destroy(struct bdev *orig) { pid_t pid; if ((pid = fork()) < 0) return -1; if (!pid) { execlp("lvremove", "lvremove", "-f", orig->src, NULL); exit(1); } return wait_for_pid(pid); } static int lvm_create(struct bdev *bdev, const char *dest, const char *n, struct bdev_specs *specs) { const char *vg, *thinpool, *fstype, *lv = n; uint64_t sz; int ret, len; 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; if (do_mkfs(bdev->src, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, bdev->src); 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; } static const struct bdev_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, }; /* * 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, int name_len) { struct btrfs_ioctl_ino_lookup_args args; int ret, e; size_t len; char *retpath; memset(&args, 0, sizeof(args)); args.treeid = dir_id; args.objectid = objid; ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); e = errno; if (ret) { ERROR("%s: ERROR: Failed to lookup path for %llu %llu %s - %s\n", __func__, (unsigned long long) dir_id, (unsigned long long) objid, name, strerror(e)); return NULL; } else INFO("%s: got path for %llu %llu - %s\n", __func__, (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; strcpy(retpath, args.name); strcat(retpath, "/"); strncat(retpath, name, name_len); } else { /* we're at the root of ref_tree */ len = name_len + 1; retpath = malloc(len); if (!retpath) return NULL; *retpath = '\0'; strncat(retpath, name, name_len); } 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; } static 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; } static int btrfs_mount(struct bdev *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; } static int btrfs_umount(struct bdev *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, fd = -1; 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) { ERROR("Error opening %s", newfull); free(newfull); return -1; } memset(&args, 0, sizeof(args)); strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; ret = ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args); INFO("btrfs: snapshot create ioctl returned %d", ret); free(newfull); close(fd); return ret; } static 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; } static int btrfs_snapshot(const char *orig, const char *new) { int fd = -1, fddst = -1, ret = -1; struct btrfs_ioctl_vol_args_v2 args; char *newdir, *newname, *newfull = NULL; newfull = strdup(new); if (!newfull) { ERROR("Error: out of memory"); goto out; } // make sure the directory doesn't already exist if (rmdir(newfull) < 0 && errno != ENOENT) { SYSERROR("Error removing empty new rootfs"); goto out; } newname = basename(newfull); newdir = dirname(newfull); fd = open(orig, O_RDONLY); if (fd < 0) { SYSERROR("Error opening original rootfs %s", orig); goto out; } fddst = open(newdir, O_RDONLY); if (fddst < 0) { SYSERROR("Error opening new container dir %s", newdir); goto out; } memset(&args, 0, sizeof(args)); args.fd = fd; strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; ret = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); INFO("btrfs: snapshot create ioctl returned %d", ret); out: if (fddst != -1) close(fddst); if (fd != -1) close(fd); free(newfull); return ret; } static 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); } static int btrfs_clonepaths(struct bdev *orig, struct bdev *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_unpriv()) return btrfs_snapshot(orig->dest, new->dest); sdata.dest = new->dest; sdata.src = orig->dest; return userns_exec_1(conf, btrfs_snapshot_wrapper, &sdata); } 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; 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)); strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; 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; } struct mytree_node { u64 objid; u64 parentid; char *name; char *dirname; }; struct my_btrfs_tree { struct mytree_node *nodes; int num; }; 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; strncpy(n->name, name, name_len); n->name[name_len] = '\0'; } if (dirname) { n->dirname = malloc(strlen(dirname) + 1); if (!n->dirname) { free(n->name); return false; } strcpy(n->dirname, dirname); } 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; 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; } snprintf(newpath, len, "%s/%s", path, tree->nodes[i].dirname); 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; 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++) { sh = (struct btrfs_ioctl_search_header *)(args.buf + off); 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 = ref->name_len; name = (char *)(ref + 1); tmppath = get_btrfs_subvol_path(fd, sh->offset, ref->dirid, 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; } static int btrfs_destroy(struct bdev *orig) { return btrfs_recursive_destroy(orig->src); } static int btrfs_create(struct bdev *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); } static const struct bdev_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, }; // // loopback dev ops // static int loop_detect(const char *path) { if (strncmp(path, "loop:", 5) == 0) return 1; return 0; } static int find_free_loopdev_no_control(int *retfd, char *namep) { struct dirent *direntp; struct loop_info64 lo; DIR *dir; int fd = -1; dir = opendir("/dev"); if (!dir) { SYSERROR("Error opening /dev"); return -1; } while ((direntp = readdir(dir))) { if (!direntp) break; if (strncmp(direntp->d_name, "loop", 4) != 0) continue; fd = openat(dirfd(dir), direntp->d_name, O_RDWR); if (fd < 0) continue; if (ioctl(fd, LOOP_GET_STATUS64, &lo) == 0 || errno != ENXIO) { close(fd); fd = -1; continue; } // We can use this fd snprintf(namep, 100, "/dev/%s", direntp->d_name); break; } closedir(dir); if (fd == -1) { ERROR("No loop device found"); return -1; } *retfd = fd; return 0; } static int find_free_loopdev(int *retfd, char *namep) { int rc, fd = -1; int ctl = open("/dev/loop-control", O_RDWR); if (ctl < 0) return find_free_loopdev_no_control(retfd, namep); rc = ioctl(ctl, LOOP_CTL_GET_FREE); if (rc >= 0) { snprintf(namep, 100, "/dev/loop%d", rc); fd = open(namep, O_RDWR); } close(ctl); if (fd == -1) { ERROR("No loop device found"); return -1; } *retfd = fd; return 0; } static int loop_mount(struct bdev *bdev) { int lfd, ffd = -1, ret = -1; struct loop_info64 lo; char loname[100]; if (strcmp(bdev->type, "loop")) return -22; if (!bdev->src || !bdev->dest) return -22; if (find_free_loopdev(&lfd, loname) < 0) return -22; ffd = open(bdev->src + 5, O_RDWR); if (ffd < 0) { SYSERROR("Error opening backing file %s", bdev->src); goto out; } if (ioctl(lfd, LOOP_SET_FD, ffd) < 0) { SYSERROR("Error attaching backing file to loop dev"); goto out; } memset(&lo, 0, sizeof(lo)); lo.lo_flags = LO_FLAGS_AUTOCLEAR; if (ioctl(lfd, LOOP_SET_STATUS64, &lo) < 0) { SYSERROR("Error setting autoclear on loop dev"); goto out; } ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); if (ret < 0) ERROR("Error mounting %s", bdev->src); else bdev->lofd = lfd; out: if (ffd > -1) close(ffd); if (ret < 0) { close(lfd); bdev->lofd = -1; } return ret; } static int loop_umount(struct bdev *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; // 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 if (do_mkfs(path, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, path); return -1; } return 0; } /* * No idea what the original blockdev will be called, but the copy will be * called $lxcpath/$lxcname/rootdev */ static int loop_clonepaths(struct bdev *orig, struct bdev *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); } static int loop_create(struct bdev *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); } static int loop_destroy(struct bdev *orig) { return unlink(orig->src + 5); } static const struct bdev_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, }; // // overlayfs ops // static int overlayfs_detect(const char *path) { if (strncmp(path, "overlayfs:", 10) == 0) return 1; // take their word for it return 0; } static char *overlayfs_name; static char *detect_overlayfs_name(void) { char *v = "overlayfs"; 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\toverlay\n") == 0) { v = "overlay"; break; } } fclose(f); free(line); return v; } // // XXXXXXX plain directory bind mount ops // static int overlayfs_mount(struct bdev *bdev) { char *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 (!overlayfs_name) overlayfs_name = detect_overlayfs_name(); // separately mount it first // mount -t overlayfs -oupperdir=${upper},lowerdir=${lower} lower dest dup = alloca(strlen(bdev->src)+1); strcpy(dup, bdev->src); if (!(lower = strchr(dup, ':'))) return -22; if (!(upper = strchr(++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, and if so // 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; } // mount without workdir option for overlayfs before v21 ret = mount(lower, bdev->dest, overlayfs_name, MS_MGC_VAL | mntflags, options); if (ret < 0) { INFO("overlayfs: error mounting %s onto %s options %s. retry with workdir", lower, bdev->dest, options); // retry with workdir option for overlayfs v22 and higher ret = mount(lower, bdev->dest, overlayfs_name, MS_MGC_VAL | mntflags, options_work); if (ret < 0) SYSERROR("overlayfs: error mounting %s onto %s options %s", lower, bdev->dest, options_work); else INFO("overlayfs: mounted %s onto %s options %s", lower, bdev->dest, options_work); } else INFO("overlayfs: mounted %s onto %s options %s", lower, bdev->dest, options); return ret; } static int overlayfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "overlayfs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } static 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; } static int rsync_delta_wrapper(void *data) { struct rsync_data_char *arg = data; return rsync_delta(arg); } static int ovl_rsync(struct ovl_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 (overlayfs_mount(data->orig) < 0) { ERROR("Failed mounting original container fs"); return -1; } if (overlayfs_mount(data->new) < 0) { ERROR("Failed mounting new container fs"); return -1; } ret = do_rsync(data->orig->dest, data->new->dest); overlayfs_umount(data->new); overlayfs_umount(data->orig); if (ret < 0) { ERROR("rsyncing %s to %s", data->orig->dest, data->new->dest); return -1; } return 0; } static int ovl_rsync_wrapper(void *data) { struct ovl_rsync_data *arg = data; return ovl_rsync(arg); } static int ovl_do_rsync(struct bdev *orig, struct bdev *new, struct lxc_conf *conf) { int ret = -1; struct ovl_rsync_data rdata; rdata.orig = orig; rdata.new = new; if (am_unpriv()) ret = userns_exec_1(conf, ovl_rsync_wrapper, &rdata); else ret = ovl_rsync(&rdata); if (ret) ERROR("copying overlayfs delta"); return ret; } static int overlayfs_clonepaths(struct bdev *orig, struct bdev *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_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_unpriv() && chown_mapped_root(delta, conf) < 0) WARN("Failed to update ownership of %s", delta); // make workdir for overlayfs.v22 or higher // workdir is /var/lib/lxc/c2/olwork // it is used to prepare files before atomically swithing with destination, // and needs to be on the same filesystem as 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_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_unpriv() && chown_mapped_root(ndelta, conf) < 0) WARN("Failed to update ownership of %s", ndelta); // make workdir for overlayfs.v22 or higher // for details, see above. lastslash = strrchr(ndelta, '/'); if (!lastslash) return -1; lastslash++; lastslashidx = lastslash - ndelta; work = malloc(lastslashidx + 7); if (!work) return -1; strncpy(work, ndelta, lastslashidx+1); strcpy(work+lastslashidx, "olwork"); if ((mkdir(work, 0755) < 0) && errno != EEXIST) { SYSERROR("error: mkdir %s", work); free(work); return -1; } if (am_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 overlayfs_mount supporting // mounting of the underlay. No big deal, just needs to be done. return -1; } return 0; } static int overlayfs_destroy(struct bdev *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); } /* * 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 */ static int overlayfs_create(struct bdev *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; } static const struct bdev_ops overlayfs_ops = { .detect = &overlayfs_detect, .mount = &overlayfs_mount, .umount = &overlayfs_umount, .clone_paths = &overlayfs_clonepaths, .destroy = &overlayfs_destroy, .create = &overlayfs_create, .can_snapshot = true, .can_backup = true, }; // // aufs ops // static int aufs_detect(const char *path) { if (strncmp(path, "aufs:", 5) == 0) return 1; // take their word for it return 0; } // // XXXXXXX plain directory bind mount ops // static int aufs_mount(struct bdev *bdev) { char *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); if (!(lower = strchr(dup, ':'))) return -22; if (!(upper = strchr(++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; } static int aufs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "aufs")) return -22; if (!bdev->src || !bdev->dest) return -22; return umount(bdev->dest); } static int aufs_clonepaths(struct bdev *orig, struct bdev *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_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_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_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_unpriv()) ret = userns_exec_1(conf, rsync_delta_wrapper, &rdata); 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; } static int aufs_destroy(struct bdev *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); } /* * 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 */ static int aufs_create(struct bdev *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; } static const struct bdev_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, }; static const struct bdev_type bdevs[] = { {.name = "zfs", .ops = &zfs_ops,}, {.name = "lvm", .ops = &lvm_ops,}, {.name = "btrfs", .ops = &btrfs_ops,}, {.name = "dir", .ops = &dir_ops,}, {.name = "aufs", .ops = &aufs_ops,}, {.name = "overlayfs", .ops = &overlayfs_ops,}, {.name = "loop", .ops = &loop_ops,}, }; static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type); void bdev_put(struct bdev *bdev) { free(bdev->mntopts); free(bdev->src); free(bdev->dest); free(bdev); } struct bdev *bdev_get(const char *type) { int i; struct bdev *bdev; for (i=0; iops = bdevs[i].ops; bdev->type = bdevs[i].name; return bdev; } static const struct bdev_type *bdev_query(const char *src) { int i; for (i=0; idetect(src); if (r) break; } if (i == numbdevs) return NULL; return &bdevs[i]; } struct bdev *bdev_init(const char *src, const char *dst, const char *mntopts) { struct bdev *bdev; const struct bdev_type *q; if (!src) return NULL; q = bdev_query(src); if (!q) return NULL; bdev = malloc(sizeof(struct bdev)); if (!bdev) return NULL; memset(bdev, 0, sizeof(struct bdev)); 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); return bdev; } struct rsync_data { struct bdev *orig; struct bdev *new; }; static int rsync_rootfs(struct rsync_data *data) { struct bdev *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; } static int rsync_rootfs_wrapper(void *data) { struct rsync_data *arg = data; return rsync_rootfs(arg); } bool bdev_is_dir(const char *path) { struct bdev *orig = bdev_init(path, NULL, NULL); bool ret = false; if (!orig) return ret; if (strcmp(orig->type, "dir") == 0) ret = true; bdev_put(orig); return ret; } bool bdev_can_backup(struct lxc_conf *conf) { struct bdev *bdev = bdev_init(conf->rootfs.path, NULL, NULL); bool ret; if (!bdev) return false; ret = bdev->ops->can_backup; bdev_put(bdev); return ret; } /* * is an unprivileged user allowed to make this kind of snapshot */ static bool unpriv_snap_allowed(struct bdev *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; } /* * If we're not snaphotting, then bdev_copy becomes a simple case of mount * the original, mount the new, and rsync the contents. */ struct bdev *bdev_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 bdev *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 (strstr(src, oldname) == NULL) { ERROR("original rootfs path %s doesn't include container name %s", src, oldname); return NULL; } orig = bdev_init(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("out of memory"); bdev_put(orig); return NULL; } ret = snprintf(orig->dest, len, "%s/%s/rootfs", oldpath, oldname); if (ret < 0 || ret >= len) { ERROR("rootfs path too long"); bdev_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") == 0) bdevtype = "overlayfs"; if (am_unpriv() && !unpriv_snap_allowed(orig, bdevtype, snap, maybe_snap)) { ERROR("Unsupported snapshot type for unprivileged users"); bdev_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; } new = bdev_get(bdevtype ? bdevtype : orig->type); if (!new) { ERROR("no such block device type: %s", bdevtype ? bdevtype : orig->type); bdev_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_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; } bdev_put(orig); return new; } pid = fork(); if (pid < 0) { SYSERROR("fork"); goto err; } if (pid > 0) { int ret = wait_for_pid(pid); bdev_put(orig); if (ret < 0) { bdev_put(new); return NULL; } return new; } data.orig = orig; data.new = new; if (am_unpriv()) ret = userns_exec_1(c0->lxc_conf, rsync_rootfs_wrapper, &data); else ret = rsync_rootfs(&data); exit(ret == 0 ? 0 : 1); err: bdev_put(orig); bdev_put(new); return NULL; } static struct bdev * do_bdev_create(const char *dest, const char *type, const char *cname, struct bdev_specs *specs) { struct bdev *bdev = bdev_get(type); if (!bdev) { return NULL; } if (bdev->ops->create(bdev, dest, cname, specs) < 0) { bdev_put(bdev); return NULL; } return bdev; } /* * bdev_create: * 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 bdev_put(). * @dest: the mountpoint (i.e. /var/lib/lxc/$name/rootfs) * @type: the bdevtype (dir, btrfs, zfs, etc) * @cname: the container name * @specs: details about the backing store to create, like fstype */ struct bdev *bdev_create(const char *dest, const char *type, const char *cname, struct bdev_specs *specs) { struct bdev *bdev; char *best_options[] = {"btrfs", "zfs", "lvm", "dir", NULL}; if (!type) return do_bdev_create(dest, "dir", cname, specs); if (strcmp(type, "best") == 0) { int i; // try for the best backing store type, according to our // opinionated preferences for (i=0; best_options[i]; i++) { if ((bdev = do_bdev_create(dest, best_options[i], cname, specs))) return bdev; } return NULL; // 'dir' should never fail, so this shouldn't happen } // -B lvm,dir if (strchr(type, ',') != NULL) { char *dup = alloca(strlen(type)+1), *saveptr = NULL, *token; strcpy(dup, type); for (token = strtok_r(dup, ",", &saveptr); token; token = strtok_r(NULL, ",", &saveptr)) { if ((bdev = do_bdev_create(dest, token, cname, specs))) return bdev; } } return do_bdev_create(dest, type, cname, specs); } char *overlay_getlower(char *p) { char *p1 = strchr(p, ':'); if (p1) *p1 = '\0'; return p; } bool rootfs_is_blockdev(struct lxc_conf *conf) { const struct bdev_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 = bdev_query(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-1.0.10/src/lxc/log.h0000644061062106075000000002126013105114536011617 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 #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 512 /* This attribute is required to silence clang warnings */ #if defined(__GNUC__) #define ATTR_UNUSED __attribute__ ((unused)) #else #define ATTR_UNUSED #endif /* predefined priorities. */ enum lxc_loglevel { LXC_LOG_PRIORITY_TRACE, LXC_LOG_PRIORITY_DEBUG, LXC_LOG_PRIORITY_INFO, LXC_LOG_PRIORITY_NOTICE, LXC_LOG_PRIORITY_WARN, LXC_LOG_PRIORITY_ERROR, LXC_LOG_PRIORITY_CRIT, LXC_LOG_PRIORITY_ALERT, LXC_LOG_PRIORITY_FATAL, LXC_LOG_PRIORITY_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 timeval 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; }; /* * 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_PRIORITY_NOTSET && category->parent) category = category->parent; return priority >= category->priority; } /* * converts a priority to a literal string */ static inline const char* lxc_log_priority_to_string(int priority) { switch (priority) { case LXC_LOG_PRIORITY_TRACE: return "TRACE"; case LXC_LOG_PRIORITY_DEBUG: return "DEBUG"; case LXC_LOG_PRIORITY_INFO: return "INFO"; case LXC_LOG_PRIORITY_NOTICE: return "NOTICE"; case LXC_LOG_PRIORITY_WARN: return "WARN"; case LXC_LOG_PRIORITY_ERROR: return "ERROR"; case LXC_LOG_PRIORITY_CRIT: return "CRIT"; case LXC_LOG_PRIORITY_ALERT: return "ALERT"; case LXC_LOG_PRIORITY_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_PRIORITY_TRACE; if (!strcasecmp("DEBUG", name)) return LXC_LOG_PRIORITY_DEBUG; if (!strcasecmp("INFO", name)) return LXC_LOG_PRIORITY_INFO; if (!strcasecmp("NOTICE", name)) return LXC_LOG_PRIORITY_NOTICE; if (!strcasecmp("WARN", name)) return LXC_LOG_PRIORITY_WARN; if (!strcasecmp("ERROR", name)) return LXC_LOG_PRIORITY_ERROR; if (!strcasecmp("CRIT", name)) return LXC_LOG_PRIORITY_CRIT; if (!strcasecmp("ALERT", name)) return LXC_LOG_PRIORITY_ALERT; if (!strcasecmp("FATAL", name)) return LXC_LOG_PRIORITY_FATAL; return LXC_LOG_PRIORITY_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, PRIORITY) \ \ ATTR_UNUSED static inline void LXC_##PRIORITY(struct lxc_log_locinfo *, \ const char *, ...) __attribute__ ((format (printf, 2, 3))); \ \ ATTR_UNUSED static inline void LXC_##PRIORITY(struct lxc_log_locinfo* locinfo, \ const char* format, ...) \ { \ if (lxc_log_priority_is_enabled(acategory, \ LXC_LOG_PRIORITY_##PRIORITY)) { \ struct lxc_log_event evt = { \ .category = (acategory)->name, \ .priority = LXC_LOG_PRIORITY_##PRIORITY, \ .fmt = format, \ .locinfo = locinfo \ }; \ va_list va_ref; \ \ gettimeofday(&evt.timestamp, NULL); \ \ 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_PRIORITY_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)) /* * 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 SYSERROR(format, ...) do { \ ERROR("%s - " format, strerror(errno), ##__VA_ARGS__); \ } while (0) #ifdef HAVE_TLS extern __thread int lxc_log_fd; #else extern int lxc_log_fd; #endif extern int lxc_log_init(const char *name, const char *file, const char *priority, const char *prefix, int quiet, const char *lxcpath); extern int lxc_log_set_file(const char *fname); extern int lxc_log_set_level(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-1.0.10/src/lxc/lxcseccomp.h0000644061062106075000000000262713105114536013204 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-1.0.10/src/lxc/lxccontainer.c0000644061062106075000000024715113105114536013533 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 "config.h" #include "lxc.h" #include "state.h" #include "conf.h" #include "confile.h" #include "console.h" #include "cgroup.h" #include "commands.h" #include "log.h" #include "bdev.h" #include "utils.h" #include "attach.h" #include "monitor.h" #include "namespace.h" #include "lxclock.h" /* major()/minor() */ #ifdef MAJOR_IN_MKDEV # include #endif #ifdef MAJOR_IN_SYSMACROS # include #endif #if HAVE_IFADDRS_H #include #else #include <../include/ifaddrs.h> #endif #define MAX_BUFFER 4096 #define NOT_SUPPORTED_ERROR "the requested function %s is not currently supported with unprivileged containers" /* 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 file_exists(const char *f) { struct stat statbuf; return stat(f, &statbuf) == 0; } static bool config_file_exists(const char *lxcpath, const char *cname) { /* $lxcpath + '/' + $cname + '/config' + \0 */ int ret, len = strlen(lxcpath) + strlen(cname) + 9; char *fname = alloca(len); ret = snprintf(fname, len, "%s/%s/config", lxcpath, cname); if (ret < 0 || 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 len = strlen(c->config_path) + strlen(c->name) + 10; char *path = alloca(len); int fd, ret; struct flock lk; ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || ret >= len) { ERROR("Error writing partial pathname"); return -1; } if (!file_exists(path)) return 0; fd = open(path, O_RDWR); if (fd < 0) { // give benefit of the doubt SYSERROR("Error opening partial file"); return 0; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; lk.l_pid = -1; if (fcntl(fd, F_GETLK, &lk) == 0 && lk.l_pid != -1) { // create is still ongoing close(fd); return 1; } // create completed but partial is still there. close(fd); return 2; } static int create_partial(struct lxc_container *c) { // $lxcpath + '/' + $name + '/partial' + \0 int len = strlen(c->config_path) + strlen(c->name) + 10; char *path = alloca(len); int fd, ret; struct flock lk; ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || ret >= len) { ERROR("Error writing partial pathname"); return -1; } if ((fd=open(path, O_RDWR | O_CREAT | O_EXCL, 0755)) < 0) { SYSERROR("Error creating partial file"); return -1; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; if (fcntl(fd, F_SETLKW, &lk) < 0) { SYSERROR("Error locking partial file %s", path); close(fd); return -1; } return fd; } static void remove_partial(struct lxc_container *c, int fd) { // $lxcpath + '/' + $name + '/partial' + \0 int len = strlen(c->config_path) + strlen(c->name) + 10; char *path = alloca(len); int ret; close(fd); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || ret >= len) { ERROR("Error writing partial pathname"); return; } if (unlink(path) < 0) SYSERROR("Error unlink 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; if (--c->numthreads < 1) { container_mem_unlock(c); lxc_container_free(c); return 1; } container_mem_unlock(c); return 0; } static bool lxcapi_is_defined(struct lxc_container *c) { struct stat statbuf; bool ret = false; int statret; if (!c) return false; if (container_mem_lock(c)) return false; if (!c->configfile) goto out; statret = stat(c->configfile, &statbuf); if (statret != 0) goto out; ret = true; out: container_mem_unlock(c); return ret; } static const char *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); } 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 lxcapi_is_running(struct lxc_container *c) { const char *s; if (!c) return false; s = lxcapi_state(c); if (!s || strcmp(s, "STOPPED") == 0) return false; return true; } static bool lxcapi_freeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_freeze(c->name, c->config_path); if (ret) return false; return true; } static bool lxcapi_unfreeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_unfreeze(c->name, c->config_path); if (ret) return false; return true; } static int lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { int ttyfd; if (!c) return -1; ttyfd = lxc_console_getfd(c, ttynum, masterfd); return ttyfd; } static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { return lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape); } static pid_t lxcapi_init_pid(struct lxc_container *c) { if (!c) return -1; return lxc_cmd_get_init_pid(c->name, c->config_path); } 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 && !lxc_config_read(fname, c->lxc_conf)) return true; return false; } static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) { bool ret = false, need_disklock = false; int lret; const char *fname; 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; } static bool lxcapi_want_daemonize(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) { ERROR("Error getting mem lock"); return false; } c->daemonize = state; /* daemonize implies close_all_fds so set it */ if (state == 1) c->lxc_conf->close_all_fds = 1; container_mem_unlock(c); return true; } static bool lxcapi_want_close_all_fds(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) { ERROR("Error getting mem lock"); return false; } c->lxc_conf->close_all_fds = state; container_mem_unlock(c); return true; } static bool 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; } static bool wait_on_daemonized_start(struct lxc_container *c, int pid) { /* we'll probably want to make this timeout configurable? */ int timeout = 5, ret, status; /* * our child is going to fork again, then exit. reap the * child */ ret = waitpid(pid, &status, 0); if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) DEBUG("failed waiting for first dual-fork child"); return lxcapi_wait(c, "RUNNING", timeout); } static bool am_single_threaded(void) { struct dirent *direntp; DIR *dir; int count=0; dir = opendir("/proc/self/task"); if (!dir) { INFO("failed to open /proc/self/task"); return false; } while ((direntp = readdir(dir))) { if (!direntp) break; if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; if (++count > 1) break; } closedir(dir); return count == 1; } /* * I can't decide if it'd be more convenient for callers if we accept '...', * or a null-terminated array (i.e. execl vs execv) */ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) { int ret; struct lxc_conf *conf; bool daemonize = false; FILE *pid_fp = NULL; char *default_args[] = { "/sbin/init", NULL, }; /* container exists */ if (!c) return false; /* If anything fails before we set error_num, we want an error in there */ c->error_num = 1; /* container has been setup */ if (!c->lxc_conf) return false; if ((ret = ongoing_create(c)) < 0) { ERROR("Error checking for incomplete creation"); return false; } if (ret == 2) { ERROR("Error: %s creation was not completed", c->name); c->destroy(c); return false; } else if (ret == 1) { ERROR("Error: creation of %s is ongoing", c->name); return false; } /* is this app meant to be run through lxcinit, as in lxc-execute? */ if (useinit && !argv) return false; if (container_mem_lock(c)) return false; conf = c->lxc_conf; daemonize = c->daemonize; container_mem_unlock(c); if (useinit) { ret = lxc_execute(c->name, argv, 1, conf, c->config_path); return ret == 0 ? true : false; } if (!argv) argv = default_args; /* * say, I'm not sure - what locks do 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 container is running... */ if (daemonize) { lxc_monitord_spawn(c->config_path); pid_t pid = fork(); if (pid < 0) return false; 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; return wait_on_daemonized_start(c, pid); } /* second fork to be reparented by init */ pid = fork(); if (pid < 0) { SYSERROR("Error doing dual-fork"); exit(1); } if (pid != 0) exit(0); /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ if (chdir("/")) { SYSERROR("Error chdir()ing to /."); exit(1); } lxc_check_inherited(conf, -1); if (null_stdfds() < 0) { ERROR("failed to close fds"); exit(1); } setsid(); } else { if (!am_single_threaded()) { ERROR("Cannot start non-daemonized container when threaded"); return false; } } /* We need to write PID file after daeminize, 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); if (daemonize) exit(1); return false; } if (fprintf(pid_fp, "%d\n", getpid()) < 0) { SYSERROR("Failed to write '%s'", c->pidfile); fclose(pid_fp); pid_fp = NULL; if (daemonize) exit(1); return false; } fclose(pid_fp); pid_fp = NULL; } conf->reboot = 0; reboot: ret = lxc_start(c->name, argv, conf, c->config_path); c->error_num = ret; if (conf->reboot == 1) { INFO("container requested reboot"); conf->reboot = 2; goto reboot; } if (c->pidfile) { unlink(c->pidfile); free(c->pidfile); c->pidfile = NULL; } if (daemonize) exit (ret == 0 ? true : false); else return (ret == 0 ? true : false); } /* * 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; va_start(ap, useinit); inargs = lxc_va_arg_list_to_argv(ap, 0, 1); va_end(ap); if (!inargs) { ERROR("Memory allocation error."); goto out; } /* pass NULL if no arguments were supplied */ bret = lxcapi_start(c, useinit, *inargs ? inargs : NULL); out: if (inargs) { char **arg; for (arg = inargs; *arg; arg++) free(*arg); free(inargs); } return bret; } static bool lxcapi_stop(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_cmd_stop(c->name, c->config_path); return ret == 0; } static int do_create_container_dir(const char *path, struct lxc_conf *conf) { int ret = -1, lasterr; char *p = alloca(strlen(path)+1); mode_t mask = umask(0002); ret = mkdir(path, 0770); lasterr = errno; umask(mask); errno = lasterr; if (ret) { if (errno == EEXIST) ret = 0; else { SYSERROR("failed to create container path %s", path); return -1; } } strcpy(p, path); if (!lxc_list_empty(&conf->id_map) && chown_mapped_root(p, conf) != 0) { ERROR("Failed to chown container dir"); ret = -1; } return ret; } /* * create the standard expected container dir */ static bool create_container_dir(struct lxc_container *c) { char *s; int len, ret; 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 || ret >= len) { free(s); return false; } ret = do_create_container_dir(s, c->lxc_conf); free(s); return ret == 0; } static const char *lxcapi_get_config_path(struct lxc_container *c); static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); /* * do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(), * it returns a mounted bdev on success, NULL on error. */ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type, struct bdev_specs *specs) { char *dest; size_t len; struct bdev *bdev; int ret; /* 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 = 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 || ret >= len) return NULL; bdev = bdev_create(dest, type, c->name, specs); if (!bdev) { ERROR("Failed to create backing store type %s", type); return NULL; } lxcapi_set_config_item(c, "lxc.rootfs", bdev->src); /* if we are not root, chown the rootfs dir to root in the * target uidmap */ if (geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) { if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) { ERROR("Error chowning %s to container root", bdev->dest); suggest_default_idmap(); bdev_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, *src; struct bdev *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); } src = c->lxc_conf->rootfs.path; /* * for an overlay create, what the user wants is the template to fill * in what will become the readonly lower layer. So don't mount for * the template */ if (strncmp(src, "overlayfs:", 10) == 0) src = overlay_getlower(src+10); if (strncmp(src, "aufs:", 5) == 0) src = overlay_getlower(src+5); bdev = bdev_init(src, 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_nsuid(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_nsuid(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; 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; if (fseek(f, 0, SEEK_END) < 0) goto out_error; if ((flen = ftell(f)) < 0) goto out_error; if (fseek(f, 0, SEEK_SET) < 0) goto out_error; if ((contents = malloc(flen + 1)) == NULL) goto out_error; if (fread(contents, 1, flen, f) != 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("bad template: %s", t); goto out_free_contents; } ret = sha1sum_file(tpath, md_value); if (ret < 0) { ERROR("Error getting 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) { lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } } static bool lxcapi_destroy(struct lxc_container *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 lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { bool ret = false; pid_t pid; char *tpath = NULL; int partial_fd; if (!c) return false; if (t) { tpath = get_template_path(t); if (!tpath) { ERROR("bad template: %s", t); goto out; } } /* * If a template is passed in, and the rootfs already is defined in * the container config and exists, then * caller is trying to create * an existing container. Return an error, but do NOT delete the * container. */ if (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:%s already exists", c->config_path, c->name); goto free_tpath; } if (!c->lxc_conf) { if (!c->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. * 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 starting configuration for %s\n", c->name); goto out; } ret = true; goto out; } if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) != 0) /* rootfs passed into configuration, but does not exist: error */ goto out; if (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 starting configuration for %s\n", c->name); ret = true; goto out; } /* Mark that this container is being created */ if ((partial_fd = create_partial(c)) < 0) goto out; /* no need to get disk lock bc we have the partial locked */ /* * Create the backing store * 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 bdev *bdev = NULL; if (!(bdev = do_bdev_create(c, bdevtype, specs))) { ERROR("Error creating backing store type %s for %s", bdevtype ? bdevtype : "(none)", c->name); exit(1); } /* save config file again to store the new rootfs location */ if (!c->save_config(c, NULL)) { ERROR("failed to save starting configuration for %s", c->name); // parent task won't see bdev in config so we delete it bdev->ops->umount(bdev); bdev->ops->destroy(bdev); exit(1); } exit(0); } 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 lxcapi_clear_config(c); if (t) { if (!prepend_lxc_header(c->configfile, tpath, argv)) { ERROR("Error prepending header to configuration file"); goto out_unlock; } } ret = load_config_locked(c, c->configfile); out_unlock: if (partial_fd >= 0) remove_partial(c, partial_fd); out: if (!ret) lxcapi_destroy(c); free_tpath: free(tpath); return ret; } static bool lxcapi_reboot(struct lxc_container *c) { pid_t pid; if (!c) return false; if (!c->is_running(c)) return false; pid = c->init_pid(c); if (pid <= 0) return false; if (kill(pid, SIGINT) < 0) return false; return true; } static bool lxcapi_shutdown(struct lxc_container *c, int timeout) { bool retv; pid_t pid; int haltsignal = SIGPWR; if (!c) return false; if (!c->is_running(c)) return true; pid = c->init_pid(c); if (pid <= 0) return true; if (c->lxc_conf && c->lxc_conf->haltsignal) haltsignal = c->lxc_conf->haltsignal; kill(pid, haltsignal); retv = c->wait(c, "STOPPED", timeout); return retv; } 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; /* * 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("Memory allocation error."); goto out; } bret = c->create(c, t, bdevtype, specs, flags, args); out: free(args); return bret; } static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) { int ret; if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; ret = lxc_clear_config_item(c->lxc_conf, key); container_mem_unlock(c); return ret == 0; } static inline bool enter_net_ns(struct lxc_container *c) { int netns, userns, ret = 0, init_pid = 0;; char new_netns_path[MAXPATHLEN]; char new_userns_path[MAXPATHLEN]; if (!c->is_running(c)) goto out; init_pid = c->init_pid(c); /* Switch to new userns */ if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) { ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid); if (ret < 0 || ret >= MAXPATHLEN) goto out; userns = open(new_userns_path, O_RDONLY); if (userns < 0) { SYSERROR("failed to open %s", new_userns_path); goto out; } if (setns(userns, CLONE_NEWUSER)) { SYSERROR("failed to setns for CLONE_NEWUSER"); close(userns); goto out; } close(userns); } /* Switch to new netns */ ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", init_pid); if (ret < 0 || ret >= MAXPATHLEN) goto out; netns = open(new_netns_path, O_RDONLY); if (netns < 0) { SYSERROR("failed to open %s", new_netns_path); goto out; } if (setns(netns, CLONE_NEWNET)) { SYSERROR("failed to setns for CLONE_NEWNET"); close(netns); goto out; } close(netns); return true; out: return false; } // 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** lxcapi_get_interfaces(struct lxc_container *c) { pid_t pid; int i, count = 0, pipefd[2]; char **interfaces = NULL; char interface[IFNAMSIZ]; if(pipe(pipefd) < 0) { SYSERROR("pipe failed"); 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 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 = write(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ); if (nbytes < 0) { ERROR("write failed"); 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 (read(pipefd[0], &interface, IFNAMSIZ) == IFNAMSIZ) { if (array_contains(&interfaces, interface, count)) continue; if(!add_to_array(&interfaces, interface, count)) ERROR("PARENT: add_to_array failed"); count++; } if (wait_for_pid(pid) != 0) { for(i=0;iifa_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 = write(pipefd[1], address, INET6_ADDRSTRLEN); if (nbytes < 0) { ERROR("write failed"); 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 (read(pipefd[0], &address, INET6_ADDRSTRLEN) == INET6_ADDRSTRLEN) { if(!add_to_array(&addresses, address, count)) ERROR("PARENT: add_to_array failed"); count++; } if (wait_for_pid(pid) != 0) { for(i=0;ilxc_conf) return -1; if (container_mem_lock(c)) return -1; ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); container_mem_unlock(c); return ret; } static char* 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, c->get_config_path(c)); container_mem_unlock(c); return ret; } static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen) { if (!key) return lxc_listconfigs(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; } static bool 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 (!c->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; } static bool mod_rdep(struct lxc_container *c, bool inc) { char path[MAXPATHLEN]; int ret, v = 0; FILE *f; bool bret = false; if (container_disk_lock(c)) return false; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret > MAXPATHLEN) goto out; f = fopen(path, "r"); if (f) { ret = fscanf(f, "%d", &v); fclose(f); if (ret != 1) { ERROR("Corrupted file %s", path); goto out; } } v += inc ? 1 : -1; f = fopen(path, "w"); if (!f) goto out; if (fprintf(f, "%d\n", v) < 0) { ERROR("Error writing new snapshots value"); fclose(f); goto out; } ret = fclose(f); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } bret = true; out: container_disk_unlock(c); 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'; } static 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, inc)) ERROR("Failed to increase numsnapshots for %s:%s", lxcpath, lxcname); lxc_container_put(p); } out: free(lxcpath); free(lxcname); fclose(f); } static bool has_snapshots(struct lxc_container *c) { char path[MAXPATHLEN]; int ret, v; FILE *f; bool bret = false; ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret > MAXPATHLEN) goto out; f = fopen(path, "r"); if (!f) goto out; ret = fscanf(f, "%d", &v); fclose(f); if (ret != 1) goto out; bret = v != 0; out: return bret; } static int lxc_rmdir_onedev_wrapper(void *data) { char *arg = (char *) data; return lxc_rmdir_onedev(arg); } static int do_bdev_destroy(struct lxc_conf *conf) { struct bdev *r; int ret = 0; r = bdev_init(conf->rootfs.path, conf->rootfs.mount, NULL); if (!r) return -1; if (r->ops->destroy(r) < 0) ret = -1; bdev_put(r); return ret; } static int bdev_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; } return do_bdev_destroy(conf); } // do we want the api to support --force, or leave that to the caller? static bool lxcapi_destroy(struct lxc_container *c) { bool bret = false; int ret; if (!c || !lxcapi_is_defined(c)) return false; 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 (c->lxc_conf && has_snapshots(c)) { ERROR("container %s has dependent snapshots", c->name); goto out; } if (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) { if (am_unpriv()) ret = userns_exec_1(c->lxc_conf, bdev_destroy_wrapper, c->lxc_conf); else ret = do_bdev_destroy(c->lxc_conf); if (ret < 0) { ERROR("Error destroying rootfs for %s", c->name); goto out; } } mod_all_rdeps(c, false); const char *p1 = lxcapi_get_config_path(c); char *path = alloca(strlen(p1) + strlen(c->name) + 2); sprintf(path, "%s/%s", p1, c->name); if (am_unpriv()) ret = userns_exec_1(c->lxc_conf, lxc_rmdir_onedev_wrapper, path); else ret = lxc_rmdir_onedev(path); if (ret < 0) { ERROR("Error destroying container directory for %s", c->name); goto out; } bret = true; out: container_disk_unlock(c); return bret; } 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; return (0 == config->cb(key, v, c->lxc_conf)); } static bool 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; } 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 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; } static bool 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; } static int 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; } 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 = read(in, buf, 8096); if (len < 0) { SYSERROR("Error reading old file %s", old); goto err; } if (len == 0) break; ret = write(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; } } } c->save_config(c, NULL); return 0; } static void new_hwaddr(char *hwaddr) { FILE *f; f = fopen("/dev/urandom", "r"); if (f) { unsigned int seed; int ret = fread(&seed, sizeof(seed), 1, f); if (ret != 1) seed = time(NULL); fclose(f); srand(seed); } else srand(time(NULL)); snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255, rand() % 255, rand() % 255); } static void network_new_hwaddrs(struct lxc_container *c) { struct lxc_list *it; lxc_list_for_each(it, &c->lxc_conf->network) { struct lxc_netdev *n = it->elem; if (n->hwaddr) new_hwaddr(n->hwaddr); } } 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; 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; } 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; } static int copy_storage(struct lxc_container *c0, struct lxc_container *c, const char *newtype, int flags, const char *bdevdata, uint64_t newsize) { struct bdev *bdev; int need_rdep; bdev = bdev_copy(c0, c->name, c->config_path, newtype, flags, bdevdata, newsize, &need_rdep); if (!bdev) { ERROR("Error copying storage"); return -1; } free(c->lxc_conf->rootfs.path); c->lxc_conf->rootfs.path = strdup(bdev->src); bdev_put(bdev); if (!c->lxc_conf->rootfs.path) { ERROR("Out of memory while setting storage path"); 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 bdev *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 = bdev_init(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"); bdev_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) { bdev_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); bdev_put(bdev); return -1; } } if (!(flags & LXC_CLONE_KEEPNAME)) { ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); bdev_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 bdev_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 *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; struct clone_update_data data; FILE *fout; pid_t pid; if (!c || !c->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 = c->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; } write_config(fout, c->lxc_conf); fclose(fout); c->lxc_conf->rootfs.path = origroot; sprintf(newpath, "%s/%s/rootfs", lxcpath, newname); if (mkdir(newpath, 0755) < 0) { SYSERROR("error creating %s", newpath); goto out; } if (am_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)) { 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)) network_new_hwaddrs(c2); // 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_unpriv()) ret = userns_exec_1(c->lxc_conf, clone_update_rootfs_wrapper, &data); else ret = clone_update_rootfs(&data); if (ret < 0) exit(1); container_mem_unlock(c); exit(0); 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 bool lxcapi_rename(struct lxc_container *c, const char *newname) { struct bdev *bdev; struct lxc_container *newc; if (!c || !c->name || !c->config_path || !c->lxc_conf) return false; bdev = bdev_init(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); bdev_put(bdev); if (!newc) { lxc_container_put(newc); return false; } if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); if (!lxcapi_destroy(c)) { ERROR("Could not destroy existing container %s", c->name); return false; } return true; } 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) { if (!c) return -1; return lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process); } static int 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 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 int 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 (!bdev_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; } // /var/lib/lxc -> /var/lib/lxcsnaps \0 ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) 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 (bdev_is_dir(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 = c->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; } 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 lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps) { char snappath[MAXPATHLEN], path2[MAXPATHLEN]; int dirlen, count = 0, ret; struct dirent *direntp; struct lxc_snapshot *snaps =NULL, *nsnaps; DIR *dir; if (!c || !lxcapi_is_defined(c)) return -1; // snappath is ${lxcpath}snaps/${lxcname}/ dirlen = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name); if (dirlen < 0 || dirlen >= MAXPATHLEN) { 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; bdev = bdev_init(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; ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) { bdev_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); bdev_put(bdev); return false; } if (strcmp(c->name, newname) == 0) { if (!lxcapi_destroy(c)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); bdev_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); bdev_put(bdev); if (rest && lxcapi_is_defined(rest)) b = true; if (rest) lxc_container_put(rest); lxc_container_put(snap); return b; } static bool lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname) { int ret; char clonelxcpath[MAXPATHLEN]; struct lxc_container *snap = NULL; if (!c || !c->name || !c->config_path) return false; ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= MAXPATHLEN) goto err; snap = lxc_container_new(snapname, clonelxcpath); if (!snap || !lxcapi_is_defined(snap)) { ERROR("Could not find snapshot %s", snapname); goto err; } if (!lxcapi_destroy(snap)) { ERROR("Could not destroy snapshot %s", snapname); goto err; } lxc_container_put(snap); return true; err: if (snap) lxc_container_put(snap); return false; } static bool lxcapi_may_control(struct lxc_container *c) { return lxc_try_cmd(c->name, c->config_path) == 0; } static bool do_add_remove_node(pid_t init_pid, const char *path, bool add, struct stat *st) { char chrootpath[MAXPATHLEN]; char *directory_path = NULL; pid_t pid; int ret; if ((pid = fork()) < 0) { SYSERROR("failed to fork a child helper"); return false; } if (pid) { if (wait_for_pid(pid) != 0) { ERROR("Failed to create note in guest"); return false; } return true; } /* prepare the path */ ret = snprintf(chrootpath, MAXPATHLEN, "/proc/%d/root", init_pid); if (ret < 0 || ret >= MAXPATHLEN) return false; if (chroot(chrootpath) < 0) exit(1); if (chdir("/") < 0) exit(1); /* remove path if it exists */ if(faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW) == 0) { if (unlink(path) < 0) { ERROR("unlink failed"); exit(1); } } if (!add) exit(0); /* create any missing directories */ directory_path = dirname(strdup(path)); if (mkdir_p(directory_path, 0755) < 0 && errno != EEXIST) { ERROR("failed to create directory"); exit(1); } /* create the device node */ if (mknod(path, st->st_mode, st->st_rdev) < 0) { ERROR("mknod failed"); exit(1); } exit(0); } 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 (!c->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(c->init_pid(c), p, add, &st)) return false; /* add or remove device to/from cgroup access list */ if (add) { if (!c->set_cgroup_item(c, "devices.allow", value)) { ERROR("set_cgroup_item failed while adding the device node"); return false; } } else { if (!c->set_cgroup_item(c, "devices.deny", value)) { ERROR("set_cgroup_item failed while removing the device node"); return false; } } return true; } static bool lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, true); } static bool lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_unpriv()) { ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, false); } 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; va_start(ap, arg); argv = lxc_va_arg_list_to_argv_const(ap, 1); va_end(ap); if (!argv) { ERROR("Memory allocation error."); return -1; } argv[0] = arg; ret = lxcapi_attach_run_wait(c, options, program, (const char * const *)argv); free((void*)argv); return ret; } struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; c = malloc(sizeof(*c)); if (!c) { fprintf(stderr, "failed to malloc lxc_container\n"); 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, "Out of memory\n"); goto err; } remove_trailing_slashes(c->config_path); c->name = malloc(strlen(name)+1); if (!c->name) { fprintf(stderr, "Error allocating lxc_container name\n"); goto err; } strcpy(c->name, name); c->numthreads = 1; if (!(c->slock = lxc_newlock(c->config_path, name))) { fprintf(stderr, "failed to create lock\n"); goto err; } if (!(c->privlock = lxc_newlock(NULL, NULL))) { fprintf(stderr, "failed to alloc privlock\n"); goto err; } if (!set_config_filename(c)) { fprintf(stderr, "Error allocating config file pathname\n"); goto err; } if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) goto err; if (ongoing_create(c) == 2) { ERROR("Error: %s creation was not completed", c->name); lxcapi_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->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->may_control = lxcapi_may_control; c->add_device_node = lxcapi_add_device_node; c->remove_device_node = lxcapi_remove_device_node; /* we'll allow the caller to update these later */ if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) { fprintf(stderr, "failed to open log\n"); goto err; } 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