diff -Nru lxc-2.0.8/aclocal.m4 lxc-2.1.0/aclocal.m4 --- lxc-2.0.8/aclocal.m4 2017-05-11 17:23:07.000000000 +0000 +++ lxc-2.1.0/aclocal.m4 2017-09-06 02:32:40.000000000 +0000 @@ -1159,7 +1159,7 @@ dnl Find a Python interpreter. Python versions prior to 2.0 are not dnl supported. (2.0 was released on October 16, 2000). m4_define_default([_AM_PYTHON_INTERPRETER_LIST], -[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl +[python python2 python3 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 dnl python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) AC_ARG_VAR([PYTHON], [the Python interpreter]) diff -Nru lxc-2.0.8/config/apparmor/abstractions/start-container lxc-2.1.0/config/apparmor/abstractions/start-container --- lxc-2.0.8/config/apparmor/abstractions/start-container 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/apparmor/abstractions/start-container 2017-09-06 02:32:37.000000000 +0000 @@ -41,3 +41,4 @@ change_profile -> lxc-*, change_profile -> unconfined, + change_profile -> :lxc-*:unconfined, diff -Nru lxc-2.0.8/config/apparmor/Makefile.in lxc-2.1.0/config/apparmor/Makefile.in --- lxc-2.0.8/config/apparmor/Makefile.in 2017-05-11 17:23:09.000000000 +0000 +++ lxc-2.1.0/config/apparmor/Makefile.in 2017-09-06 02:32:41.000000000 +0000 @@ -440,8 +440,8 @@ maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -@ENABLE_APPARMOR_FALSE@uninstall-local: @ENABLE_APPARMOR_FALSE@install-data-local: +@ENABLE_APPARMOR_FALSE@uninstall-local: clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am diff -Nru lxc-2.0.8/config/etc/default.conf.libvirt lxc-2.1.0/config/etc/default.conf.libvirt --- lxc-2.0.8/config/etc/default.conf.libvirt 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/etc/default.conf.libvirt 2017-09-06 02:32:37.000000000 +0000 @@ -1,3 +1,3 @@ -lxc.network.type = veth -lxc.network.link = virbr0 -lxc.network.flags = up +lxc.net.0.type = veth +lxc.net.0.link = virbr0 +lxc.net.0.flags = up diff -Nru lxc-2.0.8/config/etc/default.conf.lxcbr lxc-2.1.0/config/etc/default.conf.lxcbr --- lxc-2.0.8/config/etc/default.conf.lxcbr 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/etc/default.conf.lxcbr 2017-09-06 02:32:37.000000000 +0000 @@ -1,4 +1,4 @@ -lxc.network.type = veth -lxc.network.link = lxcbr0 -lxc.network.flags = up -lxc.network.hwaddr = 00:16:3e:xx:xx:xx +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 +lxc.net.0.flags = up +lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx diff -Nru lxc-2.0.8/config/etc/default.conf.unknown lxc-2.1.0/config/etc/default.conf.unknown --- lxc-2.0.8/config/etc/default.conf.unknown 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/etc/default.conf.unknown 2017-09-06 02:32:37.000000000 +0000 @@ -1 +1 @@ -lxc.network.type = empty +lxc.net.0.type = empty diff -Nru lxc-2.0.8/config/init/common/lxc-containers.in lxc-2.1.0/config/init/common/lxc-containers.in --- lxc-2.0.8/config/init/common/lxc-containers.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/init/common/lxc-containers.in 2017-09-06 02:32:37.000000000 +0000 @@ -56,7 +56,7 @@ local BRNAME try flags br [ -f "$sysconfdir"/lxc/default.conf ] || { return 0; } - BRNAME=`grep '^[ ]*lxc.network.link' "$sysconfdir"/lxc/default.conf | sed 's/^.*=[ ]*//'` + BRNAME=`grep '^[ ]*lxc.net.0.link' "$sysconfdir"/lxc/default.conf | sed 's/^.*=[ ]*//'` if [ -z "$BRNAME" ]; then return 0 fi diff -Nru lxc-2.0.8/config/init/systemd/lxc@.service.in lxc-2.1.0/config/init/systemd/lxc@.service.in --- lxc-2.0.8/config/init/systemd/lxc@.service.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/init/systemd/lxc@.service.in 2017-09-06 02:32:37.000000000 +0000 @@ -8,9 +8,9 @@ [Service] Type=simple KillMode=mixed -KillSignal=SIGPWR TimeoutStopSec=120s ExecStart=@BINDIR@/lxc-start -F -n %i +ExecStop=@BINDIR@/lxc-stop -n %i # Environment=BOOTUP=serial # Environment=CONSOLETYPE=serial Delegate=yes diff -Nru lxc-2.0.8/config/init/sysvinit/Makefile.in lxc-2.1.0/config/init/sysvinit/Makefile.in --- lxc-2.0.8/config/init/sysvinit/Makefile.in 2017-05-11 17:23:09.000000000 +0000 +++ lxc-2.1.0/config/init/sysvinit/Makefile.in 2017-09-06 02:32:41.000000000 +0000 @@ -437,8 +437,8 @@ maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -@INIT_SCRIPT_SYSV_FALSE@uninstall-local: @INIT_SCRIPT_SYSV_FALSE@install-data-local: +@INIT_SCRIPT_SYSV_FALSE@uninstall-local: clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am diff -Nru lxc-2.0.8/config/selinux/lxc.te lxc-2.1.0/config/selinux/lxc.te --- lxc-2.0.8/config/selinux/lxc.te 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/selinux/lxc.te 2017-09-06 02:32:37.000000000 +0000 @@ -7,7 +7,7 @@ # semodule -i lxc.pp # # In your container's lxc config: -# lxc.se_context = system_u:system_r:lxc_t:s0:c62,c86,c150,c228 +# lxc.selinux.context = system_u:system_r:lxc_t:s0:c62,c86,c150,c228 # # Ensure your container's rootfs files are labeled: # chcon -R system_u:object_r:lxc_file_t:s0:c62,c86,c150,c228 /path/to/rootfs diff -Nru lxc-2.0.8/config/templates/alpine.common.conf.in lxc-2.1.0/config/templates/alpine.common.conf.in --- lxc-2.0.8/config/templates/alpine.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/alpine.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -2,7 +2,7 @@ lxc.include = @LXCTEMPLATECONFIG@/common.conf # Doesn't support consoles in /dev/lxc/. -lxc.devttydir = +lxc.tty.dir = # Drop another (potentially) harmful capabilities. lxc.cap.drop = audit_write diff -Nru lxc-2.0.8/config/templates/archlinux.common.conf.in lxc-2.1.0/config/templates/archlinux.common.conf.in --- lxc-2.0.8/config/templates/archlinux.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/archlinux.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -2,14 +2,14 @@ lxc.include = @LXCTEMPLATECONFIG@/common.conf # Allow for 6 tty devices by default -lxc.tty = 6 +lxc.tty.max = 6 # Set the halt/stop signals -lxc.haltsignal=SIGRTMIN+4 -lxc.stopsignal=SIGRTMIN+14 +lxc.signal.halt=SIGRTMIN+4 +lxc.signal.stop=SIGRTMIN+14 # Uncomment to disable creating tty devices subdirectory in /dev -# lxc.devttydir = +# lxc.tty.dir = # Capabilities # Uncomment these if you don't run anything that needs the capability, and diff -Nru lxc-2.0.8/config/templates/common.conf.in lxc-2.1.0/config/templates/common.conf.in --- lxc-2.0.8/config/templates/common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,20 +1,17 @@ # Default configuration shared by all containers # Setup the LXC devices in /dev/lxc/ -lxc.devttydir = lxc +lxc.tty.dir = lxc # Allow for 1024 pseudo terminals -lxc.pts = 1024 +lxc.pty.max = 1024 # Setup 4 tty devices -lxc.tty = 4 +lxc.tty.max = 4 # Drop some harmful capabilities lxc.cap.drop = mac_admin mac_override sys_time sys_module sys_rawio -# Set the pivot directory -lxc.pivotdir = lxc_putold - # Ensure hostname is changed on clone lxc.hook.clone = @LXCHOOKDIR@/clonehostname @@ -51,7 +48,7 @@ # Blacklist some syscalls which are not safe in privileged # containers -lxc.seccomp = @LXCTEMPLATECONFIG@/common.seccomp +lxc.seccomp.profile = @LXCTEMPLATECONFIG@/common.seccomp # Lastly, include all the configs from @LXCTEMPLATECONFIG@/common.conf.d/ lxc.include = @LXCTEMPLATECONFIG@/common.conf.d/ diff -Nru lxc-2.0.8/config/templates/debian.common.conf.in lxc-2.1.0/config/templates/debian.common.conf.in --- lxc-2.0.8/config/templates/debian.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/debian.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -2,17 +2,17 @@ lxc.include = @LXCTEMPLATECONFIG@/common.conf # Doesn't support consoles in /dev/lxc/ -lxc.devttydir = +lxc.tty.dir = # When using LXC with apparmor, the container will be confined by default. # If you wish for it to instead run unconfined, copy the following line # (uncommented) to the container's configuration file. -#lxc.aa_profile = unconfined +#lxc.apparmor.profile = unconfined # If you wish to allow mounting block filesystems, then use the following # line instead, and make sure to grant access to the block device and/or loop # devices below in lxc.cgroup.devices.allow. -#lxc.aa_profile = lxc-container-default-with-mounting +#lxc.apparmor.profile = lxc-container-default-with-mounting # Extra cgroup device access ## rtc diff -Nru lxc-2.0.8/config/templates/gentoo.common.conf.in lxc-2.1.0/config/templates/gentoo.common.conf.in --- lxc-2.0.8/config/templates/gentoo.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/gentoo.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -7,7 +7,7 @@ # Looking for more security, see gentoo.moresecure.conf # Doesn't support consoles in /dev/lxc/ -lxc.devttydir = +lxc.tty.dir = # Extra cgroup device access ## rtc diff -Nru lxc-2.0.8/config/templates/Makefile.am lxc-2.1.0/config/templates/Makefile.am --- lxc-2.0.8/config/templates/Makefile.am 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/Makefile.am 2017-09-06 02:32:37.000000000 +0000 @@ -38,4 +38,8 @@ openwrt.common.conf \ sparclinux.common.conf \ sparclinux.userns.conf \ + voidlinux.common.conf \ + voidlinux.userns.conf \ + sabayon.common.conf \ + sabayon.userns.conf \ userns.conf diff -Nru lxc-2.0.8/config/templates/Makefile.in lxc-2.1.0/config/templates/Makefile.in --- lxc-2.0.8/config/templates/Makefile.in 2017-05-11 17:23:09.000000000 +0000 +++ lxc-2.1.0/config/templates/Makefile.in 2017-09-06 02:32:42.000000000 +0000 @@ -113,7 +113,9 @@ ubuntu-cloud.common.conf ubuntu-cloud.lucid.conf \ ubuntu-cloud.userns.conf ubuntu.common.conf ubuntu.lucid.conf \ ubuntu.userns.conf openwrt.common.conf sparclinux.common.conf \ - sparclinux.userns.conf userns.conf + sparclinux.userns.conf voidlinux.common.conf \ + voidlinux.userns.conf sabayon.common.conf sabayon.userns.conf \ + userns.conf CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -219,6 +221,8 @@ $(srcdir)/oracle.common.conf.in \ $(srcdir)/oracle.userns.conf.in $(srcdir)/plamo.common.conf.in \ $(srcdir)/plamo.userns.conf.in \ + $(srcdir)/sabayon.common.conf.in \ + $(srcdir)/sabayon.userns.conf.in \ $(srcdir)/slackware.common.conf.in \ $(srcdir)/slackware.userns.conf.in \ $(srcdir)/sparclinux.common.conf.in \ @@ -227,7 +231,9 @@ $(srcdir)/ubuntu-cloud.lucid.conf.in \ $(srcdir)/ubuntu-cloud.userns.conf.in \ $(srcdir)/ubuntu.common.conf.in $(srcdir)/ubuntu.lucid.conf.in \ - $(srcdir)/ubuntu.userns.conf.in $(srcdir)/userns.conf.in + $(srcdir)/ubuntu.userns.conf.in $(srcdir)/userns.conf.in \ + $(srcdir)/voidlinux.common.conf.in \ + $(srcdir)/voidlinux.userns.conf.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -483,6 +489,10 @@ openwrt.common.conf \ sparclinux.common.conf \ sparclinux.userns.conf \ + voidlinux.common.conf \ + voidlinux.userns.conf \ + sabayon.common.conf \ + sabayon.userns.conf \ userns.conf all: all-recursive @@ -581,6 +591,14 @@ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ sparclinux.userns.conf: $(top_builddir)/config.status $(srcdir)/sparclinux.userns.conf.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +voidlinux.common.conf: $(top_builddir)/config.status $(srcdir)/voidlinux.common.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +voidlinux.userns.conf: $(top_builddir)/config.status $(srcdir)/voidlinux.userns.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +sabayon.common.conf: $(top_builddir)/config.status $(srcdir)/sabayon.common.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +sabayon.userns.conf: $(top_builddir)/config.status $(srcdir)/sabayon.userns.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ userns.conf: $(top_builddir)/config.status $(srcdir)/userns.conf.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ diff -Nru lxc-2.0.8/config/templates/nesting.conf.in lxc-2.1.0/config/templates/nesting.conf.in --- lxc-2.0.8/config/templates/nesting.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/nesting.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,5 +1,5 @@ # Use a profile which allows nesting -lxc.aa_profile = lxc-container-default-with-nesting +lxc.apparmor.profile = lxc-container-default-with-nesting # Add uncovered mounts of proc and sys, else unprivileged users # cannot remount those diff -Nru lxc-2.0.8/config/templates/openwrt.common.conf.in lxc-2.1.0/config/templates/openwrt.common.conf.in --- lxc-2.0.8/config/templates/openwrt.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/openwrt.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,7 +1,7 @@ # Default console settings -lxc.devttydir = lxc -lxc.tty = 4 -lxc.pts = 1024 +lxc.tty.dir = lxc +lxc.tty.max = 4 +lxc.pty.max = 1024 # Default capabilities lxc.cap.drop = mac_admin @@ -47,4 +47,4 @@ # Blacklist some syscalls which are not safe in privileged # containers -lxc.seccomp = /usr/share/lxc/config/common.seccomp +lxc.seccomp.profile = /usr/share/lxc/config/common.seccomp diff -Nru lxc-2.0.8/config/templates/plamo.common.conf.in lxc-2.1.0/config/templates/plamo.common.conf.in --- lxc-2.0.8/config/templates/plamo.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/plamo.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -2,7 +2,7 @@ lxc.include = @LXCTEMPLATECONFIG@/common.conf # Doesn't support consoles in /dev/lxc/ -lxc.devttydir = +lxc.tty.dir = # Extra cgroup device access ## rtc diff -Nru lxc-2.0.8/config/templates/sabayon.common.conf.in lxc-2.1.0/config/templates/sabayon.common.conf.in --- lxc-2.0.8/config/templates/sabayon.common.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/config/templates/sabayon.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,79 @@ +# Default configuration for Sabayon containers + +# Setup the default mounts +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed + +# Allow for 1024 pseudo terminals +lxc.pty.max = 1024 + +# Setup 1 tty devices for lxc-console command +lxc.tty.max = 1 + +# Needed for systemd distro +lxc.autodev = 1 + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# CGroup whitelist +lxc.cgroup.devices.deny = a + +## Allow any mknod (but not reading/writing the node) +#lxc.cgroup.devices.allow = c *:* m +#lxc.cgroup.devices.allow = b *:* m + +## Allow specific devices +### /dev/null +lxc.cgroup.devices.allow = c 1:3 rwm +### /dev/zero +lxc.cgroup.devices.allow = c 1:5 rwm +### /dev/full +lxc.cgroup.devices.allow = c 1:7 rwm +### /dev/random +lxc.cgroup.devices.allow = c 1:8 rwm +### /dev/urandom +lxc.cgroup.devices.allow = c 1:9 rwm +### /dev/pts/* +#lxc.cgroup.devices.allow = c 136:* rwm +### /dev/tty +#lxc.cgroup.devices.allow = c 5:0 rwm +### /dev/console +#lxc.cgroup.devices.allow = c 5:1 rwm +### /dev/ptmx +#lxc.cgroup.devices.allow = c 5:2 rwm +### fuse +#lxc.cgroup.devices.allow = c 10:229 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm +## rtc +#lxc.cgroup.devices.allow = c 254:0 rm +## tun +#lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +#lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +#lxc.cgroup.devices.allow = c 10:232 rwm +## /dev/mem +#lxc.cgroup.devices.allow = c 1:1 rwm + +# If something doesn't work, try to comment this out. +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +lxc.cap.drop = sys_time sys_module sys_rawio mac_admin mac_override +#lxc.cap.drop = sys_admin + + +# /dev/shm needs to be mounted as tmpfs. It's needed by python (bug #496328) +# and possibly other packages. +lxc.mount.entry = none dev/shm tmpfs rw,nosuid,nodev,create=dir + +# Blacklist some syscalls which are not safe in privileged +# containers +lxc.seccomp.profile = @LXCTEMPLATECONFIG@/common.seccomp + +# Customize lxc options through common directory +lxc.include = @LXCTEMPLATECONFIG@/common.conf.d/ diff -Nru lxc-2.0.8/config/templates/sabayon.userns.conf.in lxc-2.1.0/config/templates/sabayon.userns.conf.in --- lxc-2.0.8/config/templates/sabayon.userns.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/config/templates/sabayon.userns.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff -Nru lxc-2.0.8/config/templates/slackware.common.conf.in lxc-2.1.0/config/templates/slackware.common.conf.in --- lxc-2.0.8/config/templates/slackware.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/slackware.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -2,7 +2,7 @@ lxc.include = @LXCTEMPLATECONFIG@/common.conf # Doesn't support consoles in /dev/lxc/ -lxc.devttydir = +lxc.tty.dir = # Extra cgroup device access ## rtc diff -Nru lxc-2.0.8/config/templates/ubuntu.common.conf.in lxc-2.1.0/config/templates/ubuntu.common.conf.in --- lxc-2.0.8/config/templates/ubuntu.common.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/ubuntu.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -10,7 +10,7 @@ # When using LXC with apparmor, the container will be confined by default. # If you wish for it to instead run unconfined, copy the following line # (uncommented) to the container's configuration file. -#lxc.aa_profile = unconfined +#lxc.apparmor.profile = unconfined # Uncomment the following line to autodetect squid-deb-proxy configuration on the # host and forward it to the guest at start time. @@ -19,7 +19,7 @@ # If you wish to allow mounting block filesystems, then use the following # line instead, and make sure to grant access to the block device and/or loop # devices below in lxc.cgroup.devices.allow. -#lxc.aa_profile = lxc-container-default-with-mounting +#lxc.apparmor.profile = lxc-container-default-with-mounting # Extra cgroup device access ## rtc diff -Nru lxc-2.0.8/config/templates/ubuntu.lucid.conf.in lxc-2.1.0/config/templates/ubuntu.lucid.conf.in --- lxc-2.0.8/config/templates/ubuntu.lucid.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/ubuntu.lucid.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,2 +1,2 @@ # Ubuntu 10.04 LTS doesn't have /dev/lxc/ -lxc.devttydir = +lxc.tty.dir = diff -Nru lxc-2.0.8/config/templates/userns.conf.in lxc-2.1.0/config/templates/userns.conf.in --- lxc-2.0.8/config/templates/userns.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/config/templates/userns.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -3,12 +3,4 @@ lxc.cgroup.devices.allow = # We can't move bind-mounts, so don't use /dev/lxc/ -lxc.devttydir = - -# Extra bind-mounts for userns -lxc.mount.entry = /dev/full dev/full none bind,create=file 0 0 -lxc.mount.entry = /dev/null dev/null none bind,create=file 0 0 -lxc.mount.entry = /dev/random dev/random none bind,create=file 0 0 -lxc.mount.entry = /dev/tty dev/tty none bind,create=file 0 0 -lxc.mount.entry = /dev/urandom dev/urandom none bind,create=file 0 0 -lxc.mount.entry = /dev/zero dev/zero none bind,create=file 0 0 +lxc.tty.dir = diff -Nru lxc-2.0.8/config/templates/voidlinux.common.conf.in lxc-2.1.0/config/templates/voidlinux.common.conf.in --- lxc-2.0.8/config/templates/voidlinux.common.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/config/templates/voidlinux.common.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,35 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Allow for 6 tty devices by default +lxc.tty.max = 6 + +# Set $VIRTUALIZATION so runit doesn't try to mount filesystems or start udevd +lxc.environment=VIRTUALIZATION=lxc + +# Set the halt/stop signals +lxc.signal.halt=SIGCONT + + +# Uncomment to disable creating tty devices subdirectory in /dev +# lxc.tty.dir = + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd +# +lxc.cap.drop = setfcap sys_nice sys_pacct sys_rawio diff -Nru lxc-2.0.8/config/templates/voidlinux.userns.conf.in lxc-2.1.0/config/templates/voidlinux.userns.conf.in --- lxc-2.0.8/config/templates/voidlinux.userns.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/config/templates/voidlinux.userns.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,8 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf + +# Set $VIRTUALIZATION so runit doesn't try to mount filesystems or start udevd +lxc.environment=VIRTUALIZATION=lxc + +# Set the halt/stop signals +lxc.signal.halt=SIGCONT diff -Nru lxc-2.0.8/configure lxc-2.1.0/configure --- lxc-2.0.8/configure 2017-05-11 17:23:08.000000000 +0000 +++ lxc-2.1.0/configure 2017-09-06 02:32:41.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for lxc 2.0.8. +# Generated by GNU Autoconf 2.69 for lxc 2.1.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ # Identity of this package. PACKAGE_NAME='lxc' PACKAGE_TARNAME='lxc' -PACKAGE_VERSION='2.0.8' -PACKAGE_STRING='lxc 2.0.8' +PACKAGE_VERSION='2.1.0' +PACKAGE_STRING='lxc 2.1.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -633,6 +633,8 @@ am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +HAVE_PRLIMIT_FALSE +HAVE_PRLIMIT_TRUE HAVE_FGETLN_FALSE HAVE_FGETLN_TRUE HAVE_GETSUBOPT_FALSE @@ -1503,7 +1505,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures lxc 2.0.8 to adapt to many kinds of systems. +\`configure' configures lxc 2.1.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1574,7 +1576,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of lxc 2.0.8:";; + short | recursive ) echo "Configuration of lxc 2.1.0:";; esac cat <<\_ACEOF @@ -1754,7 +1756,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -lxc configure 2.0.8 +lxc configure 2.1.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2219,7 +2221,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by lxc $as_me 2.0.8, which was +It was created by lxc $as_me 2.1.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2690,28 +2692,28 @@ fi fi -LXC_VERSION_BASE=2.0.8 +LXC_VERSION_BASE=2.1.0 LXC_VERSION_MAJOR=2 -LXC_VERSION_MINOR=0 +LXC_VERSION_MINOR=1 -LXC_VERSION_MICRO=8 +LXC_VERSION_MICRO=0 -LXC_VERSION=2.0.8 +LXC_VERSION=2.1.0 LXC_DEVEL=0 LXC_ABI_MAJOR=1 -LXC_ABI_MINOR=2 +LXC_ABI_MINOR=3 LXC_ABI_MICRO=0 -LXC_ABI=1.2.0 +LXC_ABI=1.3.0 @@ -3232,7 +3234,7 @@ # Define the identity of the package. PACKAGE='lxc' - VERSION='2.0.8' + VERSION='2.1.0' cat >>confdefs.h <<_ACEOF @@ -12834,6 +12836,25 @@ with_distro="altlinux" fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /etc/pld-release" >&5 +$as_echo_n "checking for /etc/pld-release... " >&6; } +if ${ac_cv_file__etc_pld_release+:} false; then : + $as_echo_n "(cached) " >&6 +else + test "$cross_compiling" = yes && + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 +if test -r "/etc/pld-release"; then + ac_cv_file__etc_pld_release=yes +else + ac_cv_file__etc_pld_release=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__etc_pld_release" >&5 +$as_echo "$ac_cv_file__etc_pld_release" >&6; } +if test "x$ac_cv_file__etc_pld_release" = xyes; then : + with_distro="pld" +fi + fi with_distro=`echo ${with_distro} | tr '[:upper:]' '[:lower:]'` @@ -12849,7 +12870,7 @@ distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/default" ;; - redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo) + redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo|pld) distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/sysconfig" ;; @@ -12906,6 +12927,9 @@ debian|raspbian|ubuntu) init_script=upstart,systemd ;; + pld) + init_script=sysvinit,upstart,systemd + ;; *) echo -n "Linux distribution init system unknown." init_script= @@ -13026,7 +13050,7 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then db2xman="" - dbparsers="docbook2x-man db2x_docbook2man docbook2man docbook-to-man" + dbparsers="docbook2X2man docbook2x-man db2x_docbook2man docbook2man docbook-to-man" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for docbook2x-man" >&5 $as_echo_n "checking for docbook2x-man... " >&6; } @@ -14751,7 +14775,7 @@ $as_echo_n "(cached) " >&6 else - for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do + for am_cv_pathless_PYTHON in python python2 python3 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do test "$am_cv_pathless_PYTHON" = none && break prog="import sys # split strings by '.' and convert to numeric. Append some zeros @@ -16012,7 +16036,7 @@ if test "${with_cgroup_pattern+set}" = set; then : withval=$with_cgroup_pattern; else - with_cgroup_pattern='/lxc/%n' + with_cgroup_pattern='lxc/%n' fi @@ -16836,8 +16860,34 @@ _ACEOF +# Some systems lack PR_{G,S}ET_NO_NEW_PRIVS definition => HAVE_DECL_PR_{G,S}ET_NO_NEW_PRIVS +ac_fn_c_check_decl "$LINENO" "PR_SET_NO_NEW_PRIVS" "ac_cv_have_decl_PR_SET_NO_NEW_PRIVS" "#include +" +if test "x$ac_cv_have_decl_PR_SET_NO_NEW_PRIVS" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PR_SET_NO_NEW_PRIVS $ac_have_decl +_ACEOF + +ac_fn_c_check_decl "$LINENO" "PR_GET_NO_NEW_PRIVS" "ac_cv_have_decl_PR_GET_NO_NEW_PRIVS" "#include +" +if test "x$ac_cv_have_decl_PR_GET_NO_NEW_PRIVS" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PR_GET_NO_NEW_PRIVS $ac_have_decl +_ACEOF + + # Check for some headers -for ac_header in sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h +for ac_header in sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h sys/resource.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -17126,6 +17176,36 @@ fi done +for ac_func in prlimit +do : + ac_fn_c_check_func "$LINENO" "prlimit" "ac_cv_func_prlimit" +if test "x$ac_cv_func_prlimit" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PRLIMIT 1 +_ACEOF + if true; then + HAVE_PRLIMIT_TRUE= + HAVE_PRLIMIT_FALSE='#' +else + HAVE_PRLIMIT_TRUE='#' + HAVE_PRLIMIT_FALSE= +fi + + +$as_echo "#define HAVE_PRLIMIT 1" >>confdefs.h + +else + if false; then + HAVE_PRLIMIT_TRUE= + HAVE_PRLIMIT_FALSE='#' +else + HAVE_PRLIMIT_TRUE='#' + HAVE_PRLIMIT_FALSE= +fi + +fi +done + # Check for some libraries { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sem_open" >&5 @@ -17394,7 +17474,7 @@ fi # Files requiring some variable expansion -ac_config_files="$ac_config_files Makefile lxc.pc lxc.spec config/Makefile config/apparmor/Makefile config/selinux/Makefile config/bash/Makefile config/bash/lxc config/init/Makefile config/init/common/Makefile config/init/common/lxc-containers config/init/common/lxc-net config/init/systemd/Makefile config/init/systemd/lxc.service config/init/systemd/lxc@.service config/init/systemd/lxc-net.service config/init/sysvinit/Makefile config/init/sysvinit/lxc-containers config/init/sysvinit/lxc-net config/init/upstart/lxc.conf config/init/upstart/lxc-net.conf config/init/upstart/Makefile config/etc/Makefile config/templates/Makefile config/templates/alpine.common.conf config/templates/alpine.userns.conf config/templates/archlinux.common.conf config/templates/archlinux.userns.conf config/templates/centos.common.conf config/templates/centos.userns.conf config/templates/common.conf config/templates/common.conf.d/Makefile config/templates/debian.common.conf config/templates/debian.userns.conf config/templates/fedora.common.conf config/templates/fedora.userns.conf config/templates/gentoo.common.conf config/templates/gentoo.moresecure.conf config/templates/gentoo.userns.conf config/templates/nesting.conf config/templates/opensuse.common.conf config/templates/opensuse.userns.conf config/templates/oracle.common.conf config/templates/oracle.userns.conf config/templates/plamo.common.conf config/templates/plamo.userns.conf config/templates/slackware.common.conf config/templates/slackware.userns.conf config/templates/ubuntu-cloud.common.conf config/templates/ubuntu-cloud.lucid.conf config/templates/ubuntu-cloud.userns.conf config/templates/ubuntu.common.conf config/templates/ubuntu.lucid.conf config/templates/ubuntu.userns.conf config/templates/openwrt.common.conf config/templates/sparclinux.common.conf config/templates/sparclinux.userns.conf config/templates/userns.conf config/yum/Makefile config/sysconfig/Makefile config/sysconfig/lxc doc/Makefile doc/api/Makefile doc/lxc-attach.sgml doc/lxc-autostart.sgml doc/lxc-cgroup.sgml doc/lxc-checkconfig.sgml doc/lxc-checkpoint.sgml doc/lxc-clone.sgml doc/lxc-config.sgml doc/lxc-console.sgml doc/lxc-copy.sgml doc/lxc-create.sgml doc/lxc-destroy.sgml doc/lxc-device.sgml doc/lxc-execute.sgml doc/lxc-freeze.sgml doc/lxc-info.sgml doc/lxc-ls.sgml doc/lxc-monitor.sgml doc/lxc-snapshot.sgml doc/lxc-start-ephemeral.sgml doc/lxc-start.sgml doc/lxc-stop.sgml doc/lxc-top.sgml doc/lxc-unfreeze.sgml doc/lxc-unshare.sgml doc/lxc-user-nic.sgml doc/lxc-usernsexec.sgml doc/lxc-wait.sgml doc/lxc.conf.sgml doc/lxc.container.conf.sgml doc/lxc.system.conf.sgml doc/lxc-usernet.sgml doc/lxc.sgml doc/common_options.sgml doc/see_also.sgml doc/rootfs/Makefile doc/examples/Makefile doc/examples/lxc-macvlan.conf doc/examples/lxc-vlan.conf doc/examples/lxc-no-netns.conf doc/examples/lxc-empty-netns.conf doc/examples/lxc-phys.conf doc/examples/lxc-veth.conf doc/examples/lxc-complex.conf doc/ja/Makefile doc/ja/lxc-attach.sgml doc/ja/lxc-autostart.sgml doc/ja/lxc-cgroup.sgml doc/ja/lxc-checkconfig.sgml doc/ja/lxc-checkpoint.sgml doc/ja/lxc-clone.sgml doc/ja/lxc-config.sgml doc/ja/lxc-console.sgml doc/ja/lxc-copy.sgml doc/ja/lxc-create.sgml doc/ja/lxc-destroy.sgml doc/ja/lxc-device.sgml doc/ja/lxc-execute.sgml doc/ja/lxc-freeze.sgml doc/ja/lxc-info.sgml doc/ja/lxc-ls.sgml doc/ja/lxc-monitor.sgml doc/ja/lxc-snapshot.sgml doc/ja/lxc-start-ephemeral.sgml doc/ja/lxc-start.sgml doc/ja/lxc-stop.sgml doc/ja/lxc-top.sgml doc/ja/lxc-unfreeze.sgml doc/ja/lxc-unshare.sgml doc/ja/lxc-user-nic.sgml doc/ja/lxc-usernsexec.sgml doc/ja/lxc-wait.sgml doc/ja/lxc.conf.sgml doc/ja/lxc.container.conf.sgml doc/ja/lxc.system.conf.sgml doc/ja/lxc-usernet.sgml doc/ja/lxc.sgml doc/ja/common_options.sgml doc/ja/see_also.sgml doc/ko/Makefile doc/ko/lxc-attach.sgml doc/ko/lxc-autostart.sgml doc/ko/lxc-cgroup.sgml doc/ko/lxc-checkconfig.sgml doc/ko/lxc-checkpoint.sgml doc/ko/lxc-clone.sgml doc/ko/lxc-config.sgml doc/ko/lxc-console.sgml doc/ko/lxc-copy.sgml doc/ko/lxc-create.sgml doc/ko/lxc-destroy.sgml doc/ko/lxc-device.sgml doc/ko/lxc-execute.sgml doc/ko/lxc-freeze.sgml doc/ko/lxc-info.sgml doc/ko/lxc-ls.sgml doc/ko/lxc-monitor.sgml doc/ko/lxc-snapshot.sgml doc/ko/lxc-start-ephemeral.sgml doc/ko/lxc-start.sgml doc/ko/lxc-stop.sgml doc/ko/lxc-top.sgml doc/ko/lxc-unfreeze.sgml doc/ko/lxc-unshare.sgml doc/ko/lxc-user-nic.sgml doc/ko/lxc-usernsexec.sgml doc/ko/lxc-wait.sgml doc/ko/lxc.conf.sgml doc/ko/lxc.container.conf.sgml doc/ko/lxc.system.conf.sgml doc/ko/lxc-usernet.sgml doc/ko/lxc.sgml doc/ko/common_options.sgml doc/ko/see_also.sgml hooks/Makefile templates/Makefile templates/lxc-alpine templates/lxc-altlinux templates/lxc-archlinux templates/lxc-busybox templates/lxc-centos templates/lxc-cirros templates/lxc-debian templates/lxc-download templates/lxc-fedora templates/lxc-gentoo templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle templates/lxc-plamo templates/lxc-slackware templates/lxc-sshd templates/lxc-ubuntu templates/lxc-ubuntu-cloud templates/lxc-sparclinux src/Makefile src/lxc/Makefile src/lxc/lxc.functions src/lxc/tools/lxc-checkconfig src/lxc/tools/lxc-start-ephemeral src/lxc/version.h src/python-lxc/Makefile src/python-lxc/setup.py src/lua-lxc/Makefile src/tests/Makefile src/tests/lxc-test-usernic" +ac_config_files="$ac_config_files Makefile lxc.pc lxc.spec config/Makefile config/apparmor/Makefile config/selinux/Makefile config/bash/Makefile config/bash/lxc config/init/Makefile config/init/common/Makefile config/init/common/lxc-containers config/init/common/lxc-net config/init/systemd/Makefile config/init/systemd/lxc.service config/init/systemd/lxc@.service config/init/systemd/lxc-net.service config/init/sysvinit/Makefile config/init/sysvinit/lxc-containers config/init/sysvinit/lxc-net config/init/upstart/lxc.conf config/init/upstart/lxc-net.conf config/init/upstart/Makefile config/etc/Makefile config/templates/Makefile config/templates/alpine.common.conf config/templates/alpine.userns.conf config/templates/archlinux.common.conf config/templates/archlinux.userns.conf config/templates/centos.common.conf config/templates/centos.userns.conf config/templates/common.conf config/templates/common.conf.d/Makefile config/templates/debian.common.conf config/templates/debian.userns.conf config/templates/fedora.common.conf config/templates/fedora.userns.conf config/templates/gentoo.common.conf config/templates/gentoo.moresecure.conf config/templates/gentoo.userns.conf config/templates/nesting.conf config/templates/opensuse.common.conf config/templates/opensuse.userns.conf config/templates/oracle.common.conf config/templates/oracle.userns.conf config/templates/plamo.common.conf config/templates/plamo.userns.conf config/templates/slackware.common.conf config/templates/slackware.userns.conf config/templates/ubuntu-cloud.common.conf config/templates/ubuntu-cloud.lucid.conf config/templates/ubuntu-cloud.userns.conf config/templates/ubuntu.common.conf config/templates/ubuntu.lucid.conf config/templates/ubuntu.userns.conf config/templates/openwrt.common.conf config/templates/sparclinux.common.conf config/templates/sparclinux.userns.conf config/templates/voidlinux.common.conf config/templates/voidlinux.userns.conf config/templates/sabayon.common.conf config/templates/sabayon.userns.conf config/templates/userns.conf config/yum/Makefile config/sysconfig/Makefile config/sysconfig/lxc doc/Makefile doc/api/Makefile doc/lxc-attach.sgml doc/lxc-autostart.sgml doc/lxc-cgroup.sgml doc/lxc-checkconfig.sgml doc/lxc-checkpoint.sgml doc/lxc-clone.sgml doc/lxc-config.sgml doc/lxc-console.sgml doc/lxc-copy.sgml doc/lxc-create.sgml doc/lxc-destroy.sgml doc/lxc-device.sgml doc/lxc-execute.sgml doc/lxc-freeze.sgml doc/lxc-info.sgml doc/lxc-ls.sgml doc/lxc-monitor.sgml doc/lxc-snapshot.sgml doc/lxc-start-ephemeral.sgml doc/lxc-start.sgml doc/lxc-stop.sgml doc/lxc-top.sgml doc/lxc-unfreeze.sgml doc/lxc-unshare.sgml doc/lxc-user-nic.sgml doc/lxc-usernsexec.sgml doc/lxc-wait.sgml doc/lxc.conf.sgml doc/lxc.container.conf.sgml doc/lxc.system.conf.sgml doc/lxc-usernet.sgml doc/lxc.sgml doc/common_options.sgml doc/see_also.sgml doc/rootfs/Makefile doc/examples/Makefile doc/examples/lxc-macvlan.conf doc/examples/lxc-vlan.conf doc/examples/lxc-no-netns.conf doc/examples/lxc-empty-netns.conf doc/examples/lxc-phys.conf doc/examples/lxc-veth.conf doc/examples/lxc-complex.conf doc/ja/Makefile doc/ja/lxc-attach.sgml doc/ja/lxc-autostart.sgml doc/ja/lxc-cgroup.sgml doc/ja/lxc-checkconfig.sgml doc/ja/lxc-checkpoint.sgml doc/ja/lxc-clone.sgml doc/ja/lxc-config.sgml doc/ja/lxc-console.sgml doc/ja/lxc-copy.sgml doc/ja/lxc-create.sgml doc/ja/lxc-destroy.sgml doc/ja/lxc-device.sgml doc/ja/lxc-execute.sgml doc/ja/lxc-freeze.sgml doc/ja/lxc-info.sgml doc/ja/lxc-ls.sgml doc/ja/lxc-monitor.sgml doc/ja/lxc-snapshot.sgml doc/ja/lxc-start-ephemeral.sgml doc/ja/lxc-start.sgml doc/ja/lxc-stop.sgml doc/ja/lxc-top.sgml doc/ja/lxc-unfreeze.sgml doc/ja/lxc-unshare.sgml doc/ja/lxc-user-nic.sgml doc/ja/lxc-usernsexec.sgml doc/ja/lxc-wait.sgml doc/ja/lxc.conf.sgml doc/ja/lxc.container.conf.sgml doc/ja/lxc.system.conf.sgml doc/ja/lxc-usernet.sgml doc/ja/lxc.sgml doc/ja/common_options.sgml doc/ja/see_also.sgml doc/ko/Makefile doc/ko/lxc-attach.sgml doc/ko/lxc-autostart.sgml doc/ko/lxc-cgroup.sgml doc/ko/lxc-checkconfig.sgml doc/ko/lxc-checkpoint.sgml doc/ko/lxc-clone.sgml doc/ko/lxc-config.sgml doc/ko/lxc-console.sgml doc/ko/lxc-copy.sgml doc/ko/lxc-create.sgml doc/ko/lxc-destroy.sgml doc/ko/lxc-device.sgml doc/ko/lxc-execute.sgml doc/ko/lxc-freeze.sgml doc/ko/lxc-info.sgml doc/ko/lxc-ls.sgml doc/ko/lxc-monitor.sgml doc/ko/lxc-snapshot.sgml doc/ko/lxc-start-ephemeral.sgml doc/ko/lxc-start.sgml doc/ko/lxc-stop.sgml doc/ko/lxc-top.sgml doc/ko/lxc-unfreeze.sgml doc/ko/lxc-unshare.sgml doc/ko/lxc-user-nic.sgml doc/ko/lxc-usernsexec.sgml doc/ko/lxc-wait.sgml doc/ko/lxc.conf.sgml doc/ko/lxc.container.conf.sgml doc/ko/lxc.system.conf.sgml doc/ko/lxc-usernet.sgml doc/ko/lxc.sgml doc/ko/common_options.sgml doc/ko/see_also.sgml hooks/Makefile templates/Makefile templates/lxc-alpine templates/lxc-altlinux templates/lxc-archlinux templates/lxc-busybox templates/lxc-centos templates/lxc-cirros templates/lxc-debian templates/lxc-download templates/lxc-fedora templates/lxc-fedora-legacy templates/lxc-gentoo templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle templates/lxc-plamo templates/lxc-pld templates/lxc-slackware templates/lxc-sshd templates/lxc-ubuntu templates/lxc-ubuntu-cloud templates/lxc-sparclinux templates/lxc-voidlinux templates/lxc-sabayon src/Makefile src/lxc/Makefile src/lxc/lxc.functions src/lxc/tools/lxc-checkconfig src/lxc/tools/lxc-start-ephemeral src/lxc/tools/lxc-update-config src/lxc/version.h src/python-lxc/Makefile src/lua-lxc/Makefile src/tests/Makefile src/tests/lxc-test-usernic" ac_config_commands="$ac_config_commands default" @@ -17655,6 +17735,14 @@ as_fn_error $? "conditional \"HAVE_FGETLN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_PRLIMIT_TRUE}" && test -z "${HAVE_PRLIMIT_FALSE}"; then + as_fn_error $? "conditional \"HAVE_PRLIMIT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_PRLIMIT_TRUE}" && test -z "${HAVE_PRLIMIT_FALSE}"; then + as_fn_error $? "conditional \"HAVE_PRLIMIT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -18052,7 +18140,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by lxc $as_me 2.0.8, which was +This file was extended by lxc $as_me 2.1.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18122,7 +18210,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -lxc config.status 2.0.8 +lxc config.status 2.1.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -18596,6 +18684,10 @@ "config/templates/openwrt.common.conf") CONFIG_FILES="$CONFIG_FILES config/templates/openwrt.common.conf" ;; "config/templates/sparclinux.common.conf") CONFIG_FILES="$CONFIG_FILES config/templates/sparclinux.common.conf" ;; "config/templates/sparclinux.userns.conf") CONFIG_FILES="$CONFIG_FILES config/templates/sparclinux.userns.conf" ;; + "config/templates/voidlinux.common.conf") CONFIG_FILES="$CONFIG_FILES config/templates/voidlinux.common.conf" ;; + "config/templates/voidlinux.userns.conf") CONFIG_FILES="$CONFIG_FILES config/templates/voidlinux.userns.conf" ;; + "config/templates/sabayon.common.conf") CONFIG_FILES="$CONFIG_FILES config/templates/sabayon.common.conf" ;; + "config/templates/sabayon.userns.conf") CONFIG_FILES="$CONFIG_FILES config/templates/sabayon.userns.conf" ;; "config/templates/userns.conf") CONFIG_FILES="$CONFIG_FILES config/templates/userns.conf" ;; "config/yum/Makefile") CONFIG_FILES="$CONFIG_FILES config/yum/Makefile" ;; "config/sysconfig/Makefile") CONFIG_FILES="$CONFIG_FILES config/sysconfig/Makefile" ;; @@ -18726,24 +18818,28 @@ "templates/lxc-debian") CONFIG_FILES="$CONFIG_FILES templates/lxc-debian" ;; "templates/lxc-download") CONFIG_FILES="$CONFIG_FILES templates/lxc-download" ;; "templates/lxc-fedora") CONFIG_FILES="$CONFIG_FILES templates/lxc-fedora" ;; + "templates/lxc-fedora-legacy") CONFIG_FILES="$CONFIG_FILES templates/lxc-fedora-legacy" ;; "templates/lxc-gentoo") CONFIG_FILES="$CONFIG_FILES templates/lxc-gentoo" ;; "templates/lxc-openmandriva") CONFIG_FILES="$CONFIG_FILES templates/lxc-openmandriva" ;; "templates/lxc-opensuse") CONFIG_FILES="$CONFIG_FILES templates/lxc-opensuse" ;; "templates/lxc-oracle") CONFIG_FILES="$CONFIG_FILES templates/lxc-oracle" ;; "templates/lxc-plamo") CONFIG_FILES="$CONFIG_FILES templates/lxc-plamo" ;; + "templates/lxc-pld") CONFIG_FILES="$CONFIG_FILES templates/lxc-pld" ;; "templates/lxc-slackware") CONFIG_FILES="$CONFIG_FILES templates/lxc-slackware" ;; "templates/lxc-sshd") CONFIG_FILES="$CONFIG_FILES templates/lxc-sshd" ;; "templates/lxc-ubuntu") CONFIG_FILES="$CONFIG_FILES templates/lxc-ubuntu" ;; "templates/lxc-ubuntu-cloud") CONFIG_FILES="$CONFIG_FILES templates/lxc-ubuntu-cloud" ;; "templates/lxc-sparclinux") CONFIG_FILES="$CONFIG_FILES templates/lxc-sparclinux" ;; + "templates/lxc-voidlinux") CONFIG_FILES="$CONFIG_FILES templates/lxc-voidlinux" ;; + "templates/lxc-sabayon") CONFIG_FILES="$CONFIG_FILES templates/lxc-sabayon" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/lxc/Makefile") CONFIG_FILES="$CONFIG_FILES src/lxc/Makefile" ;; "src/lxc/lxc.functions") CONFIG_FILES="$CONFIG_FILES src/lxc/lxc.functions" ;; "src/lxc/tools/lxc-checkconfig") CONFIG_FILES="$CONFIG_FILES src/lxc/tools/lxc-checkconfig" ;; "src/lxc/tools/lxc-start-ephemeral") CONFIG_FILES="$CONFIG_FILES src/lxc/tools/lxc-start-ephemeral" ;; + "src/lxc/tools/lxc-update-config") CONFIG_FILES="$CONFIG_FILES src/lxc/tools/lxc-update-config" ;; "src/lxc/version.h") CONFIG_FILES="$CONFIG_FILES src/lxc/version.h" ;; "src/python-lxc/Makefile") CONFIG_FILES="$CONFIG_FILES src/python-lxc/Makefile" ;; - "src/python-lxc/setup.py") CONFIG_FILES="$CONFIG_FILES src/python-lxc/setup.py" ;; "src/lua-lxc/Makefile") CONFIG_FILES="$CONFIG_FILES src/lua-lxc/Makefile" ;; "src/tests/Makefile") CONFIG_FILES="$CONFIG_FILES src/tests/Makefile" ;; "src/tests/lxc-test-usernic") CONFIG_FILES="$CONFIG_FILES src/tests/lxc-test-usernic" ;; diff -Nru lxc-2.0.8/configure.ac lxc-2.1.0/configure.ac --- lxc-2.0.8/configure.ac 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/configure.ac 2017-09-06 02:32:37.000000000 +0000 @@ -3,12 +3,12 @@ m4_define([lxc_devel], 0) m4_define([lxc_version_major], 2) -m4_define([lxc_version_minor], 0) -m4_define([lxc_version_micro], 8) +m4_define([lxc_version_minor], 1) +m4_define([lxc_version_micro], 0) m4_define([lxc_version_beta], []) m4_define([lxc_abi_major], 1) -m4_define([lxc_abi_minor], 2) +m4_define([lxc_abi_minor], 3) m4_define([lxc_abi_micro], 0) m4_define([lxc_abi], [lxc_abi_major.lxc_abi_minor.lxc_abi_micro]) @@ -71,6 +71,7 @@ AC_CHECK_FILE(/etc/mandriva-release,with_distro="openmandriva") AC_CHECK_FILE(/etc/pardus-release,with_distro="pardus") AC_CHECK_FILE(/etc/altlinux-release,with_distro="altlinux") + AC_CHECK_FILE(/etc/pld-release,with_distro="pld") fi with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]'` @@ -86,7 +87,7 @@ distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/default" ;; - redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo) + redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo|pld) distroconf=default.conf.lxcbr distrosysconf="$sysconfdir/sysconfig" ;; @@ -122,6 +123,9 @@ debian|raspbian|ubuntu) init_script=upstart,systemd ;; + pld) + init_script=sysvinit,upstart,systemd + ;; *) echo -n "Linux distribution init system unknown." init_script= @@ -187,7 +191,7 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then db2xman="" - dbparsers="docbook2x-man db2x_docbook2man docbook2man docbook-to-man" + dbparsers="docbook2X2man docbook2x-man db2x_docbook2man docbook2man docbook-to-man" AC_MSG_CHECKING(for docbook2x-man) for name in ${dbparsers}; do @@ -554,7 +558,7 @@ [AC_HELP_STRING( [--with-cgroup-pattern=pattern], [pattern for container cgroups] - )], [], [with_cgroup_pattern=['/lxc/%n']]) + )], [], [with_cgroup_pattern=['lxc/%n']]) # Container log path. By default, use $lxcpath. AC_MSG_CHECKING([Whether to place logfiles in container config path]) @@ -632,8 +636,12 @@ # Some systems lack PR_CAPBSET_DROP definition => HAVE_DECL_PR_CAPBSET_DROP AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include ]) +# Some systems lack PR_{G,S}ET_NO_NEW_PRIVS definition => HAVE_DECL_PR_{G,S}ET_NO_NEW_PRIVS +AC_CHECK_DECLS([PR_SET_NO_NEW_PRIVS], [], [], [#include ]) +AC_CHECK_DECLS([PR_GET_NO_NEW_PRIVS], [], [], [#include ]) + # Check for some headers -AC_CHECK_HEADERS([sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h]) +AC_CHECK_HEADERS([sys/signalfd.h pty.h ifaddrs.h sys/memfd.h sys/personality.h utmpx.h sys/timerfd.h sys/resource.h]) # lookup major()/minor()/makedev() AC_HEADER_MAJOR @@ -659,6 +667,10 @@ AM_CONDITIONAL(HAVE_FGETLN, true) AC_DEFINE(HAVE_FGETLN,1,[Have fgetln]), AM_CONDITIONAL(HAVE_FGETLN, false)) +AC_CHECK_FUNCS([prlimit], + AM_CONDITIONAL(HAVE_PRLIMIT, true) + AC_DEFINE(HAVE_PRLIMIT,1,[Have prlimit]), + AM_CONDITIONAL(HAVE_PRLIMIT, false)) # Check for some libraries AC_SEARCH_LIBS(sem_open, [rt pthread]) @@ -738,6 +750,10 @@ config/templates/openwrt.common.conf config/templates/sparclinux.common.conf config/templates/sparclinux.userns.conf + config/templates/voidlinux.common.conf + config/templates/voidlinux.userns.conf + config/templates/sabayon.common.conf + config/templates/sabayon.userns.conf config/templates/userns.conf config/yum/Makefile config/sysconfig/Makefile @@ -878,25 +894,29 @@ templates/lxc-debian templates/lxc-download templates/lxc-fedora + templates/lxc-fedora-legacy templates/lxc-gentoo templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle templates/lxc-plamo + templates/lxc-pld templates/lxc-slackware templates/lxc-sshd templates/lxc-ubuntu templates/lxc-ubuntu-cloud templates/lxc-sparclinux + templates/lxc-voidlinux + templates/lxc-sabayon src/Makefile src/lxc/Makefile src/lxc/lxc.functions src/lxc/tools/lxc-checkconfig src/lxc/tools/lxc-start-ephemeral + src/lxc/tools/lxc-update-config src/lxc/version.h src/python-lxc/Makefile - src/python-lxc/setup.py src/lua-lxc/Makefile diff -Nru lxc-2.0.8/debian/changelog lxc-2.1.0/debian/changelog --- lxc-2.0.8/debian/changelog 2017-09-06 16:10:37.000000000 +0000 +++ lxc-2.1.0/debian/changelog 2017-09-18 22:32:38.000000000 +0000 @@ -1,3 +1,41 @@ +lxc (2.1.0-0ubuntu1) artful; urgency=medium + + * New upstream release (LXC 2.1): (LP: #1715278) + - https://linuxcontainers.org/lxc/news + + - This is an intermediary release between LXC 2.0 (LTS) and LXC 3.0 (LTS). + LXC 2.1 supports both the older configuration keys and the newer ones. + A number of options and commands will also now issue deprecation + warning before they completely go away in LXC 3.0. + + It is recommended that you run "lxc-update-config" for your + containers and make sure that there is no leftover warnings. + + * Cherry-pick fixes from upstream: + - 0002-Fix-typo.patch + - 0003-network-add-missing-checks-for-empty-links.patch + - 0004-cleanup-remove-unnecessary-zeroing.patch + - 0005-console-clean-tty-state-return-0-on-peer-exit.patch + - 0006-tools-fix-lxc-upate-config.patch + - 0007-criu-use-correct-check-initialization-check.patch + - 0008-storage-overlay-do-not-write-to-invalid-memory.patch + - 0009-utils-do-not-write-to-0-sized-buffer.patch + - 0010-overlay-fix-use-after-free.patch + - 0011-lxc-unshare-do-not-pass-NULL-pointer.patch + - 0012-lxc-user-nic-remove-double-initialization.patch + - 0013-execute-enable-console-standard-dev-symlinks.patch + - 0014-start-switch-ids-at-last-possible-instance.patch + - 0015-storage-avoid-segfault.patch + - 0016-tests-Support-systemd-hybrid-cgroups.patch + + * Build depend on python3-setuptools. + * Bump standard to 4.0.0. + * Drop upstart jobs on artful and higher. + * Update lintian overrides. + * Build a manpage with help2man for lxc-update-config. + + -- Stéphane Graber Mon, 18 Sep 2017 18:32:38 -0400 + lxc (2.0.8-0ubuntu7.1) artful; urgency=medium * Cherrypick fixes for netplan to fix release-regressed autopkgtests and diff -Nru lxc-2.0.8/debian/control lxc-2.1.0/debian/control --- lxc-2.0.8/debian/control 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/control 2017-09-18 22:32:38.000000000 +0000 @@ -10,6 +10,7 @@ dh-systemd, docbook2x, dpkg-dev (>= 1.16.1~) | hardening-wrapper, + help2man, libapparmor-dev, libcap-dev, libgnutls28-dev, @@ -18,8 +19,9 @@ libselinux1-dev, linux-libc-dev, pkg-config, - python3-all-dev (>= 3.2.3) -Standards-Version: 3.9.8 + python3-all-dev (>= 3.2.3), + python3-setuptools +Standards-Version: 4.0.0 Homepage: https://linuxcontainers.org Vcs-Git: https://github.com/lxc/lxc-pkg-ubuntu Vcs-Browser: https://github.com/lxc/lxc-pkg-ubuntu diff -Nru lxc-2.0.8/debian/.git-dpm lxc-2.1.0/debian/.git-dpm --- lxc-2.0.8/debian/.git-dpm 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/.git-dpm 2017-09-18 22:32:38.000000000 +0000 @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -2dcd5c37220469fc00dab9ee82d06b3624c140c9 -2dcd5c37220469fc00dab9ee82d06b3624c140c9 -eefb68174af61e54680da53e769c8d4138d36cb0 -eefb68174af61e54680da53e769c8d4138d36cb0 -lxc_2.0.8.orig.tar.gz -65883786c24312ab36e53231e312d94851957516 -1308705 +fa8a1e7ea9decccbe884f890142dcf64a1dac9f6 +fa8a1e7ea9decccbe884f890142dcf64a1dac9f6 +d89da872d2e9f93ad7c9039b5acc39beb386352c +d89da872d2e9f93ad7c9039b5acc39beb386352c +lxc_2.1.0.orig.tar.gz +8f8c48a999dd428672a0d02fbae12aed6b812026 +1369525 diff -Nru lxc-2.0.8/debian/lxc1.lintian-overrides lxc-2.1.0/debian/lxc1.lintian-overrides --- lxc-2.0.8/debian/lxc1.lintian-overrides 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/lxc1.lintian-overrides 2017-09-18 22:32:38.000000000 +0000 @@ -1,9 +1,6 @@ -init.d-script-not-marked-as-conffile etc/init.d/lxc-instance -init.d-script-not-marked-as-conffile etc/init.d/lxc-net -init.d-script-not-included-in-package etc/init.d/lxc-instance -init.d-script-not-included-in-package etc/init.d/lxc-net non-standard-dir-perm var/cache/lxc/ 0700 != 0755 non-standard-dir-perm var/lib/lxc/ 0700 != 0755 binary-without-manpage usr/sbin/init.lxc binary-without-manpage usr/sbin/init.lxc.static systemd-service-file-missing-documentation-key lib/systemd/system/lxc-net.service +old-style-config-script usr/bin/lxc-update-config diff -Nru lxc-2.0.8/debian/lxc1.maintscript.in lxc-2.1.0/debian/lxc1.maintscript.in --- lxc-2.0.8/debian/lxc1.maintscript.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/lxc1.maintscript.in 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,3 @@ +rm_conffile /etc/init/lxc.conf 2.1.0-0ubuntu1~ +rm_conffile /etc/init/lxc-instance.conf 2.1.0-0ubuntu1~ +rm_conffile /etc/init/lxc-net.conf 2.1.0-0ubuntu1~ diff -Nru lxc-2.0.8/debian/patches/0001-Allocate-new-lxcbr0-subnet-at-startup-time.patch lxc-2.1.0/debian/patches/0001-Allocate-new-lxcbr0-subnet-at-startup-time.patch --- lxc-2.0.8/debian/patches/0001-Allocate-new-lxcbr0-subnet-at-startup-time.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0001-Allocate-new-lxcbr0-subnet-at-startup-time.patch 2017-09-18 22:32:38.000000000 +0000 @@ -1,4 +1,4 @@ -From 07543eed517a7168ae951dc5d7bd2e2b068f05da Mon Sep 17 00:00:00 2001 +From f4f2c4956d3d7719922d6d54fff99cf00509712e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 3 Nov 2015 11:42:58 -0500 Subject: Allocate new lxcbr0 subnet at startup time diff -Nru lxc-2.0.8/debian/patches/0002-Fix-typo.patch lxc-2.1.0/debian/patches/0002-Fix-typo.patch --- lxc-2.0.8/debian/patches/0002-Fix-typo.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0002-Fix-typo.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,26 @@ +From 8d16357035d33c3db9a4cc219b71532a7f43a1b2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Mon, 18 Sep 2017 19:03:48 -0400 +Subject: Fix typo +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Stéphane Graber +--- + src/lxc/storage/storage.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lxc/storage/storage.c b/src/lxc/storage/storage.c +index fee3d8df..c8909123 100644 +--- a/src/lxc/storage/storage.c ++++ b/src/lxc/storage/storage.c +@@ -374,7 +374,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname, + if (ret < 0 && errno == ENOENT) { + ret = mkdir_p(orig->dest, 0755); + if (ret < 0) +- WARN("Failed to create directoy \"%s\"", orig->dest); ++ WARN("Failed to create directory \"%s\"", orig->dest); + } + } + diff -Nru lxc-2.0.8/debian/patches/0002-systemd-hybrid-cgroups.patch lxc-2.1.0/debian/patches/0002-systemd-hybrid-cgroups.patch --- lxc-2.0.8/debian/patches/0002-systemd-hybrid-cgroups.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0002-systemd-hybrid-cgroups.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -From c6d85962aa34b61e41b13b4512e4aa8694ec1c73 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Fri, 12 May 2017 12:28:20 -0400 -Subject: systemd hybrid cgroups -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Stéphane Graber ---- - src/tests/lxc-test-apparmor-mount | 1 + - src/tests/lxc-test-unpriv | 1 + - src/tests/lxc-test-usernic.in | 1 + - 3 files changed, 3 insertions(+) - -diff --git a/src/tests/lxc-test-apparmor-mount b/src/tests/lxc-test-apparmor-mount -index bb9b1fce..9aea41e4 100755 ---- a/src/tests/lxc-test-apparmor-mount -+++ b/src/tests/lxc-test-apparmor-mount -@@ -132,6 +132,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then - done - else - for d in /sys/fs/cgroup/*; do -+ [ "$d" = "/sys/fs/cgroup/unified" ] && continue - [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children - [ ! -d $d/lxctest ] && mkdir $d/lxctest - chown -R $TUSER: $d/lxctest -diff --git a/src/tests/lxc-test-unpriv b/src/tests/lxc-test-unpriv -index 8486fbde..b66728a9 100755 ---- a/src/tests/lxc-test-unpriv -+++ b/src/tests/lxc-test-unpriv -@@ -148,6 +148,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then - done - else - for d in /sys/fs/cgroup/*; do -+ [ "$d" = "/sys/fs/cgroup/unified" ] && continue - [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children - [ ! -d $d/lxctest ] && mkdir $d/lxctest - chown -R $TUSER: $d/lxctest -diff --git a/src/tests/lxc-test-usernic.in b/src/tests/lxc-test-usernic.in -index 0b99baa0..551fe12c 100755 ---- a/src/tests/lxc-test-usernic.in -+++ b/src/tests/lxc-test-usernic.in -@@ -105,6 +105,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then - done - else - for d in /sys/fs/cgroup/*; do -+ [ "$d" = "/sys/fs/cgroup/unified" ] && continue - [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children - [ ! -d $d/lxctest ] && mkdir $d/lxctest - chown -R usernic-user: $d/lxctest diff -Nru lxc-2.0.8/debian/patches/0003-network-add-missing-checks-for-empty-links.patch lxc-2.1.0/debian/patches/0003-network-add-missing-checks-for-empty-links.patch --- lxc-2.0.8/debian/patches/0003-network-add-missing-checks-for-empty-links.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0003-network-add-missing-checks-for-empty-links.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,32 @@ +From d71f59a698d8fa17f19a596dab4c89b22a1253eb Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Wed, 6 Sep 2017 11:51:03 +0200 +Subject: network: add missing checks for empty links + +Signed-off-by: Wolfgang Bumiller +--- + src/lxc/network.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/lxc/network.c b/src/lxc/network.c +index a7f054e7..982f2eef 100644 +--- a/src/lxc/network.c ++++ b/src/lxc/network.c +@@ -2350,7 +2350,7 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler) + if (netdev->type != LXC_NET_VETH) + continue; + +- if (!is_ovs_bridge(netdev->link)) ++ if (netdev->link[0] == '\0' || !is_ovs_bridge(netdev->link)) + continue; + + if (netdev->priv.veth_attr.pair[0] != '\0') +@@ -2559,7 +2559,7 @@ bool lxc_delete_network_priv(struct lxc_handler *handler) + } + INFO("Removed interface \"%s\" from \"%s\"", hostveth, netdev->link); + +- if (!is_ovs_bridge(netdev->link)) { ++ if (netdev->link[0] == '\0' || !is_ovs_bridge(netdev->link)) { + netdev->priv.veth_attr.veth1[0] = '\0'; + continue; + } diff -Nru lxc-2.0.8/debian/patches/0003-utils-fix-num-parsing-functions.patch lxc-2.1.0/debian/patches/0003-utils-fix-num-parsing-functions.patch --- lxc-2.0.8/debian/patches/0003-utils-fix-num-parsing-functions.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0003-utils-fix-num-parsing-functions.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -From 9ebb054e240020f5ff3bc9dc5f8288938602274e Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Thu, 11 May 2017 20:08:32 +0200 -Subject: utils: fix num parsing functions - -Suggested-by: Benedikt Rosenkranz beluro@web.de -Signed-off-by: Christian Brauner ---- - src/lxc/utils.c | 30 ++++++++++++++++++++---------- - 1 file changed, 20 insertions(+), 10 deletions(-) - -diff --git a/src/lxc/utils.c b/src/lxc/utils.c -index 778d4da5..15c9f91b 100644 ---- a/src/lxc/utils.c -+++ b/src/lxc/utils.c -@@ -23,6 +23,7 @@ - - #include "config.h" - -+#include - #include - #include - #include -@@ -1998,12 +1999,18 @@ int lxc_safe_uint(const char *numstr, unsigned int *converted) - char *err = NULL; - unsigned long int uli; - -+ while (isspace(*numstr)) -+ numstr++; -+ -+ if (*numstr == '-') -+ return -EINVAL; -+ - errno = 0; - uli = strtoul(numstr, &err, 0); -- if (errno > 0) -+ if (errno == ERANGE && uli == ULONG_MAX) - return -errno; - -- if (!err || err == numstr || *err != '\0') -+ if (err == numstr || *err != '\0') - return -EINVAL; - - if (uli > UINT_MAX) -@@ -2020,13 +2027,16 @@ int lxc_safe_int(const char *numstr, int *converted) - - errno = 0; - sli = strtol(numstr, &err, 0); -- if (errno > 0) -+ if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) -+ return -errno; -+ -+ if (errno != 0 && sli == 0) - return -errno; - -- if (!err || err == numstr || *err != '\0') -+ if (err == numstr || *err != '\0') - return -EINVAL; - -- if (sli > INT_MAX) -+ if (sli > INT_MAX || sli < INT_MIN) - return -ERANGE; - - *converted = (int)sli; -@@ -2040,14 +2050,14 @@ int lxc_safe_long(const char *numstr, long int *converted) - - errno = 0; - sli = strtol(numstr, &err, 0); -- if (errno > 0) -+ if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) - return -errno; - -- if (!err || err == numstr || *err != '\0') -- return -EINVAL; -+ if (errno != 0 && sli == 0) -+ return -errno; - -- if (sli > LONG_MAX) -- return -ERANGE; -+ if (err == numstr || *err != '\0') -+ return -EINVAL; - - *converted = sli; - return 0; diff -Nru lxc-2.0.8/debian/patches/0004-cleanup-remove-unnecessary-zeroing.patch lxc-2.1.0/debian/patches/0004-cleanup-remove-unnecessary-zeroing.patch --- lxc-2.0.8/debian/patches/0004-cleanup-remove-unnecessary-zeroing.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0004-cleanup-remove-unnecessary-zeroing.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,55 @@ +From cd7fd320eeb59e1aeb857ae72f3a271588d400b3 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Wed, 6 Sep 2017 11:45:03 +0200 +Subject: cleanup: remove unnecessary zeroing + +The entire netdev is zeroed via memset() already. Unions and +all. + +Signed-off-by: Wolfgang Bumiller +--- + src/lxc/confile_legacy.c | 10 ---------- + src/lxc/confile_utils.c | 9 --------- + 2 files changed, 19 deletions(-) + +diff --git a/src/lxc/confile_legacy.c b/src/lxc/confile_legacy.c +index 80dd3851..93df4737 100644 +--- a/src/lxc/confile_legacy.c ++++ b/src/lxc/confile_legacy.c +@@ -170,16 +170,6 @@ int set_config_network_legacy_type(const char *key, const char *value, + lxc_list_init(&netdev->ipv4); + lxc_list_init(&netdev->ipv6); + +- netdev->name[0] = '\0'; +- netdev->link[0] = '\0'; +- memset(&netdev->priv, 0, sizeof(netdev->priv)); +- /* I'm not completely sure if the memset takes care to zero the arrays +- * in the union as well. So let's make extra sure and set the first byte +- * to zero so that we don't have any surprises. +- */ +- netdev->priv.veth_attr.pair[0] = '\0'; +- netdev->priv.veth_attr.veth1[0] = '\0'; +- + list = malloc(sizeof(*list)); + if (!list) { + SYSERROR("failed to allocate memory"); +diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c +index 02924fa9..d43d516d 100644 +--- a/src/lxc/confile_utils.c ++++ b/src/lxc/confile_utils.c +@@ -183,15 +183,6 @@ struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail + memset(netdev, 0, sizeof(*netdev)); + lxc_list_init(&netdev->ipv4); + lxc_list_init(&netdev->ipv6); +- netdev->name[0] = '\0'; +- netdev->link[0] = '\0'; +- memset(&netdev->priv, 0, sizeof(netdev->priv)); +- /* I'm not completely sure if the memset takes care to zero the arrays +- * in the union as well. So let's make extra sure and set the first byte +- * to zero so that we don't have any surprises. +- */ +- netdev->priv.veth_attr.pair[0] = '\0'; +- netdev->priv.veth_attr.veth1[0] = '\0'; + + /* give network a unique index */ + netdev->idx = idx; diff -Nru lxc-2.0.8/debian/patches/0004-tests-lxc_safe_-u-int-add-corner-case-tests.patch lxc-2.1.0/debian/patches/0004-tests-lxc_safe_-u-int-add-corner-case-tests.patch --- lxc-2.0.8/debian/patches/0004-tests-lxc_safe_-u-int-add-corner-case-tests.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0004-tests-lxc_safe_-u-int-add-corner-case-tests.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -From fb391577a38d330397ea4b9bf86fabcae3d2a004 Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Fri, 12 May 2017 01:16:18 +0200 -Subject: tests: lxc_safe_{u}int() add corner-case tests - -Signed-off-by: Christian Brauner ---- - src/tests/lxc-test-utils.c | 37 +++++++++++++++++++++++++++++++++++++ - 1 file changed, 37 insertions(+) - -diff --git a/src/tests/lxc-test-utils.c b/src/tests/lxc-test-utils.c -index d4363b8b..01d8cd6e 100644 ---- a/src/tests/lxc-test-utils.c -+++ b/src/tests/lxc-test-utils.c -@@ -228,7 +228,22 @@ non_test_error: - - void test_lxc_safe_uint(void) - { -+ int ret; - unsigned int n; -+ char numstr[LXC_NUMSTRLEN64]; -+ -+ lxc_test_assert_abort((-EINVAL == lxc_safe_uint(" -123", &n))); -+ lxc_test_assert_abort((-EINVAL == lxc_safe_uint("-123", &n))); -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)UINT_MAX); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((0 == lxc_safe_uint(numstr, &n)) && n == UINT_MAX); -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)UINT_MAX + 1); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((-ERANGE == lxc_safe_uint(numstr, &n))); - - lxc_test_assert_abort((0 == lxc_safe_uint("1234345", &n)) && n == 1234345); - lxc_test_assert_abort((0 == lxc_safe_uint(" 345", &n)) && n == 345); -@@ -247,7 +262,29 @@ void test_lxc_safe_uint(void) - - void test_lxc_safe_int(void) - { -+ int ret; - signed int n; -+ char numstr[LXC_NUMSTRLEN64]; -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)INT_MAX); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((0 == lxc_safe_int(numstr, &n)) && n == INT_MAX); -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)INT_MAX + 1); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((-ERANGE == lxc_safe_int(numstr, &n))); -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRId64, (int64_t)INT_MIN); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((0 == lxc_safe_int(numstr, &n)) && n == INT_MIN); -+ -+ ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRId64, (int64_t)INT_MIN - 1); -+ if (ret < 0 || ret >= LXC_NUMSTRLEN64) -+ exit(EXIT_FAILURE); -+ lxc_test_assert_abort((-ERANGE == lxc_safe_int(numstr, &n))); - - lxc_test_assert_abort((0 == lxc_safe_int("1234345", &n)) && n == 1234345); - lxc_test_assert_abort((0 == lxc_safe_int(" 345", &n)) && n == 345); diff -Nru lxc-2.0.8/debian/patches/0005-console-clean-tty-state-return-0-on-peer-exit.patch lxc-2.1.0/debian/patches/0005-console-clean-tty-state-return-0-on-peer-exit.patch --- lxc-2.0.8/debian/patches/0005-console-clean-tty-state-return-0-on-peer-exit.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0005-console-clean-tty-state-return-0-on-peer-exit.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,49 @@ +From 3ade5336f41bb48846cf761daf3da4a508f6a49e Mon Sep 17 00:00:00 2001 +From: LiFeng +Date: Tue, 5 Sep 2017 23:16:50 +0800 +Subject: console: clean tty state + return 0 on peer exit + +In the past, if the console client exited, lxc_console_cb_con return 1. And +the lxc_poll will exit, the process will wait at waitpid. At this moment, the +process could not handle any command (For example get the container state +LXC_CMD_GET_STATE or stop the container LXC_CMD_STOP.). + +I think we should clean the tty_state and return 0 in this case. So, we can use +the lxc-console to connect the console of the container. And we will not exit +the function lxc_polland we can handle the commands by lxc_cmd_process + +Reproducer prior to this commit: +- open a new terminal, get the tty device name by command tty /dev/pts/6 +- set lxc.console.path = /dev/pts/6 +- start the container and the ouptut will print to /dev/pts/6 +- close /dev/pts/6 +- try an operation e.g. getting state with lxc-ls and lxc-ls will hang + +Closes #1787. + +Signed-off-by: LiFeng +Acked-by: Christian Brauner +--- + src/lxc/console.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index 666754d2..97ae7a16 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -174,6 +174,15 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data, + if (r <= 0) { + INFO("console client on fd %d has exited", fd); + lxc_mainloop_del_handler(descr, fd); ++ if (fd == console->peer) { ++ if (console->tty_state) { ++ lxc_console_sigwinch_fini(console->tty_state); ++ console->tty_state = NULL; ++ } ++ console->peer = -1; ++ close(fd); ++ return 0; ++ } + close(fd); + return 1; + } diff -Nru lxc-2.0.8/debian/patches/0005-lxc-attach-allow-for-situations-without-dev-tty.patch lxc-2.1.0/debian/patches/0005-lxc-attach-allow-for-situations-without-dev-tty.patch --- lxc-2.0.8/debian/patches/0005-lxc-attach-allow-for-situations-without-dev-tty.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0005-lxc-attach-allow-for-situations-without-dev-tty.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -From 0f1907cc11400530a1f01e2d4f3315bcbf6ed5cc Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Fri, 12 May 2017 16:33:23 +0200 -Subject: lxc-attach: allow for situations without /dev/tty - -Closes #1552. - -Signed-off-by: Christian Brauner ---- - src/lxc/console.c | 2 +- - src/lxc/tools/lxc_attach.c | 13 +++++-------- - 2 files changed, 6 insertions(+), 9 deletions(-) - -diff --git a/src/lxc/console.c b/src/lxc/console.c -index 3baaed49..8eae7c4a 100644 ---- a/src/lxc/console.c -+++ b/src/lxc/console.c -@@ -441,7 +441,7 @@ static int lxc_console_peer_default(struct lxc_console *console) - - console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600)); - if (console->peer < 0) { -- ERROR("failed to open \"%s\"", path); -+ ERROR("failed to open \"%s\": %s", path, strerror(errno)); - return -ENOTTY; - } - DEBUG("using \"%s\" as peer tty device", path); -diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c -index c5e319fd..d35ec88c 100644 ---- a/src/lxc/tools/lxc_attach.c -+++ b/src/lxc/tools/lxc_attach.c -@@ -301,15 +301,12 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int * - - /* In the case of lxc-attach our peer pty will always be the current - * controlling terminal. We clear whatever was set by the user for -- * lxc.console.path here and set it to "/dev/tty". Doing this will (a) -- * prevent segfaults when the container has been setup with -- * lxc.console = none and (b) provide an easy way to ensure that we -- * always do the correct thing. strdup() must be used since console.path -- * is free()ed when we call lxc_container_put(). */ -+ * lxc.console.path here and set it NULL. lxc_console_peer_default() -+ * will then try to open /dev/tty. If the process doesn't have a -+ * controlling terminal we should still proceed. -+ */ - free(conf->console.path); -- conf->console.path = strdup("/dev/tty"); -- if (!conf->console.path) -- return -1; -+ conf->console.path = NULL; - - /* Create pty on the host. */ - if (lxc_console_create(conf) < 0) diff -Nru lxc-2.0.8/debian/patches/0006-start-don-t-call-lxc_map_ids-without-id-map.patch lxc-2.1.0/debian/patches/0006-start-don-t-call-lxc_map_ids-without-id-map.patch --- lxc-2.0.8/debian/patches/0006-start-don-t-call-lxc_map_ids-without-id-map.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0006-start-don-t-call-lxc_map_ids-without-id-map.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -From 0fd522e61fdbc5df0b443f5386a559a520f1beaa Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Sat, 13 May 2017 17:16:25 +0200 -Subject: start: don't call lxc_map_ids() without id map - -So far, we somehow always called lxc_map_ids(), even when no id map was -configured. Let's not do this. - -Closes #1555. - -Signed-off-by: Christian Brauner ---- - src/lxc/start.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/src/lxc/start.c b/src/lxc/start.c -index bca7f8eb..859a7da7 100644 ---- a/src/lxc/start.c -+++ b/src/lxc/start.c -@@ -1061,8 +1061,12 @@ static int lxc_spawn(struct lxc_handler *handler) - int saved_ns_fd[LXC_NS_MAX]; - int preserve_mask = 0, i, flags; - int netpipepair[2], nveths; -+ bool wants_to_map_ids; -+ struct lxc_list *id_map; - - netpipe = -1; -+ id_map = &handler->conf->id_map; -+ wants_to_map_ids = !lxc_list_empty(id_map); - - for (i = 0; i < LXC_NS_MAX; i++) - if (handler->conf->inherit_ns_fd[i] != -1) -@@ -1124,7 +1128,7 @@ static int lxc_spawn(struct lxc_handler *handler) - * it readonly. - * If the container is unprivileged then skip rootfs pinning. - */ -- if (lxc_list_empty(&handler->conf->id_map)) { -+ if (wants_to_map_ids) { - handler->pinfd = pin_rootfs(handler->conf->rootfs.path); - if (handler->pinfd == -1) - INFO("Failed to pin the rootfs for container \"%s\".", handler->name); -@@ -1178,7 +1182,7 @@ static int lxc_spawn(struct lxc_handler *handler) - * mapped to something else on the host.) later to become a valid uid - * again. - */ -- if (lxc_map_ids(&handler->conf->id_map, handler->pid)) { -+ if (wants_to_map_ids && lxc_map_ids(id_map, handler->pid)) { - ERROR("Failed to set up id mapping."); - goto out_delete_net; - } diff -Nru lxc-2.0.8/debian/patches/0006-tools-fix-lxc-upate-config.patch lxc-2.1.0/debian/patches/0006-tools-fix-lxc-upate-config.patch --- lxc-2.0.8/debian/patches/0006-tools-fix-lxc-upate-config.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0006-tools-fix-lxc-upate-config.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,30 @@ +From 67cf78b53f3e647266e3f7f39cb2e2be872f2a7d Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Wed, 6 Sep 2017 12:33:19 +0200 +Subject: tools: fix lxc-upate-config + +- replace lxc.network.[i].ipv4 with lxc.net.[i].ipv4.address +- remove lxc.rootfs.backend lines + +Closes #1790. + +Signed-off-by: Christian Brauner +--- + src/lxc/tools/lxc-update-config.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/lxc/tools/lxc-update-config.in b/src/lxc/tools/lxc-update-config.in +index 3a9defd1..5bd55087 100644 +--- a/src/lxc/tools/lxc-update-config.in ++++ b/src/lxc/tools/lxc-update-config.in +@@ -63,8 +63,10 @@ sed -i \ + -e 's/\([[:blank:]*]\|#*\)\(lxc\.init_uid\)\([[:blank:]*]\|=\)/\1lxc\.init\.uid\3/g' \ + -e 's/\([[:blank:]*]\|#*\)\(lxc\.init_gid\)\([[:blank:]*]\|=\)/\1lxc\.init\.gid\3/g' \ + -e 's/\([[:blank:]*]\|#*\)\(lxc\.limit\)\([[:blank:]*]\|=\)/\1lxc\.prlimit\3/g' \ ++-e 's/\([[:blank:]*]\|#*\)\(lxc\.network\)\(\.[[:digit:]*]\)\(\.ipv4\)/\1lxc\.net\3\4\.address/g' \ + -e 's/\([[:blank:]*]\|#*\)\(lxc\.network\)\(\.[[:digit:]*]\)/\1lxc\.net\3/g' \ + -e 's/\([[:blank:]*]\|#*\)\(lxc\.network\)\([[:blank:]*]\|=\)/\1lxc\.net\3/g' \ ++-e '/\([[:blank:]*]\|#*\)\(lxc\.rootfs\.backend\)\([[:blank:]*]\|=\)/d' \ + "${CONFIGPATH}" + + # Finally, deal with network definitions of the following form: diff -Nru lxc-2.0.8/debian/patches/0007-conf-fix-build-without-libcap.patch lxc-2.1.0/debian/patches/0007-conf-fix-build-without-libcap.patch --- lxc-2.0.8/debian/patches/0007-conf-fix-build-without-libcap.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0007-conf-fix-build-without-libcap.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -From 89eb2ee0a6538e768a44cb4e290966e67ed06532 Mon Sep 17 00:00:00 2001 -From: Dima Krasner -Date: Sun, 14 May 2017 12:24:59 +0300 -Subject: conf: fix build without libcap - -Signed-off-by: Dima Krasner ---- - src/lxc/conf.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/src/lxc/conf.c b/src/lxc/conf.c -index 923a4d90..6c695f02 100644 ---- a/src/lxc/conf.c -+++ b/src/lxc/conf.c -@@ -127,6 +127,14 @@ lxc_log_define(lxc_conf, lxc); - #define LO_FLAGS_AUTOCLEAR 4 - #endif - -+#ifndef CAP_SETUID -+#define CAP_SETUID 7 -+#endif -+ -+#ifndef CAP_SETGID -+#define CAP_SETGID 6 -+#endif -+ - /* needed for cgroup automount checks, regardless of whether we - * have included linux/capability.h or not */ - #ifndef CAP_SYS_ADMIN diff -Nru lxc-2.0.8/debian/patches/0007-criu-use-correct-check-initialization-check.patch lxc-2.1.0/debian/patches/0007-criu-use-correct-check-initialization-check.patch --- lxc-2.0.8/debian/patches/0007-criu-use-correct-check-initialization-check.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0007-criu-use-correct-check-initialization-check.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,23 @@ +From 0636c5b08a7c21576511d31c53b3e8b47dd3acce Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sat, 9 Sep 2017 18:45:47 +0200 +Subject: criu: use correct check initialization check + +Signed-off-by: Christian Brauner +--- + src/lxc/criu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lxc/criu.c b/src/lxc/criu.c +index 676d759d..96688edc 100644 +--- a/src/lxc/criu.c ++++ b/src/lxc/criu.c +@@ -551,7 +551,7 @@ static void exec_criu(struct criu_opts *opts) + external_not_veth = false; + } + +- if (n->name) { ++ if (n->name[0] != '\0') { + if (strlen(n->name) >= sizeof(eth)) + goto err; + strncpy(eth, n->name, sizeof(eth)); diff -Nru lxc-2.0.8/debian/patches/0008-start-pin-rootfs-when-privileged.patch lxc-2.1.0/debian/patches/0008-start-pin-rootfs-when-privileged.patch --- lxc-2.0.8/debian/patches/0008-start-pin-rootfs-when-privileged.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0008-start-pin-rootfs-when-privileged.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From 3f9393937a2891a12575a0e06dcb43838cc27dc1 Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Tue, 16 May 2017 00:42:30 +0200 -Subject: start: pin rootfs when privileged - -Signed-off-by: Christian Brauner ---- - src/lxc/start.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/lxc/start.c b/src/lxc/start.c -index 859a7da7..f07cc73d 100644 ---- a/src/lxc/start.c -+++ b/src/lxc/start.c -@@ -1128,7 +1128,7 @@ static int lxc_spawn(struct lxc_handler *handler) - * it readonly. - * If the container is unprivileged then skip rootfs pinning. - */ -- if (wants_to_map_ids) { -+ if (!wants_to_map_ids) { - handler->pinfd = pin_rootfs(handler->conf->rootfs.path); - if (handler->pinfd == -1) - INFO("Failed to pin the rootfs for container \"%s\".", handler->name); diff -Nru lxc-2.0.8/debian/patches/0008-storage-overlay-do-not-write-to-invalid-memory.patch lxc-2.1.0/debian/patches/0008-storage-overlay-do-not-write-to-invalid-memory.patch --- lxc-2.0.8/debian/patches/0008-storage-overlay-do-not-write-to-invalid-memory.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0008-storage-overlay-do-not-write-to-invalid-memory.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,106 @@ +From 00be99431e1abb580434be8b4755f85424c0b441 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sat, 9 Sep 2017 19:29:53 +0200 +Subject: storage/overlay: do not write to invalid memory + +Closes #1802. + +Signed-off-by: Christian Brauner +--- + src/lxc/storage/overlay.c | 39 ++++++++++++++++++++------------------- + 1 file changed, 20 insertions(+), 19 deletions(-) + +diff --git a/src/lxc/storage/overlay.c b/src/lxc/storage/overlay.c +index e63a6ba5..cff355eb 100644 +--- a/src/lxc/storage/overlay.c ++++ b/src/lxc/storage/overlay.c +@@ -109,9 +109,9 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + return -1; + } + +- strncpy(delta, new->dest, lastslashidx + 1); +- strncpy(delta + lastslashidx, "delta0", sizeof("delta0") - 1); +- delta[lastslashidx + sizeof("delta0")] = '\0'; ++ memcpy(delta, new->dest, lastslashidx + 1); ++ memcpy(delta + lastslashidx, "delta0", sizeof("delta0") - 1); ++ delta[lastslashidx + sizeof("delta0") - 1] = '\0'; + + ret = mkdir(delta, 0755); + if (ret < 0 && errno != EEXIST) { +@@ -141,12 +141,13 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + return -1; + } + +- strncpy(work, new->dest, lastslashidx + 1); +- strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); +- work[lastslashidx + sizeof("olwork")] = '\0'; ++ memcpy(work, new->dest, lastslashidx + 1); ++ memcpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); ++ work[lastslashidx + sizeof("olwork") - 1] = '\0'; + +- if (mkdir(work, 0755) < 0) { +- SYSERROR("error: mkdir %s", work); ++ ret = mkdir(work, 0755); ++ if (ret < 0) { ++ SYSERROR("Failed to create directory \"%s\"", work); + free(delta); + free(work); + return -1; +@@ -253,9 +254,9 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + return -1; + } + +- strncpy(work, ndelta, lastslashidx + 1); +- strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); +- work[lastslashidx + sizeof("olwork")] = '\0'; ++ memcpy(work, ndelta, lastslashidx + 1); ++ memcpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); ++ work[lastslashidx + sizeof("olwork") - 1] = '\0'; + + ret = mkdir(work, 0755); + if (ret < 0 && errno != EEXIST) { +@@ -417,8 +418,8 @@ int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, + return -1; + } + +- strncpy(delta, dest, len); +- strncpy(delta + len - 6, "delta0", sizeof("delta0") - 1); ++ memcpy(delta, dest, len); ++ memcpy(delta + len - 6, "delta0", sizeof("delta0") - 1); + delta[len + sizeof("delta0")] = '\0'; + + ret = mkdir_p(delta, 0755); +@@ -575,9 +576,9 @@ int ovl_mount(struct lxc_storage *bdev) + return -22; + } + +- strncpy(work, upper, lastslashidx + 1); +- strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); +- work[lastslashidx + sizeof("olwork")] = '\0'; ++ memcpy(work, upper, lastslashidx + 1); ++ memcpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); ++ work[lastslashidx + sizeof("olwork") - 1] = '\0'; + + ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata); + if (ret < 0) { +@@ -747,8 +748,9 @@ int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, + char lxcpath[MAXPATHLEN]; + char **opts; + int ret; +- size_t arrlen, dirlen, i, len, rootfslen; ++ size_t arrlen, i, len, rootfslen; + int fret = -1; ++ size_t dirlen = 0; + char *rootfs_dir = NULL, *rootfs_path = NULL, *upperdir = NULL, + *workdir = NULL; + +@@ -772,8 +774,7 @@ int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, + } + + if (rootfs_path) { +- ret = +- snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); ++ ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + diff -Nru lxc-2.0.8/debian/patches/0009-conf-ile-allow-to-clear-all-config-items.patch lxc-2.1.0/debian/patches/0009-conf-ile-allow-to-clear-all-config-items.patch --- lxc-2.0.8/debian/patches/0009-conf-ile-allow-to-clear-all-config-items.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0009-conf-ile-allow-to-clear-all-config-items.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -From 99366119f27c3228f2797a4b4a531595fe6a4e80 Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Mon, 15 May 2017 14:53:06 +0200 -Subject: conf{,ile}: allow to clear all config items - -Closes #1561. - -Signed-off-by: Christian Brauner ---- - src/lxc/conf.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ - src/lxc/conf.h | 1 + - src/lxc/confile.c | 2 +- - 3 files changed, 81 insertions(+), 1 deletion(-) - -diff --git a/src/lxc/conf.c b/src/lxc/conf.c -index 6c695f02..b6dbaa8d 100644 ---- a/src/lxc/conf.c -+++ b/src/lxc/conf.c -@@ -4811,3 +4811,82 @@ struct lxc_list *sort_cgroup_settings(struct lxc_list* cgroup_settings) - - return result; - } -+ -+int lxc_clear_simple_config_item(struct lxc_conf *c, const char *key) -+{ -+ if (strcmp(key, "lxc.utsname") == 0) { -+ free(c->utsname); -+ c->utsname = NULL; -+ } else if (strcmp(key, "lxc.arch") == 0) { -+ c->personality = -1; -+ } else if (strcmp(key, "lxc.haltsignal") == 0) { -+ c->haltsignal = 0; -+ } else if (strcmp(key, "lxc.rebootsignal") == 0) { -+ c->rebootsignal = 0; -+ } else if (strcmp(key, "lxc.stopsignal") == 0) { -+ c->stopsignal = 0; -+ } else if (strcmp(key, "lxc.init_cmd") == 0) { -+ free(c->init_cmd); -+ c->init_cmd = NULL; -+ } else if (strcmp(key, "lxc.init_uid") == 0) { -+ c->init_uid = 0; -+ } else if (strcmp(key, "lxc.init_gid") == 0) { -+ c->init_gid = 0; -+ } else if (strcmp(key, "lxc.ephemeral") == 0) { -+ c->ephemeral = 0; -+ } else if (strcmp(key, "lxc.console.logfile") == 0) { -+ free(c->console.log_path); -+ c->console.log_path = NULL; -+ } else if (strcmp(key, "lxc.console") == 0) { -+ free(c->console.path); -+ c->console.path = NULL; -+ } else if (strcmp(key, "lxc.tty") == 0) { -+ c->tty = 0; -+ } else if (strcmp(key, "lxc.devttydir") == 0) { -+ free(c->ttydir); -+ c->ttydir = NULL; -+ } else if (strcmp(key, "lxc.autodev") == 0) { -+ c->autodev = 1; -+ } else if (strcmp(key, "lxc.kmsg") == 0) { -+ c->kmsg = 0; -+ } else if (strcmp(key, "lxc.mount") == 0) { -+ free(c->fstab); -+ c->fstab = NULL; -+ } else if (strcmp(key, "lxc.rootfs") == 0) { -+ free(c->rootfs.path); -+ c->rootfs.path = NULL; -+ } else if (strcmp(key, "lxc.rootfs.mount") == 0) { -+ free(c->rootfs.mount); -+ c->rootfs.mount = NULL; -+ } else if (strcmp(key, "lxc.rootfs.options") == 0) { -+ free(c->rootfs.options); -+ c->rootfs.options = NULL; -+ } else if (strcmp(key, "lxc.rootfs.backend") == 0) { -+ free(c->rootfs.bdev_type); -+ c->rootfs.bdev_type = NULL; -+ } else if (strcmp(key, "lxc.aa_profile") == 0) { -+ free(c->lsm_aa_profile); -+ c->lsm_aa_profile = NULL; -+ } else if (strcmp(key, "lxc.aa_allow_incomplete") == 0) { -+ c->lsm_aa_allow_incomplete = 0; -+ } else if (strcmp(key, "lxc.se_context") == 0) { -+ free(c->lsm_se_context); -+ c->lsm_se_context = NULL; -+ } else if (strcmp(key, "lxc.seccomp") == 0) { -+ free(c->seccomp); -+ c->seccomp = NULL; -+ } else if (strcmp(key, "lxc.loglevel") == 0) { -+ c->loglevel = LXC_LOG_PRIORITY_NOTSET; -+ } else if (strcmp(key, "lxc.logfile") == 0) { -+ free(c->logfile); -+ c->logfile = NULL; -+ } else if (strcmp(key, "lxc.monitor.unshare") == 0) { -+ c->monitor_unshare = 0; -+ } else if (strcmp(key, "lxc.pts") == 0) { -+ c->pts = 0; -+ } else { -+ return -1; -+ } -+ -+ return 0; -+} -diff --git a/src/lxc/conf.h b/src/lxc/conf.h -index c790bf7c..2c61747b 100644 ---- a/src/lxc/conf.h -+++ b/src/lxc/conf.h -@@ -422,6 +422,7 @@ extern int lxc_clear_idmaps(struct lxc_conf *c); - extern int lxc_clear_groups(struct lxc_conf *c); - extern int lxc_clear_environment(struct lxc_conf *c); - extern int lxc_delete_autodev(struct lxc_handler *handler); -+extern int lxc_clear_simple_config_item(struct lxc_conf *c, const char *key); - - extern int do_rootfs_setup(struct lxc_conf *conf, const char *name, - const char *lxcpath); -diff --git a/src/lxc/confile.c b/src/lxc/confile.c -index 9b22c6d3..506186e9 100644 ---- a/src/lxc/confile.c -+++ b/src/lxc/confile.c -@@ -2627,7 +2627,7 @@ int lxc_clear_config_item(struct lxc_conf *c, const char *key) - return lxc_clear_environment(c); - else if (strncmp(key, "lxc.id_map", 10) == 0) - return lxc_clear_idmaps(c); -- return -1; -+ return lxc_clear_simple_config_item(c, key); - } - - /* diff -Nru lxc-2.0.8/debian/patches/0009-utils-do-not-write-to-0-sized-buffer.patch lxc-2.1.0/debian/patches/0009-utils-do-not-write-to-0-sized-buffer.patch --- lxc-2.0.8/debian/patches/0009-utils-do-not-write-to-0-sized-buffer.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0009-utils-do-not-write-to-0-sized-buffer.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,29 @@ +From d6c739d3ea5febad82c6fe0eb8e07d0c6ebac2bc Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sun, 10 Sep 2017 06:42:10 +0200 +Subject: utils: do not write to 0 sized buffer + +Signed-off-by: Christian Brauner +--- + src/lxc/utils.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/lxc/utils.c b/src/lxc/utils.c +index 07257d29..656d3ca8 100644 +--- a/src/lxc/utils.c ++++ b/src/lxc/utils.c +@@ -2330,9 +2330,11 @@ int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) + /* close the write-end of the pipe */ + close(pipefd[1]); + +- bytes = read(pipefd[0], buf, (buf_size > 0) ? (buf_size - 1) : 0); +- if (bytes > 0) +- buf[bytes - 1] = '\0'; ++ if (buf && buf_size > 0) { ++ bytes = read(pipefd[0], buf, buf_size - 1); ++ if (bytes > 0) ++ buf[bytes - 1] = '\0'; ++ } + + fret = wait_for_pid(child); + /* close the read-end of the pipe */ diff -Nru lxc-2.0.8/debian/patches/0010-overlay-fix-use-after-free.patch lxc-2.1.0/debian/patches/0010-overlay-fix-use-after-free.patch --- lxc-2.0.8/debian/patches/0010-overlay-fix-use-after-free.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0010-overlay-fix-use-after-free.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,77 @@ +From d9d6dca3b34fb198276f5d41e25f2b5561242e81 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sun, 10 Sep 2017 07:04:34 +0200 +Subject: overlay: fix use after free() + +Signed-off-by: Christian Brauner +--- + src/lxc/storage/overlay.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/src/lxc/storage/overlay.c b/src/lxc/storage/overlay.c +index cff355eb..e0cd5d5f 100644 +--- a/src/lxc/storage/overlay.c ++++ b/src/lxc/storage/overlay.c +@@ -201,9 +201,8 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + + nsrc = strchr(osrc, ':') + 1; + if ((nsrc != osrc + 8) && (nsrc != osrc + 10)) { ++ ERROR("Detected \":\" in \"%s\" at wrong position", osrc); + free(osrc); +- ERROR("Detected \":\" in \"%s\" at wrong position", +- osrc); + return -22; + } + +@@ -220,9 +219,9 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + + ret = mkdir(ndelta, 0755); + if (ret < 0 && errno != EEXIST) { ++ SYSERROR("Failed to create directory \"%s\"", ndelta); + free(osrc); + free(ndelta); +- SYSERROR("Failed to create directory \"%s\"", ndelta); + return -1; + } + +@@ -238,9 +237,9 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + */ + lastslash = strrchr(ndelta, '/'); + if (!lastslash) { ++ ERROR("Failed to detect \"/\" in \"%s\"", ndelta); + free(osrc); + free(ndelta); +- ERROR("Failed to detect \"/\" in \"%s\"", ndelta); + return -1; + } + lastslash++; +@@ -260,10 +259,10 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + + ret = mkdir(work, 0755); + if (ret < 0 && errno != EEXIST) { ++ SYSERROR("Failed to create directory \"%s\"", ndelta); + free(osrc); + free(ndelta); + free(work); +- SYSERROR("Failed to create directory \"%s\"", ndelta); + return -1; + } + +@@ -323,7 +322,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + + s1 = strrchr(clean_old_path, '/'); + if (!s1) { +- ERROR("Failed to detect \"/\" in string \"%s\"", s1); ++ ERROR("Failed to detect \"/\" in string \"%s\"", clean_old_path); + free(clean_old_path); + free(clean_new_path); + return -1; +@@ -331,7 +330,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char + + s2 = strrchr(clean_new_path, '/'); + if (!s2) { +- ERROR("Failed to detect \"/\" in string \"%s\"", s2); ++ ERROR("Failed to detect \"/\" in string \"%s\"", clean_new_path); + free(clean_old_path); + free(clean_new_path); + return -1; diff -Nru lxc-2.0.8/debian/patches/0010-utils-fix-ppc64le-builds.patch lxc-2.1.0/debian/patches/0010-utils-fix-ppc64le-builds.patch --- lxc-2.0.8/debian/patches/0010-utils-fix-ppc64le-builds.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0010-utils-fix-ppc64le-builds.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -From e22564b4b241de661ecc662767054391e0360c87 Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Thu, 18 May 2017 13:18:29 +0200 -Subject: utils: fix ppc64le builds - -I suspect that there's a glibc bug on ppc64le. Both clang and gcc a very -unhappy when you return -errno from these functions. Instead, let's return -concrete errno numbers, e.g. -EINVAL. - -Signed-off-by: Christian Brauner ---- - src/lxc/utils.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/src/lxc/utils.c b/src/lxc/utils.c -index 15c9f91b..ac14793d 100644 ---- a/src/lxc/utils.c -+++ b/src/lxc/utils.c -@@ -2008,7 +2008,7 @@ int lxc_safe_uint(const char *numstr, unsigned int *converted) - errno = 0; - uli = strtoul(numstr, &err, 0); - if (errno == ERANGE && uli == ULONG_MAX) -- return -errno; -+ return -ERANGE; - - if (err == numstr || *err != '\0') - return -EINVAL; -@@ -2028,10 +2028,10 @@ int lxc_safe_int(const char *numstr, int *converted) - errno = 0; - sli = strtol(numstr, &err, 0); - if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) -- return -errno; -+ return -ERANGE; - - if (errno != 0 && sli == 0) -- return -errno; -+ return -EINVAL; - - if (err == numstr || *err != '\0') - return -EINVAL; -@@ -2051,10 +2051,10 @@ int lxc_safe_long(const char *numstr, long int *converted) - errno = 0; - sli = strtol(numstr, &err, 0); - if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) -- return -errno; -+ return -ERANGE; - - if (errno != 0 && sli == 0) -- return -errno; -+ return -EINVAL; - - if (err == numstr || *err != '\0') - return -EINVAL; diff -Nru lxc-2.0.8/debian/patches/0011-cgroups-workaround-gcc-7-bug.patch lxc-2.1.0/debian/patches/0011-cgroups-workaround-gcc-7-bug.patch --- lxc-2.0.8/debian/patches/0011-cgroups-workaround-gcc-7-bug.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0011-cgroups-workaround-gcc-7-bug.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -From 15a840286f9cbfa8edc6dbd4366c7e81e649aed4 Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Thu, 27 Jul 2017 20:49:58 +0200 -Subject: cgroups: workaround gcc-7 bug - -Also: LOL - -Signed-off-by: Christian Brauner ---- - src/lxc/cgroups/cgfsng.c | 17 ++++++++++++++--- - 1 file changed, 14 insertions(+), 3 deletions(-) - -diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c -index ebd548b9..2964e4ac 100644 ---- a/src/lxc/cgroups/cgfsng.c -+++ b/src/lxc/cgroups/cgfsng.c -@@ -1351,7 +1351,8 @@ static inline bool cgfsng_create(void *hdata) - { - struct cgfsng_handler_data *d = hdata; - char *tmp, *cgname, *offset; -- int i, idx = 0; -+ int i, ret; -+ int idx = 0; - size_t len; - - if (!d) -@@ -1377,8 +1378,18 @@ again: - ERROR("Too many conflicting cgroup names"); - goto out_free; - } -- if (idx) -- snprintf(offset, 5, "-%d", idx); -+ if (idx) { -+ ret = snprintf(offset, 5, "-%d", idx); -+ if (ret < 0 || (size_t)ret >= 5) { -+ FILE *f = fopen("/dev/null", "w"); -+ if (f >= 0) { -+ fprintf(f, "Workaround for GCC7 bug: " -+ "https://gcc.gnu.org/bugzilla/" -+ "show_bug.cgi?id=78969"); -+ fclose(f); -+ } -+ } -+ } - for (i = 0; hierarchies[i]; i++) { - if (!create_path_for_hierarchy(hierarchies[i], cgname)) { - int j; diff -Nru lxc-2.0.8/debian/patches/0011-lxc-unshare-do-not-pass-NULL-pointer.patch lxc-2.1.0/debian/patches/0011-lxc-unshare-do-not-pass-NULL-pointer.patch --- lxc-2.0.8/debian/patches/0011-lxc-unshare-do-not-pass-NULL-pointer.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0011-lxc-unshare-do-not-pass-NULL-pointer.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,24 @@ +From 939118118a0774d79b2371560ff1473e0c9dc7a0 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sun, 10 Sep 2017 08:01:31 +0200 +Subject: lxc-unshare: do not pass NULL pointer + +Signed-off-by: Christian Brauner +--- + src/lxc/tools/lxc_unshare.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/lxc/tools/lxc_unshare.c b/src/lxc/tools/lxc_unshare.c +index c294a608..25af9711 100644 +--- a/src/lxc/tools/lxc_unshare.c ++++ b/src/lxc/tools/lxc_unshare.c +@@ -228,6 +228,9 @@ int main(int argc, char *argv[]) + * dest: del + 1 == OUNT|PID + * src: del + 3 == NT|PID + */ ++ if (!namespaces) ++ usage(argv[0]); ++ + while ((del = strstr(namespaces, "MOUNT"))) + memmove(del + 1, del + 3, strlen(del) - 2); + diff -Nru lxc-2.0.8/debian/patches/0012-cgroups-handle-hybrid-cgroup-layouts.patch lxc-2.1.0/debian/patches/0012-cgroups-handle-hybrid-cgroup-layouts.patch --- lxc-2.0.8/debian/patches/0012-cgroups-handle-hybrid-cgroup-layouts.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0012-cgroups-handle-hybrid-cgroup-layouts.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,370 +0,0 @@ -From 255a24f186da937df1840759c57970a55cde3ebb Mon Sep 17 00:00:00 2001 -From: Christian Brauner -Date: Wed, 26 Jul 2017 15:15:27 +0200 -Subject: cgroups: handle hybrid cgroup layouts - -Closes #1669. -Closes #1678. -Relates to https://github.com/systemd/systemd/issues/6408. - -Signed-off-by: Christian Brauner ---- - src/lxc/Makefile.am | 2 + - src/lxc/cgroups/cgfsng.c | 81 ++++++++++++++++++++++----------------- - src/lxc/cgroups/cgroup_utils.c | 86 ++++++++++++++++++++++++++++++++++++++++++ - src/lxc/cgroups/cgroup_utils.h | 48 +++++++++++++++++++++++ - 4 files changed, 183 insertions(+), 34 deletions(-) - create mode 100644 src/lxc/cgroups/cgroup_utils.c - create mode 100644 src/lxc/cgroups/cgroup_utils.h - -diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am -index d7c05d6f..31a3076a 100644 ---- a/src/lxc/Makefile.am -+++ b/src/lxc/Makefile.am -@@ -18,6 +18,7 @@ noinst_HEADERS = \ - bdev/lxcrsync.h \ - bdev/lxczfs.h \ - cgroups/cgroup.h \ -+ cgroups/cgroup_utils.h \ - caps.h \ - conf.h \ - console.h \ -@@ -82,6 +83,7 @@ liblxc_la_SOURCES = \ - bdev/lxczfs.c bdev/lxczfs.h \ - cgroups/cgfs.c \ - cgroups/cgfsng.c \ -+ cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ - cgroups/cgroup.c cgroups/cgroup.h \ - commands.c commands.h \ - start.c start.h \ -diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c -index 2964e4ac..dfb2c59c 100644 ---- a/src/lxc/cgroups/cgfsng.c -+++ b/src/lxc/cgroups/cgfsng.c -@@ -49,6 +49,7 @@ - - #include "bdev.h" - #include "cgroup.h" -+#include "cgroup_utils.h" - #include "commands.h" - #include "log.h" - #include "utils.h" -@@ -72,6 +73,7 @@ struct hierarchy { - char *mountpoint; - char *base_cgroup; - char *fullcgpath; -+ bool is_cgroup_v2; - }; - - /* -@@ -626,7 +628,8 @@ static bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname) - } - - clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL); -- if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */ -+ /* unified hierarchy doesn't have clone_children */ -+ if (!file_exists(clonechildrenpath)) { - free(clonechildrenpath); - free(cgpath); - return true; -@@ -768,10 +771,14 @@ static bool is_lxcfs(const char *line) - */ - static char **get_controllers(char **klist, char **nlist, char *line) - { -- // the fourth field is /sys/fs/cgroup/comma-delimited-controller-list -+ /* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */ - int i; - char *p = line, *p2, *tok, *saveptr = NULL; - char **aret = NULL; -+ bool is_cgroup_v2; -+ -+ /* handle cgroup v2 */ -+ is_cgroup_v2 = is_cgroupfs_v2(line); - - for (i = 0; i < 4; i++) { - p = strchr(p, ' '); -@@ -792,6 +799,13 @@ static char **get_controllers(char **klist, char **nlist, char *line) - return NULL; - } - *p2 = '\0'; -+ -+ /* cgroup v2 does not have separate mountpoints for controllers */ -+ if (is_cgroup_v2) { -+ must_append_controller(klist, nlist, &aret, "cgroup2"); -+ return aret; -+ } -+ - for (tok = strtok_r(p, ",", &saveptr); tok; - tok = strtok_r(NULL, ",", &saveptr)) { - must_append_controller(klist, nlist, &aret, tok); -@@ -800,15 +814,6 @@ static char **get_controllers(char **klist, char **nlist, char *line) - return aret; - } - --/* return true if the fstype is cgroup */ --static bool is_cgroupfs(char *line) --{ -- char *p = strstr(line, " - "); -- if (!p) -- return false; -- return strncmp(p, " - cgroup ", 10) == 0; --} -- - /* Add a controller to our list of hierarchies */ - static void add_controller(char **clist, char *mountpoint, char *base_cgroup) - { -@@ -821,6 +826,12 @@ static void add_controller(char **clist, char *mountpoint, char *base_cgroup) - new->base_cgroup = base_cgroup; - new->fullcgpath = NULL; - -+ /* record if this is the cgroup v2 hierarchy */ -+ if (!strcmp(base_cgroup, "cgroup2")) -+ new->is_cgroup_v2 = true; -+ else -+ new->is_cgroup_v2 = false; -+ - newentry = append_null_to_list((void ***)&hierarchies); - hierarchies[newentry] = new; - } -@@ -902,13 +913,21 @@ static bool controller_in_clist(char *cgline, char *c) - static char *get_current_cgroup(char *basecginfo, char *controller) - { - char *p = basecginfo; -+ bool is_cgroup_v2; -+ bool is_cgroup_v2_base_cgroup; -+ -+ is_cgroup_v2 = !strcmp(controller, "cgroup2"); -+ while (true) { -+ is_cgroup_v2_base_cgroup = false; -+ /* cgroup v2 entry in "/proc//cgroup": "0::/some/path" */ -+ if (is_cgroup_v2 && (*p == '0')) -+ is_cgroup_v2_base_cgroup = true; - -- while (1) { - p = strchr(p, ':'); - if (!p) - return NULL; - p++; -- if (controller_in_clist(p, controller)) { -+ if (is_cgroup_v2_base_cgroup || controller_in_clist(p, controller)) { - p = strchr(p, ':'); - if (!p) - return NULL; -@@ -923,20 +942,6 @@ static char *get_current_cgroup(char *basecginfo, char *controller) - } - } - --/* -- * Given a hierarchy @mountpoint and base @path, verify that we can create -- * directories underneath it. -- */ --static bool test_writeable(char *mountpoint, char *path) --{ -- char *fullpath = must_make_path(mountpoint, path, NULL); -- int ret; -- -- ret = access(fullpath, W_OK); -- free(fullpath); -- return ret == 0; --} -- - static void must_append_string(char ***list, char *entry) - { - int newentry = append_null_to_list((void ***)list); -@@ -965,16 +970,17 @@ static void get_existing_subsystems(char ***klist, char ***nlist) - continue; - *p2 = '\0'; - -- /* If we have a mixture between cgroup v1 and cgroup v2 -- * hierarchies, then /proc/self/cgroup contains entries of the -- * form: -+ /* If the kernel has cgroup v2 support, then /proc/self/cgroup -+ * contains an entry of the form: - * - * 0::/some/path - * -- * We need to skip those. -+ * In this case we use "cgroup2" as controller name. - */ -- if ((p2 - p) == 0) -+ if ((p2 - p) == 0) { -+ must_append_string(klist, "cgroup2"); - continue; -+ } - - for (tok = strtok_r(p, ",", &saveptr); tok; - tok = strtok_r(NULL, ",", &saveptr)) { -@@ -1082,8 +1088,10 @@ static bool parse_hierarchies(void) - while (getline(&line, &len, f) != -1) { - char **controller_list = NULL; - char *mountpoint, *base_cgroup; -+ bool is_cgroup_v2, writeable; - -- if (!is_lxcfs(line) && !is_cgroupfs(line)) -+ is_cgroup_v2 = is_cgroupfs_v2(line); -+ if (!is_lxcfs(line) && !is_cgroupfs_v1(line) && !is_cgroup_v2) - continue; - - controller_list = get_controllers(klist, nlist, line); -@@ -1109,9 +1117,14 @@ static bool parse_hierarchies(void) - free(mountpoint); - continue; - } -+ - trim(base_cgroup); - prune_init_scope(base_cgroup); -- if (!test_writeable(mountpoint, base_cgroup)) { -+ if (is_cgroup_v2) -+ writeable = test_writeable_v2(mountpoint, base_cgroup); -+ else -+ writeable = test_writeable_v1(mountpoint, base_cgroup); -+ if (!writeable) { - free_string_list(controller_list); - free(mountpoint); - free(base_cgroup); -diff --git a/src/lxc/cgroups/cgroup_utils.c b/src/lxc/cgroups/cgroup_utils.c -new file mode 100644 -index 00000000..c09ba168 ---- /dev/null -+++ b/src/lxc/cgroups/cgroup_utils.c -@@ -0,0 +1,86 @@ -+/* -+ * lxc: linux Container library -+ * -+ * Copyright © 2017 Canonical Ltd. -+ * -+ * Authors: -+ * Serge Hallyn -+ * Christian Brauner -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#include "config.h" -+ -+#include -+#include -+#include -+#include -+ -+#include "cgroup_utils.h" -+#include "utils.h" -+ -+bool is_cgroupfs_v1(char *line) -+{ -+ char *p = strstr(line, " - "); -+ if (!p) -+ return false; -+ return strncmp(p, " - cgroup ", 10) == 0; -+} -+ -+bool is_cgroupfs_v2(char *line) -+{ -+ char *p = strstr(line, " - "); -+ if (!p) -+ return false; -+ -+ return strncmp(p, " - cgroup2 ", 11) == 0; -+} -+ -+bool test_writeable_v1(char *mountpoint, char *path) -+{ -+ char *fullpath = must_make_path(mountpoint, path, NULL); -+ int ret; -+ -+ ret = access(fullpath, W_OK); -+ free(fullpath); -+ return ret == 0; -+} -+ -+bool test_writeable_v2(char *mountpoint, char *path) -+{ -+ /* In order to move ourselves into an appropriate sub-cgroup we need to -+ * have write access to the parent cgroup's "cgroup.procs" file, i.e. we -+ * need to have write access to the our current cgroups's "cgroup.procs" -+ * file. -+ */ -+ int ret; -+ char *cgroup_path, *cgroup_procs_file; -+ -+ cgroup_path = must_make_path(mountpoint, path, NULL); -+ cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL); -+ -+ ret = access(cgroup_path, W_OK); -+ free(cgroup_path); -+ if (ret < 0) { -+ free(cgroup_procs_file); -+ return false; -+ } -+ -+ ret = access(cgroup_procs_file, W_OK); -+ free(cgroup_procs_file); -+ -+ return ret == 0; -+} -diff --git a/src/lxc/cgroups/cgroup_utils.h b/src/lxc/cgroups/cgroup_utils.h -new file mode 100644 -index 00000000..49aae856 ---- /dev/null -+++ b/src/lxc/cgroups/cgroup_utils.h -@@ -0,0 +1,48 @@ -+/* -+ * lxc: linux Container library -+ * -+ * Copyright © 2017 Canonical Ltd. -+ * -+ * Authors: -+ * Serge Hallyn -+ * Christian Brauner -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#ifndef __LXC_CGROUP_UTILS_H -+#define __LXC_CGROUP_UTILS_H -+ -+#include -+#include -+ -+/* Check if given entry from /proc//mountinfo is a cgroupfs v1 mount. */ -+extern bool is_cgroupfs_v1(char *line); -+ -+/* Check if given entry from /proc//mountinfo is a cgroupfs v2 mount. */ -+extern bool is_cgroupfs_v2(char *line); -+ -+/* Given a v1 hierarchy @mountpoint and base @path, verify that we can create -+ * directories underneath it. -+ */ -+extern bool test_writeable_v1(char *mountpoint, char *path); -+ -+/* Given a v2 hierarchy @mountpoint and base @path, verify that we can create -+ * directories underneath it and that we have write access to the cgroup's -+ * "cgroup.procs" file. -+ */ -+extern bool test_writeable_v2(char *mountpoint, char *path); -+ -+#endif /* __LXC_CGROUP_UTILS_H */ diff -Nru lxc-2.0.8/debian/patches/0012-lxc-user-nic-remove-double-initialization.patch lxc-2.1.0/debian/patches/0012-lxc-user-nic-remove-double-initialization.patch --- lxc-2.0.8/debian/patches/0012-lxc-user-nic-remove-double-initialization.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0012-lxc-user-nic-remove-double-initialization.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,23 @@ +From d0561f69268848cc0c8818502812f0e9ab052dba Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sun, 10 Sep 2017 08:23:36 +0200 +Subject: lxc-user-nic: remove double initialization + +Signed-off-by: Christian Brauner +--- + src/lxc/lxc_user_nic.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lxc/lxc_user_nic.c b/src/lxc/lxc_user_nic.c +index 0f79451d..6f550f0d 100644 +--- a/src/lxc/lxc_user_nic.c ++++ b/src/lxc/lxc_user_nic.c +@@ -638,7 +638,7 @@ static int count_entries(char *buf, off_t len, char *name, char *net_type, char + { + int count = 0; + bool owner = false;; +- char *buf_end = &buf[len]; ++ char *buf_end; + + buf_end = &buf[len]; + while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL, diff -Nru lxc-2.0.8/debian/patches/0013-execute-enable-console-standard-dev-symlinks.patch lxc-2.1.0/debian/patches/0013-execute-enable-console-standard-dev-symlinks.patch --- lxc-2.0.8/debian/patches/0013-execute-enable-console-standard-dev-symlinks.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0013-execute-enable-console-standard-dev-symlinks.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,73 @@ +From 3db3301793a9d10ded11d2efa239e165e02e7a14 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sun, 10 Sep 2017 13:49:18 +0200 +Subject: execute: enable console & standard /dev symlinks + +Signed-off-by: Christian Brauner +--- + src/lxc/conf.c | 13 ++++++++----- + src/lxc/console.c | 10 ---------- + 2 files changed, 8 insertions(+), 15 deletions(-) + +diff --git a/src/lxc/conf.c b/src/lxc/conf.c +index 7a118816..52d66d53 100644 +--- a/src/lxc/conf.c ++++ b/src/lxc/conf.c +@@ -782,7 +782,7 @@ static const struct dev_symlinks dev_symlinks[] = { + {"/proc/self/fd/2", "stderr"}, + }; + +-static int setup_dev_symlinks(const struct lxc_rootfs *rootfs) ++static int lxc_setup_dev_symlinks(const struct lxc_rootfs *rootfs) + { + char path[MAXPATHLEN]; + int ret,i; +@@ -3220,13 +3220,16 @@ int lxc_setup(struct lxc_handler *handler) + } + } + +- if (!lxc_conf->is_execute && lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console, lxc_conf->ttydir)) { +- ERROR("failed to setup the console for '%s'", name); ++ ret = lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console, ++ lxc_conf->ttydir); ++ if (ret < 0) { ++ ERROR("Failed to setup console"); + return -1; + } + +- if (!lxc_conf->is_execute && setup_dev_symlinks(&lxc_conf->rootfs)) { +- ERROR("failed to setup /dev symlinks for '%s'", name); ++ ret = lxc_setup_dev_symlinks(&lxc_conf->rootfs); ++ if (ret < 0) { ++ ERROR("Failed to setup /dev symlinks"); + return -1; + } + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index 97ae7a16..016b8b5b 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -228,11 +228,6 @@ extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, + { + struct lxc_console *console = &conf->console; + +- if (conf->is_execute) { +- INFO("no console for lxc-execute."); +- return 0; +- } +- + if (!conf->rootfs.path) { + INFO("no rootfs, no console."); + return 0; +@@ -528,11 +523,6 @@ int lxc_console_create(struct lxc_conf *conf) + struct lxc_console *console = &conf->console; + int ret; + +- if (conf->is_execute) { +- INFO("not allocating a console device for lxc-execute."); +- return 0; +- } +- + if (!conf->rootfs.path) { + INFO("container does not have a rootfs, console device will be shared with the host"); + return 0; diff -Nru lxc-2.0.8/debian/patches/0013-Make-must_make_path-available-to-cgroup_utils.patch lxc-2.1.0/debian/patches/0013-Make-must_make_path-available-to-cgroup_utils.patch --- lxc-2.0.8/debian/patches/0013-Make-must_make_path-available-to-cgroup_utils.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0013-Make-must_make_path-available-to-cgroup_utils.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -From 1a134af7aa3a594a65529523bcb36dd7f8713cf4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Tue, 22 Aug 2017 18:52:33 -0400 -Subject: Make must_make_path available to cgroup_utils -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Stéphane Graber ---- - src/lxc/cgroups/cgfsng.c | 4 ++-- - src/lxc/cgroups/cgroup_utils.c | 2 ++ - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c -index dfb2c59c..24209ba0 100644 ---- a/src/lxc/cgroups/cgfsng.c -+++ b/src/lxc/cgroups/cgfsng.c -@@ -261,7 +261,7 @@ struct hierarchy *get_hierarchy(const char *c) - return NULL; - } - --static char *must_make_path(const char *first, ...) __attribute__((sentinel)); -+char *must_make_path(const char *first, ...) __attribute__((sentinel)); - - #define BATCH_SIZE 50 - static void batch_realloc(char **mem, size_t oldlen, size_t newlen) -@@ -1200,7 +1200,7 @@ out_free: - * Concatenate all passed-in strings into one path. Do not fail. If any piece is - * not prefixed with '/', add a '/'. - */ --static char *must_make_path(const char *first, ...) -+char *must_make_path(const char *first, ...) - { - va_list args; - char *cur, *dest; -diff --git a/src/lxc/cgroups/cgroup_utils.c b/src/lxc/cgroups/cgroup_utils.c -index c09ba168..0cba7ca8 100644 ---- a/src/lxc/cgroups/cgroup_utils.c -+++ b/src/lxc/cgroups/cgroup_utils.c -@@ -32,6 +32,8 @@ - #include "cgroup_utils.h" - #include "utils.h" - -+char *must_make_path(const char *first, ...) __attribute__((sentinel)); -+ - bool is_cgroupfs_v1(char *line) - { - char *p = strstr(line, " - "); diff -Nru lxc-2.0.8/debian/patches/0014-start-switch-ids-at-last-possible-instance.patch lxc-2.1.0/debian/patches/0014-start-switch-ids-at-last-possible-instance.patch --- lxc-2.0.8/debian/patches/0014-start-switch-ids-at-last-possible-instance.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0014-start-switch-ids-at-last-possible-instance.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,87 @@ +From 0b2547e2a112efa968abbe75d3cbcc23f9b54b15 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Mon, 11 Sep 2017 03:16:06 +0200 +Subject: start: switch ids at last possible instance + +This is technically not necessary but it is a privilege sensitive operation. +Meaning if anyone wants to do something that requires privilege it should be +done before the id switch. So let's move the id switch immediately before the +exec so that it's called at the last possible moment. + +Signed-off-by: Christian Brauner +--- + src/lxc/start.c | 54 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 27 insertions(+), 27 deletions(-) + +diff --git a/src/lxc/start.c b/src/lxc/start.c +index 1370d681..255638e6 100644 +--- a/src/lxc/start.c ++++ b/src/lxc/start.c +@@ -962,33 +962,6 @@ static int do_start(void *data) + goto out_warn_father; + } + +- /* The container has been setup. We can now switch to an unprivileged +- * uid/gid. +- */ +- if (handler->conf->is_execute) { +- bool have_cap_setgid; +- uid_t new_uid = handler->conf->init_uid; +- gid_t new_gid = handler->conf->init_gid; +- +- /* If we are in a new user namespace we already dropped all +- * groups when we switched to root in the new user namespace +- * further above. Only drop groups if we can, so ensure that we +- * have necessary privilege. +- */ +- #if HAVE_LIBCAP +- have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE); +- #else +- have_cap_setgid = false; +- #endif +- if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) { +- if (lxc_setgroups(0, NULL) < 0) +- goto out_warn_father; +- } +- +- if (lxc_switch_uid_gid(new_uid, new_gid) < 0) +- goto out_warn_father; +- } +- + /* The clearenv() and putenv() calls have been moved here to allow us to + * use environment variables passed to the various hooks, such as the + * start hook above. Not all of the variables like CONFIG_PATH or ROOTFS +@@ -1044,6 +1017,33 @@ static int do_start(void *data) + if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP_LIMITS)) + goto out_warn_father; + ++ /* The container has been setup. We can now switch to an unprivileged ++ * uid/gid. ++ */ ++ if (handler->conf->is_execute) { ++ bool have_cap_setgid; ++ uid_t new_uid = handler->conf->init_uid; ++ gid_t new_gid = handler->conf->init_gid; ++ ++ /* If we are in a new user namespace we already dropped all ++ * groups when we switched to root in the new user namespace ++ * further above. Only drop groups if we can, so ensure that we ++ * have necessary privilege. ++ */ ++ #if HAVE_LIBCAP ++ have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE); ++ #else ++ have_cap_setgid = false; ++ #endif ++ if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) { ++ if (lxc_setgroups(0, NULL) < 0) ++ goto out_warn_father; ++ } ++ ++ if (lxc_switch_uid_gid(new_uid, new_gid) < 0) ++ goto out_warn_father; ++ } ++ + /* After this call, we are in error because this ops should not return + * as it execs. + */ diff -Nru lxc-2.0.8/debian/patches/0014-templates-ubuntu-conditionally-move-upstart-ssh-job-.patch lxc-2.1.0/debian/patches/0014-templates-ubuntu-conditionally-move-upstart-ssh-job-.patch --- lxc-2.0.8/debian/patches/0014-templates-ubuntu-conditionally-move-upstart-ssh-job-.patch 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/patches/0014-templates-ubuntu-conditionally-move-upstart-ssh-job-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -From 2dcd5c37220469fc00dab9ee82d06b3624c140c9 Mon Sep 17 00:00:00 2001 -From: Dimitri John Ledkov -Date: Tue, 29 Aug 2017 15:11:55 +0100 -Subject: templates/ubuntu: conditionally move upstart ssh job, as it is now - optional. - -Mimic the code from the debian template. - -Signed-off-by: Dimitri John Ledkov ---- - templates/lxc-ubuntu.in | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - -diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in -index ae3a22ad..ffa4722a 100644 ---- a/templates/lxc-ubuntu.in -+++ b/templates/lxc-ubuntu.in -@@ -152,13 +152,20 @@ 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* -- 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 - -+ 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 - diff -Nru lxc-2.0.8/debian/patches/0015-storage-avoid-segfault.patch lxc-2.1.0/debian/patches/0015-storage-avoid-segfault.patch --- lxc-2.0.8/debian/patches/0015-storage-avoid-segfault.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0015-storage-avoid-segfault.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,29 @@ +From 38e133638720a784d929143b4eff8b4b8765884e Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Mon, 11 Sep 2017 03:30:00 +0200 +Subject: storage: avoid segfault + +When the "lxc.rootfs.path" property is not set and users request a container +copy we would segfault since strstr() would be called on a NULL pointer. + +Signed-off-by: Christian Brauner +--- + src/lxc/storage/storage.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/lxc/storage/storage.c b/src/lxc/storage/storage.c +index c8909123..708d070a 100644 +--- a/src/lxc/storage/storage.c ++++ b/src/lxc/storage/storage.c +@@ -337,6 +337,11 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname, + struct rsync_data data = {0}; + char cmd_output[MAXPATHLEN] = {0}; + ++ if (!src) { ++ ERROR("No rootfs specified"); ++ return NULL; ++ } ++ + /* 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. + */ diff -Nru lxc-2.0.8/debian/patches/0016-tests-Support-systemd-hybrid-cgroups.patch lxc-2.1.0/debian/patches/0016-tests-Support-systemd-hybrid-cgroups.patch --- lxc-2.0.8/debian/patches/0016-tests-Support-systemd-hybrid-cgroups.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/debian/patches/0016-tests-Support-systemd-hybrid-cgroups.patch 2017-09-18 22:32:38.000000000 +0000 @@ -0,0 +1,51 @@ +From fa8a1e7ea9decccbe884f890142dcf64a1dac9f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Fri, 12 May 2017 12:28:20 -0400 +Subject: tests: Support systemd hybrid cgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Stéphane Graber +--- + src/tests/lxc-test-apparmor-mount | 1 + + src/tests/lxc-test-unpriv | 1 + + src/tests/lxc-test-usernic.in | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/src/tests/lxc-test-apparmor-mount b/src/tests/lxc-test-apparmor-mount +index a09fd544..ddcee8a7 100755 +--- a/src/tests/lxc-test-apparmor-mount ++++ b/src/tests/lxc-test-apparmor-mount +@@ -132,6 +132,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then + done + else + for d in /sys/fs/cgroup/*; do ++ [ "$d" = "/sys/fs/cgroup/unified" ] && continue + [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children + [ ! -d $d/lxctest ] && mkdir $d/lxctest + chown -R $TUSER: $d/lxctest +diff --git a/src/tests/lxc-test-unpriv b/src/tests/lxc-test-unpriv +index 5fe09279..5f2a18f6 100755 +--- a/src/tests/lxc-test-unpriv ++++ b/src/tests/lxc-test-unpriv +@@ -148,6 +148,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then + done + else + for d in /sys/fs/cgroup/*; do ++ [ "$d" = "/sys/fs/cgroup/unified" ] && continue + [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children + [ ! -d $d/lxctest ] && mkdir $d/lxctest + chown -R $TUSER: $d/lxctest +diff --git a/src/tests/lxc-test-usernic.in b/src/tests/lxc-test-usernic.in +index f7d19a36..3e35008c 100755 +--- a/src/tests/lxc-test-usernic.in ++++ b/src/tests/lxc-test-usernic.in +@@ -105,6 +105,7 @@ elif [ -e /sys/fs/cgroup/cgmanager/sock ]; then + done + else + for d in /sys/fs/cgroup/*; do ++ [ "$d" = "/sys/fs/cgroup/unified" ] && continue + [ -f $d/cgroup.clone_children ] && echo 1 > $d/cgroup.clone_children + [ ! -d $d/lxctest ] && mkdir $d/lxctest + chown -R usernic-user: $d/lxctest diff -Nru lxc-2.0.8/debian/patches/649f249cd086e2417e3e79d38de526b8cf590c4d.patch lxc-2.1.0/debian/patches/649f249cd086e2417e3e79d38de526b8cf590c4d.patch --- lxc-2.0.8/debian/patches/649f249cd086e2417e3e79d38de526b8cf590c4d.patch 2017-09-06 16:08:49.000000000 +0000 +++ lxc-2.1.0/debian/patches/649f249cd086e2417e3e79d38de526b8cf590c4d.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -From 649f249cd086e2417e3e79d38de526b8cf590c4d Mon Sep 17 00:00:00 2001 -From: Dimitri John Ledkov -Date: Wed, 30 Aug 2017 13:45:27 +0100 -Subject: [PATCH] templates/ubuntu: support netplan in newer releases by - default -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -If netplan is present in the container, configure default networking -with neplan instead of ifupdown. Also, do not install ifupdown when -boostrapping minbase variant, unless using currently support -non-netplan releases (trusty, zenial, zesty). - -Signed-off-by: Dimitri John Ledkov -Acked-by: Christian Brauner -Acked-by: Stéphane Graber ---- - templates/lxc-ubuntu.in | 19 +++++++++++++++++-- - 1 file changed, 17 insertions(+), 2 deletions(-) - -diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in -index dee136f2a..ec41fcd2a 100644 ---- a/templates/lxc-ubuntu.in -+++ b/templates/lxc-ubuntu.in -@@ -92,7 +92,15 @@ configure_ubuntu() - password=$5 - - # configure the network using the dhcp -- cat < $rootfs/etc/network/interfaces -+ if [ -d $rootfs/etc/netplan ]; then -+ cat < $rootfs/etc/netplan/10-lxc.yaml -+network: -+ ethernets: -+ eth0: {dhcp4: true} -+ version: 2 -+EOF -+ else -+ cat < $rootfs/etc/network/interfaces - # This file describes the network interfaces available on your system - # and how to activate them. For more information, see interfaces(5). - -@@ -103,6 +111,7 @@ iface lo inet loopback - auto eth0 - iface eth0 inet dhcp - EOF -+ fi - - # set the hostname - cat < $rootfs/etc/hostname -@@ -366,7 +375,13 @@ download_ubuntu() - debootstrap_parameters="$debootstrap_parameters --variant=$variant" - fi - if [ "$variant" = 'minbase' ]; then -- packages_template="${packages_template},sudo,ifupdown,isc-dhcp-client" -+ packages_template="${packages_template},sudo" -+ # Newer releases use netplan, EOL releases not supported -+ case $release in -+ trusty|xenial|zesty) -+ packages_template="${packages_template},ifupdown,isc-dhcp-client" -+ ;; -+ esac - fi - - echo "Installing packages in template: ${packages_template}" diff -Nru lxc-2.0.8/debian/patches/db3c8336ac89a0c7a1ea29c613c58dd91220e4e3.patch lxc-2.1.0/debian/patches/db3c8336ac89a0c7a1ea29c613c58dd91220e4e3.patch --- lxc-2.0.8/debian/patches/db3c8336ac89a0c7a1ea29c613c58dd91220e4e3.patch 2017-09-06 16:09:20.000000000 +0000 +++ lxc-2.1.0/debian/patches/db3c8336ac89a0c7a1ea29c613c58dd91220e4e3.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From db3c8336ac89a0c7a1ea29c613c58dd91220e4e3 Mon Sep 17 00:00:00 2001 -From: Dimitri John Ledkov -Date: Thu, 31 Aug 2017 12:40:58 +0100 -Subject: [PATCH] Check that there is netplan binary, rather than just just a - config directory. - -Signed-off-by: Dimitri John Ledkov -Signed-off-by: Christian Brauner ---- - templates/lxc-ubuntu.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in -index ec41fcd2a..80a28fd2a 100644 ---- a/templates/lxc-ubuntu.in -+++ b/templates/lxc-ubuntu.in -@@ -92,7 +92,7 @@ configure_ubuntu() - password=$5 - - # configure the network using the dhcp -- if [ -d $rootfs/etc/netplan ]; then -+ if chroot $rootfs which netplan >/dev/null 2>&1; then - cat < $rootfs/etc/netplan/10-lxc.yaml - network: - ethernets: diff -Nru lxc-2.0.8/debian/patches/series lxc-2.1.0/debian/patches/series --- lxc-2.0.8/debian/patches/series 2017-09-06 16:09:20.000000000 +0000 +++ lxc-2.1.0/debian/patches/series 2017-09-18 22:32:38.000000000 +0000 @@ -1,16 +1,16 @@ 0001-Allocate-new-lxcbr0-subnet-at-startup-time.patch -0002-systemd-hybrid-cgroups.patch -0003-utils-fix-num-parsing-functions.patch -0004-tests-lxc_safe_-u-int-add-corner-case-tests.patch -0005-lxc-attach-allow-for-situations-without-dev-tty.patch -0006-start-don-t-call-lxc_map_ids-without-id-map.patch -0007-conf-fix-build-without-libcap.patch -0008-start-pin-rootfs-when-privileged.patch -0009-conf-ile-allow-to-clear-all-config-items.patch -0010-utils-fix-ppc64le-builds.patch -0011-cgroups-workaround-gcc-7-bug.patch -0012-cgroups-handle-hybrid-cgroup-layouts.patch -0013-Make-must_make_path-available-to-cgroup_utils.patch -0014-templates-ubuntu-conditionally-move-upstart-ssh-job-.patch -649f249cd086e2417e3e79d38de526b8cf590c4d.patch -db3c8336ac89a0c7a1ea29c613c58dd91220e4e3.patch +0002-Fix-typo.patch +0003-network-add-missing-checks-for-empty-links.patch +0004-cleanup-remove-unnecessary-zeroing.patch +0005-console-clean-tty-state-return-0-on-peer-exit.patch +0006-tools-fix-lxc-upate-config.patch +0007-criu-use-correct-check-initialization-check.patch +0008-storage-overlay-do-not-write-to-invalid-memory.patch +0009-utils-do-not-write-to-0-sized-buffer.patch +0010-overlay-fix-use-after-free.patch +0011-lxc-unshare-do-not-pass-NULL-pointer.patch +0012-lxc-user-nic-remove-double-initialization.patch +0013-execute-enable-console-standard-dev-symlinks.patch +0014-start-switch-ids-at-last-possible-instance.patch +0015-storage-avoid-segfault.patch +0016-tests-Support-systemd-hybrid-cgroups.patch diff -Nru lxc-2.0.8/debian/rules lxc-2.1.0/debian/rules --- lxc-2.0.8/debian/rules 2017-08-29 18:40:33.000000000 +0000 +++ lxc-2.1.0/debian/rules 2017-09-18 22:32:38.000000000 +0000 @@ -1,6 +1,7 @@ #!/usr/bin/make -f export DEB_BUILD_HARDENING = 1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all +include /usr/share/dpkg/default.mk DEB_DH_INSTALLINIT_ARGS = --upstart-only @@ -53,6 +54,9 @@ dh_apparmor -p lxc-common --profile-name=usr.bin.lxc-start; \ fi + # Temporary help2man + help2man -n "LXC config converter" debian/tmp/usr/bin/lxc-update-config --no-info --version-string=2.1.0 --no-discard-stderr > debian/tmp/usr/share/man/man1/lxc-update-config.1 + # cleanup .la files find debian/tmp/ -type f -name \*.la -delete @@ -94,12 +98,18 @@ dh_builddeb override_dh_installinit: +ifeq (,$(findstring $(DEB_DISTRIBUTION), trusty xenial zesty)) + # Disable upstart integration on artful+ + cp debian/lxc1.maintscript.in debian/lxc1.maintscript + rm -f debian/lxc1/etc/init/*.conf +else cp debian/lxc1/etc/init/lxc.conf debian/lxc1.upstart cp debian/lxc1/etc/init/lxc-instance.conf debian/lxc1.lxc-instance.upstart cp debian/lxc1/etc/init/lxc-net.conf debian/lxc1.lxc-net.upstart + dh_installinit --no-start --no-restart-on-upgrade --name=lxc-instance +endif dh_installinit --no-restart-on-upgrade --name=lxc dh_installinit --no-restart-on-upgrade --name=lxc-net - dh_installinit --no-start --no-restart-on-upgrade --name=lxc-instance override_dh_gencontrol: if dpkg --compare-versions "$(shell grep DISTRIB_RELEASE /etc/lsb-release | cut -d= -f2)" ge "14.10"; then \ diff -Nru lxc-2.0.8/doc/examples/lxc-complex.conf.in lxc-2.1.0/doc/examples/lxc-complex.conf.in --- lxc-2.0.8/doc/examples/lxc-complex.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-complex.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,23 +1,23 @@ # Container with network a complex network mixing macvlan, veth and # physical network devices -lxc.utsname = complex -lxc.network.type = veth -lxc.network.flags = up -lxc.network.link = br0 -lxc.network.hwaddr = 4a:49:43:49:79:bf -lxc.network.ipv4 = 10.2.3.5/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 +lxc.uts.name = complex +lxc.net.0.type = veth +lxc.net.0.flags = up +lxc.net.0.link = br0 +lxc.net.0.hwaddr = 4a:49:43:49:79:bf +lxc.net.0.ipv4.address = 10.2.3.5/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 -lxc.network.type = macvlan -lxc.network.flags = up -lxc.network.link = eth0 -lxc.network.hwaddr = 4a:49:43:49:79:bd -lxc.network.ipv4 = 10.2.3.4/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 +lxc.net.0.type = macvlan +lxc.net.0.flags = up +lxc.net.0.link = eth0 +lxc.net.0.hwaddr = 4a:49:43:49:79:bd +lxc.net.0.ipv4.address = 10.2.3.4/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 -lxc.network.type = phys -lxc.network.flags = up -lxc.network.link = dummy0 -lxc.network.hwaddr = 4a:49:43:49:79:ff -lxc.network.ipv4 = 10.2.3.6/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3297 +lxc.net.0.type = phys +lxc.net.0.flags = up +lxc.net.0.link = dummy0 +lxc.net.0.hwaddr = 4a:49:43:49:79:ff +lxc.net.0.ipv4.address = 10.2.3.6/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3297 diff -Nru lxc-2.0.8/doc/examples/lxc-empty-netns.conf.in lxc-2.1.0/doc/examples/lxc-empty-netns.conf.in --- lxc-2.0.8/doc/examples/lxc-empty-netns.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-empty-netns.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,4 +1,4 @@ # Container with new network withtout network devices -lxc.utsname = omega -lxc.network.type = empty -lxc.network.flags = up +lxc.uts.name = omega +lxc.net.0.type = empty +lxc.net.0.flags = up diff -Nru lxc-2.0.8/doc/examples/lxc-macvlan.conf.in lxc-2.1.0/doc/examples/lxc-macvlan.conf.in --- lxc-2.0.8/doc/examples/lxc-macvlan.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-macvlan.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,8 +1,8 @@ # Container with network virtualized using the macvlan device driver -lxc.utsname = alpha -lxc.network.type = macvlan -lxc.network.flags = up -lxc.network.link = eth0 -lxc.network.hwaddr = 4a:49:43:49:79:bd -lxc.network.ipv4 = 10.2.3.4/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 +lxc.uts.name = alpha +lxc.net.0.type = macvlan +lxc.net.0.flags = up +lxc.net.0.link = eth0 +lxc.net.0.hwaddr = 4a:49:43:49:79:bd +lxc.net.0.ipv4.address = 10.2.3.4/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 diff -Nru lxc-2.0.8/doc/examples/lxc-no-netns.conf.in lxc-2.1.0/doc/examples/lxc-no-netns.conf.in --- lxc-2.0.8/doc/examples/lxc-no-netns.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-no-netns.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,3 +1,3 @@ # Container with non-virtualized network -lxc.network.type = none -lxc.utsname = delta +lxc.net.0.type = none +lxc.uts.name = delta diff -Nru lxc-2.0.8/doc/examples/lxc-phys.conf.in lxc-2.1.0/doc/examples/lxc-phys.conf.in --- lxc-2.0.8/doc/examples/lxc-phys.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-phys.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,9 +1,9 @@ # Container with network virtualized using a physical network device with name # 'eth0' -lxc.utsname = gamma -lxc.network.type = phys -lxc.network.flags = up -lxc.network.link = eth0 -lxc.network.hwaddr = 4a:49:43:49:79:ff -lxc.network.ipv4 = 10.2.3.6/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3297 +lxc.uts.name = gamma +lxc.net.0.type = phys +lxc.net.0.flags = up +lxc.net.0.link = eth0 +lxc.net.0.hwaddr = 4a:49:43:49:79:ff +lxc.net.0.ipv4.address = 10.2.3.6/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3297 diff -Nru lxc-2.0.8/doc/examples/lxc-veth.conf.in lxc-2.1.0/doc/examples/lxc-veth.conf.in --- lxc-2.0.8/doc/examples/lxc-veth.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-veth.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,9 +1,9 @@ # Container with network virtualized using a pre-configured bridge named br0 and # veth pair virtual network devices -lxc.utsname = beta -lxc.network.type = veth -lxc.network.flags = up -lxc.network.link = br0 -lxc.network.hwaddr = 4a:49:43:49:79:bf -lxc.network.ipv4 = 10.2.3.5/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 +lxc.uts.name = beta +lxc.net.0.type = veth +lxc.net.0.flags = up +lxc.net.0.link = br0 +lxc.net.0.hwaddr = 4a:49:43:49:79:bf +lxc.net.0.ipv4.address = 10.2.3.5/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 diff -Nru lxc-2.0.8/doc/examples/lxc-vlan.conf.in lxc-2.1.0/doc/examples/lxc-vlan.conf.in --- lxc-2.0.8/doc/examples/lxc-vlan.conf.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/examples/lxc-vlan.conf.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,9 +1,9 @@ # Container with network virtualized using the vlan device driver -lxc.utsname = alpha -lxc.network.type = vlan -lxc.network.vlan.id = 1234 -lxc.network.flags = up -lxc.network.link = eth0 -lxc.network.hwaddr = 4a:49:43:49:79:bd -lxc.network.ipv4 = 10.2.3.4/24 -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 +lxc.uts.name = alpha +lxc.net.0.type = vlan +lxc.net.0.vlan.id = 1234 +lxc.net.0.flags = up +lxc.net.0.link = eth0 +lxc.net.0.hwaddr = 4a:49:43:49:79:bd +lxc.net.0.ipv4.address = 10.2.3.4/24 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 diff -Nru lxc-2.0.8/doc/ja/lxc.container.conf.sgml.in lxc-2.1.0/doc/ja/lxc.container.conf.sgml.in --- lxc-2.0.8/doc/ja/lxc.container.conf.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc.container.conf.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -56,82 +56,115 @@ - linux コンテナ (lxc) は、常に使用する前に作成されます。 - コンテナは、プロセスがコンテナを使う時に仮想化/隔離するシステムリソースのセットを定義することによって作成します。 - デフォルトでは、pid, sysv ipc, マウントポイントが仮想化され、隔離されます。 - 他のシステムリソースは、設定ファイルで明確に定義されない限りは、コンテナをまたいで共有されます。 - 例えば、もしネットワークが設定されていなければ、コンテナを作成する側とコンテナでネットワークを共有します。 - しかし、ネットワークが指定されれば、新しいネットワークスタックがコンテナ用に作成され、コンテナは作成元の環境のネットワークを使いません。 + LXC is the well-known and heavily tested low-level Linux container + runtime. It is in active development since 2008 and has proven itself in + critical production environments world-wide. Some of its core contributors + are the same people that helped to implement various well-known + containerization features inside the Linux kernel. + --> + LXC は良く知られた、多くのテストが行われた Linux コンテナのランタイムです。LXC は、2008 年以来アクティブに開発されており、世界中の重要な本番環境で実証されています。開発への貢献者の中には、Linux カーネル内の良く知られた様々なコンテナ機能の実装に貢献した人と同じ人もいます。 - 設定ファイルは、コンテナに割り当てられる様々なシステムリソースを定義します。 - 現時点では、utsname、ネットワーク、マウントポイント、root ファイルシステム、ユーザ名前空間、control groups がサポートされます。 + LXC's main focus is system containers. That is, containers which offer an + environment as close as possible as the one you'd get from a VM but + without the overhead that comes with running a separate kernel and + simulating all the hardware. + --> + LXC は主にシステムコンテナにフォーカスを当てています。つまり、VM で得られる環境と可能な限り近い環境を提供を提供するにも関わらず、別々のカーネルを実行したり、ハードウェアをすべてシミュレートしたりすることによるオーバーヘッドがないコンテナのことです。 + + + + + このような環境は、名前空間 (namespace)、強制アクセスコントロール、cgroup といったカーネルのセキュリティ機能の組み合わせで実現しています。 + + + + + LXC は非特権コンテナをサポートしています。非特権コンテナは、いかなる特権も持たずに実行するコンテナです。非特権コンテナの実行には、コンテナを実行しているカーネルにユーザ名前空間 (user namespace) のサポートが必要です。LXC は、ユーザ名前空間がメインラインカーネルにマージされてから、初めて非特権コンテナをサポートしたランタイムです。 + 本質的には、ユーザ名前空間は与えられた UID、GID の組を隔離します。ユーザ名前空間は、ホスト上の UID、GID のある範囲を、それとは異なるコンテナ上の UID、GID の範囲へマッピングすることで実現します。カーネルは、ホスト上では実際には UID、GID は特権を持たないにも関わらず、コンテナ内ではすべての UID、GID が期待されるように見えるように変換を行います。 + 例えば、コンテナ内では UID、GID が 0 として実行中のプロセスは、ホスト上では UID、GID が 100000 として見えるでしょう。実装と動作の詳細は、ユーザ名前空間の man ページから得られます。UID と GID のマッピングは を使って定義できます。 + + + + + Linux コンテナは、簡単な設定ファイルで定義します。設定ファイル中のオプションは key = value の形で一行で表します。'#' は、その行はコメントであることを示します。ケーパビリティや cgroup のオプションのような、リスト形式で指定するオプションでは、value がない形式で指定でき、そのように使うと、それ以前に定義した値をすべてクリアします。 + + + + - 設定ファイルのオプション一つを、key = value の形で一行で表します。 - '#' は、その行はコメントであることを示します。 - ケーパビリティや cgroup のオプションのような、リスト形式で指定するオプションでは、value がない形式で指定できます。このように使うと、それ以前に定義した値をすべてクリアします。 + LXC は、シングルドットを使って設定キーの名前空間を表します。 のような複雑な設定キーは、 や、さらに細分化された設定向けの色々なサブキーを持つことを意味します。 <!-- Configuration -->設定 + In order to ease administration of multiple related containers, it is + possible to have a container configuration file cause another file to be + loaded. For instance, network configuration can be defined in one common + file which is included by multiple containers. Then, if the containers + are moved to another host, only one file may need to be updated. + --> 複数の関係するコンテナの管理を容易にするために、コンテナの設定ファイルに別のファイルをロードすることが可能です。 例えば、ネットワークの設定を、複数のコンテナから include させるように 1 つのファイルに定義することが可能です。 その場合、コンテナが他のホストに移動すると、そのファイルだけを更新する必要があるかもしれません。 - - - - - - + + + + + + include させたいファイルを指定します。 include するファイルは、lxc 設定ファイルのフォーマットとして有効でなければいけません。 - - - + + + @@ -139,11 +172,10 @@ <!-- Architecture -->アーキテクチャ コンテナに対してアーキテクチャを設定することが可能です。 例えば、64 ビットのホスト上で 32 ビットのバイナリを動かすために 32 ビットアーキテクチャを設定することが可能です。 @@ -151,33 +183,33 @@ - - - - - - + + + + + + コンテナに設定するアーキテクチャを指定します。 - - + + - 有効なオプションは以下です。 - , - , - , - - - - + Some valid options are + , + , + , + + --> + 有効なオプションには以下のようなものがあります。 + , + , + , + + + + @@ -186,29 +218,28 @@ <!-- Hostname -->ホスト名 utsname セクションは、コンテナに設定されるホスト名を定義します。 コンテナは、システムのホスト名を変えることなく、自身のホスト名を持つ事が可能です。 このことにより、ホスト名はコンテナ専用となります。 - - - - - - + + + + + + コンテナのホスト名を指定します。 - - - + + + @@ -216,22 +247,20 @@ <!-- Halt signal -->クリーンなシャットダウン時のシグナル - lxc-stop がコンテナをクリーンにシャットダウンするためにコンテナの init プロセスに送るシグナル名か番号を指定できます。 - init システムによって、クリーンなシャットダウンを行うために使うシグナルは異なります。 - このオプションではシグナルとして kill(1) で使う形式を指定することができます。 + コンテナをクリーンにシャットダウンするためにコンテナの init プロセスに送るシグナル名か番号を指定できます。init システムによって、クリーンなシャットダウンを行うために使うシグナルは異なります。このオプションではシグナルとして kill(1) で使う形式を指定できます。 例えば SIGKILL, SIGRTMIN+14, SIGRTMAX-10 のような形式、もしくは数字を指定します。デフォルトのシグナルは SIGPWR です。 - + @@ -249,19 +278,18 @@ リブート時のシグナル <!-- Reboot signal --> - lxc-stop がコンテナをリブートするために送るシグナル名か番号を指定できます。 - このオプションではシグナルとして kill(1) で使う形式を指定することができます。 + コンテナをリブートするために送るシグナル名か番号を指定できます。このオプションではシグナルとして kill(1) で使う形式を指定できます。 例えば SIGKILL, SIGRTMIN+14, SIGRTMAX-10 のような形式、もしくは数字を指定します。デフォルトのシグナルは SIGINT です。 - + @@ -279,19 +307,18 @@ <!-- Stop signal -->強制停止時のシグナル - lxc-stop がコンテナを強制的にシャットダウンするために送るシグナル名か番号を指定することができます。 - このオプションではシグナルとして kill(1) で使う形式を指定することができます。 + コンテナを強制的にシャットダウンするために送るシグナル名か番号を指定できます。このオプションではシグナルとして kill(1) で使う形式を指定できます。 例えば SIGKILL, SIGRTMIN+14, SIGRTMAX-10 のような形式、もしくは数字を指定します。デフォルトのシグナルは SIGKILL です。 - + @@ -322,7 +349,7 @@ - + @@ -340,44 +367,41 @@ <!-- Init ID -->Init が使う ID - lxc-execute が実行するコンテナの init と、その後に起動するコマンドが使用する UID/GID を設定します。 - - - このオプションは lxc-execute がユーザ名前空間内で起動するときのみ使われます。 + Sets the UID/GID to use for the init system, and subsequent commands. + Note that using a non-root uid when booting a system container will + likely not work due to missing privileges. Setting the UID/GID is mostly + useful when running application container. - - デフォルト値は UID(0), GID(0) です。 + --> + init と後続のコマンドが使う UID/GID を設定します。システムコンテナを起動するのに非 root な UID を使うと、特権がないために動作しないでしょう。UID/GID の指定は、通常はアプリケーションコンテナの動作の際に役に立ちます。 + + デフォルト値: UID(0)、GID(0) - + - ユーザ名前空間内で init が使う UID です。 + UID to use for init. + --> + init が使う UID です。 - + - ユーザ名前空間内で init が使う GID です。 + init が使う GID です。 @@ -414,13 +438,13 @@ <!-- Network -->ネットワーク ネットワークセクションは、コンテナ内でどのようにネットワークを仮想化するかを定義します。 ネットワークの仮想化はレイヤー 2 で作動します。 @@ -431,7 +455,7 @@ - + @@ -442,56 +466,59 @@ - - - - - - - - コンテナがどの種類のネットワーク仮想化を使うかを指定します。 - 一つのネットワークの設定ごとに フィールドを指定します。 - このように、一つのコンテナに複数のネットワークインターフェースを割り当てることができるだけでなく、同じコンテナに対して複数のネットワーク仮想化の種類を指定することが出来ます。 - 仮想化の種類は以下の値を取る事が出来ます: - - - - + コンテナがどの種類のネットワーク仮想化を使うかを指定します。すべての キーに、追加のインデックス を使うと、複数のネットワークを指定できます。例えば、 は、同じタイプの異なるネットワークを 2 つ指定します。 + 同じインデックスを指定したキーはすべて同じネットワークの指定になります。例えば、 と同じネットワークの設定になります。 + 現時点では、以下のネットワーク仮想化のタイプが使えます: + + + + ホストのネットワーク名前空間を共有します。 これにより、ホストのネットワークデバイスをコンテナ内で使うことが可能になります。 もしコンテナもホストも init として upstart を使っている場合、(例えば) コンテナ内で 'halt' を実行すると、ホストがシャットダウンしてしまうことにもなります。 - + - + - ループバックインターフェースだけを作成します。 - + ループバックインターフェースだけを作成します。 + - 一方がコンテナに、もう一方が オプションで指定されたブリッジに接続されるペアの仮想イーサネットデバイスを作成します。 + 一方がコンテナに、もう一方が オプションで指定されたブリッジに接続されるペアの仮想イーサネットデバイスを作成します。 もし、ブリッジが指定されていない場合、veth ペアデバイスは作成されますが、ブリッジには接続されません。 ブリッジはコンテナが開始する前にシステムで事前に設定しておく必要があります。 lxc はコンテナ外の設定を扱うことはありません。 デフォルトでは、lxc がコンテナの外部に属するネットワークデバイスに対する名前を決定します。 - しかし、もしこの名前を自分で指定したい場合、 オプションを使って名前を設定し、lxc に対して指定をすることができます (非特権コンテナの場合をのぞきます。セキュリティ上の理由からこのオプションは無視されます)。 + しかし、もしこの名前を自分で指定したい場合、 オプションを使って名前を設定し、lxc に対して指定をすることができます (非特権コンテナの場合をのぞきます。セキュリティ上の理由からこのオプションは無視されます)。 + + + + + vlan インターフェースは で指定されたインターフェースとリンクし、コンテナに割り当てられます。 + vlan の指定は オプションで指定します。 - + - vlan インターフェースは で指定されたインターフェースとリンクし、コンテナに割り当てられます。 - vlan の指定は オプションで指定します。 - - - - - macvlan インターフェースは により指定されるインターフェースとリンクし、コンテナに割り当てられます。 - でモードを指定すると、その macvlan の指定を、同じ上位デバイスで異なる macvlan の間の通信をする時に使います。 + macvlan インターフェースは により指定されるインターフェースとリンクし、コンテナに割り当てられます。 + でモードを指定すると、その macvlan の指定を、同じ上位デバイスで異なる macvlan の間の通信をする時に使います。 指定できるモードは のいずれかです。 モードの場合、デバイスは同じ上位デバイスの他のデバイスとの通信を行いません (デフォルト)。 新しい仮想イーサネットポート集約モード (Virtual Ethernet Port Aggregator (VEPA)) である モードの場合、隣接したポートが、ソースとデスティネーションの両方が macvlan ポートに対してローカルであるフレームを全て返すと仮定します。 @@ -576,286 +603,250 @@ しかし、reflective relay からフレームが返ってきたときは、再度それを配送することはしません。 全ての MAC アドレスを知っているので、ブリッジモジュールのように、macvlan ブリッジモードは学習や STP の必要はありません。 モードの場合、物理インターフェースで受け取った全てのフレームは macvlan インターフェースに転送されます。 モードの場合、ひとつの macvlan インターフェースだけが、ひとつの物理インターフェースに対して設定できます。 - + - + - で指定された、すでに存在しているインターフェースがコンテナに割り当てられます。 - - - - - - - - - - + an already existing interface + specified by the is + assigned to the container. + --> + で指定された、すでに存在しているインターフェースがコンテナに割り当てられます。 + + + + + + + + + + ネットワークに対して行うアクションを指定します。 - + - + インターフェースを起動させます。 - - - - - - - - - - + + + + + + + + + + 実際のネットワークトラフィックに使うインターフェースを指定します。 - - - - - - - - - - + + + + + + + + + + インターフェースに対する MTU を指定します。 - - - - - - - - - - - - インターフェース名は動的に割り当てられます。 - しかし、もしコンテナが使用する設定ファイルが一般的な名前を使用するために、他の特定の名前が必要であれば (例えば eth0 など)、コンテナ内のインターフェースは、このオプションで指定した名前にリネームされます。 - - - - - - - - - - - - 仮想インターフェースの MAC アドレスは、デフォルトでは動的に割り当てられます。 - しかし、MAC アドレスの衝突や、リンクローカルIPv6 アドレスを常に同じにした場合などは、このオプションが必要です。 - アドレス中の "x" という文字は、ランダムな値に置き換えられます。 - これによりテンプレートに hwaddr を設定することが可能になります。 - - - - - - - - - - - - 仮想インターフェースに割り当てる ipv4 アドレスを指定します。 - 複数行により複数の ipv4 アドレスを指定します。 - このアドレスは x.y.z.t/m というフォーマットで指定します。 - 例えば、192.168.1.123/24。ブロードキャストアドレスも同じ行の ipv4 アドレスのすぐ後で指定しなくてはなりません。 - - - - - - - - - - - - コンテナでゲートウェイとして使う IPv4 アドレスを指定します。 - アドレスは x.y.z.t というフォーマットです。 - 例えば、192.168.1.123。 - - という特別な値を記述する事も可能です。 - これは ( で指定した) ブリッジインターフェースの最初のアドレスを使用し、それをゲートウェイに使うという意味になります。 - はネットワークタイプとして を指定している時だけ有効となります。 - - - - - - - - - - - - - 仮想インターフェースに割り当てる ipv6 アドレスを指定します。 - 複数行により複数の ipv6 アドレスを指定します。 - このアドレスは x::y/m というフォーマットで指定します。 - 例えば、2003:db8:1:0:214:1234:fe0b:3596/64。 - - - - - - - - - - - - コンテナでゲートウェイとして使う IPv6 アドレスを指定します。 - アドレスは x::y というフォーマットです。例えば、2003:db8:1:0::1。 - - という特別な値を記述する事も可能です。 - これは ( で指定した) ブリッジインターフェースの最初のアドレスを使用し、それをゲートウェイに使うという意味になります。 - はネットワークタイプとして を指定している時だけ有効となります。 - - - - - - - - - - - - ホスト側から使われる、ネットワークの作成と設定が済んだ後に実行するスクリプトを指定します。 - 以下の引数がスクリプトに渡されます: コンテナ名、設定セクション名(net)。 - その後の引数はスクリプトのフックで使われる設定セクションに依存します。 - 以下がネットワークシステムによって使われます: 実行コンテキスト (up)、ネットワークのタイプ (empty/veth/macvlan/phys) + + + + + + + + + + + + インターフェース名は動的に割り当てられます。しかし、もしコンテナが使用する設定ファイルが一般的な名前を使用するために、他の特定の名前が必要であれば (例えば eth0 など)、コンテナ内のインターフェースは、このオプションで指定した名前にリネームされます。 + + + + + + + + + + + + 仮想インターフェースの MAC アドレスは、デフォルトでは動的に割り当てられます。しかし、MAC アドレスの衝突や、リンクローカルIPv6 アドレスを常に同じにした場合などは、このオプションが必要です。アドレス中の "x" という文字は、ランダムな値に置き換えられます。これによりテンプレートに hwaddr を設定することが可能になります。 + + + + + + + + + + + + 仮想インターフェースに割り当てる ipv4 アドレスを指定します。複数行により複数の ipv4 アドレスを指定します。このアドレスは x.y.z.t/m というフォーマットで指定します。例) 192.168.1.123/24 + + + + + + + + + + + + コンテナでゲートウェイとして使う IPv4 アドレスを指定します。アドレスは x.y.z.t というフォーマットです。例) 192.168.1.123 + という特別な値を指定できます。これは ( で指定した) ブリッジインターフェースの最初のアドレスを使用し、それをゲートウェイに使うという意味になります。 はネットワークタイプとして を指定している時だけ有効となります。 + + + + + + + + + + + + + 仮想インターフェースに割り当てる ipv6 アドレスを指定します。複数行により複数の ipv6 アドレスを指定します。このアドレスは x::y/m というフォーマットで指定します。例) 2003:db8:1:0:214:1234:fe0b:3596/64 + + + + + + + + + + + + コンテナでゲートウェイとして使う IPv6 アドレスを指定します。アドレスは x::y というフォーマットです。例) 2003:db8:1:0::1 + という特別な値を記述する事も可能です。これは ( で指定した) ブリッジインターフェースの最初のアドレスを使用し、それをゲートウェイに使うという意味になります。 はネットワークタイプとして を指定している時だけ有効となります。 + + + + + + + + + + + + ホスト側から使われる、ネットワークの作成と設定が済んだ後に実行するスクリプトを指定します。以下の引数がスクリプトに渡されます: コンテナ名、設定セクション名(net)。 + その後の引数はスクリプトのフックで使われる設定セクションに依存します。以下がネットワークシステムによって使われます: 実行コンテキスト (up)、ネットワークのタイプ (empty/veth/macvlan/phys) ネットワークのタイプによっては、更に別の引数が渡されるかもしれません: veth/macvlan/phys の場合 (ホスト側の) デバイス名 - + - スクリプトからの標準出力は debug レベルでロギングされます。 - 標準エラー出力はロギングされません。 - しかし、フックの標準エラー出力を標準出力にリダイレクトすることにより保存することは可能です。 - - - - - - - - - - - - ホスト側から使われる、ネットワークを破壊する前に実行するスクリプトを指定します。 - 以下の引数がスクリプトに渡されます: コンテナ名、設定セクション名(net)。 - その後の引数はスクリプトのフックで使われる設定セクションに依存します。 - 以下がネットワークシステムによって使われます: 実行コンテキスト (up)、ネットワークのタイプ (empty/veth/macvlan/phys)。 + Standard output from the script is logged at debug level. + Standard error is not logged, but can be captured by the + hook redirecting its standard error to standard output. + --> + スクリプトからの標準出力は debug レベルでロギングされます。標準エラー出力はロギングされません。しかし、フックの標準エラー出力を標準出力にリダイレクトすることにより保存することは可能です。 + + + + + + + + + + + + ホスト側から使われる、ネットワークを破壊する前に実行するスクリプトを指定します。以下の引数がスクリプトに渡されます: コンテナ名、設定セクション名(net)。 + その後の引数はスクリプトのフックで使われる設定セクションに依存します。以下がネットワークシステムによって使われます: 実行コンテキスト (up)、ネットワークのタイプ (empty/veth/macvlan/phys)。 ネットワークのタイプによっては、更に別の引数が渡されるかもしれません: veth/macvlan/phys。そして最後に (ホスト側の) デバイス名が渡されます。 - + - スクリプトからの標準出力は debug レベルでロギングされます。 - 標準エラー出力はロギングされません。 - しかし、フックの標準エラー出力を標準出力にリダイレクトすることにより保存することは可能です。 - - - + Standard output from the script is logged at debug level. + Standard error is not logged, but can be captured by the + hook redirecting its standard error to standard output. + --> + スクリプトからの標準出力は debug レベルでロギングされます。標準エラー出力はロギングされません。しかし、フックの標準エラー出力を標準出力にリダイレクトすることにより保存することは可能です。 + + + @@ -864,29 +855,29 @@ <!-- New pseudo tty instance (devpts) -->新しい擬似端末のインスタンス (devpts) さらに厳しい隔離のために、コンテナは自身のプライベートな pseudo tty (擬似端末) を持つことが可能です。 - - - - - - + + + + + + もし設定された場合、コンテナは新しい pseudo tty インスタンスを持ち、それを自身のプライベートとします。 この値は pts インスタンスに許可される pseudo tty の最大数を指定します (この制限はまだ実装されていません)。 - - - + + + @@ -894,45 +885,49 @@ <!-- Container system console -->コンテナのシステムコンソール コンテナでルートファイルシステムを持つように設定されており、inittab ファイルでコンソールの使用が設定されている場合、このコンソールの出力がどこになされるのかを指定したいと思うでしょう。 - - - - - - + + + + + + コンソール出力を書き込むファイルのパスを指定します。 - - - - - - - - - - + + + + + + + + + + コンソールを割り当てるデバイスのパスを指定します。'none' というキーワードは、単純にコンソールを無効にします。 - この設定は、アプリケーションが書き込む事ができるコンソールデバイスファイルが rootfs に存在する場合、メッセージがホスト側に出力されるので危険です。 - - - + 'none' を指定し、コンテナ内のコンソールに対するデバイスノードを /dev/console に作成するか、もしくはホストの /dev/console をコンテナ内の /dev/console に bind mount する場合、そのコンテナはホストの /dev/console へ直接アクセスを行うことに注意が必要です。 + そのコンテナがデバイスへの書き込み権を持っている場合は危険ですので、注意してこの指定を使用する必要があります。 + + + @@ -940,14 +935,14 @@ <!-- Console through the ttys -->tty を通したコンソール このオプションはコンテナが root ファイルシステムを持つように設定されており、inittab ファイルで tty 上に getty の起動が設定されている場合に役に立ちます。 このオプションはコンテナで利用できる tty の数を指定します。 @@ -955,20 +950,20 @@ さもなければ、超過した分の getty セッションはコンソールか /var/log/messages にうっとうしいメッセージを生死を表示しながら、永久に生死を繰り返すでしょう。 - - - - - - + + + + + + コンテナに作成出来る tty の数を指定します。 - - - + + + @@ -977,15 +972,15 @@ LXC のコンソールはホストによって作られ、コンテナ内で要求されたデバイスに bind マウントされた Unix98 PTY 経由で提供されます。 デフォルトでは /dev/console/dev/ttyN に bind マウントされます。 @@ -996,20 +991,23 @@ シンボリックリンクを消去したり置き換えたりすることは可能ですから、パッケージのアップグレードは成功します。 - - - - - - + + + + + + + Specify a directory under /dev + under which to create the container console devices. Note that LXC + will move any bind-mounts or device nodes for /dev/console into + this directory. + --> コンテナのコンソールデバイスを作成するための /dev 以下のディレクトリを指定します。 - - - + LXC は /dev/console に対する bind mount や /dev/console デバイスノードをこのディレクトリ以下に移動することに注意が必要です。 + + + @@ -1017,13 +1015,13 @@ <!-- /dev directory -->/dev ディレクトリ コンテナの起動時に LXC が /dev をマウントして最小限の /dev を作成するのを止めるには、この値を 0 に設定してください。 - - - - - - - - <!-- Enable kmsg symlink -->kmsg のシンボリックリンクの有効化 - - - /dev/console へのシンボリックリンクとして /dev/kmsg を作成することを有効にします。デフォルトは 0 です。 - - - - - - - - - - /dev/kmsg へのシンボリックリンクを有効にするには 1 を設定してください。 - - - + + + @@ -1083,93 +1056,92 @@ <!-- Mount points -->マウントポイント マウントポイントセクションは、マウントするための区別された場所を指定します。 これらのマウントポイントは、コンテナだけに見え、コンテナ外で実行されるプロセスから見えることはありません。 例えば、/etc や /var や /home をマウントするときに役に立つでしょう。 - - 注意: 通常 LXC は、マウント対象と相対パス指定のバインドマウントを、適切にコンテナルート以下に閉じ込めます。 - これは、ホストのディレクトリやファイルに対して重ね合わせを行うようなマウントによる攻撃を防ぎます。(絶対パス指定のマウントソース中の各パスがシンボリックリンクである場合は無視されます。) - しかし、もしコンテナの設定が最初に、/home/joe のようなコンテナユーザのコントロール配下にあるディレクトリを、コンテナ中のある path にマウントし、その後 path 以下でマウントが行われるような場合、コンテナユーザがタイミングを見計らって自身のホームディレクトリ以下でシンボリックリンクを操作するような TOCTTOU 攻撃が成立する可能性があります。 - - - - - - - - - + 注意: 通常 LXC は、マウント対象と相対パス指定のバインドマウントを、適切にコンテナルート以下に閉じ込めます。 + これは、ホストのディレクトリやファイルに対して重ね合わせを行うようなマウントによる攻撃を防ぎます。(絶対パス指定のマウントソース中の各パスがシンボリックリンクである場合は無視されます。) + しかし、もしコンテナの設定が最初に、/home/joe のようなコンテナユーザのコントロール配下にあるディレクトリを、コンテナ中のある path にマウントし、その後 path 以下でマウントが行われるような場合、コンテナユーザがタイミングを見計らって自身のホームディレクトリ以下でシンボリックリンクを操作するような TOCTTOU 攻撃が成立する可能性があります。 + + + + + + + + + マウント情報の書かれた fstab フォーマットで書かれたファイルの場所を指定します。 マウントする場所は相対バスで書くことができます。そして、ほとんどの場合にコンテナの root からの相対パスとなるはずです。例えば、以下のように書きます。 - - -proc proc proc nodev,noexec,nosuid 0 0 - - + + + proc proc proc nodev,noexec,nosuid 0 0 + + - この例は、root ファイルシステムがどこにあっても、コンテナの /proc 以下に proc ファイルシステムをマウントします。 - これは、ブロックデバイスがバックエンドのファイルシステムだけでなく、コンテナのクローンにも柔軟に対応できます。 - - - + この例は、root ファイルシステムがどこにあっても、コンテナの /proc 以下に proc ファイルシステムをマウントします。これは、ブロックデバイスがバックエンドのファイルシステムだけでなく、コンテナのクローンにも柔軟に対応できます。 + + + ファイルシステムがイメージファイルやブロックデバイスからマウントされている場合、3 つ目のフィールド (fs_vfstype) は - mount + mount 8 のように auto を指定することはできず、明確に指定しなければいけません。 - - - - - - - - - - + + + + + + + + + + fstab フォーマットの一行と同じフォーマットのマウントポイントの指定をします。 @@ -1182,274 +1154,274 @@ fstab フォーマットに加えて、LXC ではマウントに対して独自の 2 つのオプションが使えます。 は、マウントが失敗しても失敗を返さずに無視します。 は、マウントポイントをマウントする際にディレクトリもしくはファイルを作成します。 - - - - - - - - - - - 標準のカーネルファイルシステムで自動的にマウントするものを指定します。 これは劇的に設定を容易にする可能性があります。 - - - + + + (or ): /proc を読み書き可能でマウントします。 ただし、/proc/sys/proc/sysrq-trigger は、セキュリティとコンテナの隔離の目的でリードオンリーで再マウントされます。 - - + + - : + : /proc を読み書き可能でマウントします。 - - - - - (or ): - /sys/devices/virtual/net のみ書き込み可能で、その他の /sys はリードオンリーでマウントします。 - + --> + + (or ): + /sys/devices/virtual/net のみ書き込み可能で、その他の /sys はリードオンリーでマウントします。 + - + : /sys を、セキュリティとコンテナの隔離の目的でリードオンリーでマウントします。 - - + + - : + : /sys を読み書き可能でマウントします。 - - + + - : + : /sys/fs/cgroup を tmpfs でマウントし、そのコンテナの追加が行われた全ての階層構造に対するディレクトリを作製し、その cgroup の名前でその中にサブディレクトリを作製し、そのコンテナ自身の cgroup をそのディレクトリにバインドマウントします。 コンテナは自身の cgroup ディレクトリに書き込みが可能ですが、親ディレクトリはリードオンリーで再マウントされているため書き込めません。 - - + + - : + : と同様にマウントされますが、全てリードオンリーでマウントされます。 - - + + - : + : と同様にマウントされますが、全て読み書き可能でマウントされます。 コンテナ自身の cgroup に至るまでのパスも書き込み可能になることに注意が必要ですが、cgroup ファイルシステムにはならず、 /sys/fs/cgroup の tmpfs の一部分になるでしょう。 - - - + + + - (マウントオプションなしの場合): + (マウントオプションなしの場合): コンテナが CAP_SYS_ADMIN ケーパビリティを保持している場合、 となります。保持していない場合、 となります。 - - - + + + - : + : /sys/fs/cgroup を tmpfs でマウントし、そのコンテナの追加が行われた全ての階層構造に対するディレクトリを作製し、ホストからコンテナまでの階層構造を全てバインドマウントし、コンテナ自身の cgroup を除いてリードオンリーにします。 と比べると、コンテナ自身の cgroup に至るまでの全てのパスが tmpfs の下層のシンプルなディレクトリとなり、コンテナ自身の cgroup の外ではリードオンリーになりますが、/sys/fs/cgroup/$hierarchy はホストの全ての cgroup 階層構造を含みます。 これにより、コンテナにはかなりの情報が漏洩します。 - - + + - : + : と同様にマウントされますが、全てリードオンリーでマウントされます。 - - + + - : - と同様にマウントされますが、全て読み書き可能でマウントされます。 + : + と同様にマウントされますが、全て読み書き可能でマウントされます。 この場合、コンテナは自身の cgroup から脱出する可能性があることに注意してください (コンテナが CAP_SYS_ADMIN を持ち、自身で cgroup ファイルシステムをマウント可能なら、いずれにせよそのようにするかもしれないことにも注意してください)。 - - - + + + - (マウントオプションなしの場合): + (マウントオプションなしの場合): コンテナが CAP_SYS_ADMIN ケーパビリティを保持している場合、 となります。保持していない場合、 となります。 - - - - - - cgroup 名前空間が有効の場合、 の自動マウントの指定はどれも無視されます。これは、コンテナが自身でファイルシステムをマウントするため、自動マウントがコンテナの init を混乱させる可能性があるためです。 - - - + cgroup 名前空間が有効の場合、 の自動マウントの指定はどれも無視されます。これは、コンテナが自身でファイルシステムをマウントするため、自動マウントがコンテナの init を混乱させる可能性があるためです。 + + + cgroup ファイルシステムの自動マウントが有効の場合、/sys/fs/cgroup 以下の tmpfs は常に読み書き可能でマウントされることに注意が必要です (しかし の場合は、個々の階層の /sys/fs/cgroup/$hierarchy は読み込み専用となるでしょう)。これは Ubuntu の - mountall + mountall 8 コマンドの特異な動きに対処するためのものです。特異な動きとは、/sys/fs/cgroup が読み込み専用でマウントされた状態で、コンテナが CAP_SYS_ADMIN を持たない場合、/sys/fs/cgroup を読み書き可能で再マウントしようとしてできないため、コンテナのブート時にユーザからの入力を待ってしまうというものです。 - - + + 例: - - - lxc.mount.auto = proc sys cgroup - lxc.mount.auto = proc:rw sys:rw cgroup-full:rw - - - + + + lxc.mount.auto = proc sys cgroup + lxc.mount.auto = proc:rw sys:rw cgroup-full:rw + + + @@ -1458,29 +1430,29 @@ <!-- Root file system -->ルートファイルシステム コンテナのルートファイルシステムは、ホストのルートファイルシステムと異なるようにすることも可能です。 - - - - - - - コンテナのルートファイルシステムを指定します。 この値はイメージファイル、ディレクトリ、ブロックデバイスのどれかを取ることができます。 もし指定されない場合、コンテナはホストとルートファイルシステムを共有します。 - - + + - root ファイルシステムの変更の前に、 を再帰的にどこにバインドするのかを指定します。これは - - pivot_root - 8 - - システムコールが確実に成功する事を保証します。 - どんなディレクトリでも良く、デフォルトでも通常は動くはずです。 - - - - - - - - - - + + + + + + + + + + - rootfs をマウントするときに追加したいマウントオプション。 - - - + root ファイルシステムの変更の前に、 を再帰的にどこにバインドするのかを指定します。これは + + pivot_root + 8 + + システムコールが確実に成功する事を保証します。 + どんなディレクトリでも良く、デフォルトでも通常は動くはずです。 + + + - + - - 使用するバックエンドのタイプを、例えば 'dir' や 'zfs' のように指定します。 - コンテナ起動時に LXC が推測できますが、時間がかかります。これを指定すると、余分な処理を避けられます。 + + rootfs をマウントするときに追加したいマウントオプション。 @@ -1573,40 +1527,40 @@ Control group CONTROL GROUP セクションは、(lxc とは) 別のサブシステムの設定を含みます。 lxc は、このサブシステム名の正しさはチェックしません。 実行時のエラーを検出するのに不便ですが、別の将来のサブシステムをサポート出来るという有利な点もあります。 - - - - - - - 設定する control group の値を指定します。 サブシステム名は、control group のそのままの名前です。 許される名前や値の書式は LXC が指示することはなく、コンテナが実行された時に実行されている Linux カーネルの機能に依存します。 例えば - - - + + + @@ -1614,63 +1568,108 @@ <!-- Capabilities -->ケーパビリティ コンテナが root 権限で実行されていても、コンテナ内ではケーパビリティ (capabilities) を削除する事は可能です。 - - - - - - - - コンテナ内で削除するケーパビリティ (capability) を指定します。 - 一行でスペース区切りで複数のケーパビリティを指定することも可能です。 - 指定は、"CAP_" というプレフィックスなしで、小文字でケーパビリティを指定します。 - 例えば、CAP_SYS_MODULE というケーパビリティは sys_module と指定する必要があります。 - 詳しくは以下を参照してください。 - - capabilities - 7 - - この設定を、値を指定しない状態で使った場合、それ以前に指定された削除対象のケーパビリティの指定をすべてクリアします (lxc.cap.drop に何も指定しない状態になります)。 - - - - - - - - - - + コンテナ内で削除するケーパビリティ (capability) を指定します。 + 一行でスペース区切りで複数のケーパビリティを指定することも可能です。 + 指定は、"CAP_" というプレフィックスなしで、小文字でケーパビリティを指定します。 + 例えば、CAP_SYS_MODULE というケーパビリティは sys_module と指定する必要があります。 + 詳しくは以下を参照してください。 + + capabilities + 7 + + この設定を、値を指定しない状態で使った場合、それ以前に指定された削除対象のケーパビリティの指定をすべてクリアします (lxc.cap.drop に何も指定しない状態になります)。 + + + + + + + + + + + コンテナ内で維持するケーパビリティを指定します。指定した以外の全てのケーパビリティはドロップされます。 + 特別な値 "none" が指定されている時点で、lxc はこの時点で保持することになっている全てのケーパビリティをクリアします。"none" を単独で使用するとすべてのケーパビリティを削除できます。 + + + + + + + + リソース制限 <!-- Resource limits --> + + + コンテナに対するソフトもしくはハードリミットを変更できます。非特権コンテナでは、制限を下げることしかできません。明示的に指定されていないリソースは継承されます。 + + + + + + + + + - コンテナ内で維持するケーパビリティを指定します。指定した以外の全てのケーパビリティはドロップされます。 - 特別な値 "none" が指定されている時点で、lxc はこの時点で保持することになっている全てのケーパビリティをクリアします。"none" を単独で使用するとすべてのケーパビリティを削除できます。 - - - + 設定したいリソースと制限値を指定します。制限値はコロンで区切られた 2 つの値で指定します。値は数値もしくは 'unlimited' で指定します。ソフトもハードも同じ値を指定する場合は単一の値を指定できます。指定できる名前は、"RLIMIT_" 接頭辞がなく小文字で書かれた、"RLIMIT_" リソース名です。例えば、RLIMIT_NOFILE は "nofile" と指定します。詳しくは + + setrlimit + 2 + を参照してください。 + 値を指定せずに使用した場合、lxc はこの指定以前に設定されたリソース制限をクリアします。明示的に制限が設定されていないリソースについては、コンテナを起動したプロセスから継承します。 + + + @@ -1678,71 +1677,71 @@ <!-- Apparmor profile -->Apparmor プロファイル lxc が apparmor サポートでコンパイルされ、インストールされている場合で、ホストで apparmor が有効な場合、コンテナが従って動くべき apparmor プロファイルは、コンテナの設定で指定することが可能です。 デフォルトは、ホストのカーネルで cgroup 名前空間が使える場合は lxc-container-default-cgnsです。使えない場合は lxc-container-default です。 - - - - - - - コンテナが従うべき apparmor プロファイルを指定します。 コンテナが apparmor による制限を受けないように設定するには、以下のように設定します。 - - lxc.aa_profile = unconfined + + lxc.apparmor.profile = unconfined - - もし apparmor プロファイルが変更されないままでなくてはならない場合 (ネストしたコンテナである場合や、すでに confined されている場合) は以下のように設定します。 - - lxc.aa_profile = unchanged - - - - - - - - - + もし apparmor プロファイルが変更されないままでなくてはならない場合 (ネストしたコンテナである場合や、すでに confined されている場合) は以下のように設定します。 + + lxc.apparmor.profile = unchanged + + + + + + + + + apparmor プロファイルはパス名ベースですので、多数のファイルの制限を行う際、執念深い攻撃者に対して効果的であるためにはマウントの制限が必要です。 しかし、これらのマウントの制限は upstream のカーネルではまだ実装されていません。マウントの制限なしでも、apparmor プロファイルによって予想外のダメージに対する保護が可能です。 - - + + このフラグが 0 の場合 (デフォルト)、カーネルが apparmor のマウント機能をサポートしていない場合にコンテナが起動しません。これはカーネルを更新した後に機能が退行したことが検出できるようにするためです。 不完全な apparmor の保護の下でコンテナを起動するためには、このフラグを 1 に設定してください。 - - - + + + @@ -1750,34 +1749,34 @@ <!-- SELinux context -->SELinux コンテキスト lxc が SELinux サポートでコンパイルされ、インストールされている場合で、ホストで SELinux が有効な場合、コンテナが従って動くべき SELinux コンテキストは、コンテナの設定で指定することが可能です。 デフォルトは unconfined_t であり、これは lxc がコンテキストを変えないという意味になります。 ポリシーの例と追加の情報は @DATADIR@/lxc/selinux/lxc.te ファイルを参照してください。 - - - - - - + + + + + + コンテナが従うべき SELinux コンテキストを指定するか、unconfined_t を指定します。例えば以下のように設定します。 - - lxc.se_context = system_u:system_r:lxc_t:s0:c22 - - + + lxc.selinux.context = system_u:system_r:lxc_t:s0:c22 + + @@ -1786,10 +1785,10 @@ コンテナは、起動時に seccomp プロファイルをロードすることで、利用可能なシステムコールを減らして起動することが可能です。 seccomp の設定ファイルは、1 行目がバージョン番号、2 行目がポリシーのタイプで始まる必要があり、その後に設定を書きます。 @@ -1797,10 +1796,10 @@ 現時点では、バージョン番号は 1 と 2 をサポートしています。バージョン 1 では、ポリシーはシンプルなホワイトリストですので、2 行目は "whitelist" でなければなりません。 そして残りの行には 1 行に 1 つずつ、システムコール番号を書きます。各行のシステムコール番号がホワイトリスト化され、リストにない番号は、そのコンテナではブラックリストに入ります。 @@ -1822,26 +1821,64 @@ --> 以下にブラックリストのポリシーの例を示します。これは mknod 以外の全てのシステムコールが許可され、mknod が呼ばれると、何もせずに単に 0(成功) を返します。 - -2 -blacklist -mknod errno 0 - - - - - - - - + + + 2 + blacklist + mknod errno 0 + + + + + + + + + コンテナがスタートする前にロードする seccomp の設定を含むファイルを指定します。 - - - + + + + + + + + PR_SET_NO_NEW_PRIVS + + + PR_SET_NO_NEW_PRIVS を付与すると、対象の execve() は、execve() の呼び出しなしでは実行できなかったことに対する特権を許可しなくなります (例えば、set-user-ID、set-group-ID 許可ビットや、ファイルケーパビリティが動作しなくなります)。 + 一度設定されると、このビットは解除できません。このビットの設定は fork() や clone() で生成される子プロセスにも継承され、execve() の前後で保持されます。 + PR_SET_NO_NEW_PRIVS は、コンテナに適用しようとする AppArmor プロファイルもしくは SELinux コンテキストへの変更がなされたあとに適用されます。 + + + + + + + + + + コンテナに対して PR_SET_NO_NEW_PRIVS ビットを設定するかどうかを指定します。1 に設定すると有効になります。 + + + @@ -1850,13 +1887,13 @@ コンテナは、ユーザとグループの id のマッピングを持った専用のユーザ名前空間で起動することが可能です。 たとえば、コンテナ内のユーザ id 0 を、ホストのユーザ id 200000 にマッピングすることが可能です。 @@ -1865,28 +1902,28 @@ 例えば、コンテナ内のユーザとグループの id 0 から 20,000 を 200,000 から 220,000 にマッピングします。 - - - - - - - 4 つの値を記述する必要があります。 最初の文字は 'u' か 'g' のどちらかで、ユーザかグループの ID のどちらをマッピングするかを指定します。 次はコンテナのユーザ名前空間内に現れる最初のユーザ ID です。 その次は、そのユーザ ID のホスト上での値です。 最後は、ID のマッピングをいくつ連続して行うかの数を指定します。 - - - + + + @@ -1895,49 +1932,49 @@ コンテナのフックは、コンテナの存続期間の色々な場面で実行することのできるプログラムやスクリプトです。 コンテナのフックが実行されるとき、情報がコマンドライン引数と環境変数の両方を通して渡されます。引数は: - - コンテナ名 - セクション (常に 'lxc') - フックのタイプ ('clone' や 'pre-mount' など) - 追加の引数。clone フックの場合、lxc-clone に渡される追加の引数は、フックへの引数として追加されます。stop フックの場合は、コンテナの名前空間のそれぞれに対するファイルディスクリプタへのパスが、名前空間名とともに渡されます。 - + + コンテナ名 + セクション (常に 'lxc') + フックのタイプ ('clone' や 'pre-mount' など) + 追加の引数。clone フックの場合、lxc-clone に渡される追加の引数は、フックへの引数として追加されます。stop フックの場合は、コンテナの名前空間のそれぞれに対するファイルディスクリプタへのパスが、名前空間名とともに渡されます。 + 以下の環境変数がセットされます。 - - LXC_NAME: コンテナ名 - LXC_ROOTFS_MOUNT: マウントされた root ファイルシステムへのパス - LXC_CONFIG_FILE: コンテナの設定ファイルのパス - LXC_SRC_NAME: clone フックの場合、元のコンテナの名前 - LXC_ROOTFS_PATH: コンテナの lxc.rootfs エントリ。これはマウントされた rootfs が存在する場所にはならないでしょう。それには LXC_ROOTFS_MOUNT を使用してください。 + + LXC_NAME: コンテナ名 + LXC_ROOTFS_MOUNT: マウントされた root ファイルシステムへのパス + LXC_CONFIG_FILE: コンテナの設定ファイルのパス + LXC_SRC_NAME: clone フックの場合、元のコンテナの名前 + LXC_ROOTFS_PATH: コンテナの lxc.rootfs.path エントリ。これはマウントされた rootfs が存在する場所にはならないでしょう。それには LXC_ROOTFS_MOUNT を使用してください。 @@ -1951,103 +1988,103 @@ しかし、フックの標準エラー出力を標準出力にリダイレクトすることにより保存することは可能です。 - - - - - - + + + + + + コンテナの tty、コンソールの作成、マウントが実行される前に、ホストの名前空間内で実行するフック。 - - - - - - - - - - - - コンテナのファイルシステムの名前空間で実行されますが、rootfs が設定される前に実行するフック。 これにより rootfs の操作が可能になります。 例えば、暗号化されたファイルシステムのマウントなどです。 このフック内でなされるマウントはホストには影響しません (mounts propagation を除いて)。 なので、それらはコンテナがシャットダウンする時に自動的にクリーンアップされます。 - - - + + + - - - - - - + + + + + + マウントが完了した後ですが、pivot_root の前にコンテナの名前空間で実行されるフック。 - - - - - - - - - - - - == 1 が設定されている場合で、マウントが完了し、マウント時のフックも実行された後ですが、pivot_root の前にコンテナの名前空間で実行するフック。 このフックの目的は、systemd ベースのコンテナ向けの autodev オプションが設定されている時に、コンテナの /dev ディレクトリを設定するのを支援することです。コンテナの /dev ディレクトリは、このフックが実行される時有効な ${} 環境変数からの相対パスとなります。 - - - + + + - - - - - - - コンテナの init が実行される直前にコンテナの名前空間で実行されるフック。 コンテナ内で利用可能なプログラムである必要があります。 - - - + + + @@ -2056,7 +2093,7 @@ - - コンテナのシャットダウン後、コンテナの名前空間への参照とともに、ホストの名前空間で実行されるフックです。 - それぞれの名前空間に対応する追加の引数がフックに渡されます。その引数にはコロンで区切られた名前空間のタイプ名とファイル名が含まれており、ファイル名は名前空間に対するファイルディスクリプタを取得するのに使えます。 - タイプ名は /proc/PID/ns ディレクトリ内のファイル名です。 - 例えば、マウント名前空間に対応する引数は通常は mnt:/proc/PID/fd/12 のようになります。 + --> + コンテナのシャットダウン後、コンテナの名前空間への参照とともに、ホストの名前空間で実行されるフックです。 + それぞれの名前空間に対応する追加の引数がフックに渡されます。その引数にはコロンで区切られた名前空間のタイプ名とファイル名が含まれており、ファイル名は名前空間に対するファイルディスクリプタを取得するのに使えます。 + タイプ名は /proc/PID/ns ディレクトリ内のファイル名です。 + 例えば、マウント名前空間に対応する引数は通常は mnt:/proc/PID/fd/12 のようになります。 - - - - - - + + + + + + コンテナがシャットダウンされた後にホストの名前空間で実行するフック。 - - - + + + - - - - - - - コンテナが新しいコンテナにクローンされる際に実行されるフック。詳しくは lxc-clone 1 を参照してください。 - - - + + + @@ -2143,113 +2180,113 @@ 具体的には、全てのパスはホストシステム上のパスであり、そのため、 フックの時点では使用できません。 - - - - - - + + + + + + LXC コンテナの名前。共通のログ環境内でのログメッセージに使うときに便利です。[] - - - - - - - - - - - - コンテナの設定ファイルのホスト上でのパス。 これは、他の方法では得られない追加の設定情報を見つけるために、コンテナに、元の、トップレベルの設定ファイルの位置を与えるものです。 [] - - - + + + - - - - - - + + + + + + 設定されている場合のコンテナのコンソール出力のパス。 - [] [] - - - + [] [] + + + - - - - - - + + + + + + 設定されている場合のコンテナのコンソールログ出力のパス。 - [] - - - - - - - - - - - - 初期にコンテナがマウントされる場所。 これは、コンテナインスタンスが起動するためのコンテナの rootfs へのホスト上のパスであり、インスタンスのための移行が行われる場所です。 - [] - - - - - - - - - - - - rootfs.mount へマウントされるコンテナのルートへのホスト上のパスです。 - [] - - - + [] + + + @@ -2273,11 +2310,11 @@ - - stop フックの場合のみ使われます。コンテナのシャットダウンの場合は "stop"、リブートの場合は "reboot" が設定されます。 + --> + stop フックの場合のみ使われます。コンテナのシャットダウンの場合は "stop"、リブートの場合は "reboot" が設定されます。 @@ -2328,49 +2365,65 @@ 同様に、設定ファイルのエントリは lxc-start のコマンドラインオプションで上書きすることも可能です。 - - - - - - - ログを取得するレベル。 ログレベルは 0..8 の範囲の整数です。 数字が小さいほど冗長なデバッグを意味します。 具体的には、0 = trace, 1 = debug, 2 = info, 3 = notice, 4 = warn, 5 = error, 6 = critical, 7 = alert, and 8 = fatal です。 指定されない場合、レベルのデフォルトは 5 (error) で、それ以上のエラーがロギングされます。 - - + + (フックスクリプトやネットワークインターフェースの起動、停止時のスクリプトのような) スクリプトが呼ばれた時、スクリプトの標準出力は level 1 の debug でロギングされます。 - - - - - - - - - + + + + + + + + + ログ情報を書き込むファイル。 - - - + + + + + + + + + + + ログ情報を syslog に送ります。ログレベルとして lxc.log.level の値を使用します。指定する値は使用する syslog の facility です。有効な値は daemon, local0, local1, local2, local3, local4, local5, local5, local6, local7 のいずれかです。 + + + @@ -2502,14 +2555,14 @@ <!-- Container Environment -->コンテナの環境変数 コンテナに環境変数を渡したい場合 (環境変数はコンテナの init とその子孫全てで利用可能です)、lxc.environment パラメータがその用途に使えます。 機微 (センシティブ) な情報を渡さないように注意が必要です。そのような情報を持たないコンテナ内のプロセスでこれらの環境変数が利用可能になってしまいます。環境変数は常に /proc/PID/environ 経由で利用可能になります。 @@ -2524,25 +2577,25 @@ - - - - - - + + + + + + コンテナに渡したい環境変数を指定します。 例: - - - lxc.environment = APP_ENV=production - lxc.environment = SYSLOG_SERVER=192.0.2.42 - - - + + + lxc.environment = APP_ENV=production + lxc.environment = SYSLOG_SERVER=192.0.2.42 + + + @@ -2552,8 +2605,8 @@ <!-- Examples -->例 以下に紹介するいくつかの例に加えて、他の設定例が @DOCDIR@/examples にあります。 @@ -2562,23 +2615,23 @@ この設定は、片方をブリッジである br0 と接続される veth ペアデバイスを使うコンテナを設定します (ブリッジは管理者によりあらかじめシステム上に設定済みである必要があります)。 仮想ネットワークデバイスは、コンテナ内では eth0 とリネームされます。 - lxc.utsname = myhostname - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.name = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 1.2.3.5/24 1.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.uts.name = myhostname + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.name = eth0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 1.2.3.5/24 1.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 @@ -2589,8 +2642,8 @@ この設定は、コンテナ内のユーザとグループ両方の id 0-9999 の範囲を、ホスト上の 100000-109999 へマッピングします。 - lxc.id_map = u 0 100000 10000 - lxc.id_map = g 0 100000 10000 + lxc.idmap = u 0 100000 10000 + lxc.idmap = g 0 100000 10000 @@ -2607,11 +2660,11 @@ devices.allow は、特定のデバイスを使用可能にします。 - lxc.cgroup.cpuset.cpus = 0,1 - lxc.cgroup.cpu.shares = 1234 - lxc.cgroup.devices.deny = a - lxc.cgroup.devices.allow = c 1:3 rw - lxc.cgroup.devices.allow = b 8:0 rw + lxc.cgroup.cpuset.cpus = 0,1 + lxc.cgroup.cpu.shares = 1234 + lxc.cgroup.devices.deny = a + lxc.cgroup.devices.allow = c 1:3 rw + lxc.cgroup.devices.allow = b 8:0 rw @@ -2624,37 +2677,37 @@ この例は、control group を使って、複雑なネットワークスタックを作成し、新しいホスト名を指定し、いくつかの場所をマウントし、ルートファイルシステムを変更するような複雑な設定を示します。 - lxc.utsname = complex - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 10.2.3.5/24 10.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 - lxc.network.ipv6 = 2003:db8:1:0:214:5432:feab:3588 - lxc.network.type = macvlan - lxc.network.flags = up - lxc.network.link = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bd - lxc.network.ipv4 = 10.2.3.4/24 - lxc.network.ipv4 = 192.168.10.125/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 - lxc.network.type = phys - lxc.network.flags = up - lxc.network.link = dummy0 - lxc.network.hwaddr = 4a:49:43:49:79:ff - lxc.network.ipv4 = 10.2.3.6/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3297 - lxc.cgroup.cpuset.cpus = 0,1 - lxc.cgroup.cpu.shares = 1234 - lxc.cgroup.devices.deny = a - lxc.cgroup.devices.allow = c 1:3 rw - lxc.cgroup.devices.allow = b 8:0 rw - lxc.mount = /etc/fstab.complex - lxc.mount.entry = /lib /root/myrootfs/lib none ro,bind 0 0 - lxc.rootfs = /mnt/rootfs.complex - lxc.cap.drop = sys_module mknod setuid net_raw - lxc.cap.drop = mac_override + lxc.uts.name = complex + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 10.2.3.5/24 10.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:5432:feab:3588 + lxc.net.1.type = macvlan + lxc.net.1.flags = up + lxc.net.1.link = eth0 + lxc.net.1.hwaddr = 4a:49:43:49:79:bd + lxc.net.1.ipv4.address = 10.2.3.4/24 + lxc.net.1.ipv4.address = 192.168.10.125/24 + lxc.net.1.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 + lxc.net.2.type = phys + lxc.net.2.flags = up + lxc.net.2.link = dummy0 + lxc.net.2.hwaddr = 4a:49:43:49:79:ff + lxc.net.2.ipv4.address = 10.2.3.6/24 + lxc.net.2.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3297 + lxc.cgroup.cpuset.cpus = 0,1 + lxc.cgroup.cpu.shares = 1234 + lxc.cgroup.devices.deny = a + lxc.cgroup.devices.allow = c 1:3 rw + lxc.cgroup.devices.allow = b 8:0 rw + lxc.mount.fstab = /etc/fstab.complex + lxc.mount.entry = /lib /root/myrootfs/lib none ro,bind 0 0 + lxc.rootfs.path = dir:/mnt/rootfs.complex + lxc.cap.drop = sys_module mknod setuid net_raw + lxc.cap.drop = mac_override @@ -2664,23 +2717,23 @@ See Also - chroot - 1 + chroot + 1 , - pivot_root - 8 + pivot_root + 8 , - fstab - 5 + fstab + 5 - capabilities - 7 + capabilities + 7 diff -Nru lxc-2.0.8/doc/ja/lxc-copy.sgml.in lxc-2.1.0/doc/ja/lxc-copy.sgml.in --- lxc-2.0.8/doc/ja/lxc-copy.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc-copy.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -61,7 +61,8 @@ -p, --newpath newpath -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -75,7 +76,8 @@ -e, --ephemeral -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -84,6 +86,20 @@ lxc-copy -n, --name name -P, --lxcpath path + -N, --newname newname + -p, --newpath newpath + -e, --ephemeral + -B, --backingstorage backingstorage + -s, --snapshot + -t, --tmpfs + -K, --keepname + -M, --keepmac + -- hook arguments + + + lxc-copy + -n, --name name + -P, --lxcpath path -N, --newname newname -p, --newpath newpath -R, --rename @@ -136,9 +152,14 @@ -e is used in combination with -D a non-ephemeral snapshot of the original container is created and started. + Ephemeral containers can also be placed on a tmpfs with -t + flag. NOTE: If an ephemeral container that is placed on a tmpfs is rebooted + all changes made to it will currently be lost! --> -e オプションを指定した場合は、元のコンテナの一時的なスナップショットを作成し、起動します。一時的なコンテナの場合、設定ファイルに lxc.ephemeral = 1 がセットされ、シャットダウン後に削除されます。 -e-D を同時に指定すると、元のコンテナの一時的ではないスナップショットを作成し、起動します。 + -t オプションを指定して、一時的なコンテナを tmpfs 上に置くことができます。 + 注意: tmpfs 上に置いた一時的なコンテナをリブートすると、コンテナに対して行ったすべての変更は失われます! @@ -266,6 +287,22 @@ + + + + このオプションを指定すると、一時的なコンテナを tmpfs 上に置きます。 + 注意: tmpfs 上に置かれた一時的なコンテナをリブートすると、コンテナに対して行ったすべての変更が失われます。 + このフラグは -e オプションを指定して作成した一時的なコンテナに対してのみ有効です。一時的なスナップショットを作成する元のコンテナは、ディレクトリバックエンド上に存在しなければなりません。 + + + + + このオプションを指定すると、元のコンテナのホスト名をコピー先でもそのまま使います。 + + + + + - このオプションを指定すると、元のコンテナのホスト名をコピー先でもそのまま使います。 - - - - diff -Nru lxc-2.0.8/doc/ja/lxc-info.sgml.in lxc-2.1.0/doc/ja/lxc-info.sgml.in --- lxc-2.0.8/doc/ja/lxc-info.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc-info.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -220,7 +220,7 @@ - lxc-info -n foo -c lxc.network.0.veth.pair + lxc-info -n foo -c lxc.net.0.veth.pair + 定義済みのコンテナ (設定ファイルが存在するコンテナ) のみを表示します。 + + + diff -Nru lxc-2.0.8/doc/ja/lxc.sgml.in lxc-2.1.0/doc/ja/lxc.sgml.in --- lxc-2.0.8/doc/ja/lxc.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -166,14 +166,14 @@ - 2.6.32 以上のバージョンが採用されているディストリビューションならば、lxc は動作するでしょう。 + 3.10 以上のバージョンが採用されているディストリビューションならば、lxc は動作するでしょう。 機能的には若干少ない形ですが、充分に楽しめるはずです。 ヘルパースクリプトの lxc-checkconfig を使って、あなたのカーネルの設定に関する情報を取得できるでしょう。 @@ -556,12 +556,12 @@ command into the container. The pid of the first process is 1. If no command is specified lxc-start will - run the command defined in lxc.init_cmd or if not set, + run the command defined in lxc.init.cmd or if not set, /sbin/init . --> lxc-start コマンドは、コンテナ内の特定のコマンドを直接実行します。 最初のプロセスの pid が 1 となります。 - もし、実行するコマンドが指定されない場合は、lxc-start は lxc.init_cmd で設定されたコマンドを実行します。もし lxc.init_cmd が設定されていない場合は /sbin/init を実行します。 + もし、実行するコマンドが指定されない場合は、lxc-start は lxc.init.cmd で設定されたコマンドを実行します。もし lxc.init.cmd が設定されていない場合は /sbin/init を実行します。 diff -Nru lxc-2.0.8/doc/ja/lxc-start.sgml.in lxc-2.1.0/doc/ja/lxc-start.sgml.in --- lxc-2.0.8/doc/ja/lxc-start.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc-start.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -94,12 +94,12 @@ - もし command が指定されない場合は、lxc-start はシステムコンテナを実行するためのコマンドとして、lxc.init_cmd で設定されたコマンドを使用します。 - もし lxc.init_cmd が設定されていない場合は、デフォルトで "/sbin/init" を使用します。 + もし command が指定されない場合は、lxc-start はシステムコンテナを実行するためのコマンドとして、lxc.init.cmd で設定されたコマンドを使用します。 + もし lxc.init.cmd が設定されていない場合は、デフォルトで "/sbin/init" を使用します。 diff -Nru lxc-2.0.8/doc/ja/lxc-stop.sgml.in lxc-2.1.0/doc/ja/lxc-stop.sgml.in --- lxc-2.0.8/doc/ja/lxc-stop.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ja/lxc-stop.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -73,18 +73,18 @@ lxc-stop reboots, cleanly shuts down, or kills all the processes inside the container. By default, it will request a clean shutdown of the container by sending - lxc.haltsignal (defaults to SIGPWR) to + lxc.signal.halt (defaults to SIGPWR) to the container's init process, waiting up to 60 seconds for the container to exit, and then returning. If the container fails to cleanly exit in - 60 seconds, it will be sent the lxc.stopsignal + 60 seconds, it will be sent the lxc.signal.stop (defaults to SIGKILL) to force it to shut down. A request to reboot will - send the lxc.rebootsignal (defaults to SIGINT) to the + send the lxc.signal.reboot (defaults to SIGINT) to the container's init process. --> lxc-stop は、リブート、クリーンシャットダウン、コンテナ内の全てのプロセスの kill のどれかを行います。 - デフォルトでは、コンテナのクリーンなシャットダウンを lxc.haltsignal (デフォルトでは SIGPWR) をコンテナの init プロセスに送ることでリクエストし、コンテナの終了を 60 秒待ち、return します。 - コンテナが 60 秒の間にクリーンに終了するのに失敗した場合、lxc.stopsignal (デフォルトは SIGKILL です) を送り、強制的にシャットダウンします。 - リブートのリクエストは lxc.rebootsignal に設定されたシグナルをコンテナの init プロセスに送ります (デフォルトは SIGINT です)。 + デフォルトでは、コンテナのクリーンなシャットダウンを lxc.signal.halt (デフォルトでは SIGPWR) をコンテナの init プロセスに送ることでリクエストし、コンテナの終了を 60 秒待ち、return します。 + コンテナが 60 秒の間にクリーンに終了するのに失敗した場合、lxc.signal.stop (デフォルトは SIGKILL です) を送り、強制的にシャットダウンします。 + リブートのリクエストは lxc.signal.reboot に設定されたシグナルをコンテナの init プロセスに送ります (デフォルトは SIGINT です)。 diff -Nru lxc-2.0.8/doc/ko/lxc.container.conf.sgml.in lxc-2.1.0/doc/ko/lxc.container.conf.sgml.in --- lxc-2.0.8/doc/ko/lxc.container.conf.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ko/lxc.container.conf.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -187,7 +187,7 @@ - + @@ -219,7 +219,7 @@ - + @@ -248,7 +248,7 @@ - + @@ -277,7 +277,7 @@ - + @@ -308,7 +308,7 @@ - + @@ -343,7 +343,7 @@ - + @@ -356,7 +356,7 @@ - + @@ -415,7 +415,7 @@ - + @@ -428,14 +428,14 @@ - + 컨테이너가 어떤 종류의 네트워크 가상화를 사용할지 지정한다. - 필드부터 새로운 네트워크 설정이 시작된다. 이 방법으로 여러개의 네트워크 가상화 형태를 같은 컨테이너에 지정할 수 있다. 그리고 여러개의 네트워크 인터페이스를 하나의 컨테이너에 지정할 수도 있다. + 필드부터 새로운 네트워크 설정이 시작된다. 이 방법으로 여러개의 네트워크 가상화 형태를 같은 컨테이너에 지정할 수 있다. 그리고 여러개의 네트워크 인터페이스를 하나의 컨테이너에 지정할 수도 있다. 지정 가능한 형태는 아래와 같다. @@ -473,7 +473,7 @@ a virtual ethernet pair device is created with one side assigned to the container and the other side attached to a bridge specified by - the option. + the option. If the bridge is not specified, then the veth pair device will be created but not attached to any bridge. Otherwise, the bridge has to be created on the system @@ -485,33 +485,33 @@ container, but if you wish to handle this name yourselves, you can tell lxc to set a specific name with - the option (except for + the option (except for unprivileged containers where this option is ignored for security reasons). --> - 한 쪽은 컨테이너로, 다른 한쪽은 옵션으로 지정한 브리지로 붙은 가상 이더넷(veth) 장치 쌍을 생성한다. + 한 쪽은 컨테이너로, 다른 한쪽은 옵션으로 지정한 브리지로 붙은 가상 이더넷(veth) 장치 쌍을 생성한다. 만약 브리지가 지정되지 않았다면, 어떤 브리지에도 붙지 않은 veth 장치 쌍을 만든다. 브리지는 컨테이너 시작전에 시스템에서 생성해야 한다. - lxc는 컨테이너 이외의 설정에 대해서는 다루지 않는다. 기본값으로 lxc는 컨테이너 바깥에 속할 네트워크 디바이스의 이름을 정해준다. 이름을 변경하기 원한다면, lxc가 지정한 이름으로 설정하도록 옵션을 사용하여야 한다. (비특권 컨테이너는 불가능하다. 이 옵션은 보안상의 이유로 무시될 것이다) + lxc는 컨테이너 이외의 설정에 대해서는 다루지 않는다. 기본값으로 lxc는 컨테이너 바깥에 속할 네트워크 디바이스의 이름을 정해준다. 이름을 변경하기 원한다면, lxc가 지정한 이름으로 설정하도록 옵션을 사용하여야 한다. (비특권 컨테이너는 불가능하다. 이 옵션은 보안상의 이유로 무시될 것이다) - vlan 인터페이스는 로 지정한 인터페이스에 연결되고, 컨테이너로 할당된다. vlan의 식별자는 옵션으로 지정한다. + vlan 인터페이스는 로 지정한 인터페이스에 연결되고, 컨테이너로 할당된다. vlan의 식별자는 옵션으로 지정한다. - macvlan 인터페이스는 로 지정한 인터페이스에 연결되고, 컨테이너로 할당된다. - 은 같은 상위 디바이스에 있는 다른 macvlan과 통신할 때 사용하는 모드를 지정한다. + macvlan 인터페이스는 로 지정한 인터페이스에 연결되고, 컨테이너로 할당된다. + 은 같은 상위 디바이스에 있는 다른 macvlan과 통신할 때 사용하는 모드를 지정한다. 지정할 수 있는 모드는 이다. 모드는 디바이스가 같은 상위디바이스의 어떤 장치와도 통신하지 않는다. (기본값) 새로운 가상 이더넷 포트 통합모드(Virtual Ethernet Port Aggregator), 즉 모드는 인접한 브리지가 소스와 목적지가 로컬인 모든 프레임들을 macvlan 포트로 반환한다고 가정한다. 즉, 브리지가 reflective relay로 설정되어 있다는 것이다. @@ -558,17 +558,17 @@ - 로 지정한 이미 존재하는 인터페이스를 컨테이너로 할당된다. + 로 지정한 이미 존재하는 인터페이스를 컨테이너로 할당된다. - + @@ -590,7 +590,7 @@ - + @@ -605,7 +605,7 @@ - + @@ -619,7 +619,7 @@ - + @@ -638,7 +638,7 @@ - + @@ -658,7 +658,7 @@ - + @@ -679,7 +679,7 @@ - + @@ -691,7 +691,7 @@ Can also have the special value , which means to take the primary address from the bridge interface (as specified by the - option) and use that as + option) and use that as the gateway. is only available when using the and network types. @@ -700,7 +700,7 @@ 주소 형식은 x.y.z.t로, 예를 들면 192.168.1.123이다. 라는 특별한 값을 지정할 수있다. - 이것은 ( 에서 지정된) 브리지 인터페이스의 첫번째 주소를 가져와 게이트 주소로 사용한다. + 이것은 ( 에서 지정된) 브리지 인터페이스의 첫번째 주소를 가져와 게이트 주소로 사용한다. 는 네트워크 형태가 일 때만 지정 가능하다. @@ -709,7 +709,7 @@ - + @@ -728,7 +728,7 @@ - + @@ -740,7 +740,7 @@ Can also have the special value , which means to take the primary address from the bridge interface (as specified by the - option) and use that as + option) and use that as the gateway. is only available when using the and network types. @@ -749,7 +749,7 @@ 주소 형식은 x::y로, 예를 들면 2003:db8:1:0::1이다. 라는 특별한 값을 지정할 수있다. - 이것은 ( 에서 지정된) 브리지 인터페이스의 첫번째 주소를 가져와 게이트 주소로 사용한다. + 이것은 ( 에서 지정된) 브리지 인터페이스의 첫번째 주소를 가져와 게이트 주소로 사용한다. 는 네트워크 형태가 일 때만 지정 가능하다. @@ -757,7 +757,7 @@ - + @@ -790,7 +790,7 @@ - + @@ -837,7 +837,7 @@ - + @@ -881,7 +881,7 @@ - + @@ -920,7 +920,7 @@ - + @@ -957,7 +957,7 @@ - + @@ -1069,7 +1069,7 @@ - + @@ -1426,7 +1426,7 @@ - + @@ -1471,7 +1471,7 @@ - 루트 파일시스템을 변경하기 전에, 을 어디에 재귀적으로 바인드할지 정한다. 이는 + 루트 파일시스템을 변경하기 전에, 을 어디에 재귀적으로 바인드할지 정한다. 이는 pivot_root 8 @@ -1505,23 +1505,6 @@ - - - - - - - - 사용하고자 하는 백엔드 루트파일 시스템의 종류를 지정한다. 'dir' 또는 'zfs'로 지정할 수 있다. 컨테이너 시작시 어떤 종류인지 추정하는 동안, 시간이 소요될 수 있다. 이 값을 지정함으로써 추가적인 처리를 피할 수 있다. - - - - @@ -1647,7 +1630,7 @@ - + @@ -1659,7 +1642,7 @@ 컨테이너가 따라야할 apparmor 프로파일을 지정한다. 컨테이너가 apparmor로 인한 제한을 받지 않도록 하려면, 아래와 같이 지정하면 된다. - lxc.aa_profile = unconfined + lxc.apparmor.profile = unconfined apparmor 프로파일이 변경되지 않아야 한다면(중첩 컨테이너 안에 있고, 이미 confined된 경우), 아래와 같이 지정하면 된다. - lxc.aa_profile = unchanged + lxc.apparmor.profile = unchanged - + @@ -1721,7 +1704,7 @@ - + @@ -1731,7 +1714,7 @@ --> 컨테이너가 따라야할 SELinux 컨텍스트를 지정하거나, unconfined_t를 지정할 수 있다. 예를 들어 아래와 같이 지정 가능하다. - lxc.se_context = system_u:system_r:lxc_t:s0:c22 + lxc.selinux.context = system_u:system_r:lxc_t:s0:c22 @@ -1785,7 +1768,7 @@ - + @@ -1801,6 +1784,42 @@ + PR_SET_NO_NEW_PRIVS + + + PR_SET_NO_NEW_PRIVS가 적용되면, execve()는, execve()를 호출되기 전에는 실행하지 못했던 것을 수행하기 위해 권한을 부여하는 류의 동작을 하지 않게 된다. (예를 들어, set-user-ID와 set-group-ID 모드, 파일 캐퍼빌리티가 동작하지 않는 것이다.) + 일단 적용되면 이 비트는 해제할 수 없다. 이 비트는 fork()와 clone()으로 생성된 자식에게도 상속되며, execve() 이후에도 그대로 적용된다. + PR_SET_NO_NEW_PRIVS는 컨테이너의 AppArmor 프로필 또는 SELinux 문맥이 변경된 이후에 적용된다. + + + + + + + + + + PR_SET_NO_NEW_PRIVS가 컨테이너에 적용되어야 하는지 여부를 지정한다. 1을 지정하면 적용된다. + + + + + + + <!-- UID mappings -->UID 매핑 컨테이너 훅이 실행될 때, 정보는 명령어 인수나 환경 변수를 통해 넘겨진다. @@ -1887,7 +1906,7 @@ LXC_ROOTFS_MOUNT: 마운트될 루트 파일시스템의 경로 LXC_CONFIG_FILE: 컨테이너 설정파일의 경로 LXC_SRC_NAME: clone 훅의 경우, 원본 컨테이너의 이름 - LXC_ROOTFS_PATH: 컨테이너의 lxc.rootfs 항목. 이 것은 마운트된 루트 파일시스템을 가리키는 것이 아님에 주의해야한다. 그 목적을 위해서는 LXC_ROOTFS_MOUNT를 사용해야 한다. + LXC_ROOTFS_PATH: 컨테이너의 lxc.rootfs.path 항목. 이 것은 마운트된 루트 파일시스템을 가리키는 것이 아님에 주의해야한다. 그 목적을 위해서는 LXC_ROOTFS_MOUNT를 사용해야 한다. @@ -2131,10 +2150,10 @@ NULL이 아니라면, 컨테이너의 콘솔의 출력이 저장될 경로. - [] [] + [] [] @@ -2187,10 +2206,10 @@ rootfs.mount에 마운트된 컨테이너 루트의 호스트에서의 경로이다. - [] + [] @@ -2272,7 +2291,7 @@ - + @@ -2302,7 +2321,7 @@ - + @@ -2313,6 +2332,22 @@ + + + + + + + + 로그정보를 syslog에 보낸다. 로그 수준은 lxc.log.level로 지정할 수 있다. 인자는 syslog에 정의된 값으로만 지정할 수 있다. 사용 가능한 값은 다음과 같다 : daemon, local0, local1, local2, local3, local4, local5, local5, local7 + + + @@ -2511,14 +2546,14 @@ 이 설정은 컨테이너가 한 쪽은 (이전에 시스템에 이미 생성된) br0 브리지에 연결되어 있는 veth 장치 쌍을 사용하도록 세팅한다. 가상 네트워크 장치는 컨테이너 내에서 eth0라는 이름을 갖는다. - lxc.utsname = myhostname - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.name = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 1.2.3.5/24 1.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.uts.name = myhostname + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.name = eth0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 1.2.3.5/24 1.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 @@ -2529,8 +2564,8 @@ 이 설정은 UID와 GID 둘다를 컨테이너의 0 ~ 9999를 호스트의 100000 ~ 109999로 매핑한다. - lxc.id_map = u 0 100000 10000 - lxc.id_map = g 0 100000 10000 + lxc.idmap = u 0 100000 10000 + lxc.idmap = g 0 100000 10000 @@ -2561,35 +2596,35 @@ 아래의 예제는 복잡한 네트워크 스택, 컨트롤 그룹 사용, 호스트 이름 설정, 몇몇 장소 마운트, 루트 파일시스템 변경 등의 복잡한 설정을 보여준다. - lxc.utsname = complex - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 10.2.3.5/24 10.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 - lxc.network.ipv6 = 2003:db8:1:0:214:5432:feab:3588 - lxc.network.type = macvlan - lxc.network.flags = up - lxc.network.link = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bd - lxc.network.ipv4 = 10.2.3.4/24 - lxc.network.ipv4 = 192.168.10.125/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 - lxc.network.type = phys - lxc.network.flags = up - lxc.network.link = dummy0 - lxc.network.hwaddr = 4a:49:43:49:79:ff - lxc.network.ipv4 = 10.2.3.6/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3297 + lxc.uts.name = complex + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 10.2.3.5/24 10.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:5432:feab:3588 + lxc.net.1.type = macvlan + lxc.net.1.flags = up + lxc.net.1.link = eth0 + lxc.net.1.hwaddr = 4a:49:43:49:79:bd + lxc.net.1.ipv4.address = 10.2.3.4/24 + lxc.net.1.ipv4.address = 192.168.10.125/24 + lxc.net.1.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 + lxc.net.2.type = phys + lxc.net.2.flags = up + lxc.net.2.link = dummy0 + lxc.net.2.hwaddr = 4a:49:43:49:79:ff + lxc.net.2.ipv4.address = 10.2.3.6/24 + lxc.net.2.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3297 lxc.cgroup.cpuset.cpus = 0,1 lxc.cgroup.cpu.shares = 1234 lxc.cgroup.devices.deny = a lxc.cgroup.devices.allow = c 1:3 rw lxc.cgroup.devices.allow = b 8:0 rw - lxc.mount = /etc/fstab.complex + lxc.mount.fstab = /etc/fstab.complex lxc.mount.entry = /lib /root/myrootfs/lib none ro,bind 0 0 - lxc.rootfs = /mnt/rootfs.complex + lxc.rootfs.path = dir:/mnt/rootfs.complex lxc.cap.drop = sys_module mknod setuid net_raw lxc.cap.drop = mac_override diff -Nru lxc-2.0.8/doc/ko/lxc-copy.sgml.in lxc-2.1.0/doc/ko/lxc-copy.sgml.in --- lxc-2.0.8/doc/ko/lxc-copy.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ko/lxc-copy.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -61,7 +61,8 @@ -p, --newpath newpath -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -75,7 +76,8 @@ -e, --ephemeral -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -84,6 +86,20 @@ lxc-copy -n, --name name -P, --lxcpath path + -N, --newname newname + -p, --newpath newpath + -e, --ephemeral + -B, --backingstorage backingstorage + -s, --snapshot + -t, --tmpfs + -K, --keepname + -M, --keepmac + -- hook arguments + + + lxc-copy + -n, --name name + -P, --lxcpath path -N, --newname newname -p, --newpath newpath -R, --rename @@ -133,8 +149,13 @@ -e is used in combination with -D a non-ephemeral snapshot of the original container is created and started. + Ephemeral containers can also be placed on a tmpfs with -t + flag. NOTE: If an ephemeral container that is placed on a tmpfs is rebooted + all changes made to it will currently be lost! --> -e가 지정되면, 원본 컨테이너의 임시 스냅샷이 생성되고 시작된다. 임시 컨테이너는 자신의 설정파일 안에 lxc.ephemeral = 1를 가지게 되며, 종료시에 제거된다. -e와 함께 -D를 같이 지정하면 원본 컨테이너의 영구적인 스냅샷이 생성되고 실행된다. + -t를 지정하면, 임시 컨테이너는 tmpfs 상에 놓이게 된다. + 주의: tmpfs 상에 놓인 임시 컨테이너는 재부팅시 모든 변경 사항이 사라진다. @@ -272,6 +293,22 @@ + + + + 이 옵션이 지정되면 임시 컨테이너는 tmpfs 상에 놓이게 된다. + 주의: tmpfs 상에 놓인 임시 컨테이너는 재부팅시 모든 변경 사항이 사라진다. + 이 옵션은 -e를 지정하여 임시 컨테이너를 생성할 때만 사용이 가능하다. 임시 스냅샷을 생성한 원본 컨테이너는 반드시 일반적인 디렉토리에 위치하고 있어야 한다. + + + + @@ -295,27 +332,27 @@ - + - 이 옵션을 -e와 지정하면 영구적인 컨테이너가 생성되고 시작된다. + When this option is specified the hostname of the original + container will be kept for the copy. + --> + 이 옵션이 지정되면 원본 컨테이너의 호스트이름이 복사본에서도 그대로 유지된다. - + - 이 옵션이 지정되면 원본 컨테이너의 호스트이름이 복사본에서도 그대로 유지된다. + When this option is specified with + -e a non-ephemeral container is created + and started.--> + 이 옵션을 -e와 지정하면 영구적인 컨테이너가 생성되고 시작된다. diff -Nru lxc-2.0.8/doc/ko/lxc-info.sgml.in lxc-2.1.0/doc/ko/lxc-info.sgml.in --- lxc-2.0.8/doc/ko/lxc-info.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ko/lxc-info.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -220,7 +220,7 @@ - lxc-info -n foo -c lxc.network.0.veth.pair + lxc-info -n foo -c lxc.net.0.veth.pair - 배포판들에 포함된 2.6.32 이상의 커널에서는 lxc가 동작한다. 매우 작은 기능만 있지만 충분히 사용할 수 있다. + 배포판들에 포함된 3.10 이상의 커널에서는 lxc가 동작한다. 매우 작은 기능만 있지만 충분히 사용할 수 있다. lxc-checkconfig 스크립트를 사용하면 현재 커널 설정에 대한 정보를 얻을 수 있다. @@ -542,10 +542,10 @@ command into the container. The pid of the first process is 1. If no command is specified lxc-start will - run the command defined in lxc.init_cmd or if not set, + run the command defined in lxc.init.cmd or if not set, /sbin/init . --> - lxc-start 명령어는 지정한 명령어를 컨테이너 내에서 직접 실행한다. 첫 프로세스의 pid는 1번이다. 만약 어떤 명령어도 지정되지 않으면, lxc.init_cmd에 지정된 명령어를 실행한다. 이마저도 지정되있지 않으면, /sbin/init를 실행한다. + lxc-start 명령어는 지정한 명령어를 컨테이너 내에서 직접 실행한다. 첫 프로세스의 pid는 1번이다. 만약 어떤 명령어도 지정되지 않으면, lxc.init.cmd에 지정된 명령어를 실행한다. 이마저도 지정되있지 않으면, /sbin/init를 실행한다. diff -Nru lxc-2.0.8/doc/ko/lxc-start.sgml.in lxc-2.1.0/doc/ko/lxc-start.sgml.in --- lxc-2.0.8/doc/ko/lxc-start.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ko/lxc-start.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -95,11 +95,11 @@ - 만약 명령어가 지정되지 않았다면, lxc-start는 lxc.init_cmd에 정의된 명령어를 사용한다. 만약 그마저도 없다면 "/sbin/init"명령어를 사용한다. + 만약 명령어가 지정되지 않았다면, lxc-start는 lxc.init.cmd에 정의된 명령어를 사용한다. 만약 그마저도 없다면 "/sbin/init"명령어를 사용한다. diff -Nru lxc-2.0.8/doc/ko/lxc-stop.sgml.in lxc-2.1.0/doc/ko/lxc-stop.sgml.in --- lxc-2.0.8/doc/ko/lxc-stop.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/ko/lxc-stop.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -73,16 +73,16 @@ lxc-stop reboots, cleanly shuts down, or kills all the processes inside the container. By default, it will request a clean shutdown of the container by sending - lxc.haltsignal (defaults to SIGPWR) to + lxc.signal.halt (defaults to SIGPWR) to the container's init process, waiting up to 60 seconds for the container to exit, and then returning. If the container fails to cleanly exit in - 60 seconds, it will be sent the lxc.stopsignal + 60 seconds, it will be sent the lxc.signal.stop (defaults to SIGKILL) to force it to shut down. A request to reboot will - send the lxc.rebootsignal (defaults to SIGINT) to the + send the lxc.signal.reboot (defaults to SIGINT) to the container's init process. --> - lxc-stop 는 재뷰탕, 종료, 또는 컨테이너 내의 모든 프로세스를 강제종료 시킨다. 기본 동작은 컨테이너에게 lxc.haltsignal 시그널(기본값은 SIGPWR)을 컨테이너 init 프로세스에게 날려, 컨테이너가 종료되게 요청하는 것이다. 60초 동안 컨테이너가 종료되는 것을 기다리고 리턴된다. -만약 컨테이너가 60초안에 종료되지 않는다면 lxc.stopsignal 시그널(기본값은 SIGKILL)을 날려 강제로 종료시킨다. 재부팅 요청시에는 lxc.rebootsignal 시그널(기본값은 SIGINT)를 컨테이너 init 프로세스에게 날린다. + lxc-stop 는 재뷰탕, 종료, 또는 컨테이너 내의 모든 프로세스를 강제종료 시킨다. 기본 동작은 컨테이너에게 lxc.signal.halt 시그널(기본값은 SIGPWR)을 컨테이너 init 프로세스에게 날려, 컨테이너가 종료되게 요청하는 것이다. 60초 동안 컨테이너가 종료되는 것을 기다리고 리턴된다. +만약 컨테이너가 60초안에 종료되지 않는다면 lxc.signal.stop 시그널(기본값은 SIGKILL)을 날려 강제로 종료시킨다. 재부팅 요청시에는 lxc.signal.reboot 시그널(기본값은 SIGINT)를 컨테이너 init 프로세스에게 날린다. diff -Nru lxc-2.0.8/doc/lxc.container.conf lxc-2.1.0/doc/lxc.container.conf --- lxc-2.0.8/doc/lxc.container.conf 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc.container.conf 2017-09-06 02:32:37.000000000 +0000 @@ -1,5 +1,5 @@ # the fstab mount file -lxc.mount = ./fstab +lxc.mount.fstab = ./fstab # the hostname to be set into the container lxc.utsname = virtnode @@ -12,25 +12,25 @@ # should be an existing interface, usually it is eth0 # * phys : the network will use a physical network device, the specified # link should be an existing interface -lxc.network.type = macvlan +lxc.net.0.type = macvlan # specify the flags to be used for the network, actually only is allowed # which mean the network should be set up when created. If the network is set # up, the loopback is automatically set up too. -lxc.network.flags = up +lxc.net.0.flags = up # specify the physical network device which will communicate with the # outside world -lxc.network.link = eth0 +lxc.net.0.link = eth0 # NIC ethernet mac address -lxc.network.hwaddr = 4a:49:43:49:79:bd +lxc.net.0.hwaddr = 4a:49:43:49:79:bd # specify the ipv4 address of the container. Several lines are allowed and # will mean several addresses will be assigned to the interface -lxc.network.ipv4 = 1.2.3.5/24 +lxc.net.0.ipv4.address = 1.2.3.5/24 # specify the ipv6 address of the container. Several lines are allowed and # will mean several addresses will be assigned to the interface -lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 +lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 diff -Nru lxc-2.0.8/doc/lxc.container.conf.sgml.in lxc-2.1.0/doc/lxc.container.conf.sgml.in --- lxc-2.0.8/doc/lxc.container.conf.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc.container.conf.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -49,43 +49,71 @@ Description - The linux containers (lxc) are always created - before being used. This creation defines a set of system - resources to be virtualized / isolated when a process is using - the container. By default, the pids, sysv ipc and mount points - are virtualized and isolated. The other system resources are - shared across containers, until they are explicitly defined in - the configuration file. For example, if there is no network - configuration, the network will be shared between the creator of - the container and the container itself, but if the network is - specified, a new network stack is created for the container and - the container can no longer use the network of its ancestor. + LXC is the well-known and heavily tested low-level Linux container + runtime. It is in active development since 2008 and has proven itself in + critical production environments world-wide. Some of its core contributors + are the same people that helped to implement various well-known + containerization features inside the Linux kernel. - The configuration file defines the different system resources to - be assigned for the container. At present, the utsname, the - network, the mount points, the root file system, the user namespace, - and the control groups are supported. + LXC's main focus is system containers. That is, containers which offer an + environment as close as possible as the one you'd get from a VM but + without the overhead that comes with running a separate kernel and + simulating all the hardware. - Each option in the configuration file has the form key - = value fitting in one line. The '#' character means - the line is a comment. List options, like capabilities and cgroups - options, can be used with no value to clear any previously - defined values of that option. + This is achieved through a combination of kernel security features such as + namespaces, mandatory access control and control groups. + + + + LXC has supports unprivileged containers. Unprivileged containers are + containers that are run without any privilege. This requires support for + user namespaces in the kernel that the container is run on. LXC was the + first runtime to support unprivileged containers after user namespaces + were merged into the mainline kernel. + + + + In essence, user namespaces isolate given sets of UIDs and GIDs. This is + achieved by establishing a mapping between a range of UIDs and GIDs on the + host to a different (unprivileged) range of UIDs and GIDs in the + container. The kernel will translate this mapping in such a way that + inside the container all UIDs and GIDs appear as you would expect from the + host whereas on the host these UIDs and GIDs are in fact unprivileged. For + example, a process running as UID and GID 0 inside the container might + appear as UID and GID 100000 on the host. The implementation and working + details can be gathered from the corresponding user namespace man page. + UID and GID mappings can be defined with the + key. + + + + Linux containers are defined with a simple configuration file. Each + option in the configuration file has the form key = + value fitting in one line. The "#" character means the line is a + comment. List options, like capabilities and cgroups options, can be used + with no value to clear any previously defined values of that option. + + + + LXC namespaces configuration keys by using single dots. This means complex + configuration keys such as expose various + subkeys such as , + , , and + others for even more fine-grained configuration. Configuration - In order to ease administration of multiple related containers, it - is possible to have a container configuration file cause another - file to be loaded. For instance, network configuration - can be defined in one common file which is included by multiple - containers. Then, if the containers are moved to another host, - only one file may need to be updated. + In order to ease administration of multiple related containers, it is + possible to have a container configuration file cause another file to be + loaded. For instance, network configuration can be defined in one common + file which is included by multiple containers. Then, if the containers + are moved to another host, only one file may need to be updated. @@ -106,11 +134,10 @@ Architecture - Allows one to set the architecture for the container. For example, - set a 32bits architecture for a container running 32bits - binaries on a 64bits host. This fixes the container scripts - which rely on the architecture to do some work like - downloading the packages. + Allows one to set the architecture for the container. For example, set a + 32bits architecture for a container running 32bits binaries on a 64bits + host. This fixes the container scripts which rely on the architecture to + do some work like downloading the packages. @@ -123,7 +150,7 @@ Specify the architecture for the container. - Valid options are + Some valid options are , , , @@ -138,15 +165,14 @@ Hostname - The utsname section defines the hostname to be set for the - container. That means the container can set its own hostname - without changing the one from the system. That makes the - hostname private for the container. + The utsname section defines the hostname to be set for the container. + That means the container can set its own hostname without changing the + one from the system. That makes the hostname private for the container. - + @@ -160,17 +186,17 @@ Halt signal - Allows one to specify signal name or number, sent by lxc-stop to the - container's init process to cleanly shutdown the container. Different - init systems could use different signals to perform clean shutdown - sequence. This option allows the signal to be specified in kill(1) - fashion, e.g. SIGPWR, SIGRTMIN+14, SIGRTMAX-10 or plain number. The - default signal is SIGPWR. + Allows one to specify signal name or number sent to the container's + init process to cleanly shutdown the container. Different init systems + could use different signals to perform clean shutdown sequence. This + option allows the signal to be specified in kill(1) fashion, e.g. + SIGPWR, SIGRTMIN+14, SIGRTMAX-10 or plain number. The default signal is + SIGPWR. - + @@ -184,15 +210,15 @@ Reboot signal - Allows one to specify signal name or number, sent by lxc-stop to - reboot the container. This option allows signal to be specified in - kill(1) fashion, e.g. SIGTERM, SIGRTMIN+14, SIGRTMAX-10 or plain number. - The default signal is SIGINT. + Allows one to specify signal name or number to reboot the container. + This option allows signal to be specified in kill(1) fashion, e.g. + SIGTERM, SIGRTMIN+14, SIGRTMAX-10 or plain number. The default signal + is SIGINT. - + @@ -206,15 +232,15 @@ Stop signal - Allows one to specify signal name or number, sent by lxc-stop to forcibly - shutdown the container. This option allows signal to be specified in - kill(1) fashion, e.g. SIGKILL, SIGRTMIN+14, SIGRTMAX-10 or plain number. - The default signal is SIGKILL. + Allows one to specify signal name or number to forcibly shutdown the + container. This option allows signal to be specified in kill(1) fashion, + e.g. SIGKILL, SIGRTMIN+14, SIGRTMAX-10 or plain number. The default + signal is SIGKILL. - + @@ -237,7 +263,7 @@ - + @@ -251,30 +277,31 @@ Init ID - Sets the UID/GID to use for the init system, and subsequent command, executed by lxc-execute. - - These options are only used when lxc-execute is started in a private user namespace. + Sets the UID/GID to use for the init system, and subsequent commands. + Note that using a non-root uid when booting a system container will + likely not work due to missing privileges. Setting the UID/GID is mostly + useful when running application container. Defaults to: UID(0), GID(0) - + - UID to use within a private user namesapce for init. + UID to use for init. - + - GID to use within a private user namesapce for init. + GID to use for init. @@ -315,7 +342,7 @@ - + @@ -325,18 +352,22 @@ - + specify what kind of network virtualization to be used - for the container. Each time - a field is found a new - round of network configuration begins. In this way, - several network virtualization types can be specified - for the same container, as well as assigning several - network interfaces for one container. The different - virtualization types can be: + for the container. + Multiple networks can be specified by using an additional index + + after all keys. For example, + and + specify two different + networks of the same type. All keys sharing the same index + will be treated as belonging to the same + network. For example, + will belong to . + Currently, the different virtualization types can be: @@ -357,7 +388,7 @@ a virtual ethernet pair device is created with one side assigned to the container and the other side attached to a bridge specified by - the option. + the option. If the bridge is not specified, then the veth pair device will be created but not attached to any bridge. Otherwise, the bridge has to be created on the system @@ -369,7 +400,7 @@ container, but if you wish to handle this name yourselves, you can tell lxc to set a specific name with - the option (except for + the option (except for unprivileged containers where this option is ignored for security reasons). @@ -377,17 +408,17 @@ a vlan interface is linked with the interface specified by - the and assigned to + the and assigned to the container. The vlan identifier is specified with the - option . + option . a macvlan interface is linked with the interface specified by - the and assigned to + the and assigned to the container. - specifies the + specifies the mode the macvlan will use to communicate between different macvlan on the same upper device. The accepted modes are , , @@ -419,7 +450,7 @@ an already existing interface - specified by the is + specified by the is assigned to the container. @@ -427,12 +458,11 @@ - + - specify an action to do for the - network. + Specify an action to do for the network. activates the interface. @@ -442,88 +472,81 @@ - + - specify the interface to be used for real network - traffic. - + Specify the interface to be used for real network traffic. + - + - specify the maximum transfer unit for this interface. + Specify the maximum transfer unit for this interface. - + - the interface name is dynamically allocated, but if - another name is needed because the configuration files - being used by the container use a generic name, - eg. eth0, this option will rename the interface in the - container. + The interface name is dynamically allocated, but if another name + is needed because the configuration files being used by the + container use a generic name, eg. eth0, this option will rename + the interface in the container. - + - the interface mac address is dynamically allocated by - default to the virtual interface, but in some cases, - this is needed to resolve a mac address conflict or to - always have the same link-local ipv6 address. - Any "x" in address will be replaced by random value, - this allows setting hwaddr templates. + The interface mac address is dynamically allocated by default to + the virtual interface, but in some cases, this is needed to + resolve a mac address conflict or to always have the same + link-local ipv6 address. Any "x" in address will be replaced by + random value, this allows setting hwaddr templates. - + - specify the ipv4 address to assign to the virtualized - interface. Several lines specify several ipv4 addresses. - The address is in format x.y.z.t/m, - eg. 192.168.1.123/24. The broadcast address should be - specified on the same line, right after the ipv4 - address. + Specify the ipv4 address to assign to the virtualized interface. + Several lines specify several ipv4 addresses. The address is in + format x.y.z.t/m, eg. 192.168.1.123/24. - + - specify the ipv4 address to use as the gateway inside the - container. The address is in format x.y.z.t, eg. - 192.168.1.123. + Specify the ipv4 address to use as the gateway inside the + container. The address is in format x.y.z.t, eg. 192.168.1.123. Can also have the special value , which means to take the primary address from the bridge interface (as specified by the - option) and use that as + option) and use that as the gateway. is only available when using the and network types. @@ -534,32 +557,31 @@ - + - specify the ipv6 address to assign to the virtualized - interface. Several lines specify several ipv6 addresses. - The address is in format x::y/m, - eg. 2003:db8:1:0:214:1234:fe0b:3596/64 + Specify the ipv6 address to assign to the virtualized + interface. Several lines specify several ipv6 addresses. The + address is in format x::y/m, eg. + 2003:db8:1:0:214:1234:fe0b:3596/64 - + - specify the ipv6 address to use as the gateway inside the - container. The address is in format x::y, - eg. 2003:db8:1:0::1 + Specify the ipv6 address to use as the gateway inside the + container. The address is in format x::y, eg. 2003:db8:1:0::1 Can also have the special value , which means to take the primary address from the bridge interface (as specified by the - option) and use that as + option) and use that as the gateway. is only available when using the and network types. @@ -569,11 +591,11 @@ - + - add a configuration option to specify a script to be + Add a configuration option to specify a script to be executed after creating and configuring the network used from the host side. The following arguments are passed to the script: container name and config section name @@ -594,11 +616,11 @@ - + - add a configuration option to specify a script to be + Add a configuration option to specify a script to be executed before destroying the network used from the host side. The following arguments are passed to the script: container name and config section name (net) @@ -628,7 +650,7 @@ - + @@ -663,7 +685,7 @@ - + @@ -696,7 +718,7 @@ - + @@ -725,7 +747,7 @@ - + @@ -770,25 +792,6 @@ - Enable kmsg symlink - - Enable creating /dev/kmsg as symlink to /dev/console. This defaults to 0. - - - - - - - - - Set this to 1 to enable /dev/kmsg symlinking. - - - - - - - Mount points The mount points section specifies the different places to be @@ -812,7 +815,7 @@ - + @@ -822,9 +825,9 @@ most cases should be a relative path, which will become relative to the mounted container root. For instance, - -proc proc proc nodev,noexec,nosuid 0 0 - + + proc proc proc nodev,noexec,nosuid 0 0 + Will mount a proc filesystem under the container's /proc, regardless of where the root filesystem comes from. This @@ -1039,7 +1042,7 @@ - + @@ -1073,7 +1076,7 @@ - where to recursively bind + where to recursively bind before pivoting. This is to ensure success of the pivot_root @@ -1096,20 +1099,6 @@ - - - - - - - specify the rootfs backend type to use, for instance 'dir' or - 'zfs'. While this can be guessed by lxc at container startup, - doing so takes time. Specifying it here avoids extra - processing. - - - - @@ -1140,6 +1129,25 @@ + + + + + + + specify a directory or path in which the container's cgroup will + be created. For example, setting + for a container + named "c1" will create the container's cgroup as a sub-cgroup of + "my-cgroup". For example, if the user's current cgroup "my-user" + is located in the root cgroup of the cpuset controllerin in a + cgroup v1 hierarchy this would create the cgroup + "/sys/fs/cgroup/cpuset/my-user/my-cgroup/first/c1" for the + container. Any missing cgroups will be created by LXC. This + presupposes that the user has write access to its current cgroup. + + + @@ -1189,6 +1197,40 @@ + Resource limits + + The soft and hard resource limits for the container can be changed. + Unprivileged containers can only lower them. Resources which are not + explicitly specified will be inherited. + + + + + + + + + Specify the resource limit to be set. A limit is specified as two + colon separated values which are either numeric or the word + 'unlimited'. A single value can be used as a shortcut to set both + soft and hard limit to the same value. The permitted names the + "RLIMIT_" resource names in lowercase without the "RLIMIT_" + prefix, eg. RLIMIT_NOFILE should be specified as "nofile". See + + setrlimit + 2 + . + If used with no value, lxc will clear the resource limit + specified up to this point. A resource with no explicitly + configured limitation will be inherited from the process starting + up the container. + + + + + + + Apparmor profile If lxc was compiled and installed with apparmor support, and the host @@ -1201,7 +1243,7 @@ - + @@ -1209,17 +1251,17 @@ be run. To specify that the container should be unconfined, use - lxc.aa_profile = unconfined + lxc.apparmor.profile = unconfined If the apparmor profile should remain unchanged (i.e. if you are nesting containers and are already confined), then use - lxc.aa_profile = unchanged + lxc.apparmor.profile = unchanged - + @@ -1255,14 +1297,14 @@ - + Specify the SELinux context under which the container should be run or unconfined_t. For example - lxc.se_context = system_u:system_r:lxc_t:s0:c22 + lxc.selinux.context = system_u:system_r:lxc_t:s0:c22 @@ -1295,15 +1337,17 @@ allowed except for mknod, which will simply do nothing and return 0 (success), looks like: - -2 -blacklist -mknod errno 0 - + + + 2 + blacklist + mknod errno 0 + + - + @@ -1316,6 +1360,34 @@ + PR_SET_NO_NEW_PRIVS + + With PR_SET_NO_NEW_PRIVS active execve() promises not to grant + privileges to do anything that could not have been done without + the execve() call (for example, rendering the set-user-ID and + set-group-ID mode bits, and file capabilities non-functional). + Once set, this bit cannot be unset. The setting of this bit is + inherited by children created by fork() and clone(), and preserved + across execve(). + Note that PR_SET_NO_NEW_PRIVS is applied after the container has + changed into its intended AppArmor profile or SElinux context. + + + + + + + + + Specify whether the PR_SET_NO_NEW_PRIVS flag should be set for the + container. Set to 1 to activate. + + + + + + + UID mappings A container can be started in a private user namespace with @@ -1330,7 +1402,7 @@ - + @@ -1373,7 +1445,7 @@ LXC_ROOTFS_MOUNT: the path to the mounted root filesystem. LXC_CONFIG_FILE: the path to the container configuration file. LXC_SRC_NAME: in the case of the clone hook, this is the original container's name. - LXC_ROOTFS_PATH: this is the lxc.rootfs entry for the container. Note this is likely not where the mounted rootfs is to be found, use LXC_ROOTFS_MOUNT for that. + LXC_ROOTFS_PATH: this is the lxc.rootfs.path entry for the container. Note this is likely not where the mounted rootfs is to be found, use LXC_ROOTFS_MOUNT for that. @@ -1566,7 +1638,7 @@ The path to the console output of the container if not NULL. - [] [] + [] [] @@ -1609,7 +1681,7 @@ The host relative path to the container root which has been mounted to the rootfs.mount location. - [] + [] @@ -1674,7 +1746,7 @@ - + @@ -1694,7 +1766,7 @@ - + @@ -1702,6 +1774,19 @@ + + + + + + + Send logging info to syslog. It respects the log level defined in + lxc.log.level. The argument should be the syslog + facility to use, valid ones are: daemon, local0, local1, local2, + local3, local4, local5, local5, local6, local7. + + + @@ -1852,14 +1937,14 @@ virtual network device visible in the container is renamed to eth0. - lxc.utsname = myhostname - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.name = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 10.2.3.5/24 10.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.uts.name = myhostname + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.name = eth0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 10.2.3.5/24 10.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 @@ -1869,8 +1954,8 @@ range 0-9999 in the container to the ids 100000-109999 on the host. - lxc.id_map = u 0 100000 10000 - lxc.id_map = g 0 100000 10000 + lxc.idmap = u 0 100000 10000 + lxc.idmap = g 0 100000 10000 @@ -1895,35 +1980,35 @@ network stack, using the control groups, setting a new hostname, mounting some locations and a changing root file system. - lxc.utsname = complex - lxc.network.type = veth - lxc.network.flags = up - lxc.network.link = br0 - lxc.network.hwaddr = 4a:49:43:49:79:bf - lxc.network.ipv4 = 10.2.3.5/24 10.2.3.255 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3597 - lxc.network.ipv6 = 2003:db8:1:0:214:5432:feab:3588 - lxc.network.type = macvlan - lxc.network.flags = up - lxc.network.link = eth0 - lxc.network.hwaddr = 4a:49:43:49:79:bd - lxc.network.ipv4 = 10.2.3.4/24 - lxc.network.ipv4 = 192.168.10.125/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596 - lxc.network.type = phys - lxc.network.flags = up - lxc.network.link = dummy0 - lxc.network.hwaddr = 4a:49:43:49:79:ff - lxc.network.ipv4 = 10.2.3.6/24 - lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3297 + lxc.uts.name = complex + lxc.net.0.type = veth + lxc.net.0.flags = up + lxc.net.0.link = br0 + lxc.net.0.hwaddr = 4a:49:43:49:79:bf + lxc.net.0.ipv4.address = 10.2.3.5/24 10.2.3.255 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3597 + lxc.net.0.ipv6.address = 2003:db8:1:0:214:5432:feab:3588 + lxc.net.1.type = macvlan + lxc.net.1.flags = up + lxc.net.1.link = eth0 + lxc.net.1.hwaddr = 4a:49:43:49:79:bd + lxc.net.1.ipv4.address = 10.2.3.4/24 + lxc.net.1.ipv4.address = 192.168.10.125/24 + lxc.net.1.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3596 + lxc.net.2.type = phys + lxc.net.2.flags = up + lxc.net.2.link = dummy0 + lxc.net.2.hwaddr = 4a:49:43:49:79:ff + lxc.net.2.ipv4.address = 10.2.3.6/24 + lxc.net.2.ipv6.address = 2003:db8:1:0:214:1234:fe0b:3297 lxc.cgroup.cpuset.cpus = 0,1 lxc.cgroup.cpu.shares = 1234 lxc.cgroup.devices.deny = a lxc.cgroup.devices.allow = c 1:3 rw lxc.cgroup.devices.allow = b 8:0 rw - lxc.mount = /etc/fstab.complex + lxc.mount.fstab = /etc/fstab.complex lxc.mount.entry = /lib /root/myrootfs/lib none ro,bind 0 0 - lxc.rootfs = /mnt/rootfs.complex + lxc.rootfs.path = dir:/mnt/rootfs.complex lxc.cap.drop = sys_module mknod setuid net_raw lxc.cap.drop = mac_override diff -Nru lxc-2.0.8/doc/lxc-copy.sgml.in lxc-2.1.0/doc/lxc-copy.sgml.in --- lxc-2.0.8/doc/lxc-copy.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc-copy.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -55,7 +55,8 @@ -p, --newpath newpath -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -69,7 +70,8 @@ -e, --ephemeral -B, --backingstorage backingstorage -s, --snapshot - -K, --keepdata + -K, --keepname + -D, --keepdata -M, --keepmac -L, --fssize size [unit] -- hook arguments @@ -78,6 +80,20 @@ lxc-copy -n, --name name -P, --lxcpath path + -N, --newname newname + -p, --newpath newpath + -e, --ephemeral + -B, --backingstorage backingstorage + -s, --snapshot + -t, --tmpfs + -K, --keepname + -M, --keepmac + -- hook arguments + + + lxc-copy + -n, --name name + -P, --lxcpath path -N, --newname newname -p, --newpath newpath -R, --rename @@ -115,7 +131,11 @@ config file and will be destroyed on shutdown. When -e is used in combination with -D a non-ephemeral snapshot of the original - container is created and started. + container is created and started. + Ephemeral containers can also be placed on a tmpfs with -t + flag. NOTE: If an ephemeral container that is placed on a tmpfs is rebooted + all changes made to it will currently be lost! + When -e is specified and no newname is given via @@ -223,6 +243,19 @@ + + + When this option is specified the ephemeral container will be + placed on a tmpfs. NOTE: Rebooting an ephemeral container that is + located on a tmpfs will currently cause all changes made to it to be + lost. This flag will only work for ephemeral containers created with + the -e flag. The original container, from + which the ephemeral snapshot is created, must be stored as a simple + directory. + + + + Specify the backing storage type to be used for the copy @@ -239,18 +272,18 @@ - + - When this option is specified with - -e a non-ephemeral container is created - and started. + When this option is specified the hostname of the original + container will be kept for the copy. - + - When this option is specified the hostname of the original - container will be kept for the copy. + When this option is specified with + -e a non-ephemeral container is created + and started. diff -Nru lxc-2.0.8/doc/lxc-info.sgml.in lxc-2.1.0/doc/lxc-info.sgml.in --- lxc-2.0.8/doc/lxc-info.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc-info.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -175,7 +175,7 @@ - lxc-info -n foo -c lxc.network.0.veth.pair + lxc-info -n foo -c lxc.net.0.veth.pair prints the veth pair name of foo. diff -Nru lxc-2.0.8/doc/lxc-ls.sgml.in lxc-2.1.0/doc/lxc-ls.sgml.in --- lxc-2.0.8/doc/lxc-ls.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc-ls.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -55,6 +55,7 @@ --frozen --running --stopped + --defined -f -F format -g groups @@ -128,6 +129,17 @@ + + + + + + + + List only defined containers. + + + diff -Nru lxc-2.0.8/doc/lxc.sgml.in lxc-2.1.0/doc/lxc.sgml.in --- lxc-2.0.8/doc/lxc.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -52,136 +52,67 @@ - Quick start - - You are in a hurry, and you don't want to read this man page. Ok, - without warranty, here are the commands to launch a shell inside - a container with a predefined configuration template, it may - work. - @BINDIR@/lxc-execute -n foo -f - @DOCDIR@/examples/lxc-macvlan.conf /bin/bash - - - - Overview - The container technology is actively being pushed into the - mainstream linux kernel. It provides the resource management - through the control groups aka process containers and resource - isolation through the namespaces. + The container technology is actively being pushed into the mainstream + Linux kernel. It provides resource management through control groups and + resource isolation via namespaces. - The linux containers, lxc, aims to use these - new functionalities to provide a userspace container object - which provides full resource isolation and resource control for - an applications or a system. + lxc, aims to use these new functionalities to provide a + userspace container object which provides full resource isolation and + resource control for an applications or a full system. - The first objective of this project is to make the life easier - for the kernel developers involved in the containers project and - especially to continue working on the Checkpoint/Restart new - features. The lxc is small enough to easily - manage a container with simple command lines and complete enough - to be used for other purposes. + lxc is small enough to easily manage a container with + simple command lines and complete enough to be used for other purposes. Requirements - The lxc relies on a set of functionalities - provided by the kernel which needs to be active. Depending of - the missing functionalities the lxc will - work with a restricted number of functionalities or will simply - fail. + The kernel version >= 3.10 shipped with the distros, will work with + lxc, this one will have less functionalities but enough + to be interesting. - + - The following list gives the kernel features to be enabled in - the kernel to have the full features container: + lxc relies on a set of functionalities provided by the + kernel. The helper script lxc-checkconfig will give + you information about your kernel configuration, required, and missing + features. - - * General setup - * Control Group support - -> Namespace cgroup subsystem - -> Freezer cgroup subsystem - -> Cpuset support - -> Simple CPU accounting cgroup subsystem - -> Resource counters - -> Memory resource controllers for Control Groups - * Group CPU scheduler - -> Basis for grouping tasks (Control Groups) - * Namespaces support - -> UTS namespace - -> IPC namespace - -> User namespace - -> Pid namespace - -> Network namespace - * Device Drivers - * Character devices - -> Support multiple instances of devpts - * Network device support - -> MAC-VLAN support - -> Virtual ethernet pair device - * Networking - * Networking options - -> 802.1d Ethernet Bridging - * Security options - -> File POSIX Capabilities - - - - - The kernel version >= 2.6.32 shipped with the distros, will - work with lxc, this one will have less - functionalities but enough to be interesting. - - The helper script lxc-checkconfig will give - you information about your kernel configuration. - - - - The control group can be mounted anywhere, eg: - mount -t cgroup cgroup /cgroup. - - It is however recommended to use cgmanager, cgroup-lite or systemd - to mount the cgroup hierarchy under /sys/fs/cgroup. - - - Functional specification - A container is an object isolating some resources of the host, - for the application or system running in it. + A container is an object isolating some resources of the host, for the + application or system running in it. - The application / system will be launched inside a - container specified by a configuration that is either - initially created or passed as parameter of the starting commands. + The application / system will be launched inside a container specified by + a configuration that is either initially created or passed as a parameter + of the commands. - How to run an application in a container ? + How to run an application in a container - Before running an application, you should know what are the - resources you want to isolate. The default configuration is to - isolate the pids, the sysv ipc and the mount points. If you want - to run a simple shell inside a container, a basic configuration - is needed, especially if you want to share the rootfs. If you - want to run an application like sshd, you - should provide a new network stack and a new hostname. If you - want to avoid conflicts with some files - eg. /var/run/httpd.pid, you should - remount /var/run with an empty - directory. If you want to avoid the conflicts in all the cases, - you can specify a rootfs for the container. The rootfs can be a - directory tree, previously bind mounted with the initial rootfs, - so you can still use your distro but with your + Before running an application, you should know what are the resources you + want to isolate. The default configuration is to isolate PIDs, the sysv + IPC and mount points. If you want to run a simple shell inside a + container, a basic configuration is needed, especially if you want to + share the rootfs. If you want to run an application like + sshd, you should provide a new network stack and a new + hostname. If you want to avoid conflicts with some files eg. + /var/run/httpd.pid, you should remount + /var/run with an empty directory. If you want to + avoid the conflicts in all the cases, you can specify a rootfs for the + container. The rootfs can be a directory tree, previously bind mounted + with the initial rootfs, so you can still use your distro but with your own /etc and /home @@ -225,15 +156,17 @@ - How to run a system in a container ? + How to run a system in a container - Running a system inside a container is paradoxically easier - than running an application. Why ? Because you don't have to care - about the resources to be isolated, everything need to be + + Running a system inside a container is paradoxically easier + than running an application. Why? Because you don't have to care + about the resources to be isolated, everything needs to be isolated, the other resources are specified as being isolated but without configuration because the container will set them up. eg. the ipv4 address will be setup by the system container init scripts. Here is an example of the mount points file: + [root@lxc debian]$ cat fstab @@ -242,26 +175,17 @@ /dev/pts /home/root/debian/rootfs/dev/pts none bind 0 0 - More information can be added to the container to facilitate the - configuration. For example, make accessible from the container - the resolv.conf file belonging to the host. - - - /etc/resolv.conf /home/root/debian/rootfs/etc/resolv.conf none bind 0 0 - - - Container life cycle When the container is created, it contains the configuration - information. When a process is launched, the container will be - starting and running. When the last process running inside the - container exits, the container is stopped. + information. When a process is launched, the container will be starting + and running. When the last process running inside the container exits, + the container is stopped. - In case of failure when the container is initialized, it will - pass through the aborting state. + In case of failure when the container is initialized, it will pass + through the aborting state. @@ -306,17 +230,14 @@ - Creating / Destroying container - (persistent container) + Creating / Destroying containers - A persistent container object can be - created via the lxc-create - command. It takes a container name as parameter and - optional configuration file and template. - The name is used by the different - commands to refer to this - container. The lxc-destroy command will - destroy the container object. + A persistent container object can be created via the + lxc-create command. It takes a container name as + parameter and optional configuration file and template. The name is + used by the different commands to refer to this container. The + lxc-destroy command will destroy the container + object. lxc-create -n foo lxc-destroy -n foo @@ -326,33 +247,30 @@ Volatile container - It is not mandatory to create a container object - before to start it. - The container can be directly started with a - configuration file as parameter. + + It is not mandatory to create a container object before starting it. + The container can be directly started with a configuration file as + parameter. Starting / Stopping container - When the container has been created, it is ready to run an - application / system. - This is the purpose of the lxc-execute and - lxc-start commands. - If the container was not created before - starting the application, the container will use the - configuration file passed as parameter to the command, - and if there is no such parameter either, then - it will use a default isolation. - If the application is ended, the container will be stopped also, - but if needed the lxc-stop command can - be used to kill the still running application. + + When the container has been created, it is ready to run an application / + system. This is the purpose of the lxc-execute and + lxc-start commands. If the container was not created + before starting the application, the container will use the + configuration file passed as parameter to the command, and if there is + no such parameter either, then it will use a default isolation. If the + application ended, the container will be stopped, but if needed the + lxc-stop command can be used to stop the container. - Running an application inside a container is not exactly the - same thing as running a system. For this reason, there are two - different commands to run an application into a container: + Running an application inside a container is not exactly the same thing + as running a system. For this reason, there are two different commands + to run an application into a container: lxc-execute -n foo [-f config] /bin/bash lxc-start -n foo [-f config] [/bin/bash] @@ -360,39 +278,35 @@ - lxc-execute command will run the - specified command into the container via an intermediate - process, lxc-init. - This lxc-init after launching the specified command, - will wait for its end and all other reparented processes. - (to support daemons in the container). - In other words, in the - container, lxc-init has the pid 1 and the - first process of the application has the pid 2. + The lxc-execute command will run the specified command + into a container via an intermediate process, + lxc-init. + This lxc-init after launching the specified command, will wait for its + end and all other reparented processes. (to support daemons in the + container). In other words, in the container, + lxc-init has PID 1 and the first process of the + application has PID 2. - lxc-start command will run directly the specified - command into the container. - The pid of the first process is 1. If no command is - specified lxc-start will - run the command defined in lxc.init_cmd or if not set, - /sbin/init . + The lxc-start command will directly run the specified + command in the container. The PID of the first process is 1. If no + command is specified lxc-start will run the command + defined in lxc.init.cmd or if not set, /sbin/init . - To summarize, lxc-execute is for running - an application and lxc-start is better suited for + To summarize, lxc-execute is for running an + application and lxc-start is better suited for running a system. - If the application is no longer responding, is inaccessible or is - not able to finish by itself, a - wild lxc-stop command will kill all the - processes in the container without pity. + If the application is no longer responding, is inaccessible or is not + able to finish by itself, a wild lxc-stop command + will kill all the processes in the container without pity. - lxc-stop -n foo + lxc-stop -n foo -k @@ -400,11 +314,10 @@ Connect to an available tty - If the container is configured with the ttys, it is possible - to access it through them. It is up to the container to - provide a set of available tty to be used by the following - command. When the tty is lost, it is possible to reconnect it - without login again. + If the container is configured with ttys, it is possible to access it + through them. It is up to the container to provide a set of available + ttys to be used by the following command. When the tty is lost, it is + possible to reconnect to it without login again. lxc-console -n foo -t 3 @@ -430,30 +343,28 @@ - This feature is enabled if the cgroup freezer is enabled in the - kernel. + This feature is enabled if the freezer cgroup v1 controller is enabled + in the kernel. Getting information about container - When there are a lot of containers, it is hard to follow - what has been created or destroyed, what is running or what are - the pids running into a specific container. For this reason, the - following commands may be useful: + + When there are a lot of containers, it is hard to follow what has been + created or destroyed, what is running or what are the PIDs running in a + specific container. For this reason, the following commands may be useful: - lxc-ls + lxc-ls -f lxc-info -n foo - lxc-ls lists the containers of the - system. + lxc-ls lists containers. - lxc-info gives information for a specific - container. + lxc-info gives information for a specific container. @@ -464,22 +375,20 @@ lxc-info -n $i done - - Monitoring container - It is sometime useful to track the states of a container, - for example to monitor it or just to wait for a specific - state in a script. + + It is sometime useful to track the states of a container, for example to + monitor it or just to wait for a specific state in a script. - lxc-monitor command will monitor one or - several containers. The parameter of this command accept a - regular expression for example: + lxc-monitor command will monitor one or several + containers. The parameter of this command accepts a regular expression + for example: lxc-monitor -n "foo|bar" @@ -504,8 +413,8 @@ state change and exit. This is useful for scripting to synchronize the launch of a container or the end. The parameter is an ORed combination of different states. The - following example shows how to wait for a container if he went - to the background. + following example shows how to wait for a container if it successfully + started as a daemon. - Setting the control group for container - The container is tied with the control groups, when a - container is started a control group is created and associated - with it. The control group properties can be read and modified - when the container is running by using the lxc-cgroup command. + cgroup settings for containers + + The container is tied with the control groups, when a container is + started a control group is created and associated with it. The control + group properties can be read and modified when the container is running + by using the lxc-cgroup command. lxc-cgroup command is used to set or get a @@ -553,18 +463,14 @@ - - Bugs - The lxc is still in development, so the - command syntax and the API can change. The version 1.0.0 will be - the frozen version. - - &seealso; Author Daniel Lezcano daniel.lezcano@free.fr + Christian Brauner christian.brauner@ubuntu.com + Serge Hallyn serge@hallyn.com + Stéphane Graber stgraber@ubuntu.com diff -Nru lxc-2.0.8/doc/lxc-start.sgml.in lxc-2.1.0/doc/lxc-start.sgml.in --- lxc-2.0.8/doc/lxc-start.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc-start.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -80,7 +80,7 @@ If no command is specified, lxc-start will - use the command defined in lxc.init_cmd or if not set, the default + use the command defined in lxc.init.cmd or if not set, the default "/sbin/init" command to run a system container. diff -Nru lxc-2.0.8/doc/lxc-stop.sgml.in lxc-2.1.0/doc/lxc-stop.sgml.in --- lxc-2.0.8/doc/lxc-stop.sgml.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/doc/lxc-stop.sgml.in 2017-09-06 02:32:37.000000000 +0000 @@ -66,12 +66,12 @@ lxc-stop reboots, cleanly shuts down, or kills all the processes inside the container. By default, it will request a clean shutdown of the container by sending - lxc.haltsignal (defaults to SIGPWR) to + lxc.signal.halt (defaults to SIGPWR) to the container's init process, waiting up to 60 seconds for the container to exit, and then returning. If the container fails to cleanly exit in - 60 seconds, it will be sent the lxc.stopsignal + 60 seconds, it will be sent the lxc.signal.stop (defaults to SIGKILL) to force it to shut down. A request to reboot will - send the lxc.rebootsignal (defaults to SIGINT) to the + send the lxc.signal.reboot (defaults to SIGINT) to the container's init process. diff -Nru lxc-2.0.8/lxc.pc.in lxc-2.1.0/lxc.pc.in --- lxc-2.0.8/lxc.pc.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/lxc.pc.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,8 +1,7 @@ -prefix=@prefix@ bindir=@BINDIR@ -libdir=${prefix}/@LIBDIR@ +libdir=@LIBDIR@ localstatedir=@LOCALSTATEDIR@ -includedir=${prefix}/@INCLUDEDIR@ +includedir=@INCLUDEDIR@ rootfsmountdir=@LXCROOTFSMOUNT@ Name: lxc diff -Nru lxc-2.0.8/lxc.spec lxc-2.1.0/lxc.spec --- lxc-2.0.8/lxc.spec 2017-05-11 17:23:18.000000000 +0000 +++ lxc-2.1.0/lxc.spec 2017-09-06 02:32:50.000000000 +0000 @@ -60,7 +60,7 @@ %endif Name: lxc -Version: 2.0.8 +Version: 2.1.0 Release: %{?beta_rel:0.1.%{beta_rel}}%{?!beta_rel:%{norm_rel}}%{?dist} URL: http://linuxcontainers.org Source: http://linuxcontainers.org/downloads/%{name}-%{version}%{?beta_dot}.tar.gz @@ -97,6 +97,7 @@ %if %{with_python} Requires: python3 BuildRequires: python3-devel +BuildRequires: python3-setuptools %endif %description diff -Nru lxc-2.0.8/lxc.spec.in lxc-2.1.0/lxc.spec.in --- lxc-2.0.8/lxc.spec.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/lxc.spec.in 2017-09-06 02:32:37.000000000 +0000 @@ -97,6 +97,7 @@ %if %{with_python} Requires: python3 BuildRequires: python3-devel +BuildRequires: python3-setuptools %endif %description diff -Nru lxc-2.0.8/Makefile.in lxc-2.1.0/Makefile.in --- lxc-2.0.8/Makefile.in 2017-05-11 17:23:09.000000000 +0000 +++ lxc-2.1.0/Makefile.in 2017-09-06 02:32:41.000000000 +0000 @@ -110,7 +110,8 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = lxc.pc lxc.spec src/lxc/tools/lxc-checkconfig \ - src/lxc/tools/lxc-start-ephemeral + src/lxc/tools/lxc-start-ephemeral \ + src/lxc/tools/lxc-update-config CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -203,7 +204,8 @@ $(top_srcdir)/config/install-sh $(top_srcdir)/config/ltmain.sh \ $(top_srcdir)/config/missing \ $(top_srcdir)/src/lxc/tools/lxc-checkconfig.in \ - $(top_srcdir)/src/lxc/tools/lxc-start-ephemeral.in AUTHORS \ + $(top_srcdir)/src/lxc/tools/lxc-start-ephemeral.in \ + $(top_srcdir)/src/lxc/tools/lxc-update-config.in AUTHORS \ COPYING ChangeLog INSTALL NEWS README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) @@ -495,6 +497,8 @@ cd $(top_builddir) && $(SHELL) ./config.status $@ src/lxc/tools/lxc-start-ephemeral: $(top_builddir)/config.status $(top_srcdir)/src/lxc/tools/lxc-start-ephemeral.in cd $(top_builddir) && $(SHELL) ./config.status $@ +src/lxc/tools/lxc-update-config: $(top_builddir)/config.status $(top_srcdir)/src/lxc/tools/lxc-update-config.in + cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo diff -Nru lxc-2.0.8/README lxc-2.1.0/README --- lxc-2.0.8/README 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/README 2017-09-06 02:32:37.000000000 +0000 @@ -1,93 +0,0 @@ -Please see the COPYING file for details on copying and usage. -Please refer to the INSTALL file for instructions on how to build. - -What is lxc: - - The container technology is actively being pushed into the mainstream linux - kernel. It provides the resource management through the control groups aka - process containers and resource isolation through the namespaces. - - The linux containers, lxc, aims to use these new functionalities to pro- - vide a userspace container object which provides full resource isolation - and resource control for an application or a system. - - The first objective of this project is to make the life easier for the ker- - nel developers involved in the containers project and especially to con- - tinue working on the Checkpoint/Restart new features. The lxc is small - enough to easily manage a container with simple command lines and complete - enough to be used for other purposes. - -Using lxc: - - Refer the lxc* man pages (generated from doc/* files) - -Downloading the current source code: - - Source for the latest released version can always be downloaded from - http://linuxcontainers.org/downloads/ - - You can browse the up to the minute source code and change history online. - http://github.com/lxc/lxc - - For detailed build instruction refer to INSTALL and man lxc man page - but a short command line should work: - ./autogen.sh && ./configure && make && sudo make install - preceded by ./autogen.sh if configure do not exist yet. - -Troubleshooting: - - If you get an error message at the autogen.sh or configure stage, make - sure you have, autoconf, automake, pkg-config, make and gcc installed on - your machine. - - The configure script will usually give you hints as to what you are missing, - looking for those in your package manager will usually give you the package - that you need to install. - - Also pay a close attention to the feature summary showed at the end of - the configure run, features are automatically enabled/disabled based on - whether the needed development packages are installed on your machine. - If you want a feature but don't know what to install, force it with - --enable- and look at the error message from configure. - -Getting help: - - when you find you need help, you can check out one of the two - lxc mailing list archives and register if interested: - http://lists.linuxcontainers.org/listinfo/lxc-devel - http://lists.linuxcontainers.org/listinfo/lxc-users - -Portability: - - lxc is developed and tested on Linux since kernel mainline version 2.6.27 - (without network) and 2.6.29 with network isolation. - It's compiled with gcc, and should work on most architectures as long as the - required kernel features are available. This includes (but isn't limited to): - i686, x86_64, ppc, ppc64, S390, armel and armhf. - -AUTHOR - Daniel Lezcano - -Seccomp with LXC ----------------- - -To restrict a container with seccomp, you must specify a profile which is -basically a whitelist of system calls it may execute. In the container -config file, add a line like - -lxc.seccomp = /var/lib/lxc/q1/seccomp.full - -I created a usable (but basically worthless) seccomp.full file using - -cat > seccomp.full << EOF -1 -whitelist -EOF -for i in `seq 0 300`; do - echo $i >> seccomp.full -done -for i in `seq 1024 1079`; do - echo $i >> seccomp.full -done - - -- Serge Hallyn Fri, 27 Jul 2012 15:47:02 +0600 diff -Nru lxc-2.0.8/src/config.h.in lxc-2.1.0/src/config.h.in --- lxc-2.0.8/src/config.h.in 2017-05-11 17:23:08.000000000 +0000 +++ lxc-2.1.0/src/config.h.in 2017-09-06 02:35:15.000000000 +0000 @@ -19,6 +19,14 @@ you don't. */ #undef HAVE_DECL_PR_CAPBSET_DROP +/* Define to 1 if you have the declaration of `PR_GET_NO_NEW_PRIVS', and to 0 + if you don't. */ +#undef HAVE_DECL_PR_GET_NO_NEW_PRIVS + +/* Define to 1 if you have the declaration of `PR_SET_NO_NEW_PRIVS', and to 0 + if you don't. */ +#undef HAVE_DECL_PR_SET_NO_NEW_PRIVS + /* Define to 1 if you have the declaration of `seccomp_syscall_resolve_name_arch', and to 0 if you don't. */ #undef HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH @@ -92,6 +100,9 @@ /* Define to 1 if you have the `pivot_root' function. */ #undef HAVE_PIVOT_ROOT +/* Define to 1 if you have the `prlimit' function. */ +#undef HAVE_PRLIMIT + /* Define to 1 if you have the `pthread_atfork' function. */ #undef HAVE_PTHREAD_ATFORK @@ -137,6 +148,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PERSONALITY_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SIGNALFD_H diff -Nru lxc-2.0.8/src/include/getline.h lxc-2.1.0/src/include/getline.h --- lxc-2.0.8/src/include/getline.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/include/getline.h 2017-09-06 02:32:37.000000000 +0000 @@ -27,8 +27,8 @@ * SUCH DAMAGE. */ -#ifndef _getline_h -#define _getline_h +#ifndef _GETLINE_H +#define _GETLINE_H #include diff -Nru lxc-2.0.8/src/include/getsubopt.h lxc-2.1.0/src/include/getsubopt.h --- lxc-2.0.8/src/include/getsubopt.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/include/getsubopt.h 2017-09-06 02:32:37.000000000 +0000 @@ -1,4 +1,4 @@ -#ifndef _getsubopt_h -#define _getsubopt_h +#ifndef _GETSUBOPT_H +#define _GETSUBOPT_H int getsubopt (char **optionp, char *const *tokens, char **valuep); #endif diff -Nru lxc-2.0.8/src/include/lxcmntent.h lxc-2.1.0/src/include/lxcmntent.h --- lxc-2.0.8/src/include/lxcmntent.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/include/lxcmntent.h 2017-09-06 02:32:37.000000000 +0000 @@ -18,8 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _lxcmntent_h -#define _lxcmntent_h +#ifndef _LXCMNTENT_H +#define _LXCMNTENT_H #if IS_BIONIC struct mntent diff -Nru lxc-2.0.8/src/include/openpty.h lxc-2.1.0/src/include/openpty.h --- lxc-2.0.8/src/include/openpty.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/include/openpty.h 2017-09-06 02:32:37.000000000 +0000 @@ -21,8 +21,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _openpty_h -#define _openpty_h +#ifndef _OPENPTY_H +#define _OPENPTY_H #include #include diff -Nru lxc-2.0.8/src/include/prlimit.c lxc-2.1.0/src/include/prlimit.c --- lxc-2.0.8/src/include/prlimit.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/include/prlimit.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include /* __le64, __l32 ... */ +#include +#include +#include +#include +#include +#include + +#if defined(__LP64__) +#error This code is only needed on 32-bit systems! +#endif + +#define RLIM64_INFINITY (~0ULL) + +typedef uint64_t u64; + +// There is no prlimit system call, so we need to use prlimit64. +int prlimit(pid_t pid, int resource, const struct rlimit *n32, struct rlimit *o32) +{ + struct rlimit64 n64; + if (n32 != NULL) { + n64.rlim_cur = (n32->rlim_cur == RLIM_INFINITY) + ? RLIM64_INFINITY + : n32->rlim_cur; + n64.rlim_max = (n32->rlim_max == RLIM_INFINITY) + ? RLIM64_INFINITY + : n32->rlim_max; + } + + struct rlimit64 o64; + int result = prlimit64( + pid, resource, (n32 != NULL) ? (const struct rlimit64 *)&n64 : NULL, + (o32 != NULL) ? &o64 : NULL); + + if (result != -1 && o32 != NULL) { + o32->rlim_cur = (o64.rlim_cur == RLIM64_INFINITY) + ? RLIM_INFINITY + : o64.rlim_cur; + o32->rlim_max = (o64.rlim_max == RLIM64_INFINITY) + ? RLIM_INFINITY + : o64.rlim_max; + } + + return result; +} diff -Nru lxc-2.0.8/src/include/prlimit.h lxc-2.1.0/src/include/prlimit.h --- lxc-2.0.8/src/include/prlimit.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/include/prlimit.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PRLIMIT_H +#define _PRLIMIT_H + +#include +#include +#include + +#define RLIM_SAVED_CUR RLIM_INFINITY +#define RLIM_SAVED_MAX RLIM_INFINITY + +int prlimit(pid_t, int, const struct rlimit*, struct rlimit*); +int prlimit64(pid_t, int, const struct rlimit64*, struct rlimit64*); + +#endif diff -Nru lxc-2.0.8/src/lua-lxc/test/apitest.lua lxc-2.1.0/src/lua-lxc/test/apitest.lua --- lxc-2.0.8/src/lua-lxc/test/apitest.lua 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lua-lxc/test/apitest.lua 2017-09-06 02:32:37.000000000 +0000 @@ -225,7 +225,7 @@ function test_container_cmd() log(0, "Test get config from running container...") - veth_pair = lxc.cmd_get_config_item(optarg["n"], "lxc.network.0.veth.pair") + veth_pair = lxc.cmd_get_config_item(optarg["n"], "lxc.net.0.veth.pair") log(0, " veth.pair:%s", veth_pair) end @@ -233,11 +233,11 @@ log(0, "Test set/clear configuration items...") -- test setting a 'single type' item - assert(container:get_config_item("lxc.utsname") == optarg["n"]) - container:set_config_item("lxc.utsname", "foobar") - assert(container:get_config_item("lxc.utsname") == "foobar") - container:set_config_item("lxc.utsname", optarg["n"]) - assert(container:get_config_item("lxc.utsname") == optarg["n"]) + assert(container:get_config_item("lxc.uts.name") == optarg["n"]) + container:set_config_item("lxc.uts.name", "foobar") + assert(container:get_config_item("lxc.uts.name") == "foobar") + container:set_config_item("lxc.uts.name", optarg["n"]) + assert(container:get_config_item("lxc.uts.name") == optarg["n"]) -- test clearing/setting a 'list type' item container:clear_config_item("lxc.cap.drop") @@ -281,7 +281,7 @@ log(0, "Test network %d config...", net_nr) local netcfg - netcfg = container:get_keys("lxc.network." .. net_nr) + netcfg = container:get_keys("lxc.net." .. net_nr) if (netcfg == nil) then return end @@ -289,7 +289,7 @@ log(0, " %s = %s", k, v or "") end assert(netcfg["flags"] == "up") - assert(container:get_config_item("lxc.network."..net_nr..".type") == "veth") + assert(container:get_config_item("lxc.net."..net_nr..".type") == "veth") end diff -Nru lxc-2.0.8/src/lxc/af_unix.c lxc-2.1.0/src/lxc/af_unix.c --- lxc-2.0.8/src/lxc/af_unix.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/af_unix.c 2017-09-06 02:32:37.000000000 +0000 @@ -22,6 +22,8 @@ */ #include "config.h" +#include +#include #include #include #include @@ -36,13 +38,10 @@ int lxc_abstract_unix_open(const char *path, int type, int flags) { - int fd; + int fd, ret; size_t len; struct sockaddr_un addr; - if (flags & O_TRUNC) - unlink(path); - fd = socket(PF_UNIX, type, 0); if (fd < 0) return -1; @@ -65,18 +64,24 @@ /* addr.sun_path[0] has already been set to 0 by memset() */ strncpy(&addr.sun_path[1], &path[1], strlen(&path[1])); - if (bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1)) { + ret = bind(fd, (struct sockaddr *)&addr, + offsetof(struct sockaddr_un, sun_path) + len + 1); + if (ret < 0) { int tmp = errno; close(fd); errno = tmp; return -1; } - if (type == SOCK_STREAM && listen(fd, 100)) { - int tmp = errno; - close(fd); - errno = tmp; - return -1; + if (type == SOCK_STREAM) { + ret = listen(fd, 100); + if (ret < 0) { + int tmp = errno; + close(fd); + errno = tmp; + return -1; + } + } return fd; @@ -84,21 +89,13 @@ int lxc_abstract_unix_close(int fd) { - struct sockaddr_un addr; - socklen_t addrlen = sizeof(addr); - - if (!getsockname(fd, (struct sockaddr *)&addr, &addrlen) && - addr.sun_path[0]) - unlink(addr.sun_path); - close(fd); - return 0; } int lxc_abstract_unix_connect(const char *path) { - int fd; + int fd, ret; size_t len; struct sockaddr_un addr; @@ -120,62 +117,76 @@ /* addr.sun_path[0] has already been set to 0 by memset() */ strncpy(&addr.sun_path[1], &path[1], strlen(&path[1])); - if (connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1)) { - int tmp = errno; - /* special case to connect to older containers */ - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) - return fd; + ret = connect(fd, (struct sockaddr *)&addr, + offsetof(struct sockaddr_un, sun_path) + len + 1); + if (ret < 0) { close(fd); - errno = tmp; return -1; } return fd; } -int lxc_abstract_unix_send_fd(int fd, int sendfd, void *data, size_t size) +int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, + void *data, size_t size) { - struct msghdr msg = { 0 }; + int ret; + struct msghdr msg; struct iovec iov; - struct cmsghdr *cmsg; - char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0}; + struct cmsghdr *cmsg = NULL; char buf[1] = {0}; - int *val; + char *cmsgbuf; + size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); + + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + cmsgbuf = malloc(cmsgbufsize); + if (!cmsgbuf) + return -1; msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); + msg.msg_controllen = cmsgbufsize; cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; - val = (int *)(CMSG_DATA(cmsg)); - *val = sendfd; + cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); - msg.msg_name = NULL; - msg.msg_namelen = 0; + msg.msg_controllen = cmsg->cmsg_len; + + memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; - return sendmsg(fd, &msg, MSG_NOSIGNAL); + ret = sendmsg(fd, &msg, MSG_NOSIGNAL); + free(cmsgbuf); + return ret; } -int lxc_abstract_unix_recv_fd(int fd, int *recvfd, void *data, size_t size) +int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, + void *data, size_t size) { - struct msghdr msg = { 0 }; + int ret; + struct msghdr msg; struct iovec iov; - struct cmsghdr *cmsg; - int ret, *val; - char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0}; + struct cmsghdr *cmsg = NULL; char buf[1] = {0}; + char *cmsgbuf; + size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int)); + + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + cmsgbuf = malloc(cmsgbufsize); + if (!cmsgbuf) + return -1; - msg.msg_name = NULL; - msg.msg_namelen = 0; msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); + msg.msg_controllen = cmsgbufsize; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); @@ -188,29 +199,24 @@ cmsg = CMSG_FIRSTHDR(&msg); - /* if the message is wrong the variable will not be - * filled and the peer will notified about a problem */ - *recvfd = -1; - - if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && - cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - val = (int *) CMSG_DATA(cmsg); - *recvfd = *val; + memset(recvfds, -1, num_recvfds * sizeof(int)); + if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && + cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); } + out: + free(cmsgbuf); return ret; } int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) { - struct msghdr msg = { 0 }; + struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred = { - .pid = getpid(), - .uid = getuid(), - .gid = getgid(), + .pid = getpid(), .uid = getuid(), .gid = getgid(), }; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; @@ -237,7 +243,7 @@ int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) { - struct msghdr msg = { 0 }; + struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred; @@ -262,10 +268,11 @@ cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && - cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS) { + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); - if (cred.uid && (cred.uid != getuid() || cred.gid != getgid())) { + if (cred.uid && + (cred.uid != getuid() || cred.gid != getgid())) { INFO("message denied for '%d/%d'", cred.uid, cred.gid); return -EACCES; } diff -Nru lxc-2.0.8/src/lxc/af_unix.h lxc-2.1.0/src/lxc/af_unix.h --- lxc-2.0.8/src/lxc/af_unix.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/af_unix.h 2017-09-06 02:32:37.000000000 +0000 @@ -24,14 +24,18 @@ #ifndef __LXC_AF_UNIX_H #define __LXC_AF_UNIX_H +#include + /* does not enforce \0-termination */ extern int lxc_abstract_unix_open(const char *path, int type, int flags); extern int lxc_abstract_unix_close(int fd); /* does not enforce \0-termination */ extern int lxc_abstract_unix_connect(const char *path); -extern int lxc_abstract_unix_send_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_fds(int fd, int *sendfds, int num_sendfds, + void *data, size_t size); +extern int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, + void *data, size_t size); extern int lxc_abstract_unix_send_credential(int fd, void *data, size_t size); extern int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size); -#endif +#endif /* __LXC_AF_UNIX_H */ diff -Nru lxc-2.0.8/src/lxc/arguments.c lxc-2.1.0/src/lxc/arguments.c --- lxc-2.0.8/src/lxc/arguments.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/arguments.c 2017-09-06 02:32:37.000000000 +0000 @@ -21,26 +21,26 @@ * 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 /* for isprint() */ -#include +#include #include #include -#include #include "arguments.h" #include "utils.h" #include "version.h" -/*---------------------------------------------------------------------------*/ -static int build_shortopts(const struct option *a_options, - char *a_shortopts, size_t a_size) +static int build_shortopts(const struct option *a_options, char *a_shortopts, + size_t a_size) { - const struct option *opt; size_t i = 0; + const struct option *opt; if (!a_options || !a_shortopts || !a_size) return -1; @@ -79,12 +79,11 @@ return 0; - is2big: +is2big: errno = E2BIG; return -1; } -/*---------------------------------------------------------------------------*/ static void print_usage(const struct option longopts[], const struct lxc_arguments *a_args) @@ -96,8 +95,9 @@ for (opt = longopts, i = 1; opt->name; opt++, i++) { int j; - char *uppername = strdup(opt->name); + char *uppername; + uppername = strdup(opt->name); if (!uppername) exit(-ENOMEM); @@ -129,7 +129,8 @@ exit(0); } -static void print_version() { +static void print_version() +{ printf("%s\n", LXC_VERSION); exit(0); } @@ -164,13 +165,14 @@ { if (args->lxcpath_additional != -1 && args->lxcpath_cnt > args->lxcpath_additional) { - fprintf(stderr, "This command only accepts %d -P,--lxcpath arguments\n", + fprintf(stderr, + "This command only accepts %d -P,--lxcpath arguments\n", args->lxcpath_additional + 1); exit(EXIT_FAILURE); } - args->lxcpath = realloc(args->lxcpath, (args->lxcpath_cnt + 1) * - sizeof(args->lxcpath[0])); + args->lxcpath = realloc( + args->lxcpath, (args->lxcpath_cnt + 1) * sizeof(args->lxcpath[0])); if (args->lxcpath == NULL) { lxc_error(args, "no memory"); return -ENOMEM; @@ -179,11 +181,11 @@ return 0; } -extern int lxc_arguments_parse(struct lxc_arguments *args, - int argc, char * const argv[]) +extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, + char *const argv[]) { + int ret = 0; char shortopts[256]; - int ret = 0; ret = build_shortopts(args->options, shortopts, sizeof(shortopts)); if (ret < 0) { @@ -192,28 +194,43 @@ return ret; } - while (1) { - int c, index = 0; + while (true) { + int c; + int index = 0; c = getopt_long(argc, argv, shortopts, args->options, &index); if (c == -1) break; switch (c) { - case 'n': args->name = optarg; break; - case 'o': args->log_file = optarg; break; - case 'l': args->log_priority = optarg; break; - case 'q': args->quiet = 1; break; - case OPT_RCFILE: args->rcfile = optarg; break; + case 'n': + args->name = optarg; + break; + case 'o': + args->log_file = optarg; + break; + case 'l': + args->log_priority = optarg; + break; + case 'q': + args->quiet = 1; + break; + case OPT_RCFILE: + args->rcfile = optarg; + break; case 'P': remove_trailing_slashes(optarg); ret = lxc_arguments_lxcpath_add(args, optarg); if (ret < 0) return ret; break; - case OPT_USAGE: print_usage(args->options, args); - case OPT_VERSION: print_version(); - case '?': print_help(args, 1); - case 'h': print_help(args, 0); + case OPT_USAGE: + print_usage(args->options, args); + case OPT_VERSION: + print_version(); + case '?': + print_help(args, 1); + case 'h': + print_help(args, 0); default: if (args->parser) { ret = args->parser(args, c, optarg); @@ -231,7 +248,8 @@ /* If no lxcpaths were given, use default */ if (!args->lxcpath_cnt) { - ret = lxc_arguments_lxcpath_add(args, lxc_global_config_value("lxc.lxcpath")); + ret = lxc_arguments_lxcpath_add( + args, lxc_global_config_value("lxc.lxcpath")); if (ret < 0) return ret; } @@ -259,7 +277,8 @@ errno = 0; val = strtol(str, &endptr, 10); if (errno) { - lxc_error(args, "invalid statefd '%s' : %m", str); + lxc_error(args, "invalid statefd '%s' : %s", str, + strerror(errno)); return -1; } diff -Nru lxc-2.0.8/src/lxc/arguments.h lxc-2.1.0/src/lxc/arguments.h --- lxc-2.0.8/src/lxc/arguments.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/arguments.h 2017-09-06 02:32:37.000000000 +0000 @@ -32,14 +32,14 @@ struct lxc_arguments; -typedef int (*lxc_arguments_parser_t) (struct lxc_arguments *, int, char*); -typedef int (*lxc_arguments_checker_t) (const 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 *); + void (*helpfn)(const struct lxc_arguments *); const char *progname; - const struct option* options; + const struct option *options; lxc_arguments_parser_t parser; lxc_arguments_checker_t checker; @@ -58,7 +58,7 @@ int lxcpath_additional; /* for lxc-start */ - const char *share_ns[32]; // size must be greater than LXC_NS_MAX + const char *share_ns[32]; /* size must be greater than LXC_NS_MAX */ /* for lxc-console */ unsigned int ttynum; @@ -130,6 +130,10 @@ bool ls_line; bool ls_running; bool ls_stopped; + bool ls_defined; + + /* lxc-copy */ + bool tmpfs; /* remaining arguments */ char *const *argv; @@ -139,29 +143,31 @@ void *data; }; -#define LXC_COMMON_OPTIONS \ - {"name", required_argument, 0, 'n'}, \ - {"help", no_argument, 0, 'h'}, \ - {"usage", no_argument, 0, OPT_USAGE}, \ - {"version", no_argument, 0, OPT_VERSION}, \ - {"quiet", no_argument, 0, 'q'}, \ - {"logfile", required_argument, 0, 'o'}, \ - {"logpriority", required_argument, 0, 'l'}, \ - {"lxcpath", required_argument, 0, 'P'}, \ - {"rcfile", required_argument, 0, OPT_RCFILE}, \ - {0, 0, 0, 0} +#define LXC_COMMON_OPTIONS \ + { "name", required_argument, 0, 'n' }, \ + { "help", no_argument, 0, 'h' }, \ + { "usage", no_argument, 0, OPT_USAGE }, \ + { "version", no_argument, 0, OPT_VERSION }, \ + { "quiet", no_argument, 0, 'q' }, \ + { "logfile", required_argument, 0, 'o' }, \ + { "logpriority", required_argument, 0, 'l' }, \ + { "lxcpath", required_argument, 0, 'P' }, \ + { "rcfile", required_argument, 0, OPT_RCFILE }, \ + { 0, 0, 0, 0 } /* option keys for long only options */ -#define OPT_USAGE 0x1000 -#define OPT_VERSION OPT_USAGE-1 -#define OPT_RCFILE OPT_USAGE-2 +#define OPT_USAGE 0x1000 +#define OPT_VERSION OPT_USAGE - 1 +#define OPT_RCFILE OPT_USAGE - 2 -extern int lxc_arguments_parse(struct lxc_arguments *args, - int argc, char *const argv[]); +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); +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) +#define lxc_error(arg, fmt, args...) \ + if (!(arg)->quiet) \ + fprintf(stderr, "%s: " fmt "\n", (arg)->progname, ##args) -#endif +#endif /* __LXC_ARGUMENTS_H */ diff -Nru lxc-2.0.8/src/lxc/attach.c lxc-2.1.0/src/lxc/attach.c --- lxc-2.0.8/src/lxc/attach.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/attach.c 2017-09-06 02:32:37.000000000 +0000 @@ -39,10 +39,20 @@ #include #include -#if !HAVE_DECL_PR_CAPBSET_DROP +#include + +#ifndef HAVE_DECL_PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif +#ifndef HAVE_DECL_PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + +#ifndef HAVE_DECL_PR_GET_NO_NEW_PRIVS +#define PR_GET_NO_NEW_PRIVS 39 +#endif + #include "af_unix.h" #include "attach.h" #include "caps.h" @@ -58,14 +68,12 @@ #include "namespace.h" #include "utils.h" -#include - #if HAVE_SYS_PERSONALITY_H #include #endif #ifndef SOCK_CLOEXEC -# define SOCK_CLOEXEC 02000000 +#define SOCK_CLOEXEC 02000000 #endif #ifndef MS_REC @@ -73,7 +81,7 @@ #endif #ifndef MS_SLAVE -#define MS_SLAVE (1<<19) +#define MS_SLAVE (1 << 19) #endif lxc_log_define(lxc_attach, lxc); @@ -118,7 +126,7 @@ static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label) { int fret = -1; - const char* name; + const char *name; char *command = NULL; name = lsm_name(); @@ -136,7 +144,8 @@ if (strcmp(name, "AppArmor") == 0) { int size; - command = malloc(strlen(lsm_label) + strlen("changeprofile ") + 1); + command = + malloc(strlen(lsm_label) + strlen("changeprofile ") + 1); if (!command) { SYSERROR("Failed to write apparmor profile."); goto out; @@ -175,15 +184,15 @@ } /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ -#define __PROC_STATUS_LEN (5 + 21 + 7 + 1) +#define __PROC_STATUS_LEN (5 + (LXC_NUMSTRLEN64) + 7 + 1) static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { + int ret; + bool found; FILE *proc_file; char proc_fn[__PROC_STATUS_LEN]; - bool found; - int ret; - char *line = NULL; size_t line_bufsz = 0; + char *line = NULL; struct lxc_proc_context_info *info = NULL; /* Read capabilities. */ @@ -200,6 +209,7 @@ info = calloc(1, sizeof(*info)); if (!info) { SYSERROR("Could not allocate memory."); + fclose(proc_file); return NULL; } @@ -216,7 +226,8 @@ fclose(proc_file); if (!found) { - SYSERROR("Could not read capability bounding set from %s.", proc_fn); + SYSERROR("Could not read capability bounding set from %s.", + proc_fn); errno = ENOENT; goto on_error; } @@ -243,7 +254,6 @@ int fd[LXC_NS_MAX]; int i, j, saved_errno; - if (access("/proc/self/ns", X_OK)) { ERROR("Does this kernel version support namespaces?"); return -1; @@ -267,7 +277,8 @@ close(fd[j]); errno = saved_errno; - SYSERROR("Failed to open namespace: \"%s\".", ns_info[i].proc_name); + SYSERROR("Failed to open namespace: \"%s\".", + ns_info[i].proc_name); return -1; } } @@ -283,7 +294,8 @@ close(fd[j]); errno = saved_errno; - SYSERROR("Failed to attach to namespace \"%s\".", ns_info[i].proc_name); + SYSERROR("Failed to attach to namespace \"%s\".", + ns_info[i].proc_name); return -1; } @@ -306,7 +318,7 @@ } if (detect_shared_rootfs()) { - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { SYSERROR("Failed to make / rslave."); ERROR("Continuing..."); } @@ -346,9 +358,9 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) { - int last_cap = lxc_caps_last_cap(); - int cap; + int cap, last_cap; + last_cap = lxc_caps_last_cap(); for (cap = 0; cap <= last_cap; cap++) { if (ctx->capability_mask & (1LL << cap)) continue; @@ -362,11 +374,12 @@ return 0; } -static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char** extra_env, char** extra_keep) +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; + char **extra_keep_store = NULL; if (extra_keep) { size_t count, i; @@ -402,6 +415,7 @@ if (clearenv()) { char **p; + SYSERROR("Failed to clear environment."); if (extra_keep_store) { for (p = extra_keep_store; *p; p++) @@ -413,6 +427,7 @@ 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) @@ -461,10 +476,9 @@ static char *lxc_attach_getpwshell(uid_t uid) { + int fd, ret; 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 @@ -482,21 +496,20 @@ } if (pid) { + int status; FILE *pipe_f; - char *line = NULL; - size_t line_bufsz = 0; int found = 0; - int status; + size_t line_bufsz = 0; + char *line = NULL; 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; + long value; + char *token; + char *endptr = NULL, *saveptr = NULL; /* If we already found something, just continue to read * until the pipe doesn't deliver any more data, but @@ -607,7 +620,7 @@ } } -static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) +static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid) { FILE *proc_file; char proc_fn[__PROC_STATUS_LEN]; @@ -631,11 +644,11 @@ */ ret = sscanf(line, "Uid: %ld", &value); if (ret != EOF && ret == 1) { - uid = (uid_t) value; + uid = (uid_t)value; } else { ret = sscanf(line, "Gid: %ld", &value); if (ret != EOF && ret == 1) - gid = (gid_t) value; + gid = (gid_t)value; } if (uid != (uid_t)-1 && gid != (gid_t)-1) break; @@ -657,47 +670,56 @@ struct attach_clone_payload { int ipc_socket; - lxc_attach_options_t* options; - struct lxc_proc_context_info* init_ctx; + lxc_attach_options_t *options; + struct lxc_proc_context_info *init_ctx; lxc_attach_exec_t exec_function; - void* exec_payload; + 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 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) +static bool fetch_seccomp(struct lxc_container *c, + lxc_attach_options_t *options) { - struct lxc_container *c; char *path; - if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) + if (!(options->namespaces & CLONE_NEWNS) || + !(options->attach_flags & LXC_ATTACH_LSM)) { + free(c->lxc_conf->seccomp); + c->lxc_conf->seccomp = NULL; 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", "")) { + /* Remove current setting. */ + if (!c->set_config_item(c, "lxc.seccomp", "") && + !c->set_config_item(c, "lxc.seccomp.profile", "")) { return false; } /* Fetch the current profile path over the cmd interface. */ - path = c->get_running_config_item(c, "lxc.seccomp"); + path = c->get_running_config_item(c, "lxc.seccomp.profile"); + if (!path) { + INFO("Failed to get running config item for lxc.seccomp.profile"); + path = c->get_running_config_item(c, "lxc.seccomp"); + } if (!path) { + INFO("Failed to get running config item for lxc.seccomp"); return true; } /* Copy the value into the new lxc_conf. */ - if (!c->set_config_item(c, "lxc.seccomp", path)) { + if (!c->set_config_item(c, "lxc.seccomp.profile", path)) { free(path); return false; } @@ -709,30 +731,60 @@ return false; } + INFO("Retrieved seccomp policy."); + return true; +} + +static bool no_new_privs(struct lxc_container *c, lxc_attach_options_t *options) +{ + char *val; + + /* Remove current setting. */ + if (!c->set_config_item(c, "lxc.no_new_privs", "")) + return false; + + /* Retrieve currently active setting. */ + val = c->get_running_config_item(c, "lxc.no_new_privs"); + if (!val) { + INFO("Failed to get running config item for lxc.no_new_privs."); + return false; + } + + /* Set currently active setting. */ + if (!c->set_config_item(c, "lxc.no_new_privs", val)) { + free(val); + return false; + } + free(val); + return true; } static signed long get_personality(const char *name, const char *lxcpath) { - char *p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); + char *p; signed long ret; + p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); if (!p) return -1; + ret = lxc_config_parse_arch(p); free(p); + return ret; } -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 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]; + char *cwd, *new_cwd; signed long personality; + pid_t attached_pid, expected, init_pid, pid; + struct lxc_proc_context_info *init_ctx; if (!options) options = &attach_static_default_options; @@ -745,22 +797,28 @@ init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { - ERROR("Failed to get context of init process: %ld.", - (long)init_pid); + ERROR("Failed to get context of init process: %ld", (long)init_pid); return -1; } personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { - ERROR("Failed to get personality of the container."); + 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)) + init_ctx->container = lxc_container_new(name, lxcpath); + if (!init_ctx->container) + return -1; + + if (!fetch_seccomp(init_ctx->container, options)) WARN("Failed to get seccomp policy."); + if (!no_new_privs(init_ctx->container, options)) + WARN("Could not determine whether PR_SET_NO_NEW_PRIVS is set."); + cwd = getcwd(NULL, 0); /* Determine which namespaces the container was created with @@ -830,7 +888,6 @@ * setns() (otherwise, user namespaces will hate us). */ pid = fork(); - if (pid < 0) { SYSERROR("Failed to create first subprocess."); free(cwd); @@ -854,6 +911,11 @@ goto on_error; } + /* Setup resource limits */ + if (!lxc_list_empty(&init_ctx->container->lxc_conf->limits)) + if (setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid) < 0) + goto on_error; + /* Open /proc before setns() to the containers namespace so we * don't rely on any information from inside the container. */ @@ -873,7 +935,8 @@ } /* Get pid of attached process from intermediate process. */ - ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL); + ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, + sizeof(attached_pid), NULL); if (ret <= 0) { if (ret != 0) ERROR("Expected to receive pid: %s.", strerror(errno)); @@ -904,7 +967,8 @@ /* Wait for the attached process to finish initializing. */ expected = 1; - ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); + ret = lxc_read_nointr_expect(ipc_sockets[0], &status, + sizeof(status), &expected); if (ret <= 0) { if (ret != 0) ERROR("Expected to receive sequence number 1: %s.", strerror(errno)); @@ -923,7 +987,8 @@ * up its LSM labels. */ expected = 3; - ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); + ret = lxc_read_nointr_expect(ipc_sockets[0], &status, + sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 3: %s.", strerror(errno)); @@ -931,9 +996,12 @@ } /* Open LSM fd and send it to child. */ - if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { + 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); @@ -941,7 +1009,7 @@ goto on_error; /* Send child fd of the LSM security module to write to. */ - ret = lxc_abstract_unix_send_fd(ipc_sockets[0], labelfd, NULL, 0); + ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); saved_errno = errno; close(labelfd); if (ret <= 0) { @@ -974,7 +1042,7 @@ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); if (to_cleanup_pid) - (void) wait_for_pid(to_cleanup_pid); + (void)wait_for_pid(to_cleanup_pid); lxc_proc_put_context_info(init_ctx); return -1; } @@ -987,7 +1055,8 @@ /* Wait for the parent to have setup cgroups. */ expected = 0; status = -1; - ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected); + ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), + &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); shutdown(ipc_sockets[1], SHUT_RDWR); @@ -1024,7 +1093,7 @@ .options = options, .init_ctx = init_ctx, .exec_function = exec_function, - .exec_payload = exec_payload + .exec_payload = exec_payload, }; /* We use clone_parent here to make this subprocess a direct * child of the initial process. Then this intermediate process @@ -1061,21 +1130,17 @@ 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; + int expected, fd, lsm_labelfd, ret, status; + long flags; #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; + 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; /* Wait for the initial thread to signal us that it's ready for us to * start initializing. @@ -1094,7 +1159,8 @@ * 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)) { + 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); @@ -1131,7 +1197,9 @@ /* 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); + 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); @@ -1175,7 +1243,8 @@ rexit(-1); } } - if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { + if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && + setuid(new_uid)) { SYSERROR("Switching to container uid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); @@ -1202,6 +1271,19 @@ rexit(-1); } + if ((init_ctx->container && init_ctx->container->lxc_conf && + init_ctx->container->lxc_conf->no_new_privs) || + (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. " + "Process can use execve() gainable " + "privileges."); + shutdown(ipc_socket, SHUT_RDWR); + rexit(-1); + } + INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() " + "gainable privileges."); + } /* Tell the (grand)parent to send us LSM label fd. */ status = 3; @@ -1212,10 +1294,11 @@ rexit(-1); } - if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { + 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); + ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0); if (ret <= 0) { ERROR("Expected to receive file descriptor: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); @@ -1234,7 +1317,8 @@ } if (init_ctx->container && init_ctx->container->lxc_conf && - lxc_seccomp_load(init_ctx->container->lxc_conf) != 0) { + init_ctx->container->lxc_conf->seccomp && + (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) { ERROR("Failed to load seccomp policy."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); diff -Nru lxc-2.0.8/src/lxc/attach.h lxc-2.1.0/src/lxc/attach.h --- lxc-2.0.8/src/lxc/attach.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/attach.h 2017-09-06 02:32:37.000000000 +0000 @@ -24,8 +24,8 @@ #ifndef __LXC_ATTACH_H #define __LXC_ATTACH_H -#include #include +#include struct lxc_conf; @@ -36,6 +36,8 @@ unsigned long long capability_mask; }; -extern int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process); +extern int lxc_attach(const char *name, const char *lxcpath, + lxc_attach_exec_t exec_function, void *exec_payload, + lxc_attach_options_t *options, pid_t *attached_process); -#endif +#endif /* __LXC_ATTACH_H */ diff -Nru lxc-2.0.8/src/lxc/attach_options.h lxc-2.1.0/src/lxc/attach_options.h --- lxc-2.0.8/src/lxc/attach_options.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/attach_options.h 2017-09-06 02:32:37.000000000 +0000 @@ -35,27 +35,28 @@ * 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_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 + /* 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 */ + /* Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges. */ + LXC_ATTACH_NO_NEW_PRIVS = 0x00040000, /*!< PR_SET_NO_NEW_PRIVS */ + + /* 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 + LXC_ATTACH_DEFAULT = 0x0000FFFF /*!< Mask of flags to apply by default */ }; /*! All Linux Security Module flags */ @@ -82,13 +83,14 @@ int namespaces; /*! Initial personality (\c -1 to autodetect). - * \warning This may be ignored if lxc is compiled without personality support) + * \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. + * If the current directory does not exist in the container, the root + * directory will be used instead because of kernel defaults. */ char* initial_cwd; @@ -132,18 +134,18 @@ } 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 \ +#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 \ } /*! diff -Nru lxc-2.0.8/src/lxc/bdev/bdev.c lxc-2.1.0/src/lxc/bdev/bdev.c --- lxc-2.0.8/src/lxc/bdev/bdev.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/bdev.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,988 +0,0 @@ -/* - * 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 -#include -#include - -#include "bdev.h" -#include "conf.h" -#include "config.h" -#include "error.h" -#include "log.h" -#include "lxc.h" -#include "lxcaufs.h" -#include "lxcbtrfs.h" -#include "lxcdir.h" -#include "lxclock.h" -#include "lxclvm.h" -#include "lxcloop.h" -#include "lxcnbd.h" -#include "lxcoverlay.h" -#include "lxcrbd.h" -#include "lxcrsync.h" -#include "lxczfs.h" -#include "namespace.h" -#include "parse.h" -#include "utils.h" - -#ifndef BLKGETSIZE64 -#define BLKGETSIZE64 _IOR(0x12, 114, size_t) -#endif - -lxc_log_define(bdev, lxc); - -/* aufs */ -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, -}; - -/* btrfs */ -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, -}; - -/* dir */ -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, -}; - -/* loop */ -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, -}; - -/* lvm */ -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, -}; - -/* nbd */ -const struct bdev_ops nbd_ops = { - .detect = &nbd_detect, - .mount = &nbd_mount, - .umount = &nbd_umount, - .clone_paths = &nbd_clonepaths, - .destroy = &nbd_destroy, - .create = &nbd_create, - .can_snapshot = true, - .can_backup = false, -}; - -/* overlay */ -static const struct bdev_ops ovl_ops = { - .detect = &ovl_detect, - .mount = &ovl_mount, - .umount = &ovl_umount, - .clone_paths = &ovl_clonepaths, - .destroy = &ovl_destroy, - .create = &ovl_create, - .can_snapshot = true, - .can_backup = true, -}; - -/* rbd */ -static const struct bdev_ops rbd_ops = { - .detect = &rbd_detect, - .mount = &rbd_mount, - .umount = &rbd_umount, - .clone_paths = &rbd_clonepaths, - .destroy = &rbd_destroy, - .create = &rbd_create, - .can_snapshot = false, - .can_backup = false, -}; - -/* zfs */ -static const struct 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, -}; - -struct bdev_type { - const char *name; - const struct bdev_ops *ops; -}; - -static const struct bdev_type bdevs[] = { - {.name = "zfs", .ops = &zfs_ops,}, - {.name = "lvm", .ops = &lvm_ops,}, - {.name = "rbd", .ops = &rbd_ops,}, - {.name = "btrfs", .ops = &btrfs_ops,}, - {.name = "dir", .ops = &dir_ops,}, - {.name = "aufs", .ops = &aufs_ops,}, - {.name = "overlayfs", .ops = &ovl_ops,}, - {.name = "loop", .ops = &loop_ops,}, - {.name = "nbd", .ops = &nbd_ops,}, -}; - -static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type); - -/* helpers */ -static const struct bdev_type *bdev_query(struct lxc_conf *conf, const char *src); -static struct bdev *bdev_get(const char *type); -static struct bdev *do_bdev_create(const char *dest, const char *type, - const char *cname, struct bdev_specs *specs); -static int find_fstype_cb(char *buffer, void *data); -static char *linkderef(char *path, char *dest); -static bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap, - bool maybesnap); - -/* the bulk of this needs to become a common helper */ -char *dir_new_path(char *src, const char *oldname, const char *name, - const char *oldpath, const char *lxcpath) -{ - char *ret, *p, *p2; - int l1, l2, nlen; - - nlen = strlen(src) + 1; - l1 = strlen(oldpath); - p = src; - /* if src starts with oldpath, look for oldname only after - * that path */ - if (strncmp(src, oldpath, l1) == 0) { - p += l1; - nlen += (strlen(lxcpath) - l1); - } - l2 = strlen(oldname); - while ((p = strstr(p, oldname)) != NULL) { - p += l2; - nlen += strlen(name) - l2; - } - - ret = malloc(nlen); - if (!ret) - return NULL; - - p = ret; - if (strncmp(src, oldpath, l1) == 0) { - p += sprintf(p, "%s", lxcpath); - src += l1; - } - - while ((p2 = strstr(src, oldname)) != NULL) { - 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; -} - -/* - * attach_block_device returns true if all went well, - * meaning either a block device was attached or was not - * needed. It returns false if something went wrong and - * container startup should be stopped. - */ -bool attach_block_device(struct lxc_conf *conf) -{ - char *path; - - if (!conf->rootfs.path) - return true; - path = conf->rootfs.path; - if (!requires_nbd(path)) - return true; - path = strchr(path, ':'); - if (!path) - return false; - path++; - if (!attach_nbd(path, conf)) - return false; - return true; -} - -bool bdev_can_backup(struct lxc_conf *conf) -{ - struct bdev *bdev = bdev_init(conf, NULL, NULL, NULL); - bool ret; - - if (!bdev) - return false; - ret = bdev->ops->can_backup; - bdev_put(bdev); - return ret; -} - -/* - * 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(c0->lxc_conf, src, NULL, NULL); - if (!orig) { - ERROR("failed to detect blockdev type for %s", src); - return NULL; - } - - if (!orig->dest) { - int ret; - size_t len; - struct stat sb; - - len = strlen(oldpath) + strlen(oldname) + strlen("/rootfs") + 2; - orig->dest = malloc(len); - if (!orig->dest) { - ERROR("out of memory"); - bdev_put(orig); - return NULL; - } - ret = snprintf(orig->dest, len, "%s/%s/rootfs", oldpath, oldname); - if (ret < 0 || (size_t)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; -} - -/* - * 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, rbd, 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", "rbd", 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); -} - -bool bdev_destroy(struct lxc_conf *conf) -{ - struct bdev *r; - bool ret = false; - - r = bdev_init(conf, conf->rootfs.path, conf->rootfs.mount, NULL); - if (!r) - return ret; - - if (r->ops->destroy(r) == 0) - ret = true; - bdev_put(r); - - return ret; -} - -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; - } - if (!bdev_destroy(conf)) - return -1; - else - return 0; -} - -struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, - const char *mntopts) -{ - struct bdev *bdev; - const struct bdev_type *q; - - if (!src) - src = conf->rootfs.path; - - if (!src) - return NULL; - - q = bdev_query(conf, 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); - if (strcmp(bdev->type, "nbd") == 0) - bdev->nbd_idx = conf->nbd_idx; - - return bdev; -} - -bool bdev_is_dir(struct lxc_conf *conf, const char *path) -{ - struct bdev *orig = bdev_init(conf, path, NULL, NULL); - bool ret = false; - if (!orig) - return ret; - if (strcmp(orig->type, "dir") == 0) - ret = true; - bdev_put(orig); - return ret; -} - -void bdev_put(struct bdev *bdev) -{ - free(bdev->mntopts); - free(bdev->src); - free(bdev->dest); - free(bdev); -} - -/* - * return block size of dev->src in units of bytes - */ -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; -} - -void detach_block_device(struct lxc_conf *conf) -{ - if (conf->nbd_idx != -1) - detach_nbd_idx(conf->nbd_idx); -} - -/* - * 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 - */ -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); -} - -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, (char *)NULL); - exit(1); -} - -/* - * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm - * is a block device. - */ -int is_blktype(struct bdev *b) -{ - if (strcmp(b->type, "lvm") == 0) - return 1; - return 0; -} - -int mount_unknown_fs(const char *rootfs, const char *target, - const char *options) -{ - 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", - }; - - size_t i; - 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; -} - -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, 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; -} - -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; -} - -static struct bdev *bdev_get(const char *type) -{ - size_t i; - struct bdev *bdev; - - for (i = 0; i < numbdevs; i++) { - if (strcmp(bdevs[i].name, type) == 0) - break; - } - if (i == numbdevs) - return NULL; - bdev = malloc(sizeof(struct bdev)); - if (!bdev) - return NULL; - memset(bdev, 0, sizeof(struct bdev)); - bdev->ops = bdevs[i].ops; - bdev->type = bdevs[i].name; - return bdev; -} - -static const struct bdev_type *get_bdev_by_name(const char *name) -{ - size_t i; - - for (i = 0; i < numbdevs; i++) { - if (strcmp(bdevs[i].name, name) == 0) - return &bdevs[i]; - } - - ERROR("Backing store %s unknown but not caught earlier\n", name); - return NULL; -} - -static const struct bdev_type *bdev_query(struct lxc_conf *conf, const char *src) -{ - size_t i; - - if (conf->rootfs.bdev_type) - return get_bdev_by_name(conf->rootfs.bdev_type); - - for (i = 0; i < numbdevs; i++) { - int r; - r = bdevs[i].ops->detect(src); - if (r) - break; - } - - if (i == numbdevs) - return NULL; - return &bdevs[i]; -} - -/* - * 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 char *linkderef(char *path, char *dest) -{ - struct stat sbuf; - ssize_t ret; - - ret = stat(path, &sbuf); - if (ret < 0) - return NULL; - if (!S_ISLNK(sbuf.st_mode)) - return path; - ret = readlink(path, dest, MAXPATHLEN); - if (ret < 0) { - SYSERROR("error reading link %s", path); - return NULL; - } else if (ret >= MAXPATHLEN) { - ERROR("link in %s too long", path); - return NULL; - } - dest[ret] = '\0'; - return dest; -} - -/* - * is an unprivileged user allowed to make this kind of snapshot - */ -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; -} - -bool is_valid_bdev_type(const char *type) -{ - if (strcmp(type, "dir") == 0 || - strcmp(type, "btrfs") == 0 || - strcmp(type, "aufs") == 0 || - strcmp(type, "loop") == 0 || - strcmp(type, "lvm") == 0 || - strcmp(type, "nbd") == 0 || - strcmp(type, "overlayfs") == 0 || - strcmp(type, "rbd") == 0 || - strcmp(type, "zfs") == 0) - return true; - return false; -} diff -Nru lxc-2.0.8/src/lxc/bdev/bdev.h lxc-2.1.0/src/lxc/bdev/bdev.h --- lxc-2.0.8/src/lxc/bdev/bdev.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/bdev.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -/* - * 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_BDEV_H -#define __LXC_BDEV_H -/* blockdev operations for: - * aufs, dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs, nbd (qcow2, raw, vdi, qed) - */ - -#include -#include -#include - -#include "config.h" - -/* define constants if the kernel/glibc headers don't define them */ -#ifndef MS_DIRSYNC -#define MS_DIRSYNC 128 -#endif - -#ifndef MS_REC -#define MS_REC 16384 -#endif - -#ifndef MNT_DETACH -#define MNT_DETACH 2 -#endif - -#ifndef MS_SLAVE -#define MS_SLAVE (1 << 19) -#endif - -#ifndef MS_RELATIME -#define MS_RELATIME (1 << 21) -#endif - -#ifndef MS_STRICTATIME -#define MS_STRICTATIME (1 << 24) -#endif - -#define DEFAULT_FS_SIZE 1073741824 -#define DEFAULT_FSTYPE "ext3" - -struct bdev; - -struct bdev_ops { - /* detect whether path is of this bdev type */ - int (*detect)(const char *path); - // mount requires src and dest to be set. - int (*mount)(struct bdev *bdev); - int (*umount)(struct bdev *bdev); - int (*destroy)(struct bdev *bdev); - int (*create)(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); - /* given original mount, rename the paths for cloned container */ - int (*clone_paths)(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); - bool can_snapshot; - bool can_backup; -}; - -/* - * When lxc-start (conf.c) is mounting a rootfs, then src will be the - * 'lxc.rootfs' value, dest will be mount dir (i.e. $libdir/lxc) When clone - * or create is doing so, then dest will be $lxcpath/$lxcname/rootfs, since - * we may need to rsync from one to the other. - * data is so far unused. - */ -struct bdev { - const struct bdev_ops *ops; - const char *type; - char *src; - char *dest; - char *mntopts; - // turn the following into a union if need be - // lofd is the open fd for the mounted loopback file - int lofd; - // index for the connected nbd device - int nbd_idx; -}; - -bool bdev_is_dir(struct lxc_conf *conf, const char *path); -bool bdev_can_backup(struct lxc_conf *conf); - -/* - * Instantiate a bdev object. The src is used to determine which blockdev - * type this should be. The dst and data are optional, and will be used - * in case of mount/umount. - * - * Optionally, src can be 'dir:/var/lib/lxc/c1' or 'lvm:/dev/lxc/c1'. For - * other backing stores, this will allow additional options. In particular, - * "overlayfs:/var/lib/lxc/canonical/rootfs:/var/lib/lxc/c1/delta" will mean - * use /var/lib/lxc/canonical/rootfs as lower dir, and /var/lib/lxc/c1/delta - * as the upper, writeable layer. - */ -struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, - const char *data); - -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 *bdev_create(const char *dest, const char *type, - const char *cname, struct bdev_specs *specs); -void bdev_put(struct bdev *bdev); -bool bdev_destroy(struct lxc_conf *conf); -/* callback function to be used with userns_exec_1() */ -int bdev_destroy_wrapper(void *data); - -/* Some helpers for lvm, rdb, and/or loop: - * Maybe they should move to a separate implementation and header-file - * (bdev_utils.{c,h}) which can be included in bdev.c? - */ -int blk_getsize(struct bdev *bdev, uint64_t *size); -int detect_fs(struct bdev *bdev, char *type, int len); -int do_mkfs(const char *path, const char *fstype); -int is_blktype(struct bdev *b); -int mount_unknown_fs(const char *rootfs, const char *target, - const char *options); -bool rootfs_is_blockdev(struct lxc_conf *conf); -/* - * these are really for qemu-nbd support, as container shutdown - * must explicitly request device detach. - */ -bool attach_block_device(struct lxc_conf *conf); -void detach_block_device(struct lxc_conf *conf); - -bool is_valid_bdev_type(const char *type); - -#endif // __LXC_BDEV_H diff -Nru lxc-2.0.8/src/lxc/bdev/lxcaufs.c lxc-2.1.0/src/lxc/bdev/lxcaufs.c --- lxc-2.0.8/src/lxc/bdev/lxcaufs.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcaufs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,420 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "lxcaufs.h" -#include "lxcrsync.h" -#include "utils.h" - -lxc_log_define(lxcaufs, lxc); - -/* the bulk of this needs to become a common helper */ -extern char *dir_new_path(char *src, const char *oldname, const char *name, - const char *oldpath, const char *lxcpath); - -int aufs_clonepaths(struct 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; -} - -/* - * to say 'lxc-create -t ubuntu -n o1 -B aufs' means you want - * $lxcpath/$lxcname/rootfs to have the created container, while all - * changes after starting the container are written to - * $lxcpath/$lxcname/delta0 - */ -int aufs_create(struct 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; -} - -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, NULL); -} - -int aufs_detect(const char *path) -{ - if (strncmp(path, "aufs:", 5) == 0) - return 1; // take their word for it - return 0; -} - -int aufs_mount(struct bdev *bdev) -{ - char *tmp, *options, *dup, *lower, *upper; - int len; - unsigned long mntflags; - char *mntdata; - int ret; - const char *xinopath = "/dev/shm/aufs.xino"; - - if (strcmp(bdev->type, "aufs")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - - // separately mount it first - // mount -t aufs -obr=${upper}=rw:${lower}=ro lower dest - dup = alloca(strlen(bdev->src)+1); - strcpy(dup, bdev->src); - /* support multiple lower layers */ - if (!(lower = strstr(dup, ":/"))) - return -22; - lower++; - upper = lower; - while ((tmp = strstr(++upper, ":/"))) { - upper = tmp; - } - if (--upper == lower) - return -22; - *upper = '\0'; - upper++; - - if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { - free(mntdata); - return -22; - } - - // TODO We should check whether bdev->src is a blockdev, and if so - // but for now, only support aufs of a basic directory - - // AUFS does not work on top of certain filesystems like (XFS or Btrfs) - // so add xino=/dev/shm/aufs.xino parameter to mount options. - // The same xino option can be specified to multiple aufs mounts, and - // a xino file is not shared among multiple aufs mounts. - // - // see http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg02587.html - // http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05126.html - if (mntdata) { - len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,,xino=") + strlen(mntdata) + 1; - options = alloca(len); - ret = snprintf(options, len, "br=%s=rw:%s=ro,%s,xino=%s", upper, lower, mntdata, xinopath); - } - else { - len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,xino=") + 1; - options = alloca(len); - ret = snprintf(options, len, "br=%s=rw:%s=ro,xino=%s", upper, lower, xinopath); - } - - if (ret < 0 || ret >= len) { - free(mntdata); - return -1; - } - - ret = mount(lower, bdev->dest, "aufs", MS_MGC_VAL | mntflags, options); - if (ret < 0) - SYSERROR("aufs: error mounting %s onto %s options %s", - lower, bdev->dest, options); - else - INFO("aufs: mounted %s onto %s options %s", - lower, bdev->dest, options); - return ret; -} - -int aufs_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "aufs")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - return umount(bdev->dest); -} - -char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen) -{ - char *rootfsdir = NULL; - char *s1 = NULL; - char *s2 = NULL; - char *s3 = NULL; - - if (!rootfs_path || !rootfslen) - return NULL; - - s1 = strdup(rootfs_path); - if (!s1) - return NULL; - - if ((s2 = strstr(s1, ":/"))) { - s2 = s2 + 1; - if ((s3 = strstr(s2, ":/"))) - *s3 = '\0'; - rootfsdir = strdup(s2); - if (!rootfsdir) { - free(s1); - return NULL; - } - } - - if (!rootfsdir) - rootfsdir = s1; - else - free(s1); - - *rootfslen = strlen(rootfsdir); - - return rootfsdir; -} - -int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path) -{ - char lxcpath[MAXPATHLEN]; - char *rootfs_path = NULL; - char *rootfsdir = NULL; - char *scratch = NULL; - char *tmp = NULL; - char *upperdir = NULL; - char **opts = NULL; - int fret = -1; - int ret = 0; - size_t arrlen = 0; - size_t i; - size_t len = 0; - size_t rootfslen = 0; - - /* When rootfs == NULL we have a container without a rootfs. */ - if (rootfs && rootfs->path) - rootfs_path = rootfs->path; - - opts = lxc_string_split(mntent->mnt_opts, ','); - if (opts) - arrlen = lxc_array_len((void **)opts); - else - goto err; - - for (i = 0; i < arrlen; i++) { - if (strstr(opts[i], "br=") && (strlen(opts[i]) > (len = strlen("br=")))) - tmp = opts[i] + len; - } - if (!tmp) - goto err; - - upperdir = strtok_r(tmp, ":=", &scratch); - if (!upperdir) - goto err; - - if (rootfs_path) { - ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - rootfsdir = aufs_get_rootfs(rootfs->path, &rootfslen); - if (!rootfsdir) - goto err; - } - - /* - * We neither allow users to create upperdirs and workdirs outside the - * containerdir nor inside the rootfs. The latter might be debatable. - * When we have a container without a rootfs we skip the checks. - */ - ret = 0; - if (!rootfs_path) - ret = mkdir_p(upperdir, 0755); - else if ((strncmp(upperdir, lxcpath, strlen(lxcpath)) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) - ret = mkdir_p(upperdir, 0755); - if (ret < 0) - WARN("Failed to create upperdir"); - - fret = 0; - -err: - free(rootfsdir); - lxc_free_array((void **)opts, free); - return fret; -} - diff -Nru lxc-2.0.8/src/lxc/bdev/lxcaufs.h lxc-2.1.0/src/lxc/bdev/lxcaufs.h --- lxc-2.0.8/src/lxc/bdev/lxcaufs.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcaufs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_AUFS_H -#define __LXC_AUFS_H - -#define _GNU_SOURCE -#include - -#if IS_BIONIC -#include <../include/lxcmntent.h> -#else -#include -#endif - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* defined in conf.h */ -struct lxc_rootfs; - -/* - * Functions associated with an aufs bdev struct. - */ -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); -int aufs_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int aufs_destroy(struct bdev *orig); -int aufs_detect(const char *path); -int aufs_mount(struct bdev *bdev); -int aufs_umount(struct bdev *bdev); - -/* - * Get rootfs path for aufs backed containers. Allocated memory must be freed - * by caller. - */ -char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen); - -/* - * Create directories for aufs mounts. - */ -int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path); - -#endif /* __LXC_AUFS_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcbtrfs.c lxc-2.1.0/src/lxc/bdev/lxcbtrfs.c --- lxc-2.0.8/src/lxc/bdev/lxcbtrfs.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcbtrfs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,750 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "lxcbtrfs.h" -#include "lxcrsync.h" -#include "utils.h" - -lxc_log_define(lxcbtrfs, lxc); - -/* defined in lxccontainer.c: needs to become common helper */ -extern char *dir_new_path(char *src, const char *oldname, const char *name, - const char *oldpath, const char *lxcpath); - -/* - * Return the full path of objid under dirid. Let's say dirid is - * /lxc/c1/rootfs, and objid is /lxc/c1/rootfs/a/b/c. Then we will - * return a/b/c. If instead objid is for /lxc/c1/rootfs/a, we will - * simply return a. - */ -char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name, - 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; -} - -/* - * Taken from btrfs toolsuite. Test if path is a subvolume. - * return 0; path exists but it is not a subvolume - * return 1; path exists and it is a subvolume - * return < 0; error - */ -int is_btrfs_subvol(const char *path) -{ - struct stat st; - struct statfs stfs; - int ret; - - ret = stat(path, &st); - if (ret < 0) - return -errno; - - if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) - return 0; - - ret = statfs(path, &stfs); - if (ret < 0) - return -errno; - - return stfs.f_type == BTRFS_SUPER_MAGIC; -} - -int btrfs_detect(const char *path) -{ - struct stat st; - int ret; - - if (!is_btrfs_fs(path)) - return 0; - - // and make sure it's a subvolume. - ret = stat(path, &st); - if (ret < 0) - return 0; - - if (st.st_ino == 256 && S_ISDIR(st.st_mode)) - return 1; - - return 0; -} - -int btrfs_mount(struct 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; -} - -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; -} - -int btrfs_same_fs(const char *orig, const char *new) -{ - int fd_orig = -1, fd_new = -1, ret = -1; - struct btrfs_ioctl_fs_info_args orig_args, new_args; - - fd_orig = open(orig, O_RDONLY); - if (fd_orig < 0) { - SYSERROR("Error opening original rootfs %s", orig); - goto out; - } - ret = ioctl(fd_orig, BTRFS_IOC_FS_INFO, &orig_args); - if (ret < 0) { - SYSERROR("BTRFS_IOC_FS_INFO %s", orig); - goto out; - } - - fd_new = open(new, O_RDONLY); - if (fd_new < 0) { - SYSERROR("Error opening new container dir %s", new); - ret = -1; - goto out; - } - ret = ioctl(fd_new, BTRFS_IOC_FS_INFO, &new_args); - if (ret < 0) { - SYSERROR("BTRFS_IOC_FS_INFO %s", new); - goto out; - } - - if (strncmp(orig_args.fsid, new_args.fsid, BTRFS_FSID_SIZE) != 0) { - ret = -1; - goto out; - } - ret = 0; -out: - if (fd_new != -1) - close(fd_new); - if (fd_orig != -1) - close(fd_orig); - return ret; -} - -int btrfs_snapshot(const char *orig, const char *new) -{ - 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); -} - -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; -} - -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; - u64 dir_id; - - fd = open(path, O_RDONLY); - if (fd < 0) { - ERROR("Failed to open %s\n", path); - return -1; - } - - if (btrfs_list_get_path_rootid(fd, &root_id)) { - e = errno; - close(fd); - if (e == EPERM || e == EACCES) { - WARN("Will simply try removing"); - goto ignore_search; - } - - return -1; - } - - tree = create_my_btrfs_tree(root_id, path, strlen(path)); - if (!tree) { - ERROR("Out of memory\n"); - close(fd); - return -1; - } - /* Walk all subvols looking for any under this id */ - memset(&args, 0, sizeof(args)); - - /* search in the tree of tree roots */ - sk->tree_id = 1; - - sk->max_type = BTRFS_ROOT_REF_KEY; - sk->min_type = BTRFS_ROOT_ITEM_KEY; - sk->min_objectid = 0; - sk->max_objectid = (u64)-1; - sk->max_offset = (u64)-1; - sk->min_offset = 0; - sk->max_transid = (u64)-1; - sk->nr_items = 4096; - - while(1) { - ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); - e = errno; - if (ret < 0) { - close(fd); - free_btrfs_tree(tree); - if (e == EPERM || e == EACCES) { - WARN("Warn: can't perform the search under %s. Will simply try removing", path); - goto ignore_search; - } - - ERROR("Error: can't perform the search under %s\n", path); - return -1; - } - if (sk->nr_items == 0) - break; - - off = 0; - for (i = 0; i < sk->nr_items; i++) { - memcpy(&sh, args.buf + off, sizeof(sh)); - off += sizeof(sh); - /* - * A backref key with the name and dirid of the parent - * comes followed by the reoot ref key which has the - * name of the child subvol in question. - */ - if (sh.objectid != root_id && sh.type == BTRFS_ROOT_BACKREF_KEY) { - ref = (struct btrfs_root_ref *)(args.buf + off); - name_len = btrfs_stack_root_ref_name_len(ref); - name = (char *)(ref + 1); - dir_id = btrfs_stack_root_ref_dirid(ref); - tmppath = get_btrfs_subvol_path(fd, sh.offset, - dir_id, name, name_len); - if (!add_btrfs_tree_node(tree, sh.objectid, - sh.offset, name, - name_len, tmppath)) { - ERROR("Out of memory"); - free_btrfs_tree(tree); - free(tmppath); - close(fd); - return -1; - } - free(tmppath); - } - off += sh.len; - - /* - * record the mins in sk so we can make sure the - * next search doesn't repeat this root - */ - sk->min_objectid = sh.objectid; - sk->min_type = sh.type; - sk->min_offset = sh.offset; - } - sk->nr_items = 4096; - sk->min_offset++; - if (!sk->min_offset) - sk->min_type++; - else - continue; - - if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) { - sk->min_type = BTRFS_ROOT_ITEM_KEY; - sk->min_objectid++; - } else - continue; - - if (sk->min_objectid >= sk->max_objectid) - break; - } - close(fd); - - /* now actually remove them */ - - if (!do_remove_btrfs_children(tree, root_id, path)) { - free_btrfs_tree(tree); - ERROR("failed pruning\n"); - return -1; - } - - free_btrfs_tree(tree); - /* All child subvols have been removed, now remove this one */ -ignore_search: - return btrfs_do_destroy_subvol(path); -} - -bool btrfs_try_remove_subvol(const char *path) -{ - if (!btrfs_detect(path)) - return false; - return btrfs_recursive_destroy(path) == 0; -} - -int btrfs_destroy(struct bdev *orig) -{ - return btrfs_recursive_destroy(orig->src); -} - -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); -} - diff -Nru lxc-2.0.8/src/lxc/bdev/lxcbtrfs.h lxc-2.1.0/src/lxc/bdev/lxcbtrfs.h --- lxc-2.0.8/src/lxc/bdev/lxcbtrfs.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcbtrfs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,416 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_BTRFS_H -#define __LXC_BTRFS_H - -#define _GNU_SOURCE -#include /* __le64, __l32 ... */ -#include -#include -#include - -#ifndef BTRFS_SUPER_MAGIC -# define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -struct btrfs_ioctl_space_info { - unsigned long long flags; - unsigned long long total_bytes; - unsigned long long used_bytes; -}; - -struct btrfs_ioctl_space_args { - unsigned long long space_slots; - unsigned long long total_spaces; - struct btrfs_ioctl_space_info spaces[]; -}; - -#define BTRFS_IOCTL_MAGIC 0x94 -#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long) -#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ - struct btrfs_ioctl_space_args) - -#define BTRFS_FSID_SIZE 16 -struct btrfs_ioctl_fs_info_args { - unsigned long long max_id; - unsigned long long num_devices; - char fsid[BTRFS_FSID_SIZE]; - unsigned long long reserved[124]; -}; - -#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ - struct btrfs_ioctl_fs_info_args) - - -#define BTRFS_SUBVOL_NAME_MAX 4039 -#define BTRFS_PATH_NAME_MAX 4087 - -struct btrfs_ioctl_vol_args { - signed long long fd; - char name[BTRFS_PATH_NAME_MAX + 1]; -}; - -#define BTRFS_IOCTL_MAGIC 0x94 -#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \ - struct btrfs_ioctl_vol_args_v2) -#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ - struct btrfs_ioctl_vol_args_v2) -#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ - struct btrfs_ioctl_vol_args) -#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ - struct btrfs_ioctl_vol_args) - -#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) - -struct btrfs_ioctl_vol_args_v2 { - signed long long fd; - unsigned long long transid; - unsigned long long flags; - union { - struct { - unsigned long long size; - //struct btrfs_qgroup_inherit *qgroup_inherit; - void *qgroup_inherit; - }; - unsigned long long unused[4]; - }; - char name[BTRFS_SUBVOL_NAME_MAX + 1]; -}; - -/* - * root backrefs tie subvols and snapshots to the directory entries that - * reference them - */ -#define BTRFS_ROOT_BACKREF_KEY 144 - -/* - * root items point to tree roots. There are typically in the root - * tree used by the super block to find all the other trees - */ -#define BTRFS_ROOT_ITEM_KEY 132 - -/* - * root refs make a fast index for listing all of the snapshots and - * subvolumes referenced by a given root. They point directly to the - * directory item in the root that references the subvol - */ -#define BTRFS_ROOT_REF_KEY 156 - -#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL -#define BTRFS_DIR_ITEM_KEY 84 - -/* - * * this is used for both forward and backward root refs - * */ -struct btrfs_root_ref { - __le64 dirid; - __le64 sequence; - __le16 name_len; -} __attribute__ ((__packed__)); - -struct btrfs_disk_key { - __le64 objectid; - u8 type; - __le64 offset; -} __attribute__ ((__packed__)); - -struct btrfs_dir_item { - struct btrfs_disk_key location; - __le64 transid; - __le16 data_len; - __le16 name_len; - u8 type; -} __attribute__ ((__packed__)); - -#define BTRFS_IOCTL_MAGIC 0x94 -#define BTRFS_VOL_NAME_MAX 255 -#define BTRFS_PATH_NAME_MAX 4087 - -struct btrfs_ioctl_search_key { - /* which root are we searching. 0 is the tree of tree roots */ - __u64 tree_id; - - /* keys returned will be >= min and <= max */ - __u64 min_objectid; - __u64 max_objectid; - - /* keys returned will be >= min and <= max */ - __u64 min_offset; - __u64 max_offset; - - /* max and min transids to search for */ - __u64 min_transid; - __u64 max_transid; - - /* keys returned will be >= min and <= max */ - __u32 min_type; - __u32 max_type; - - /* - * how many items did userland ask for, and how many are we - * returning - */ - __u32 nr_items; - - /* align to 64 bits */ - __u32 unused; - - /* some extra for later */ - __u64 unused1; - __u64 unused2; - __u64 unused3; - __u64 unused4; -}; - -struct btrfs_ioctl_search_header { - __u64 transid; - __u64 objectid; - __u64 offset; - __u32 type; - __u32 len; -} __attribute__((may_alias)); - -#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) -/* - * the buf is an array of search headers where - * each header is followed by the actual item - * the type field is expanded to 32 bits for alignment - */ -struct btrfs_ioctl_search_args { - struct btrfs_ioctl_search_key key; - char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; -}; - -#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ - struct btrfs_ioctl_search_args) -#define BTRFS_UUID_SIZE 16 - -struct btrfs_timespec { - __le64 sec; - __le32 nsec; -} __attribute__ ((__packed__)); - -struct btrfs_inode_item { - /* nfs style generation number */ - __le64 generation; - /* transid that last touched this inode */ - __le64 transid; - __le64 size; - __le64 nbytes; - __le64 block_group; - __le32 nlink; - __le32 uid; - __le32 gid; - __le32 mode; - __le64 rdev; - __le64 flags; - - /* modification sequence number for NFS */ - __le64 sequence; - - /* - * a little future expansion, for more than this we can - * just grow the inode item and version it - */ - __le64 reserved[4]; - struct btrfs_timespec atime; - struct btrfs_timespec ctime; - struct btrfs_timespec mtime; - struct btrfs_timespec otime; -} __attribute__ ((__packed__)); - -struct btrfs_root_item_v0 { - struct btrfs_inode_item inode; - __le64 generation; - __le64 root_dirid; - __le64 bytenr; - __le64 byte_limit; - __le64 bytes_used; - __le64 last_snapshot; - __le64 flags; - __le32 refs; - struct btrfs_disk_key drop_progress; - u8 drop_level; - u8 level; -} __attribute__ ((__packed__)); - -struct btrfs_root_item { - struct btrfs_inode_item inode; - __le64 generation; - __le64 root_dirid; - __le64 bytenr; - __le64 byte_limit; - __le64 bytes_used; - __le64 last_snapshot; - __le64 flags; - __le32 refs; - struct btrfs_disk_key drop_progress; - u8 drop_level; - u8 level; - - /* - * The following fields appear after subvol_uuids+subvol_times - * were introduced. - */ - - /* - * This generation number is used to test if the new fields are valid - * and up to date while reading the root item. Every time the root item - * is written out, the "generation" field is copied into this field. If - * anyone ever mounted the fs with an older kernel, we will have - * mismatching generation values here and thus must invalidate the - * new fields. See btrfs_update_root and btrfs_find_last_root for - * details. - * the offset of generation_v2 is also used as the start for the memset - * when invalidating the fields. - */ - __le64 generation_v2; - u8 uuid[BTRFS_UUID_SIZE]; - u8 parent_uuid[BTRFS_UUID_SIZE]; - u8 received_uuid[BTRFS_UUID_SIZE]; - __le64 ctransid; /* updated when an inode changes */ - __le64 otransid; /* trans when created */ - __le64 stransid; /* trans when sent. non-zero for received subvol */ - __le64 rtransid; /* trans when received. non-zero for received subvol */ - struct btrfs_timespec ctime; - struct btrfs_timespec otime; - struct btrfs_timespec stime; - struct btrfs_timespec rtime; - __le64 reserved[8]; /* for future */ -} __attribute__ ((__packed__)); - -#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ - struct btrfs_ioctl_ino_lookup_args) - -#define BTRFS_INO_LOOKUP_PATH_MAX 4080 -struct btrfs_ioctl_ino_lookup_args { - __u64 treeid; - __u64 objectid; - char name[BTRFS_INO_LOOKUP_PATH_MAX]; -}; - -/* - * All files have objectids in this range. - */ -#define BTRFS_FIRST_FREE_OBJECTID 256ULL -#define BTRFS_LAST_FREE_OBJECTID -256ULL -#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL - -/* - * The followings are macro for correctly getting member of - * structures in both low and big endian platforms as per - * btrfs-progs - */ -#ifdef __CHECKER__ -#define __force __attribute__((force)) -#else -#define __force -#endif - -#if __BYTE_ORDER == __BIG_ENDIAN -#define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) -#define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) -#define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) -#define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) -#define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) -#define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) -#else -#define cpu_to_le64(x) ((__force __le64)(u64)(x)) -#define le64_to_cpu(x) ((__force u64)(__le64)(x)) -#define cpu_to_le32(x) ((__force __le32)(u32)(x)) -#define le32_to_cpu(x) ((__force u32)(__le32)(x)) -#define cpu_to_le16(x) ((__force __le16)(u16)(x)) -#define le16_to_cpu(x) ((__force u16)(__le16)(x)) -#endif - -#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \ -static inline u##bits btrfs_##name(type *s) \ -{ \ - return le##bits##_to_cpu(s->member); \ -} \ -static inline void btrfs_set_##name(type *s, u##bits val) \ -{ \ - s->member = cpu_to_le##bits(val); \ -} - -/* defined as btrfs_stack_root_ref_dirid */ -BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64); -/* defined as btrfs_stack_root_ref_sequence */ -BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64); -/* defined as btrfs_stack_root_ref_name_len */ -BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16); - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -struct mytree_node { - u64 objid; - u64 parentid; - char *name; - char *dirname; -}; - -struct my_btrfs_tree { - struct mytree_node *nodes; - int num; -}; - -/* - * Functions associated with a btrfs bdev struct. - */ -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); -int btrfs_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int btrfs_destroy(struct bdev *orig); -int btrfs_detect(const char *path); -int btrfs_mount(struct bdev *bdev); -int btrfs_umount(struct bdev *bdev); - -/* - * Helper functions - */ -char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name, - int name_len); -int btrfs_list_get_path_rootid(int fd, u64 *treeid); -bool is_btrfs_fs(const char *path); -int is_btrfs_subvol(const char *path); -bool btrfs_try_remove_subvol(const char *path); -int btrfs_same_fs(const char *orig, const char *new); -int btrfs_snapshot(const char *orig, const char *new); - -#endif // __LXC_BTRFS_H diff -Nru lxc-2.0.8/src/lxc/bdev/lxcdir.c lxc-2.1.0/src/lxc/bdev/lxcdir.c --- lxc-2.0.8/src/lxc/bdev/lxcdir.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcdir.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "utils.h" - -lxc_log_define(lxcdir, lxc); - -/* - * for a simple directory bind mount, we substitute the old container - * name and paths for the new - */ -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; -} - -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; -} - -int dir_destroy(struct bdev *orig) -{ - if (lxc_rmdir_onedev(orig->src, NULL) < 0) - return -1; - return 0; -} - -int dir_detect(const char *path) -{ - if (strncmp(path, "dir:", 4) == 0) - return 1; // take their word for it - if (is_dir(path)) - return 1; - return 0; -} - -int dir_mount(struct 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; -} - -int dir_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "dir")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - return umount(bdev->dest); -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxcdir.h lxc-2.1.0/src/lxc/bdev/lxcdir.h --- lxc-2.0.8/src/lxc/bdev/lxcdir.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcdir.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_DIR_H -#define __LXC_DIR_H - -#define _GNU_SOURCE -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * Functions associated with a dir bdev struct. - */ -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 dir_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int dir_destroy(struct bdev *orig); -int dir_detect(const char *path); -int dir_mount(struct bdev *bdev); -int dir_umount(struct bdev *bdev); - -#endif /* __LXC_DIR_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcloop.c lxc-2.1.0/src/lxc/bdev/lxcloop.c --- lxc-2.0.8/src/lxc/bdev/lxcloop.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcloop.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,237 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "lxcloop.h" -#include "utils.h" - -lxc_log_define(lxcloop, lxc); - -static int do_loop_create(const char *path, uint64_t size, const char *fstype); - -/* - * No idea what the original blockdev will be called, but the copy will be - * called $lxcpath/$lxcname/rootdev - */ -int loop_clonepaths(struct 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); -} - -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); -} - -int loop_destroy(struct bdev *orig) -{ - return unlink(orig->src + 5); -} - -int loop_detect(const char *path) -{ - if (strncmp(path, "loop:", 5) == 0) - return 1; - return 0; -} - -int loop_mount(struct bdev *bdev) -{ - int ret, loopfd; - char loname[MAXPATHLEN]; - - if (strcmp(bdev->type, "loop")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - - loopfd = lxc_prepare_loop_dev(bdev->src + 5, loname, LO_FLAGS_AUTOCLEAR); - if (loopfd < 0) - return -1; - DEBUG("prepared loop device \"%s\"", loname); - - ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); - if (ret < 0) - ERROR("failed to mount rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); - else - bdev->lofd = loopfd; - DEBUG("mounted rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); - - return ret; -} - -int loop_umount(struct 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; -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxcloop.h lxc-2.1.0/src/lxc/bdev/lxcloop.h --- lxc-2.0.8/src/lxc/bdev/lxcloop.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcloop.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_LOOP_H -#define __LXC_LOOP_H - -#define _GNU_SOURCE -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * functions associated with a loop bdev struct - */ -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); -int loop_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int loop_destroy(struct bdev *orig); -int loop_detect(const char *path); -int loop_mount(struct bdev *bdev); -int loop_umount(struct bdev *bdev); - -#endif /* __LXC_LOOP_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxclvm.c lxc-2.1.0/src/lxc/bdev/lxclvm.c --- lxc-2.0.8/src/lxc/bdev/lxclvm.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxclvm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,433 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _GNU_SOURCE -#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ -#include /* Required for PRIu64 to work. */ -#include -#include -#include -#include -#include -#include -#include - -#include "bdev.h" -#include "config.h" -#include "log.h" -#include "lxclvm.h" -#include "utils.h" - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -# include -#endif - -lxc_log_define(lxclvm, lxc); - -extern char *dir_new_path(char *src, const char *oldname, const char *name, - const char *oldpath, const char *lxcpath); - - /* - * LVM ops - */ - - /* - * path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not - * yet exist. This function will attempt to create /dev/$vg/$lv of size - * $size. If thinpool is specified, we'll check for it's existence and if - * it's - * a valid thin pool, and if so, we'll create the requested lv from that - * thin - * pool. - */ - static int do_lvm_create(const char *path, uint64_t size, - const char *thinpool) -{ - int ret, pid, len; - char sz[24], *pathdup, *vg, *lv, *tp = NULL; - - if ((pid = fork()) < 0) { - SYSERROR("failed fork"); - return -1; - } - if (pid > 0) - return wait_for_pid(pid); - - // specify bytes to lvcreate - ret = snprintf(sz, 24, "%"PRIu64"b", size); - if (ret < 0 || ret >= 24) - exit(EXIT_FAILURE); - - pathdup = strdup(path); - if (!pathdup) - exit(EXIT_FAILURE); - - lv = strrchr(pathdup, '/'); - if (!lv) - exit(EXIT_FAILURE); - - *lv = '\0'; - lv++; - - vg = strrchr(pathdup, '/'); - if (!vg) - exit(EXIT_FAILURE); - vg++; - - if (thinpool) { - len = strlen(pathdup) + strlen(thinpool) + 2; - tp = alloca(len); - - ret = snprintf(tp, len, "%s/%s", pathdup, thinpool); - if (ret < 0 || ret >= len) - exit(EXIT_FAILURE); - - ret = lvm_is_thin_pool(tp); - INFO("got %d for thin pool at path: %s", ret, tp); - if (ret < 0) - exit(EXIT_FAILURE); - - if (!ret) - tp = NULL; - } - - (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); - if (!tp) - execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); - else - execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL); - - SYSERROR("execlp"); - exit(EXIT_FAILURE); -} - - -/* - * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM - * prefix "LVM-", then this is an lvm2 LV - */ -int lvm_detect(const char *path) -{ - char devp[MAXPATHLEN], buf[4]; - FILE *fout; - int ret; - struct stat statbuf; - - if (strncmp(path, "lvm:", 4) == 0) - return 1; // take their word for it - - ret = stat(path, &statbuf); - if (ret != 0) - return 0; - if (!S_ISBLK(statbuf.st_mode)) - return 0; - - ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid", - major(statbuf.st_rdev), minor(statbuf.st_rdev)); - if (ret < 0 || ret >= MAXPATHLEN) { - ERROR("lvm uuid pathname too long"); - return 0; - } - fout = fopen(devp, "r"); - if (!fout) - return 0; - ret = fread(buf, 1, 4, fout); - fclose(fout); - if (ret != 4 || strncmp(buf, "LVM-", 4) != 0) - return 0; - return 1; -} - -int lvm_mount(struct 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); -} - -int lvm_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "lvm")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - return umount(bdev->dest); -} - -int lvm_compare_lv_attr(const char *path, int pos, const char expected) -{ - struct lxc_popen_FILE *f; - int ret, len, status, start=0; - char *cmd, output[12]; - const char *lvscmd = "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null"; - - len = strlen(lvscmd) + strlen(path) - 1; - cmd = alloca(len); - - ret = snprintf(cmd, len, lvscmd, path); - if (ret < 0 || ret >= len) - return -1; - - f = lxc_popen(cmd); - - if (f == NULL) { - SYSERROR("popen failed"); - return -1; - } - - ret = fgets(output, 12, f->f) == NULL; - - status = lxc_pclose(f); - - if (ret || WEXITSTATUS(status)) - // Assume either vg or lvs do not exist, default - // comparison to false. - return 0; - - len = strlen(output); - while(start < len && output[start] == ' ') start++; - - if (start + pos < len && output[start + pos] == expected) - return 1; - - return 0; -} - -int lvm_is_thin_volume(const char *path) -{ - return lvm_compare_lv_attr(path, 6, 't'); -} - -int lvm_is_thin_pool(const char *path) -{ - return lvm_compare_lv_attr(path, 0, 't'); -} - -int lvm_snapshot(const char *orig, const char *path, uint64_t size) -{ - int ret, pid; - char sz[24], *pathdup, *lv; - - if ((pid = fork()) < 0) { - SYSERROR("failed fork"); - return -1; - } - if (pid > 0) - return wait_for_pid(pid); - - // specify bytes to lvcreate - ret = snprintf(sz, 24, "%"PRIu64"b", size); - if (ret < 0 || ret >= 24) - exit(EXIT_FAILURE); - - pathdup = strdup(path); - if (!pathdup) - exit(EXIT_FAILURE); - lv = strrchr(pathdup, '/'); - if (!lv) { - free(pathdup); - exit(EXIT_FAILURE); - } - *lv = '\0'; - lv++; - - // check if the original lv is backed by a thin pool, in which case we - // cannot specify a size that's different from the original size. - ret = lvm_is_thin_volume(orig); - if (ret == -1) { - free(pathdup); - return -1; - } - - (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); - if (!ret) { - ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL); - } else { - ret = execlp("lvcreate", "lvcreate", "-s", "-n", lv, orig, (char *)NULL); - } - - free(pathdup); - exit(EXIT_FAILURE); -} - -int lvm_clonepaths(struct 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; -} - -int lvm_destroy(struct bdev *orig) -{ - pid_t pid; - - if ((pid = fork()) < 0) - return -1; - if (!pid) { - (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); - execlp("lvremove", "lvremove", "-f", orig->src, (char *)NULL); - exit(EXIT_FAILURE); - } - return wait_for_pid(pid); -} - -int lvm_create(struct 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; -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxclvm.h lxc-2.1.0/src/lxc/bdev/lxclvm.h --- lxc-2.0.8/src/lxc/bdev/lxclvm.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxclvm.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_LVM_H -#define __LXC_LVM_H - -#define _GNU_SOURCE -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * Functions associated with an lvm bdev struct. - */ -int lvm_detect(const char *path); -int lvm_mount(struct bdev *bdev); -int lvm_umount(struct bdev *bdev); -int lvm_compare_lv_attr(const char *path, int pos, const char expected); -int lvm_is_thin_volume(const char *path); -int lvm_is_thin_pool(const char *path); -int lvm_snapshot(const char *orig, const char *path, uint64_t size); -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); -int lvm_destroy(struct bdev *orig); -int lvm_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); - -#endif /* __LXC_LVM_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcnbd.c lxc-2.1.0/src/lxc/bdev/lxcnbd.c --- lxc-2.0.8/src/lxc/bdev/lxcnbd.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcnbd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,311 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "lxcnbd.h" -#include "utils.h" - -lxc_log_define(lxcnbd, lxc); - -struct nbd_attach_data { - const char *nbd; - const char *path; -}; - -static bool clone_attach_nbd(const char *nbd, const char *path); -static int do_attach_nbd(void *d); -static bool nbd_busy(int idx); -static void nbd_detach(const char *path); -static int nbd_get_partition(const char *src); -static bool wait_for_partition(const char *path); - -bool attach_nbd(char *src, struct lxc_conf *conf) -{ - char *orig = alloca(strlen(src)+1), *p, path[50]; - int i = 0; - - strcpy(orig, src); - /* if path is followed by a partition, drop that for now */ - p = strchr(orig, ':'); - if (p) - *p = '\0'; - while (1) { - sprintf(path, "/dev/nbd%d", i); - if (!file_exists(path)) - return false; - if (nbd_busy(i)) { - i++; - continue; - } - if (!clone_attach_nbd(path, orig)) - return false; - conf->nbd_idx = i; - return true; - } -} - -void detach_nbd_idx(int idx) -{ - int ret; - char path[50]; - - ret = snprintf(path, 50, "/dev/nbd%d", idx); - if (ret < 0 || ret >= 50) - return; - - nbd_detach(path); -} - -int nbd_clonepaths(struct 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) -{ - return -ENOSYS; -} - -int nbd_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs) -{ - return -ENOSYS; -} - -int nbd_destroy(struct bdev *orig) -{ - return -ENOSYS; -} - -int nbd_detect(const char *path) -{ - if (strncmp(path, "nbd:", 4) == 0) - return 1; - return 0; -} - -int nbd_mount(struct bdev *bdev) -{ - int ret = -1, partition; - char path[50]; - - if (strcmp(bdev->type, "nbd")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - - /* nbd_idx should have been copied by bdev_init from the lxc_conf */ - if (bdev->nbd_idx < 0) - return -22; - partition = nbd_get_partition(bdev->src); - if (partition) - ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx, - partition); - else - ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx); - if (ret < 0 || ret >= 50) { - ERROR("Error setting up nbd device path"); - return ret; - } - - /* It might take awhile for the partition files to show up */ - if (partition) { - if (!wait_for_partition(path)) - return -2; - } - ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts); - if (ret < 0) - ERROR("Error mounting %s", bdev->src); - - return ret; -} - -int nbd_umount(struct bdev *bdev) -{ - int ret; - - if (strcmp(bdev->type, "nbd")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - ret = umount(bdev->dest); - return ret; -} - -bool requires_nbd(const char *path) -{ - if (strncmp(path, "nbd:", 4) == 0) - return true; - return false; -} - -static int do_attach_nbd(void *d) -{ - struct nbd_attach_data *data = d; - const char *nbd, *path; - pid_t pid; - sigset_t mask; - int sfd; - ssize_t s; - struct signalfd_siginfo fdsi; - - sigemptyset(&mask); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGCHLD); - - nbd = data->nbd; - path = data->path; - - if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { - SYSERROR("Error blocking signals for nbd watcher"); - exit(1); - } - - sfd = signalfd(-1, &mask, 0); - if (sfd == -1) { - SYSERROR("Error opening signalfd for nbd task"); - exit(1); - } - - if (prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) < 0) - SYSERROR("Error setting parent death signal for nbd watcher"); - - pid = fork(); - if (pid) { - for (;;) { - s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); - if (s != sizeof(struct signalfd_siginfo)) - SYSERROR("Error reading from signalfd"); - - if (fdsi.ssi_signo == SIGHUP) { - /* container has exited */ - nbd_detach(nbd); - exit(0); - } else if (fdsi.ssi_signo == SIGCHLD) { - int status; - /* If qemu-nbd fails, or is killed by a signal, - * then exit */ - while (waitpid(-1, &status, WNOHANG) > 0) { - if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) || - WIFSIGNALED(status)) { - nbd_detach(nbd); - exit(1); - } - } - } - } - } - - close(sfd); - if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) - WARN("Warning: unblocking signals for nbd watcher"); - - execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL); - SYSERROR("Error executing qemu-nbd"); - exit(1); -} - -static bool clone_attach_nbd(const char *nbd, const char *path) -{ - pid_t pid; - struct nbd_attach_data data; - - data.nbd = nbd; - data.path = path; - - pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID); - if (pid < 0) - return false; - return true; -} - -static bool nbd_busy(int idx) -{ - char path[100]; - int ret; - - ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx); - if (ret < 0 || ret >= 100) - return true; - return file_exists(path); -} - -static void nbd_detach(const char *path) -{ - int ret; - pid_t pid = fork(); - - if (pid < 0) { - SYSERROR("Error forking to detach nbd"); - return; - } - if (pid) { - ret = wait_for_pid(pid); - if (ret < 0) - ERROR("nbd disconnect returned an error"); - return; - } - execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL); - SYSERROR("Error executing qemu-nbd"); - exit(1); -} - -/* - * Pick the partition # off the end of a nbd:file:p - * description. Return 1-9 for the partition id, or 0 - * for no partition. - */ -static int nbd_get_partition(const char *src) -{ - char *p = strchr(src, ':'); - if (!p) - return 0; - p = strchr(p+1, ':'); - if (!p) - return 0; - p++; - if (*p < '1' || *p > '9') - return 0; - return *p - '0'; -} - -static bool wait_for_partition(const char *path) -{ - int count = 0; - while (count < 5) { - if (file_exists(path)) - return true; - sleep(1); - count++; - } - ERROR("Device %s did not show up after 5 seconds", path); - return false; -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxcnbd.h lxc-2.1.0/src/lxc/bdev/lxcnbd.h --- lxc-2.0.8/src/lxc/bdev/lxcnbd.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcnbd.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_NBD_H -#define __LXC_NBD_H - -#define _GNU_SOURCE -#include -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * Functions associated with an nbd bdev struct. - */ -int nbd_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 nbd_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int nbd_destroy(struct bdev *orig); -int nbd_detect(const char *path); -int nbd_mount(struct bdev *bdev); -int nbd_umount(struct bdev *bdev); - -/* helpers */ -bool attach_nbd(char *src, struct lxc_conf *conf); -void detach_nbd_idx(int idx); -bool requires_nbd(const char *path); - -#endif /* __LXC_NBD_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcoverlay.c lxc-2.1.0/src/lxc/bdev/lxcoverlay.c --- lxc-2.0.8/src/lxc/bdev/lxcoverlay.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcoverlay.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,767 +0,0 @@ -/* - * 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 "bdev.h" -#include "conf.h" -#include "confile.h" -#include "log.h" -#include "lxccontainer.h" -#include "lxcoverlay.h" -#include "lxcrsync.h" -#include "utils.h" - -lxc_log_define(lxcoverlay, lxc); - -static char *ovl_name; -static char *ovl_version[] = {"overlay", "overlayfs"}; - -/* defined in lxccontainer.c: needs to become common helper */ -extern char *dir_new_path(char *src, const char *oldname, const char *name, - const char *oldpath, const char *lxcpath); - -static char *ovl_detect_name(void); -static int ovl_do_rsync(struct bdev *orig, struct bdev *new, - struct lxc_conf *conf); -static int ovl_rsync(struct rsync_data *data); -static int ovl_rsync_wrapper(void *data); -static int ovl_remount_on_enodev(const char *lower, const char *target, - const char *name, unsigned long mountflags, - const void *options); - -int ovl_clonepaths(struct 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: - * The workdir will be - * /var/lib/lxc/c2/olwork - * and is used to prepare files before they are atomically - * switched to the overlay destination. Workdirs need to be on - * the same filesystem as the upperdir so it's OK for it to be - * empty. - */ - work = malloc(lastslashidx + 7); - if (!work) { - free(delta); - return -1; - } - strncpy(work, new->dest, lastslashidx + 1); - strcpy(work + lastslashidx, "olwork"); - if (mkdir(work, 0755) < 0) { - SYSERROR("error: mkdir %s", work); - free(delta); - free(work); - return -1; - } - if (am_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 (see comment further - * up) - */ - lastslash = strrchr(ndelta, '/'); - if (!lastslash) { - free(osrc); - free(ndelta); - return -1; - } - lastslash++; - lastslashidx = lastslash - ndelta; - - work = malloc(lastslashidx + 7); - if (!work) { - free(osrc); - free(ndelta); - return -1; - } - strncpy(work, ndelta, lastslashidx + 1); - strcpy(work + lastslashidx, "olwork"); - if ((mkdir(work, 0755) < 0) && errno != EEXIST) { - SYSERROR("error: mkdir %s", work); - free(osrc); - free(ndelta); - free(work); - return -1; - } - if (am_unpriv() && chown_mapped_root(work, conf) < 0) - WARN("Failed to update ownership of %s", work); - free(work); - - len = strlen(nsrc) + strlen(ndelta) + 12; - new->src = malloc(len); - if (!new->src) { - free(osrc); - free(ndelta); - return -ENOMEM; - } - ret = snprintf(new->src, len, "overlayfs:%s:%s", nsrc, ndelta); - free(osrc); - free(ndelta); - if (ret < 0 || ret >= len) - return -ENOMEM; - - return ovl_do_rsync(orig, new, conf); - } else { - ERROR("overlayfs clone of %s container is not yet supported", - orig->type); - /* - * Note, supporting this will require ovl_mount supporting - * mounting of the underlay. No big deal, just needs to be done. - */ - return -1; - } - - return 0; -} - -/* - * to say 'lxc-create -t ubuntu -n o1 -B overlayfs' means you want - * $lxcpath/$lxcname/rootfs to have the created container, while all - * changes after starting the container are written to - * $lxcpath/$lxcname/delta0 - */ -int ovl_create(struct 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; -} - -int ovl_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, NULL); -} - -int ovl_detect(const char *path) -{ - if (strncmp(path, "overlayfs:", 10) == 0) - return 1; // take their word for it - return 0; -} - -char *ovl_getlower(char *p) -{ - char *p1 = strchr(p, ':'); - if (p1) - *p1 = '\0'; - return p; -} - -int ovl_mount(struct bdev *bdev) -{ - char *tmp, *options, *dup, *lower, *upper; - char *options_work, *work, *lastslash; - int lastslashidx; - int len, len2; - unsigned long mntflags; - char *mntdata; - int ret, ret2; - - if (strcmp(bdev->type, "overlayfs")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - - if (!ovl_name) - ovl_name = ovl_detect_name(); - - /* - * separately mount it first: - * mount -t overlayfs * -oupperdir=${upper},lowerdir=${lower} lower dest - */ - dup = alloca(strlen(bdev->src) + 1); - strcpy(dup, bdev->src); - /* support multiple lower layers */ - if (!(lower = strstr(dup, ":/"))) - return -22; - lower++; - upper = lower; - while ((tmp = strstr(++upper, ":/"))) { - upper = tmp; - } - if (--upper == lower) - return -22; - *upper = '\0'; - upper++; - - // if delta doesn't yet exist, create it - if (mkdir_p(upper, 0755) < 0 && errno != EEXIST) - return -22; - - /* - * overlayfs.v22 or higher needs workdir option: - * if upper is - * /var/lib/lxc/c2/delta0 - * then workdir is - * /var/lib/lxc/c2/olwork - */ - lastslash = strrchr(upper, '/'); - if (!lastslash) - return -22; - lastslash++; - lastslashidx = lastslash - upper; - - work = alloca(lastslashidx + 7); - strncpy(work, upper, lastslashidx + 7); - strcpy(work + lastslashidx, "olwork"); - - if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { - free(mntdata); - return -22; - } - - if (mkdir_p(work, 0755) < 0 && errno != EEXIST) { - free(mntdata); - return -22; - } - - /* - * TODO: - * We should check whether bdev->src is a blockdev but for now only - * support overlays of a basic directory - */ - - if (mntdata) { - len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=,") + strlen(mntdata) + 1; - options = alloca(len); - ret = snprintf(options, len, "upperdir=%s,lowerdir=%s,%s", upper, lower, mntdata); - - len2 = strlen(lower) + strlen(upper) + strlen(work) - + strlen("upperdir=,lowerdir=,workdir=") + strlen(mntdata) + 1; - options_work = alloca(len2); - ret2 = snprintf(options, len2, "upperdir=%s,lowerdir=%s,workdir=%s,%s", - upper, lower, work, mntdata); - } else { - len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=") + 1; - options = alloca(len); - ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, lower); - - len2 = strlen(lower) + strlen(upper) + strlen(work) - + strlen("upperdir=,lowerdir=,workdir=") + 1; - options_work = alloca(len2); - ret2 = snprintf(options_work, len2, "upperdir=%s,lowerdir=%s,workdir=%s", - upper, lower, work); - } - - if (ret < 0 || ret >= len || ret2 < 0 || ret2 >= len2) { - free(mntdata); - return -1; - } - - /* Assume we need a workdir as we are on a overlay version >= v22. */ - ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, - MS_MGC_VAL | mntflags, options_work); - if (ret < 0) { - INFO("Overlayfs: Error mounting %s onto %s with options %s. " - "Retrying without workdir: %s.", - lower, bdev->dest, options_work, strerror(errno)); - - /* Assume we cannot use a workdir as we are on a version <= v21. */ - ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, - MS_MGC_VAL | mntflags, options); - if (ret < 0) - SYSERROR("Overlayfs: Error mounting %s onto %s with " - "options %s: %s.", - lower, bdev->dest, options, - strerror(errno)); - else - INFO("Overlayfs: Mounted %s onto %s with options %s.", - lower, bdev->dest, options); - } else { - INFO("Overlayfs: Mounted %s onto %s with options %s.", lower, - bdev->dest, options_work); - } - return ret; -} - -int ovl_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "overlayfs")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - return umount(bdev->dest); -} - -char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen) -{ - char *rootfsdir = NULL; - char *s1 = NULL; - char *s2 = NULL; - char *s3 = NULL; - - if (!rootfs_path || !rootfslen) - return NULL; - - s1 = strdup(rootfs_path); - if (!s1) - return NULL; - - if ((s2 = strstr(s1, ":/"))) { - s2 = s2 + 1; - if ((s3 = strstr(s2, ":/"))) - *s3 = '\0'; - rootfsdir = strdup(s2); - if (!rootfsdir) { - free(s1); - return NULL; - } - } - - if (!rootfsdir) - rootfsdir = s1; - else - free(s1); - - *rootfslen = strlen(rootfsdir); - - return rootfsdir; -} - -int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path) -{ - char lxcpath[MAXPATHLEN]; - char *rootfs_path = NULL; - char *rootfsdir = NULL; - char *upperdir = NULL; - char *workdir = NULL; - char **opts = NULL; - int fret = -1; - int ret = 0; - size_t arrlen = 0; - size_t dirlen = 0; - size_t i; - size_t len = 0; - size_t rootfslen = 0; - - /* When rootfs == NULL we have a container without a rootfs. */ - if (rootfs && rootfs->path) - rootfs_path = rootfs->path; - - opts = lxc_string_split(mntent->mnt_opts, ','); - if (opts) - arrlen = lxc_array_len((void **)opts); - else - goto err; - - for (i = 0; i < arrlen; i++) { - if (strstr(opts[i], "upperdir=") && (strlen(opts[i]) > (len = strlen("upperdir=")))) - upperdir = opts[i] + len; - else if (strstr(opts[i], "workdir=") && (strlen(opts[i]) > (len = strlen("workdir=")))) - workdir = opts[i] + len; - } - - if (rootfs_path) { - ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - rootfsdir = ovl_get_rootfs(rootfs_path, &rootfslen); - if (!rootfsdir) - goto err; - - dirlen = strlen(lxcpath); - } - - /* - * We neither allow users to create upperdirs and workdirs outside the - * containerdir nor inside the rootfs. The latter might be debatable. - * When we have a container without a rootfs we skip the checks. - */ - ret = 0; - if (upperdir) { - if (!rootfs_path) - ret = mkdir_p(upperdir, 0755); - else if ((strncmp(upperdir, lxcpath, dirlen) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) - ret = mkdir_p(upperdir, 0755); - if (ret < 0) - WARN("Failed to create upperdir"); - } - - ret = 0; - if (workdir) { - if (!rootfs_path) - ret = mkdir_p(workdir, 0755); - else if ((strncmp(workdir, lxcpath, dirlen) == 0) && (strncmp(workdir, rootfsdir, rootfslen) != 0)) - ret = mkdir_p(workdir, 0755); - if (ret < 0) - WARN("Failed to create workdir"); - } - - fret = 0; - -err: - free(rootfsdir); - lxc_free_array((void **)opts, free); - return fret; -} - -/* - * To be called from lxcapi_clone() in lxccontainer.c: When we clone a container - * with overlay lxc.mount.entry entries we need to update absolute paths for - * upper- and workdir. This update is done in two locations: - * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done - * independent of each other since lxc_conf->mountlist may container more mount - * entries (e.g. from other included files) than lxc_conf->unexpanded_config . - */ -int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, - const char *lxc_name, const char *newpath, - const char *newname) -{ - char new_upper[MAXPATHLEN]; - char new_work[MAXPATHLEN]; - char old_upper[MAXPATHLEN]; - char old_work[MAXPATHLEN]; - char *cleanpath = NULL; - size_t i; - int fret = -1; - int ret = 0; - struct lxc_list *iterator; - const char *ovl_dirs[] = {"br", "upperdir", "workdir"}; - - cleanpath = strdup(newpath); - if (!cleanpath) - goto err; - - remove_trailing_slashes(cleanpath); - - /* - * We have to update lxc_conf->unexpanded_config separately from - * lxc_conf->mount_list. - */ - for (i = 0; i < sizeof(ovl_dirs) / sizeof(ovl_dirs[0]); i++) { - if (!clone_update_unexp_ovl_paths(lxc_conf, lxc_path, newpath, - lxc_name, newname, - ovl_dirs[i])) - goto err; - } - - ret = snprintf(old_work, MAXPATHLEN, "workdir=%s/%s", lxc_path, lxc_name); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - ret = snprintf(new_work, MAXPATHLEN, "workdir=%s/%s", cleanpath, newname); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - lxc_list_for_each(iterator, &lxc_conf->mount_list) { - char *mnt_entry = NULL; - char *new_mnt_entry = NULL; - char *tmp = NULL; - char *tmp_mnt_entry = NULL; - mnt_entry = iterator->elem; - - if (strstr(mnt_entry, "overlay")) - tmp = "upperdir"; - else if (strstr(mnt_entry, "aufs")) - tmp = "br"; - - if (!tmp) - continue; - - ret = snprintf(old_upper, MAXPATHLEN, "%s=%s/%s", tmp, lxc_path, lxc_name); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - ret = snprintf(new_upper, MAXPATHLEN, "%s=%s/%s", tmp, cleanpath, newname); - if (ret < 0 || ret >= MAXPATHLEN) - goto err; - - if (strstr(mnt_entry, old_upper)) { - tmp_mnt_entry = lxc_string_replace(old_upper, new_upper, mnt_entry); - } - - if (strstr(mnt_entry, old_work)) { - if (tmp_mnt_entry) - new_mnt_entry = lxc_string_replace(old_work, new_work, tmp_mnt_entry); - else - new_mnt_entry = lxc_string_replace(old_work, new_work, mnt_entry); - } - - if (new_mnt_entry) { - free(iterator->elem); - iterator->elem = strdup(new_mnt_entry); - } else if (tmp_mnt_entry) { - free(iterator->elem); - iterator->elem = strdup(tmp_mnt_entry); - } - - free(new_mnt_entry); - free(tmp_mnt_entry); - } - - fret = 0; -err: - free(cleanpath); - return fret; -} - -static int ovl_remount_on_enodev(const char *lower, const char *target, - const char *name, unsigned long mountflags, - const void *options) -{ - int ret; - ret = mount(lower, target, ovl_name, MS_MGC_VAL | mountflags, options); - if (ret < 0 && errno == ENODEV) /* Try other module name. */ - ret = mount(lower, target, - ovl_name == ovl_version[0] ? ovl_version[1] - : ovl_version[0], - MS_MGC_VAL | mountflags, options); - return ret; -} - -static int ovl_rsync(struct rsync_data *data) -{ - int ret; - - if (setgid(0) < 0) { - ERROR("Failed to setgid to 0"); - return -1; - } - if (setgroups(0, NULL) < 0) - WARN("Failed to clear groups"); - if (setuid(0) < 0) { - ERROR("Failed to setuid to 0"); - return -1; - } - - if (unshare(CLONE_NEWNS) < 0) { - SYSERROR("Unable to unshare mounts ns"); - return -1; - } - if (detect_shared_rootfs()) { - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { - SYSERROR("Failed to make / rslave"); - ERROR("Continuing..."); - } - } - if (ovl_mount(data->orig) < 0) { - ERROR("Failed mounting original container fs"); - return -1; - } - if (ovl_mount(data->new) < 0) { - ERROR("Failed mounting new container fs"); - return -1; - } - ret = do_rsync(data->orig->dest, data->new->dest); - - ovl_umount(data->new); - ovl_umount(data->orig); - - if (ret < 0) { - ERROR("rsyncing %s to %s", data->orig->dest, data->new->dest); - return -1; - } - - return 0; -} - -static char *ovl_detect_name(void) -{ - char *v = ovl_version[0]; - char *line = NULL; - size_t len = 0; - FILE *f = fopen("/proc/filesystems", "r"); - if (!f) - return v; - - while (getline(&line, &len, f) != -1) { - if (strcmp(line, "nodev\toverlayfs\n") == 0) { - v = ovl_version[1]; - break; - } - } - - fclose(f); - free(line); - return v; -} - -static int ovl_do_rsync(struct bdev *orig, struct bdev *new, struct lxc_conf *conf) -{ - int ret = -1; - struct 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 ovl_rsync_wrapper(void *data) -{ - struct rsync_data *arg = data; - return ovl_rsync(arg); -} - diff -Nru lxc-2.0.8/src/lxc/bdev/lxcoverlay.h lxc-2.1.0/src/lxc/bdev/lxcoverlay.h --- lxc-2.0.8/src/lxc/bdev/lxcoverlay.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcoverlay.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_OVERLAY_H -#define __LXC_OVERLAY_H - -#include -#include -#include -#include - -#if IS_BIONIC -#include <../include/lxcmntent.h> -#else -#include -#endif - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* defined in conf.h */ -struct lxc_rootfs; - -/* - * Functions associated with an overlay bdev struct. - */ -int ovl_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 ovl_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int ovl_destroy(struct bdev *orig); -int ovl_detect(const char *path); -int ovl_mount(struct bdev *bdev); -int ovl_umount(struct bdev *bdev); - -/* - * To be called from lxcapi_clone() in lxccontainer.c: When we clone a container - * with overlay lxc.mount.entry entries we need to update absolute paths for - * upper- and workdir. This update is done in two locations: - * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done - * independent of each other since lxc_conf->mountlist may container more mount - * entries (e.g. from other included files) than lxc_conf->unexpanded_config . - */ -int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, - const char *lxc_name, const char *newpath, - const char *newname); - -/* - * To be called from functions in lxccontainer.c: Get lower directory for - * overlay rootfs. - */ -char *ovl_getlower(char *p); - -/* - * Get rootfs path for overlay backed containers. Allocated memory must be freed - * by caller. - */ -char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen); - -/* - * Create upper- and workdirs for overlay mounts. - */ -int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path); - -#endif /* __LXC_OVERLAY_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcrbd.c lxc-2.1.0/src/lxc/bdev/lxcrbd.c --- lxc-2.0.8/src/lxc/bdev/lxcrbd.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcrbd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _GNU_SOURCE -#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ -#include /* Required for PRIu64 to work. */ -#include -#include -#include -#include - -#include "bdev.h" -#include "log.h" -#include "utils.h" - -lxc_log_define(lxcrbd, lxc); - -int rbd_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) -{ - ERROR("rbd clonepaths not implemented"); - return -1; -} - -int rbd_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs) -{ - const char *rbdpool, *rbdname = n, *fstype; - uint64_t size; - int ret, len; - char sz[24]; - pid_t pid; - - if (!specs) - return -1; - - rbdpool = specs->rbd.rbdpool; - if (!rbdpool) - rbdpool = lxc_global_config_value("lxc.bdev.rbd.rbdpool"); - - if (specs->rbd.rbdname) - rbdname = specs->rbd.rbdname; - - /* source device /dev/rbd/lxc/ctn */ - len = strlen(rbdpool) + strlen(rbdname) + 11; - bdev->src = malloc(len); - if (!bdev->src) - return -1; - - ret = snprintf(bdev->src, len, "/dev/rbd/%s/%s", rbdpool, rbdname); - if (ret < 0 || ret >= len) - return -1; - - // fssize is in bytes. - size = specs->fssize; - if (!size) - size = DEFAULT_FS_SIZE; - - // in megabytes for rbd tool - ret = snprintf(sz, 24, "%"PRIu64, size / 1024 / 1024 ); - if (ret < 0 || ret >= 24) - exit(1); - - if ((pid = fork()) < 0) - return -1; - if (!pid) { - execlp("rbd", "rbd", "create" , "--pool", rbdpool, rbdname, "--size", sz, (char *)NULL); - exit(1); - } - if (wait_for_pid(pid) < 0) - return -1; - - if ((pid = fork()) < 0) - return -1; - if (!pid) { - execlp("rbd", "rbd", "map", "--pool", rbdpool, rbdname, (char *)NULL); - exit(1); - } - if (wait_for_pid(pid) < 0) - return -1; - - fstype = specs->fstype; - if (!fstype) - fstype = DEFAULT_FSTYPE; - - 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 && errno != EEXIST) { - ERROR("Error creating %s", bdev->dest); - return -1; - } - - return 0; -} - -int rbd_destroy(struct bdev *orig) -{ - pid_t pid; - char *rbdfullname; - - if ( file_exists(orig->src) ) { - if ((pid = fork()) < 0) - return -1; - if (!pid) { - execlp("rbd", "rbd", "unmap" , orig->src, (char *)NULL); - exit(1); - } - if (wait_for_pid(pid) < 0) - return -1; - } - - if ((pid = fork()) < 0) - return -1; - if (!pid) { - rbdfullname = alloca(strlen(orig->src) - 8); - strcpy( rbdfullname, &orig->src[9] ); - execlp("rbd", "rbd", "rm" , rbdfullname, (char *)NULL); - exit(1); - } - return wait_for_pid(pid); - -} - -int rbd_detect(const char *path) -{ - if ( memcmp(path, "/dev/rbd/", 9) == 0) - return 1; - return 0; -} - -int rbd_mount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "rbd")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - - if ( !file_exists(bdev->src) ) { - // if blkdev does not exist it should be mapped, because it is not persistent on reboot - ERROR("Block device %s is not mapped.", bdev->src); - return -1; - } - - return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts); -} - -int rbd_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "rbd")) - return -22; - if (!bdev->src || !bdev->dest) - return -22; - return umount(bdev->dest); -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxcrbd.h lxc-2.1.0/src/lxc/bdev/lxcrbd.h --- lxc-2.0.8/src/lxc/bdev/lxcrbd.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcrbd.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_RDB_H -#define __LXC_RDB_H - -#define _GNU_SOURCE -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * Functions associated with an rdb bdev struct. - */ -int rbd_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 rbd_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -int rbd_destroy(struct bdev *orig); -int rbd_detect(const char *path); -int rbd_mount(struct bdev *bdev); -int rbd_umount(struct bdev *bdev); - -#endif /* __LXC_RDB_H */ diff -Nru lxc-2.0.8/src/lxc/bdev/lxcrsync.c lxc-2.1.0/src/lxc/bdev/lxcrsync.c --- lxc-2.0.8/src/lxc/bdev/lxcrsync.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcrsync.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -/* - * 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 "bdev.h" -#include "log.h" -#include "lxcrsync.h" -#include "utils.h" - -lxc_log_define(lxcrsync, lxc); - -/* the bulk of this needs to become a common helper */ -int do_rsync(const char *src, const char *dest) -{ - // call out to rsync - pid_t pid; - char *s; - size_t l; - - pid = fork(); - if (pid < 0) - return -1; - if (pid > 0) - return wait_for_pid(pid); - - l = strlen(src) + 2; - s = malloc(l); - if (!s) - exit(1); - strcpy(s, src); - s[l-2] = '/'; - s[l-1] = '\0'; - - execlp("rsync", "rsync", "-aHXS", "--delete", s, dest, (char *)NULL); - exit(1); -} - -int rsync_delta(struct rsync_data_char *data) -{ - if (setgid(0) < 0) { - ERROR("Failed to setgid to 0"); - return -1; - } - if (setgroups(0, NULL) < 0) - WARN("Failed to clear groups"); - if (setuid(0) < 0) { - ERROR("Failed to setuid to 0"); - return -1; - } - if (do_rsync(data->src, data->dest) < 0) { - ERROR("rsyncing %s to %s", data->src, data->dest); - return -1; - } - - return 0; -} - -int rsync_delta_wrapper(void *data) -{ - struct rsync_data_char *arg = data; - return rsync_delta(arg); -} - -int rsync_rootfs(struct rsync_data *data) -{ - struct 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; -} - -int rsync_rootfs_wrapper(void *data) -{ - struct rsync_data *arg = data; - return rsync_rootfs(arg); -} - diff -Nru lxc-2.0.8/src/lxc/bdev/lxcrsync.h lxc-2.1.0/src/lxc/bdev/lxcrsync.h --- lxc-2.0.8/src/lxc/bdev/lxcrsync.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxcrsync.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_RSYNC_H -#define __LXC_RSYNC_H - -#define _GNU_SOURCE -#include - -struct rsync_data { - struct bdev *orig; - struct bdev *new; -}; - -struct rsync_data_char { - char *src; - char *dest; -}; - -int do_rsync(const char *src, const char *dest); -int rsync_delta_wrapper(void *data); -int rsync_delta(struct rsync_data_char *data); -int rsync_rootfs(struct rsync_data *data); -int rsync_rootfs_wrapper(void *data); - -#endif // __LXC_RSYNC_H diff -Nru lxc-2.0.8/src/lxc/bdev/lxczfs.c lxc-2.1.0/src/lxc/bdev/lxczfs.c --- lxc-2.0.8/src/lxc/bdev/lxczfs.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxczfs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,300 +0,0 @@ -/* - * 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 "bdev.h" -#include "config.h" -#include "log.h" -#include "lxczfs.h" -#include "utils.h" - -lxc_log_define(lxczfs, lxc); - -/* - * zfs ops: - * There are two ways we could do this. We could always specify the 'zfs device' - * (i.e. tank/lxc lxc/container) as rootfs. But instead (at least right now) we - * have lxc-create specify $lxcpath/$lxcname/rootfs as the mountpoint, so that - * it is always mounted. That means 'mount' is really never needed and could be - * noop, but for the sake of flexibility let's always bind-mount. - */ - -int zfs_list_entry(const char *path, char *output, size_t inlen) -{ - struct lxc_popen_FILE *f; - int found=0; - - f = lxc_popen("zfs list 2> /dev/null"); - if (f == NULL) { - SYSERROR("popen failed"); - return 0; - } - - while (fgets(output, inlen, f->f)) { - if (strstr(output, path)) { - found = 1; - break; - } - } - (void) lxc_pclose(f); - - return found; -} - -int zfs_detect(const char *path) -{ - char *output = malloc(LXC_LOG_BUFFER_SIZE); - - if (!output) { - ERROR("out of memory"); - return 0; - } - - int found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); - free(output); - - return found; -} - -int zfs_mount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "zfs")) - return -22; - - if (!bdev->src || !bdev->dest) - return -22; - - char *mntdata; - unsigned long mntflags; - if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { - free(mntdata); - return -22; - } - - int ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata); - free(mntdata); - - return ret; -} - -int zfs_umount(struct bdev *bdev) -{ - if (strcmp(bdev->type, "zfs")) - return -22; - - if (!bdev->src || !bdev->dest) - return -22; - - return umount(bdev->dest); -} - -int zfs_clone(const char *opath, const char *npath, const char *oname, - const char *nname, const char *lxcpath, int snapshot) -{ - // use the 'zfs list | grep opath' entry to get the zfsroot - char output[MAXPATHLEN], option[MAXPATHLEN]; - char *p; - const char *zfsroot = output; - int ret; - pid_t pid; - - if (zfs_list_entry(opath, output, MAXPATHLEN)) { - // zfsroot is output up to ' ' - if ((p = strchr(output, ' ')) == NULL) - return -1; - *p = '\0'; - - if ((p = strrchr(output, '/')) == NULL) - return -1; - *p = '\0'; - } else { - zfsroot = lxc_global_config_value("lxc.bdev.zfs.root"); - } - - ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", lxcpath, nname); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; - - // zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname - if (!snapshot) { - if ((pid = fork()) < 0) - return -1; - if (!pid) { - char dev[MAXPATHLEN]; - ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, nname); - if (ret < 0 || ret >= MAXPATHLEN) - exit(EXIT_FAILURE); - execlp("zfs", "zfs", "create", option, dev, (char *)NULL); - exit(EXIT_FAILURE); - } - return wait_for_pid(pid); - } else { - // if snapshot, do - // 'zfs snapshot zfsroot/oname@nname - // zfs clone zfsroot/oname@nname zfsroot/nname - char path1[MAXPATHLEN], path2[MAXPATHLEN]; - - ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", zfsroot, - oname, nname); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; - (void) snprintf(path2, MAXPATHLEN, "%s/%s", zfsroot, nname); - - // if the snapshot exists, delete it - if ((pid = fork()) < 0) - return -1; - if (!pid) { - int dev0 = open("/dev/null", O_WRONLY); - if (dev0 >= 0) - dup2(dev0, STDERR_FILENO); - execlp("zfs", "zfs", "destroy", path1, (char *)NULL); - exit(EXIT_FAILURE); - } - // it probably doesn't exist so destroy probably will fail. - (void) wait_for_pid(pid); - - // run first (snapshot) command - if ((pid = fork()) < 0) - return -1; - if (!pid) { - execlp("zfs", "zfs", "snapshot", path1, (char *)NULL); - exit(EXIT_FAILURE); - } - if (wait_for_pid(pid) < 0) - return -1; - - // run second (clone) command - if ((pid = fork()) < 0) - return -1; - if (!pid) { - execlp("zfs", "zfs", "clone", option, path1, path2, (char *)NULL); - exit(EXIT_FAILURE); - } - return wait_for_pid(pid); - } -} - -int zfs_clonepaths(struct 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. - */ -int zfs_destroy(struct bdev *orig) -{ - pid_t pid; - char output[MAXPATHLEN]; - char *p; - - if ((pid = fork()) < 0) - return -1; - if (pid) - return wait_for_pid(pid); - - if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) { - ERROR("Error: zfs entry for %s not found", orig->src); - return -1; - } - - // zfs mount is output up to ' ' - if ((p = strchr(output, ' ')) == NULL) - return -1; - *p = '\0'; - - execlp("zfs", "zfs", "destroy", "-r", output, (char *)NULL); - exit(EXIT_FAILURE); -} - -int zfs_create(struct 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(EXIT_FAILURE); - - execlp("zfs", "zfs", "create", option, dev, (char *)NULL); - exit(EXIT_FAILURE); -} diff -Nru lxc-2.0.8/src/lxc/bdev/lxczfs.h lxc-2.1.0/src/lxc/bdev/lxczfs.h --- lxc-2.0.8/src/lxc/bdev/lxczfs.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/bdev/lxczfs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LXC_ZFS_H -#define __LXC_ZFS_H - -#define _GNU_SOURCE -#include -#include - -/* defined in bdev.h */ -struct bdev; - -/* defined in lxccontainer.h */ -struct bdev_specs; - -/* defined conf.h */ -struct lxc_conf; - -/* - * Functions associated with an zfs bdev struct. - */ -int zfs_clone(const char *opath, const char *npath, const char *oname, - const char *nname, const char *lxcpath, int snapshot); -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 zfs_create(struct bdev *bdev, const char *dest, const char *n, - struct bdev_specs *specs); -/* - * TODO: detect whether this was a clone, and if so then also delete the - * snapshot it was based on, so that we don't hold the original - * container busy. - */ -int zfs_destroy(struct bdev *orig); -int zfs_detect(const char *path); -int zfs_list_entry(const char *path, char *output, size_t inlen); -int zfs_mount(struct bdev *bdev); -int zfs_umount(struct bdev *bdev); - -#endif /* __LXC_ZFS_H */ diff -Nru lxc-2.0.8/src/lxc/caps.c lxc-2.1.0/src/lxc/caps.c --- lxc-2.0.8/src/lxc/caps.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/caps.c 2017-09-06 02:32:37.000000000 +0000 @@ -54,19 +54,19 @@ caps = cap_get_proc(); if (!caps) { - ERROR("failed to cap_get_proc: %m"); + ERROR("failed to cap_get_proc: %s", strerror(errno)); return -1; } ret = cap_clear_flag(caps, CAP_EFFECTIVE); if (ret) { - ERROR("failed to cap_clear_flag: %m"); + ERROR("failed to cap_clear_flag: %s", strerror(errno)); goto out; } ret = cap_set_proc(caps); if (ret) { - ERROR("failed to cap_set_proc: %m"); + ERROR("failed to cap_set_proc: %s", strerror(errno)); goto out; } @@ -88,7 +88,7 @@ caps = cap_get_proc(); if (!caps) { - ERROR("failed to cap_get_proc: %m"); + ERROR("failed to cap_get_proc: %s", strerror(errno)); return -1; } @@ -102,21 +102,22 @@ INFO("Last supported cap was %d", cap-1); break; } else { - ERROR("failed to cap_get_flag: %m"); + ERROR("failed to cap_get_flag: %s", + strerror(errno)); goto out; } } ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); if (ret) { - ERROR("failed to cap_set_flag: %m"); + ERROR("failed to cap_set_flag: %s", strerror(errno)); goto out; } } ret = cap_set_proc(caps); if (ret) { - ERROR("failed to cap_set_proc: %m"); + ERROR("failed to cap_set_proc: %s", strerror(errno)); goto out; } @@ -140,22 +141,26 @@ INFO("command is run as setuid root (uid : %d)", uid); if (prctl(PR_SET_KEEPCAPS, 1)) { - ERROR("failed to 'PR_SET_KEEPCAPS': %m"); + ERROR("failed to 'PR_SET_KEEPCAPS': %s", + strerror(errno)); return -1; } if (setresgid(gid, gid, gid)) { - ERROR("failed to change gid to '%d': %m", gid); + ERROR("failed to change gid to '%d': %s", gid, + strerror(errno)); return -1; } if (setresuid(uid, uid, uid)) { - ERROR("failed to change uid to '%d': %m", uid); + ERROR("failed to change uid to '%d': %s", uid, + strerror(errno)); return -1; } if (lxc_caps_up()) { - ERROR("failed to restore capabilities: %m"); + ERROR("failed to restore capabilities: %s", + strerror(errno)); return -1; } } diff -Nru lxc-2.0.8/src/lxc/caps.h lxc-2.1.0/src/lxc/caps.h --- lxc-2.0.8/src/lxc/caps.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/caps.h 2017-09-06 02:32:37.000000000 +0000 @@ -21,11 +21,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef __LXC_CAPS_H +#define __LXC_CAPS_H + #include "config.h" #include -#ifndef __LXC_CAPS_H -#define __LXC_CAPS_H #if HAVE_LIBCAP #include diff -Nru lxc-2.0.8/src/lxc/cgroups/cgfs.c lxc-2.1.0/src/lxc/cgroups/cgfs.c --- lxc-2.0.8/src/lxc/cgroups/cgfs.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgfs.c 2017-09-06 02:32:37.000000000 +0000 @@ -39,7 +39,6 @@ #include #include -#include "bdev.h" #include "error.h" #include "commands.h" #include "list.h" @@ -49,6 +48,7 @@ #include "cgroup.h" #include "start.h" #include "state.h" +#include "storage.h" #if IS_BIONIC #include <../include/lxcmntent.h> @@ -165,7 +165,7 @@ dir = opendir(dirname); if (!dir) { - ERROR("%s: failed to open %s", __func__, dirname); + ERROR("Failed to open %s", dirname); return -1; } @@ -190,7 +190,7 @@ } ret = lstat(pathname, &mystat); if (ret) { - SYSERROR("%s: failed to stat %s", __func__, pathname); + SYSERROR("Failed to stat %s", pathname); failed=1; if (!saved_errno) saved_errno = errno; @@ -206,7 +206,7 @@ } if (rmdir(dirname) < 0) { - SYSERROR("%s: failed to delete %s", __func__, dirname); + SYSERROR("Failed to delete %s", dirname); if (!saved_errno) saved_errno = errno; failed=1; @@ -214,7 +214,7 @@ ret = closedir(dir); if (ret) { - SYSERROR("%s: failed to close directory %s", __func__, dirname); + SYSERROR("Failed to close directory %s", dirname); if (!saved_errno) saved_errno = errno; failed=1; @@ -791,7 +791,7 @@ len = strlen(oldname) + strlen(mountpath) + 22; fulloldpath = alloca(len); - ret = snprintf(fulloldpath, len, "%s/%s/%ld", mountpath, oldname, (unsigned long)pid); + ret = snprintf(fulloldpath, len, "%s/%s/%lu", mountpath, oldname, (unsigned long)pid); if (ret < 0 || ret >= len) return NULL; @@ -1800,7 +1800,9 @@ 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' + /* 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); @@ -1874,7 +1876,8 @@ return 0; if (recurse) { if (conf && !lxc_list_empty(&conf->id_map)) - r = userns_exec_1(conf, rmdir_wrapper, buf); + r = userns_exec_1(conf, rmdir_wrapper, buf, + "rmdir_wrapper"); else r = cgroup_rmdir(buf); } else @@ -2067,9 +2070,10 @@ 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 + /* 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; @@ -2228,7 +2232,7 @@ return ret; } /* Callers don't do this, but regression/sanity check */ - ERROR("%s: was not expecting 0 bufsize", __func__); + ERROR("was not expecting 0 bufsize"); return -1; } buf[ret] = '\0'; @@ -2339,7 +2343,7 @@ return &cgfs_ops; } -static void *cgfs_init(const char *name) +static void *cgfs_init(struct lxc_handler *handler) { struct cgfs_data *d; @@ -2348,7 +2352,7 @@ return NULL; memset(d, 0, sizeof(*d)); - d->name = strdup(name); + d->name = strdup(handler->name); if (!d->name) goto err1; @@ -2616,7 +2620,8 @@ /* 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) { + if (userns_exec_1(conf, chown_cgroup_wrapper, &data, + "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new namespace"); return false; } diff -Nru lxc-2.0.8/src/lxc/cgroups/cgfsng.c lxc-2.1.0/src/lxc/cgroups/cgfsng.c --- lxc-2.0.8/src/lxc/cgroups/cgfsng.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgfsng.c 2017-09-06 02:32:37.000000000 +0000 @@ -47,10 +47,15 @@ #include #include -#include "bdev.h" +#include +#include + #include "cgroup.h" +#include "cgroup_utils.h" #include "commands.h" +#include "conf.h" #include "log.h" +#include "storage/storage.h" #include "utils.h" lxc_log_define(lxc_cgfsng, lxc); @@ -72,21 +77,26 @@ char *mountpoint; char *base_cgroup; char *fullcgpath; + bool is_cgroup_v2; }; /* * The cgroup data which is attached to the lxc_handler. - * @cgroup_pattern - a copy of the lxc.cgroup.pattern - * @container_cgroup - if not null, the cgroup which was created for - * the container. For each hierarchy, it is created under the - * @hierarchy->base_cgroup directory. Relative to the base_cgroup - * it is the same for all hierarchies. - * @name - the container name + * @cgroup_pattern : A copy of the lxc.cgroup.pattern + * @container_cgroup : If not null, the cgroup which was created for the + * container. For each hierarchy, it is created under the + * @hierarchy->base_cgroup directory. Relative to the + * base_cgroup it is the same for all hierarchies. + * @name : The name of the container. + * @cgroup_meta : A copy of the container's cgroup information. This + * overrides @cgroup_pattern. */ struct cgfsng_handler_data { char *cgroup_pattern; - char *container_cgroup; // cgroup we created for the container - char *name; // container name + char *container_cgroup; /* cgroup we created for the container */ + char *name; /* container name */ + /* per-container cgroup information */ + struct lxc_cgroup cgroup_meta; }; /* @@ -118,36 +128,12 @@ } } -/* Re-alllocate a pointer, do not fail */ -static void *must_realloc(void *orig, size_t sz) -{ - void *ret; - - do { - ret = realloc(orig, sz); - } while (!ret); - return ret; -} - /* Allocate a pointer, do not fail */ static void *must_alloc(size_t sz) { return must_realloc(NULL, sz); } -/* return copy of string @entry; do not fail. */ -static char *must_copy_string(const char *entry) -{ - char *ret; - - if (!entry) - return NULL; - do { - ret = strdup(entry); - } while (!ret); - return ret; -} - /* * This is a special case - return a copy of @entry * prepending 'name='. I.e. turn systemd into name=systemd. @@ -239,6 +225,10 @@ free(d->cgroup_pattern); free(d->container_cgroup); free(d->name); + if (d->cgroup_meta.dir) + free(d->cgroup_meta.dir); + if (d->cgroup_meta.controllers) + free(d->cgroup_meta.controllers); free(d); } @@ -259,8 +249,6 @@ return NULL; } -static char *must_make_path(const char *first, ...) __attribute__((sentinel)); - #define BATCH_SIZE 50 static void batch_realloc(char **mem, size_t oldlen, size_t newlen) { @@ -407,7 +395,7 @@ c2 = c1; else if (c1 < c2) c1 = c2; - else if (!c1 && c2) // The reverse case is obvs. not needed. + else if (!c1 && c2) /* The reverse case is obvs. not needed. */ c1 = c2; /* If the above logic is correct, c1 should always hold a valid string @@ -435,7 +423,7 @@ bool bret = false, flipped_bit = false; lastslash = strrchr(path, '/'); - if (!lastslash) { // bug... this shouldn't be possible + if (!lastslash) { /* bug... this shouldn't be possible */ ERROR("Invalid path: %s.", path); return bret; } @@ -567,7 +555,7 @@ int ret; lastslash = strrchr(path, '/'); - if (!lastslash) { // bug... this shouldn't be possible + if (!lastslash) { /* bug... this shouldn't be possible */ ERROR("cgfsng:copy_parent_file: bad path %s", path); return false; } @@ -626,7 +614,8 @@ } clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL); - if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */ + /* unified hierarchy doesn't have clone_children */ + if (!file_exists(clonechildrenpath)) { free(clonechildrenpath); free(cgpath); return true; @@ -768,10 +757,14 @@ */ static char **get_controllers(char **klist, char **nlist, char *line) { - // the fourth field is /sys/fs/cgroup/comma-delimited-controller-list + /* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */ int i; char *p = line, *p2, *tok, *saveptr = NULL; char **aret = NULL; + bool is_cgroup_v2; + + /* handle cgroup v2 */ + is_cgroup_v2 = is_cgroupfs_v2(line); for (i = 0; i < 4; i++) { p = strchr(p, ' '); @@ -783,8 +776,10 @@ return NULL; /* note - if we change how mountinfo works, then our caller * will need to verify /sys/fs/cgroup/ in this field */ - if (strncmp(p, "/sys/fs/cgroup/", 15) != 0) + if (strncmp(p, "/sys/fs/cgroup/", 15) != 0) { + INFO("cgfsng: found hierarchy not under /sys/fs/cgroup: \"%s\"", p); return NULL; + } p += 15; p2 = strchr(p, ' '); if (!p2) { @@ -792,6 +787,13 @@ return NULL; } *p2 = '\0'; + + /* cgroup v2 does not have separate mountpoints for controllers */ + if (is_cgroup_v2) { + must_append_controller(klist, nlist, &aret, "cgroup2"); + return aret; + } + for (tok = strtok_r(p, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { must_append_controller(klist, nlist, &aret, tok); @@ -800,15 +802,6 @@ return aret; } -/* return true if the fstype is cgroup */ -static bool is_cgroupfs(char *line) -{ - char *p = strstr(line, " - "); - if (!p) - return false; - return strncmp(p, " - cgroup ", 10) == 0; -} - /* Add a controller to our list of hierarchies */ static void add_controller(char **clist, char *mountpoint, char *base_cgroup) { @@ -821,6 +814,12 @@ new->base_cgroup = base_cgroup; new->fullcgpath = NULL; + /* record if this is the cgroup v2 hierarchy */ + if (!strcmp(base_cgroup, "cgroup2")) + new->is_cgroup_v2 = true; + else + new->is_cgroup_v2 = false; + newentry = append_null_to_list((void ***)&hierarchies); hierarchies[newentry] = new; } @@ -902,13 +901,21 @@ static char *get_current_cgroup(char *basecginfo, char *controller) { char *p = basecginfo; + bool is_cgroup_v2; + bool is_cgroup_v2_base_cgroup; + + is_cgroup_v2 = !strcmp(controller, "cgroup2"); + while (true) { + is_cgroup_v2_base_cgroup = false; + /* cgroup v2 entry in "/proc//cgroup": "0::/some/path" */ + if (is_cgroup_v2 && (*p == '0')) + is_cgroup_v2_base_cgroup = true; - while (1) { p = strchr(p, ':'); if (!p) return NULL; p++; - if (controller_in_clist(p, controller)) { + if (is_cgroup_v2_base_cgroup || controller_in_clist(p, controller)) { p = strchr(p, ':'); if (!p) return NULL; @@ -923,20 +930,6 @@ } } -/* - * Given a hierarchy @mountpoint and base @path, verify that we can create - * directories underneath it. - */ -static bool test_writeable(char *mountpoint, char *path) -{ - char *fullpath = must_make_path(mountpoint, path, NULL); - int ret; - - ret = access(fullpath, W_OK); - free(fullpath); - return ret == 0; -} - static void must_append_string(char ***list, char *entry) { int newentry = append_null_to_list((void ***)list); @@ -965,16 +958,17 @@ continue; *p2 = '\0'; - /* If we have a mixture between cgroup v1 and cgroup v2 - * hierarchies, then /proc/self/cgroup contains entries of the - * form: + /* If the kernel has cgroup v2 support, then /proc/self/cgroup + * contains an entry of the form: * * 0::/some/path * - * We need to skip those. + * In this case we use "cgroup2" as controller name. */ - if ((p2 - p) == 0) + if ((p2 - p) == 0) { + must_append_string(klist, "cgroup2"); continue; + } for (tok = strtok_r(p, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { @@ -1001,8 +995,12 @@ printf("Cgroup information:\n"); printf(" container name: %s\n", d->name ? d->name : "(null)"); printf(" lxc.cgroup.use: %s\n", cgroup_use ? cgroup_use : "(null)"); - printf(" lxc.cgroup.pattern: %s\n", d->cgroup_pattern ? d->cgroup_pattern : "(null)"); - printf(" cgroup: %s\n", d->container_cgroup ? d->container_cgroup : "(null)"); + printf(" lxc.cgroup.pattern: %s\n", + d->cgroup_pattern ? d->cgroup_pattern : "(null)"); + printf(" lxc.cgroup.dir: %s\n", + d->cgroup_meta.dir ? d->cgroup_meta.dir : "(null)"); + printf(" cgroup: %s\n", + d->container_cgroup ? d->container_cgroup : "(null)"); } static void lxc_cgfsng_print_hierarchies() @@ -1082,8 +1080,10 @@ while (getline(&line, &len, f) != -1) { char **controller_list = NULL; char *mountpoint, *base_cgroup; + bool is_cgroup_v2, writeable; - if (!is_lxcfs(line) && !is_cgroupfs(line)) + is_cgroup_v2 = is_cgroupfs_v2(line); + if (!is_lxcfs(line) && !is_cgroupfs_v1(line) && !is_cgroup_v2) continue; controller_list = get_controllers(klist, nlist, line); @@ -1109,9 +1109,14 @@ free(mountpoint); continue; } + trim(base_cgroup); prune_init_scope(base_cgroup); - if (!test_writeable(mountpoint, base_cgroup)) { + if (is_cgroup_v2) + writeable = test_writeable_v2(mountpoint, base_cgroup); + else + writeable = test_writeable_v1(mountpoint, base_cgroup); + if (!writeable) { free_string_list(controller_list); free(mountpoint); free(base_cgroup); @@ -1136,8 +1141,10 @@ /* verify that all controllers in cgroup.use and all crucial * controllers are accounted for */ - if (!all_controllers_found()) + if (!all_controllers_found()) { + INFO("cgfsng: not all controllers were find, deferring to cgfs driver"); return false; + } return true; } @@ -1147,7 +1154,7 @@ const char *tmp; errno = 0; tmp = lxc_global_config_value("lxc.cgroup.use"); - if (!cgroup_use && errno != 0) { // lxc.cgroup.use can be NULL + if (!cgroup_use && errno != 0) { /* lxc.cgroup.use can be NULL */ SYSERROR("cgfsng: error reading list of cgroups to use"); return false; } @@ -1156,18 +1163,25 @@ return parse_hierarchies(); } -static void *cgfsng_init(const char *name) +static void *cgfsng_init(struct lxc_handler *handler) { - struct cgfsng_handler_data *d; const char *cgroup_pattern; + struct cgfsng_handler_data *d; d = must_alloc(sizeof(*d)); memset(d, 0, sizeof(*d)); - d->name = must_copy_string(name); + /* copy container name */ + d->name = must_copy_string(handler->name); + + /* copy per-container cgroup information */ + d->cgroup_meta.dir = must_copy_string(handler->conf->cgroup_meta.dir); + d->cgroup_meta.controllers = must_copy_string(handler->conf->cgroup_meta.controllers); + /* copy system-wide cgroup information */ cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - if (!cgroup_pattern) { // lxc.cgroup.pattern is only NULL on error + if (!cgroup_pattern) { + /* lxc.cgroup.pattern is only NULL on error. */ ERROR("Error getting cgroup pattern"); goto out_free; } @@ -1183,35 +1197,9 @@ return NULL; } -/* - * Concatenate all passed-in strings into one path. Do not fail. If any piece is - * not prefixed with '/', add a '/'. - */ -static char *must_make_path(const char *first, ...) -{ - va_list args; - char *cur, *dest; - size_t full_len = strlen(first); - - dest = must_copy_string(first); - - va_start(args, first); - while ((cur = va_arg(args, char *)) != NULL) { - full_len += strlen(cur); - if (cur[0] != '/') - full_len++; - dest = must_realloc(dest, full_len + 1); - if (cur[0] != '/') - strcat(dest, "/"); - strcat(dest, cur); - } - va_end(args); - - return dest; -} - static int cgroup_rmdir(char *dirname) { + int ret; struct dirent *direntp; DIR *dir; int r = 0; @@ -1221,8 +1209,8 @@ return -1; while ((direntp = readdir(dir))) { - struct stat mystat; char *pathname; + struct stat mystat; if (!direntp) break; @@ -1233,32 +1221,40 @@ pathname = must_make_path(dirname, direntp->d_name, NULL); - if (lstat(pathname, &mystat)) { + ret = lstat(pathname, &mystat); + if (ret < 0) { if (!r) - WARN("failed to stat %s", pathname); + WARN("Failed to stat %s", pathname); r = -1; goto next; } if (!S_ISDIR(mystat.st_mode)) goto next; - if (cgroup_rmdir(pathname) < 0) + + ret = cgroup_rmdir(pathname); + if (ret < 0) r = -1; next: free(pathname); } - if (rmdir(dirname) < 0) { + ret = rmdir(dirname); + if (ret < 0) { if (!r) - WARN("%s: failed to delete %s: %m", __func__, dirname); + WARN("Failed to delete \"%s\": %s", dirname, + strerror(errno)); r = -1; } - if (closedir(dir) < 0) { + ret = closedir(dir); + if (ret < 0) { if (!r) - WARN("%s: failed to delete %s: %m", __func__, dirname); + WARN("Failed to delete \"%s\": %s", dirname, + strerror(errno)); r = -1; } + return r; } @@ -1280,7 +1276,7 @@ { int r; if (conf && !lxc_list_empty(&conf->id_map)) - r = userns_exec_1(conf, rmdir_wrapper, path); + r = userns_exec_1(conf, rmdir_wrapper, path, "rmdir_wrapper"); else r = cgroup_rmdir(path); @@ -1324,7 +1320,7 @@ static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname) { h->fullcgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL); - if (dir_exists(h->fullcgpath)) { // it must not already exist + if (dir_exists(h->fullcgpath)) { /* it must not already exist */ ERROR("Path \"%s\" already existed.", h->fullcgpath); return false; } @@ -1349,24 +1345,29 @@ */ static inline bool cgfsng_create(void *hdata) { - struct cgfsng_handler_data *d = hdata; - char *tmp, *cgname, *offset; - int i, idx = 0; + int i; size_t len; + char *cgname, *offset, *tmp; + int idx = 0; + struct cgfsng_handler_data *d = hdata; if (!d) return false; + if (d->container_cgroup) { WARN("cgfsng_create called a second time"); return false; } - tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern); + if (d->cgroup_meta.dir) + tmp = lxc_string_join("/", (const char *[]){d->cgroup_meta.dir, d->name, NULL}, false); + else + tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern); if (!tmp) { ERROR("Failed expanding cgroup name pattern"); return false; } - len = strlen(tmp) + 5; // leave room for -NNN\0 + len = strlen(tmp) + 5; /* leave room for -NNN\0 */ cgname = must_alloc(len); strcpy(cgname, tmp); free(tmp); @@ -1377,12 +1378,24 @@ ERROR("Too many conflicting cgroup names"); goto out_free; } - if (idx) - snprintf(offset, 5, "-%d", idx); + if (idx) { + int ret; + + ret = snprintf(offset, 5, "-%d", idx); + if (ret < 0 || (size_t)ret >= 5) { + FILE *f = fopen("/dev/null", "w"); + if (f >= 0) { + fprintf(f, "Workaround for GCC7 bug: " + "https://gcc.gnu.org/bugzilla/" + "show_bug.cgi?id=78969"); + fclose(f); + } + } + } for (i = 0; hierarchies[i]; i++) { if (!create_path_for_hierarchy(hierarchies[i], cgname)) { int j; - SYSERROR("Failed to create %s: %s", hierarchies[i]->fullcgpath, strerror(errno)); + ERROR("Failed to create \"%s\"", hierarchies[i]->fullcgpath); free(hierarchies[i]->fullcgpath); hierarchies[i]->fullcgpath = NULL; for (j = 0; j < i; j++) @@ -1425,7 +1438,7 @@ struct chown_data { struct cgfsng_handler_data *d; - uid_t origuid; // target uid in parent namespace + uid_t origuid; /* target uid in parent namespace */ }; /* @@ -1473,16 +1486,18 @@ */ fullpath = must_make_path(path, "tasks", NULL); if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT) - WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid); + WARN("Failed chowning %s to %d: %s", fullpath, (int) destuid, + strerror(errno)); if (chmod(fullpath, 0664) < 0) - WARN("Error chmoding %s: %m", path); + WARN("Error chmoding %s: %s", path, strerror(errno)); free(fullpath); fullpath = must_make_path(path, "cgroup.procs", NULL); if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT) - WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid); + WARN("Failed chowning %s to %d: %s", fullpath, (int) destuid, + strerror(errno)); if (chmod(fullpath, 0664) < 0) - WARN("Error chmoding %s: %m", path); + WARN("Error chmoding %s: %s", path, strerror(errno)); free(fullpath); } @@ -1503,7 +1518,8 @@ wrap.d = d; wrap.origuid = geteuid(); - if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap) < 0) { + if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap, + "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new namespace"); return false; } @@ -1544,7 +1560,8 @@ char *source = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL); char *rwpath = must_make_path(dest, h->base_cgroup, container_cgroup, NULL); if (mount(source, rwpath, "cgroup", MS_BIND, NULL) < 0) - WARN("Failed to mount %s read-write: %m", rwpath); + WARN("Failed to mount %s read-write: %s", rwpath, + strerror(errno)); INFO("Made %s read-write", rwpath); free(rwpath); free(source); @@ -1813,13 +1830,6 @@ const char *inpath, const char *filename) { - /* - * XXX Remove this case after 2.0 release. It's for dealing with - * containers spawned under the old buggy cgfsng which wasn't around - * for long. - */ - if (strncmp(inpath, "/sys/fs/cgroup/", 15) == 0) - return must_make_path(inpath, filename, NULL); return must_make_path(h->mountpoint, inpath, filename, NULL); } @@ -1837,7 +1847,7 @@ struct hierarchy *h = hierarchies[i]; path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]); - if (!path) // not running + if (!path) /* not running */ continue; fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs"); @@ -1870,7 +1880,7 @@ *p = '\0'; path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem); - if (!path) // not running + if (!path) /* not running */ return -1; h = get_hierarchy(subsystem); @@ -1902,7 +1912,7 @@ *p = '\0'; path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem); - if (!path) // not running + if (!path) /* not running */ return -1; h = get_hierarchy(subsystem); @@ -1918,20 +1928,106 @@ } /* + * take devices cgroup line + * /dev/foo rwx + * and convert it to a valid + * type major:minor mode + * line. Return <0 on error. Dest is a preallocated buffer + * long enough to hold the output. + */ +static int convert_devpath(const char *invalue, char *dest) +{ + int n_parts; + char *p, *path, type; + struct stat sb; + unsigned long minor, major; + int ret = -EINVAL; + char *mode = NULL; + + path = must_copy_string(invalue); + + /* + * read path followed by mode; ignore any trailing text. + * A ' # comment' would be legal. Technically other text + * is not legal, we could check for that if we cared to + */ + for (n_parts = 1, p = path; *p && n_parts < 3; p++) { + if (*p != ' ') + continue; + *p = '\0'; + if (n_parts != 1) + break; + p++; + n_parts++; + while (*p == ' ') + p++; + mode = p; + if (*p == '\0') + goto out; + } + + if (n_parts == 1) + goto out; + + ret = stat(path, &sb); + if (ret < 0) + goto out; + + mode_t m = sb.st_mode & S_IFMT; + switch (m) { + case S_IFBLK: + type = 'b'; + break; + case S_IFCHR: + type = 'c'; + break; + default: + ERROR("Unsupported device type %i for %s", m, path); + ret = -EINVAL; + goto out; + } + + major = MAJOR(sb.st_rdev); + minor = MINOR(sb.st_rdev); + ret = snprintf(dest, 50, "%c %lu:%lu %s", type, major, minor, mode); + if (ret < 0 || ret >= 50) { + ERROR("Error on configuration value \"%c %lu:%lu %s\" (max 50 " + "chars)", type, major, minor, mode); + ret = -ENAMETOOLONG; + goto out; + } + ret = 0; + +out: + free(path); + return ret; +} + +/* * Called from setup_limits - here we have the container's cgroup_data because * we created the cgroups */ static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfsng_handler_data *d) { char *subsystem = NULL, *p; - int ret = -1; + int ret = 0; struct hierarchy *h; + /* "b|c <2^64-1>:<2^64-1> r|w|m" = 47 chars max */ + char converted_value[50]; subsystem = alloca(strlen(filename) + 1); strcpy(subsystem, filename); if ((p = strchr(subsystem, '.')) != NULL) *p = '\0'; + if (strcmp("devices.allow", filename) == 0 && value[0] == '/') { + ret = convert_devpath(value, converted_value); + if (ret < 0) + return ret; + value = converted_value; + + } + h = get_hierarchy(subsystem); if (h) { char *fullpath = must_make_path(h->fullcgpath, filename, NULL); @@ -1971,9 +2067,8 @@ cg->subsystem, cg->value, d->name); goto out; } + DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); } - - DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); } ret = true; diff -Nru lxc-2.0.8/src/lxc/cgroups/cgmanager.c lxc-2.1.0/src/lxc/cgroups/cgmanager.c --- lxc-2.0.8/src/lxc/cgroups/cgmanager.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgmanager.c 2017-09-06 02:32:37.000000000 +0000 @@ -41,7 +41,6 @@ #include #include -#include "bdev.h" #include "error.h" #include "commands.h" #include "list.h" @@ -51,6 +50,7 @@ #include "cgroup.h" #include "start.h" #include "state.h" +#include "storage.h" #define CGM_SUPPORTS_GET_ABS 3 #define CGM_SUPPORTS_NAMED 4 @@ -139,7 +139,7 @@ cgm_lock(); if (!dbus_threads_initialized) { - // tell dbus to do struct locking for thread safety + /* tell dbus to do struct locking for thread safety */ dbus_threads_init_default(); dbus_threads_initialized = true; } @@ -169,7 +169,7 @@ return false; } - // get the api version + /* get the api version */ if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) { NihError *nerr; nerr = nih_error_get(); @@ -395,7 +395,7 @@ goto out; } if (send_creds(sv[0], getpid(), getuid(), getgid())) { - SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); + SYSERROR("Error sending pid over SCM_CREDENTIAL"); goto out; } fds.fd = sv[0]; @@ -410,7 +410,7 @@ goto out; } if (send_creds(sv[0], getpid(), newuid, 0)) { - SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__); + SYSERROR("Error sending pid over SCM_CREDENTIAL"); goto out; } fds.fd = sv[0]; @@ -497,7 +497,8 @@ /* 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) { + if (userns_exec_1(conf, chown_cgroup_wrapper, &data, + "chown_cgroup_wrapper") < 0) { ERROR("Error requesting cgroup chown in new namespace"); return false; } @@ -539,7 +540,7 @@ INFO("cgroup removal attempt: %s:%s did not exist", controller, path); } -static void *cgm_init(const char *name) +static void *cgm_init(struct lxc_handler *handler) { struct cgm_data *d; @@ -553,7 +554,7 @@ } memset(d, 0, sizeof(*d)); - d->name = strdup(name); + d->name = strdup(handler->name); if (!d->name) { cgm_dbus_disconnect(); goto err1; @@ -561,7 +562,7 @@ d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - // cgm_create immediately gets called so keep the connection open + /* cgm_create immediately gets called so keep the connection open */ return d; err1: @@ -619,10 +620,10 @@ 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 + /* 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) @@ -638,7 +639,7 @@ while (*tmp == '/') tmp++; again: - if (index == 100) { // turn this into a warn later + if (index == 100) { /* turn this into a warn later */ ERROR("cgroup error? 100 cgroups with this name already running"); goto bad; } @@ -661,7 +662,7 @@ if (existed == 1) goto next; } - // success + /* success */ cgroup_path = strdup(tmp); if (!cgroup_path) { cleanup_cgroups(tmp); @@ -946,7 +947,7 @@ close(p[1]); return -1; } - if (!pid) // do_cgm_get exits + 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)); @@ -961,12 +962,12 @@ goto out; } memset(value, 0, len); - if (newlen < 0) { // child is reporting an error + if (newlen < 0) { /* child is reporting an error */ close(p[0]); ret = -1; goto out; } - if (newlen == 0) { // empty read + if (newlen == 0) { /* empty read */ close(p[0]); ret = 0; goto out; @@ -982,7 +983,7 @@ value[len-1] = '\0'; newlen = len-1; } else if (newlen+1 < len) { - // cgmanager doesn't add eol to last entry + /* cgmanager doesn't add eol to last entry */ value[newlen++] = '\n'; value[newlen] = '\0'; } @@ -996,7 +997,7 @@ 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 retval = 0; /* value we are sending to the parent over outp */ int ret; char *cglast; @@ -1082,7 +1083,7 @@ close(p[0]); return -1; } - if (!pid) // do_cgm_set exits + 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)); @@ -1327,7 +1328,7 @@ size_t sz = 0; FILE *f = NULL; - if (subsystems) // already initialized + if (subsystems) /* already initialized */ return true; subsystems_inone = malloc(2 * sizeof(char *)); @@ -1438,7 +1439,7 @@ if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS) cgm_all_controllers_same = false; - // if root, try to escape to root cgroup + /* if root, try to escape to root cgroup */ if (geteuid() == 0 && !cgm_escape(NULL)) { free_subsystems(); return NULL; @@ -1501,7 +1502,7 @@ cg = iterator->elem; if (do_devices != !strncmp("devices", cg->subsystem, 7)) continue; - if (strlen(cg->subsystem) > 100) // i smell a rat + if (strlen(cg->subsystem) > 100) /* i smell a rat */ goto out; strcpy(controller, cg->subsystem); p = strchr(controller, '.'); @@ -1558,7 +1559,7 @@ } /* - * TODO: this should be re-written to use the get_config_item("lxc.id_map") + * TODO: this should be re-written to use the get_config_item("lxc.idmap") * cmd api instead of getting the idmap from c->lxc_conf. The reason is * that the id_maps may be different if the container was started with a * -f or -s argument. @@ -1647,7 +1648,7 @@ 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? + /* Host doesn't have cgmanager running? Then how did we get here? */ return false; } diff -Nru lxc-2.0.8/src/lxc/cgroups/cgroup.c lxc-2.1.0/src/lxc/cgroups/cgroup.c --- lxc-2.0.8/src/lxc/cgroups/cgroup.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgroup.c 2017-09-06 02:32:37.000000000 +0000 @@ -37,8 +37,7 @@ extern struct cgroup_ops *cgfsng_ops_init(void); extern struct cgroup_ops *cgm_ops_init(void); -__attribute__((constructor)) -void cgroup_ops_init(void) +__attribute__((constructor)) void cgroup_ops_init(void) { if (ops) { INFO("cgroup driver %s", ops->name); @@ -46,9 +45,9 @@ } DEBUG("cgroup_init"); - #if HAVE_CGMANAGER +#if HAVE_CGMANAGER ops = cgm_ops_init(); - #endif +#endif if (!ops) ops = cgfsng_ops_init(); if (!ops) @@ -60,14 +59,15 @@ bool cgroup_init(struct lxc_handler *handler) { if (handler->cgroup_data) { - ERROR("cgroup_init called on already inited handler"); + ERROR("cgroup_init called on already initialized handler"); return true; } if (ops) { INFO("cgroup driver %s initing for %s", ops->name, handler->name); - handler->cgroup_data = ops->init(handler->name); + handler->cgroup_data = ops->init(handler); } + return handler->cgroup_data != NULL; } @@ -79,22 +79,21 @@ } } -/* Create the container cgroups for all requested controllers */ +/* Create the container cgroups for all requested controllers. */ bool cgroup_create(struct lxc_handler *handler) { if (ops) return ops->create(handler->cgroup_data); + return false; } -/* - * Enter the container init into its new cgroups for all - * requested controllers - */ +/* Enter the container init into its new cgroups for all requested controllers. */ bool cgroup_enter(struct lxc_handler *handler) { if (ops) return ops->enter(handler->cgroup_data, handler->pid); + return false; } @@ -102,13 +101,16 @@ { if (ops && ops->create_legacy) return ops->create_legacy(handler->cgroup_data, handler->pid); + return true; } -const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem) +const char *cgroup_get_cgroup(struct lxc_handler *handler, + const char *subsystem) { if (ops) return ops->get_cgroup(handler->cgroup_data, subsystem); + return NULL; } @@ -116,6 +118,7 @@ { if (ops) return ops->escape(handler->cgroup_data); + return false; } @@ -139,6 +142,7 @@ { if (ops) return ops->unfreeze(handler->cgroup_data); + return false; } @@ -147,6 +151,7 @@ if (ops) return ops->setup_limits(handler->cgroup_data, &handler->conf->cgroup, with_devices); + return false; } @@ -154,14 +159,15 @@ { if (ops && ops->chown) return ops->chown(handler->cgroup_data, handler->conf); + return true; } bool cgroup_mount(const char *root, struct lxc_handler *handler, int type) { - if (ops) { + if (ops) return ops->mount_cgroup(handler->cgroup_data, root, type); - } + return false; } @@ -171,8 +177,9 @@ if (ops->nrtasks) return ops->nrtasks(handler->cgroup_data); else - WARN("CGROUP driver %s doesn't implement nrtasks", ops->name); + WARN("cgroup driver \"%s\" doesn't implement nrtasks", ops->name); } + return -1; } @@ -180,20 +187,25 @@ { if (ops) return ops->attach(name, lxcpath, pid); + return false; } -int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath) +int lxc_cgroup_set(const char *filename, const char *value, const char *name, + const char *lxcpath) { if (ops) return ops->set(filename, value, name, lxcpath); + return -1; } -int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +int lxc_cgroup_get(const char *filename, char *value, size_t len, + const char *name, const char *lxcpath) { if (ops) return ops->get(filename, value, len, name, lxcpath); + return -1; } @@ -219,30 +231,32 @@ point = cg + strlen(cg) - strlen(INIT_SCOPE); if (point < cg) return; + if (strcmp(point, INIT_SCOPE) == 0) { if (point == cg) - *(point+1) = '\0'; + *(point + 1) = '\0'; else *point = '\0'; } } -/* - * Return true if this is a subsystem which we cannot do - * without. +/* Return true if this is a subsystem which we cannot do without. * - * systemd is questionable here. The way callers currently - * use this, if systemd is not mounted then it will be ignored. - * But if systemd is mounted, then it must be setup so that lxc - * can create cgroups in it, else containers will fail. + * systemd is questionable here. The way callers currently use this, if systemd + * is not mounted then it will be ignored. But if systemd is mounted, then it + * must be setup so that lxc can create cgroups in it, else containers will + * fail. */ bool is_crucial_cgroup_subsystem(const char *s) { if (strcmp(s, "systemd") == 0) return true; + if (strcmp(s, "name=systemd") == 0) return true; + if (strcmp(s, "freezer") == 0) return true; + return false; } diff -Nru lxc-2.0.8/src/lxc/cgroups/cgroup.h lxc-2.1.0/src/lxc/cgroups/cgroup.h --- lxc-2.0.8/src/lxc/cgroups/cgroup.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgroup.h 2017-09-06 02:32:37.000000000 +0000 @@ -41,7 +41,7 @@ struct cgroup_ops { const char *name; - void *(*init)(const char *name); + void *(*init)(struct lxc_handler *handler); void (*destroy)(void *hdata, struct lxc_conf *conf); bool (*create)(void *hdata); bool (*enter)(void *hdata, pid_t pid); diff -Nru lxc-2.0.8/src/lxc/cgroups/cgroup_utils.c lxc-2.1.0/src/lxc/cgroups/cgroup_utils.c --- lxc-2.0.8/src/lxc/cgroups/cgroup_utils.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgroup_utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Serge Hallyn + * Christian Brauner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "cgroup_utils.h" +#include "utils.h" + +bool is_cgroupfs_v1(char *line) +{ + char *p = strstr(line, " - "); + if (!p) + return false; + return strncmp(p, " - cgroup ", 10) == 0; +} + +bool is_cgroupfs_v2(char *line) +{ + char *p = strstr(line, " - "); + if (!p) + return false; + + return strncmp(p, " - cgroup2 ", 11) == 0; +} + +bool test_writeable_v1(char *mountpoint, char *path) +{ + char *fullpath = must_make_path(mountpoint, path, NULL); + int ret; + + ret = access(fullpath, W_OK); + free(fullpath); + return ret == 0; +} + +bool test_writeable_v2(char *mountpoint, char *path) +{ + /* In order to move ourselves into an appropriate sub-cgroup we need to + * have write access to the parent cgroup's "cgroup.procs" file, i.e. we + * need to have write access to the our current cgroups's "cgroup.procs" + * file. + */ + int ret; + char *cgroup_path, *cgroup_procs_file; + + cgroup_path = must_make_path(mountpoint, path, NULL); + cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL); + + ret = access(cgroup_path, W_OK); + free(cgroup_path); + if (ret < 0) { + free(cgroup_procs_file); + return false; + } + + ret = access(cgroup_procs_file, W_OK); + free(cgroup_procs_file); + + return ret == 0; +} diff -Nru lxc-2.0.8/src/lxc/cgroups/cgroup_utils.h lxc-2.1.0/src/lxc/cgroups/cgroup_utils.h --- lxc-2.0.8/src/lxc/cgroups/cgroup_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/cgroups/cgroup_utils.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Serge Hallyn + * Christian Brauner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_CGROUP_UTILS_H +#define __LXC_CGROUP_UTILS_H + +#include +#include + +/* Check if given entry from /proc//mountinfo is a cgroupfs v1 mount. */ +extern bool is_cgroupfs_v1(char *line); + +/* Check if given entry from /proc//mountinfo is a cgroupfs v2 mount. */ +extern bool is_cgroupfs_v2(char *line); + +/* Given a v1 hierarchy @mountpoint and base @path, verify that we can create + * directories underneath it. + */ +extern bool test_writeable_v1(char *mountpoint, char *path); + +/* Given a v2 hierarchy @mountpoint and base @path, verify that we can create + * directories underneath it and that we have write access to the cgroup's + * "cgroup.procs" file. + */ +extern bool test_writeable_v2(char *mountpoint, char *path); + +#endif /* __LXC_CGROUP_UTILS_H */ diff -Nru lxc-2.0.8/src/lxc/commands.c lxc-2.1.0/src/lxc/commands.c --- lxc-2.0.8/src/lxc/commands.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/commands.c 2017-09-06 02:32:37.000000000 +0000 @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -41,9 +40,12 @@ #include "utils.h" #include "cgroup.h" #include "commands.h" +#include "commands_utils.h" #include "console.h" #include "confile.h" +#include "lxclock.h" #include "mainloop.h" +#include "monitor.h" #include "af_unix.h" #include "config.h" @@ -74,74 +76,20 @@ 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", + [LXC_CMD_CONSOLE] = "console", + [LXC_CMD_CONSOLE_WINCH] = "console_winch", + [LXC_CMD_STOP] = "stop", + [LXC_CMD_GET_STATE] = "get_state", + [LXC_CMD_GET_INIT_PID] = "get_init_pid", + [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags", + [LXC_CMD_GET_CGROUP] = "get_cgroup", + [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item", + [LXC_CMD_GET_NAME] = "get_name", + [LXC_CMD_GET_LXCPATH] = "get_lxcpath", + [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client", }; if (cmd >= LXC_CMD_MAX) @@ -168,15 +116,16 @@ */ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) { - int ret,rspfd; + int ret, rspfd; struct lxc_cmd_rsp *rsp = &cmd->rsp; - ret = lxc_abstract_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp)); + ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); if (ret < 0) { WARN("Command %s failed to receive response: %s.", lxc_cmd_str(cmd->req.cmd), strerror(errno)); return -1; } + TRACE("Command \"%s received response", lxc_cmd_str(cmd->req.cmd)); if (cmd->req.cmd == LXC_CMD_CONSOLE) { struct lxc_cmd_console_rsp_data *rspdata; @@ -257,12 +206,61 @@ return 0; } +static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd, + const char *lxcpath, const char *hashed_sock_name) +{ + int client_fd; + int ret = -1; + + client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name); + if (client_fd < 0 && client_fd == -ECONNREFUSED) + return -ECONNREFUSED; + else if (client_fd < 0) + return -1; + + TRACE("Command \"%s\" connected to command socket", + lxc_cmd_str(cmd->req.cmd)); + + ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, sizeof(cmd->req)); + if (ret != sizeof(cmd->req)) { + close(client_fd); + + if (errno == EPIPE) + return -EPIPE; + + if (ret >= 0) + return -EMSGSIZE; + + return -1; + } + + TRACE("Command \"%s\" requested data of length %d", + lxc_cmd_str(cmd->req.cmd), cmd->req.datalen); + + if (cmd->req.datalen > 0) { + ret = send(client_fd, cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL); + if (ret != cmd->req.datalen) { + close(client_fd); + + if (errno == EPIPE) + return -EPIPE; + + if (ret >= 0) + return -EMSGSIZE; + + return -1; + } + } + + return client_fd; +} + /* * 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 + * @cmd : command with initialized request to send * @stopped : output indicator if the container was not running * @lxcpath : the lxcpath in which the container is running * @@ -279,68 +277,43 @@ 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; + int client_fd, ret = -1; + bool stay_connected = false; + + if (cmd->req.cmd == LXC_CMD_CONSOLE || + cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT) + stay_connected = true; *stopped = 0; - /* -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; + TRACE("command %s tries to connect command socket", + lxc_cmd_str(cmd->req.cmd)); - sock = lxc_abstract_unix_connect(path); - if (sock < 0) { - if (errno == ECONNREFUSED) + client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name); + if (client_fd < 0) { + TRACE("command %s failed to connect command socket: %s", + lxc_cmd_str(cmd->req.cmd), strerror(errno)); + if (client_fd == -ECONNREFUSED) { *stopped = 1; - else - SYSERROR("Command %s failed to connect to \"@%s\".", - lxc_cmd_str(cmd->req.cmd), offset); - return -1; - } + return -1; + } - ret = lxc_abstract_unix_send_credential(sock, &cmd->req, sizeof(cmd->req)); - if (ret != sizeof(cmd->req)) { - if (errno == EPIPE) + if (client_fd == -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; - } + goto out; } - ret = lxc_cmd_rsp_recv(sock, cmd); + ret = lxc_cmd_rsp_recv(client_fd, cmd); out: if (!stay_connected || ret <= 0) - close(sock); + close(client_fd); if (stay_connected && ret > 0) - cmd->rsp.ret = sock; + cmd->rsp.ret = client_fd; return ret; epipe: - close(sock); *stopped = 1; return 0; } @@ -462,20 +435,26 @@ }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); - if (ret < 0) + if (ret < 0) { + TRACE("command %s failed for container \"%s\": %s.", + lxc_cmd_str(cmd.req.cmd), name, strerror(errno)); return NULL; + } if (!ret) { - WARN("Container \"%s\" has stopped before sending its state.", name); + 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.", + ERROR("command %s failed for container \"%s\": %s", lxc_cmd_str(cmd.req.cmd), name, strerror(-cmd.rsp.ret)); return NULL; } + TRACE("command %s successful for container \"%s\"", + lxc_cmd_str(cmd.req.cmd), name); + return cmd.rsp.data; } @@ -502,7 +481,7 @@ * 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) + * @item : the configuration item to retrieve (ex: lxc.net.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 @@ -534,14 +513,18 @@ int cilen; struct lxc_cmd_rsp rsp; char *cidata; + struct lxc_config_t *item; memset(&rsp, 0, sizeof(rsp)); - cilen = lxc_get_config_item(handler->conf, req->data, NULL, 0); + item = lxc_get_config(req->data); + if (!item) + goto err1; + cilen = item->get(req->data, NULL, 0, handler->conf, NULL); if (cilen <= 0) goto err1; cidata = alloca(cilen + 1); - if (lxc_get_config_item(handler->conf, req->data, cidata, cilen + 1) != cilen) + if (item->get(req->data, cidata, cilen + 1, handler->conf, NULL) != cilen) goto err1; cidata[cilen] = '\0'; rsp.data = cidata; @@ -563,7 +546,7 @@ * * Returns the state on success, < 0 on failure */ -lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath) +int lxc_cmd_get_state(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { @@ -752,7 +735,7 @@ memset(&rsp, 0, sizeof(rsp)); rsp.data = INT_TO_PTR(ttynum); - if (lxc_abstract_unix_send_fd(fd, masterfd, &rsp, sizeof(rsp)) < 0) { + if (lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)) < 0) { ERROR("Failed to send tty to client."); lxc_console_free(handler->conf, fd); goto out_close; @@ -843,22 +826,129 @@ return lxc_cmd_rsp_send(fd, &rsp); } +int lxc_cmd_add_state_client(const char *name, const char *lxcpath, + lxc_state_t states[MAX_STATE], + int *state_client_fd) +{ + int stopped; + ssize_t ret; + int state = -1; + struct lxc_cmd_rr cmd = { + .req = { + .cmd = LXC_CMD_ADD_STATE_CLIENT, + .data = states, + .datalen = (sizeof(lxc_state_t) * MAX_STATE) + }, + }; + + /* Lock the whole lxc_cmd_add_state_client_callback() call to ensure + * that lxc_set_state() doesn't cause us to miss a state. + */ + process_lock(); + /* Check if already in requested state. */ + state = lxc_getstate(name, lxcpath); + if (state < 0) { + process_unlock(); + TRACE("failed to retrieve state of container: %s", + strerror(errno)); + return -1; + } else if (states[state]) { + process_unlock(); + TRACE("container is %s state", lxc_state2str(state)); + return state; + } + + if ((state == STARTING) && !states[RUNNING] && !states[STOPPING] && !states[STOPPED]) { + process_unlock(); + TRACE("container is in %s state and caller requested to be " + "informed about a previous state", + lxc_state2str(state)); + return state; + } else if ((state == RUNNING) && !states[STOPPING] && !states[STOPPED]) { + process_unlock(); + TRACE("container is in %s state and caller requested to be " + "informed about a previous state", + lxc_state2str(state)); + return state; + } else if ((state == STOPPING) && !states[STOPPED]) { + process_unlock(); + TRACE("container is in %s state and caller requested to be " + "informed about a previous state", + lxc_state2str(state)); + return state; + } else if ((state == STOPPED) || (state == ABORTING)) { + process_unlock(); + TRACE("container is in %s state and caller requested to be " + "informed about a previous state", + lxc_state2str(state)); + return state; + } + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); + process_unlock(); + if (ret < 0) { + ERROR("failed to execute command: %s", strerror(errno)); + return -1; + } + /* We should now be guaranteed to get an answer from the state sending + * function. + */ + + if (cmd.rsp.ret < 0) { + ERROR("failed to receive socket fd"); + return -1; + } + + *state_client_fd = cmd.rsp.ret; + return MAX_STATE; +} + +static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp = {0}; + + if (req->datalen < 0) { + TRACE("Requested datalen was < 0"); + return -1; + } + + if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE)) { + TRACE("Requested datalen was too large"); + return -1; + } + + if (!req->data) { + TRACE("No states requested"); + return -1; + } + + rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data); + if (rsp.ret < 0) + ERROR("Failed to add state client %d to state client list", fd); + else + TRACE("Added state client %d to state client list", fd); + + 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, + [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, + [LXC_CMD_CONSOLE_WINCH] = lxc_cmd_console_winch_callback, + [LXC_CMD_STOP] = lxc_cmd_stop_callback, + [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, + [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, + [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, + [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, + [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, + [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, + [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, + [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, }; if (req->cmd >= LXC_CMD_MAX) { @@ -892,24 +982,30 @@ goto out_close; } + TRACE("Processing \"%s\" command", lxc_cmd_str(req.cmd)); if (ret < 0) { - SYSERROR("Failed to receive data on command socket."); + SYSERROR("Failed to receive data on command socket for \"%s\"", + lxc_cmd_str(req.cmd)); goto out_close; } if (!ret) { - DEBUG("Peer has disconnected."); + DEBUG("Peer has disconnected for \"%s\"", lxc_cmd_str(req.cmd)); goto out_close; } if (ret != sizeof(req)) { - WARN("Failed to receive full command request. Ignoring request."); + WARN("Failed to receive full command request. Ignoring request " + "for \"%s\"", + lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } if (req.datalen > LXC_CMD_DATA_MAX) { - ERROR("Received command data length %d is too large.", req.datalen); + ERROR("Received command data length %d is too large for " + "command \"%s\"", + req.datalen, lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } @@ -920,7 +1016,9 @@ reqdata = alloca(req.datalen); ret = recv(fd, reqdata, req.datalen, 0); if (ret != req.datalen) { - WARN("Failed to receive full command request. Ignoring request."); + WARN("Failed to receive full command request. Ignoring " + "request for \"%s\"", + lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } @@ -991,7 +1089,8 @@ * because we print the sockname out sometimes. */ len = sizeof(path) - 2; - if (fill_sock_name(offset, len, name, lxcpath, NULL)) + if (lxc_make_abstract_socket_name(offset, len, name, lxcpath, NULL, + "command")) return -1; fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); diff -Nru lxc-2.0.8/src/lxc/commands.h lxc-2.1.0/src/lxc/commands.h --- lxc-2.0.8/src/lxc/commands.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/commands.h 2017-09-06 02:32:37.000000000 +0000 @@ -24,6 +24,10 @@ #ifndef __LXC_COMMANDS_H #define __LXC_COMMANDS_H +#include +#include +#include + #include "state.h" #define LXC_CMD_DATA_MAX (MAXPATHLEN * 2) @@ -43,6 +47,7 @@ LXC_CMD_GET_CONFIG_ITEM, LXC_CMD_GET_NAME, LXC_CMD_GET_LXCPATH, + LXC_CMD_ADD_STATE_CLIENT, LXC_CMD_MAX, } lxc_cmd_t; @@ -82,9 +87,26 @@ extern char *lxc_cmd_get_name(const char *hashed_sock); extern char *lxc_cmd_get_lxcpath(const char *hashed_sock); extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath); -extern lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath); +extern int lxc_cmd_get_state(const char *name, const char *lxcpath); extern int lxc_cmd_stop(const char *name, const char *lxcpath); +/* lxc_cmd_add_state_client Register a new state client fd in the container's + * in-memory handler. + * + * @param[in] name Name of container to connect to. + * @param[in] lxcpath The lxcpath in which the container is running. + * @param[in] states The states to wait for. + * @param[out] state_client_fd The state client fd from which the state can be + * received. + * @return Return < 0 on error + * == MAX_STATE when state needs to retrieved + * via socket fd + * < MAX_STATE current container state + */ +extern int lxc_cmd_add_state_client(const char *name, const char *lxcpath, + lxc_state_t states[MAX_STATE], + int *state_client_fd); + struct lxc_epoll_descr; struct lxc_handler; diff -Nru lxc-2.0.8/src/lxc/commands_utils.c lxc-2.1.0/src/lxc/commands_utils.c --- lxc-2.0.8/src/lxc/commands_utils.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/commands_utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,220 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_unix.h" +#include "commands.h" +#include "commands_utils.h" +#include "initutils.h" +#include "log.h" +#include "monitor.h" +#include "state.h" +#include "utils.h" + +lxc_log_define(lxc_commands_utils, lxc); + +int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout) +{ + int ret; + struct lxc_msg msg; + struct timeval out; + + if (timeout >= 0) { + memset(&out, 0, sizeof(out)); + out.tv_sec = timeout; + ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO, + (const void *)&out, sizeof(out)); + if (ret < 0) { + SYSERROR("Failed to set %ds timeout on containter " + "state socket", + timeout); + return -1; + } + } + + memset(&msg, 0, sizeof(msg)); + +again: + ret = recv(state_client_fd, &msg, sizeof(msg), 0); + if (ret < 0) { + if (errno == EINTR) { + TRACE("Caught EINTR; retrying"); + goto again; + } + + ERROR("failed to receive message: %s", strerror(errno)); + return -1; + } + + if (ret == 0) { + ERROR("length of message was 0"); + return -1; + } + + TRACE("received state %s from state client %d", + lxc_state2str(msg.value), state_client_fd); + + return msg.value; +} + +/* Register a new state client and retrieve state from command socket. */ +int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, + lxc_state_t states[MAX_STATE], int timeout) +{ + int ret; + int state_client_fd; + + ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd); + if (ret < 0) + return -1; + + if (ret < MAX_STATE) + return ret; + + ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); + close(state_client_fd); + return ret; +} + +int lxc_make_abstract_socket_name(char *path, int len, const char *lxcname, + const char *lxcpath, + const char *hashed_sock_name, + const char *suffix) +{ + 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/%s", hashed_sock_name, suffix); + if (ret < 0 || ret >= len) { + ERROR("Failed to create abstract socket name"); + return -1; + } + return 0; + } + + if (!lxcpath) { + lxcpath = lxc_global_config_value("lxc.lxcpath"); + if (!lxcpath) { + ERROR("Failed to allocate memory"); + return -1; + } + } + + ret = snprintf(path, len, "%s/%s/%s", lxcpath, name, suffix); + if (ret < 0) { + ERROR("Failed to create abstract socket name"); + return -1; + } + if (ret < len) + return 0; + + /* ret >= len; lxcpath or name is too long. hash both */ + tmplen = strlen(name) + strlen(lxcpath) + 2; + tmppath = alloca(tmplen); + ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name); + if (ret < 0 || (size_t)ret >= tmplen) { + ERROR("Failed to create abstract socket name"); + return -1; + } + + hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT); + ret = snprintf(path, len, "lxc/%016" PRIx64 "/%s", hash, suffix); + if (ret < 0 || ret >= len) { + ERROR("Failed to create abstract socket name"); + return -1; + } + + return 0; +} + +int lxc_cmd_connect(const char *name, const char *lxcpath, + const char *hashed_sock_name) +{ + int ret, client_fd; + char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = {0}; + char *offset = &path[1]; + + /* -2 here because this is an abstract unix socket so it needs a + * leading \0, and we null terminate, so it needs a trailing \0. + * Although null termination isn't required by the API, we do it anyway + * because we print the sockname out sometimes. + */ + size_t len = sizeof(path) - 2; + ret = lxc_make_abstract_socket_name(offset, len, name, lxcpath, + hashed_sock_name, "command"); + if (ret < 0) + return -1; + + /* Get new client fd. */ + client_fd = lxc_abstract_unix_connect(path); + if (client_fd < 0) { + if (errno == ECONNREFUSED) + return -ECONNREFUSED; + return -1; + } + + return client_fd; +} + +int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, + lxc_state_t states[MAX_STATE]) +{ + struct state_client *newclient; + struct lxc_list *tmplist; + + newclient = malloc(sizeof(*newclient)); + if (!newclient) + return -ENOMEM; + + /* copy requested states */ + memcpy(newclient->states, states, sizeof(newclient->states)); + newclient->clientfd = state_client_fd; + + tmplist = malloc(sizeof(*tmplist)); + if (!tmplist) { + free(newclient); + return -ENOMEM; + } + + lxc_list_add_elem(tmplist, newclient); + lxc_list_add_tail(&handler->state_clients, tmplist); + + TRACE("added state client %d to state client list", state_client_fd); + + return 0; +} diff -Nru lxc-2.0.8/src/lxc/commands_utils.h lxc-2.1.0/src/lxc/commands_utils.h --- lxc-2.0.8/src/lxc/commands_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/commands_utils.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,84 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __LXC_COMMANDS_UTILS_H +#define __LXC_COMMANDS_UTILS_H + +#include + +#include "state.h" +#include "commands.h" + +int lxc_make_abstract_socket_name(char *path, int len, const char *lxcname, + const char *lxcpath, + const char *hashed_sock_name, + const char *suffix); + +/* lxc_cmd_sock_get_state Register a new state client fd in the container's + * in-memory handler and retrieve the requested + * states. + * + * @param[in] name Name of container to connect to. + * @param[in] lxcpath The lxcpath in which the container is running. + * @param[in] states The states to wait for. + * @return Return < 0 on error + * < MAX_STATE current container state + */ +extern int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, + lxc_state_t states[MAX_STATE], int timeout); + +/* lxc_cmd_sock_rcv_state Retrieve the requested state from a state client + * fd registerd in the container's in-memory + * handler. + * + * @param[int] state_client_fd The state client fd from which the state can be + * received. + * @return Return < 0 on error + * < MAX_STATE current container state + */ +extern int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout); + +/* lxc_add_state_client Add a new state client to the container's + * in-memory handler. + * + * @param[int] state_client_fd The state client fd to add. + * @param[int] handler The container's in-memory handler. + * @param[in] states The states to wait for. + * + * @return Return < 0 on error + * 0 on success + */ +extern int lxc_add_state_client(int state_client_fd, + struct lxc_handler *handler, + lxc_state_t states[MAX_STATE]); + +/* lxc_cmd_connect Connect to the container's command socket. + * + * @param[in] name Name of container to connect to. + * @param[in] lxcpath The lxcpath in which the container is running. + * @param[in] hashed_sock_name The hashed name of the socket (optional). Can be + * NULL. + * + * @return Return < 0 on error + * >= 0 client fd + */ +extern int lxc_cmd_connect(const char *name, const char *lxcpath, + const char *hashed_sock_name); + +#endif /* __LXC_COMMANDS_UTILS_H */ diff -Nru lxc-2.0.8/src/lxc/conf.c lxc-2.1.0/src/lxc/conf.c --- lxc-2.0.8/src/lxc/conf.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/conf.c 2017-09-06 02:32:37.000000000 +0000 @@ -73,19 +73,20 @@ #endif #include "af_unix.h" -#include "bdev.h" #include "caps.h" /* for lxc_caps_last_cap() */ #include "cgroup.h" #include "conf.h" +#include "confile_utils.h" #include "error.h" #include "log.h" -#include "lxcaufs.h" #include "lxclock.h" -#include "lxcoverlay.h" #include "lxcseccomp.h" #include "namespace.h" #include "network.h" #include "parse.h" +#include "storage.h" +#include "storage/aufs.h" +#include "storage/overlay.h" #include "utils.h" #include "lsm/lsm.h" @@ -99,6 +100,9 @@ #if IS_BIONIC #include <../include/lxcmntent.h> +#ifndef HAVE_PRLIMIT +#include <../include/prlimit.h> +#endif #else #include #endif @@ -127,6 +131,14 @@ #define LO_FLAGS_AUTOCLEAR 4 #endif +#ifndef CAP_SETUID +#define CAP_SETUID 7 +#endif + +#ifndef CAP_SETGID +#define CAP_SETGID 6 +#endif + /* needed for cgroup automount checks, regardless of whether we * have included linux/capability.h or not */ #ifndef CAP_SYS_ADMIN @@ -161,11 +173,6 @@ } #endif -/* Define __S_ISTYPE if missing from the C library */ -#ifndef __S_ISTYPE -#define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) -#endif - #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif @@ -227,10 +234,9 @@ extern int memfd_create(const char *name, unsigned int flags); #endif -char *lxchook_names[NUM_LXC_HOOKS] = { - "pre-start", "pre-mount", "mount", "autodev", "start", "stop", "post-stop", "clone", "destroy" }; - -typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *); +char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount", + "autodev", "start", "stop", + "post-stop", "clone", "destroy"}; struct mount_opt { char *name; @@ -243,6 +249,11 @@ int value; }; +struct limit_opt { + char *name; + int value; +}; + /* * The lxc_conf of the container currently being worked on in an * API call @@ -257,38 +268,6 @@ /* Declare this here, since we don't want to reshuffle the whole file. */ static int in_caplist(int cap, struct lxc_list *caps); -static int instantiate_veth(struct lxc_handler *, struct lxc_netdev *); -static int instantiate_macvlan(struct lxc_handler *, struct lxc_netdev *); -static int instantiate_vlan(struct lxc_handler *, struct lxc_netdev *); -static int instantiate_phys(struct lxc_handler *, struct lxc_netdev *); -static int instantiate_empty(struct lxc_handler *, struct lxc_netdev *); -static int instantiate_none(struct lxc_handler *, struct lxc_netdev *); - -static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = { - [LXC_NET_VETH] = instantiate_veth, - [LXC_NET_MACVLAN] = instantiate_macvlan, - [LXC_NET_VLAN] = instantiate_vlan, - [LXC_NET_PHYS] = instantiate_phys, - [LXC_NET_EMPTY] = instantiate_empty, - [LXC_NET_NONE] = instantiate_none, -}; - -static int shutdown_veth(struct lxc_handler *, struct lxc_netdev *); -static int shutdown_macvlan(struct lxc_handler *, struct lxc_netdev *); -static int shutdown_vlan(struct lxc_handler *, struct lxc_netdev *); -static int shutdown_phys(struct lxc_handler *, struct lxc_netdev *); -static int shutdown_empty(struct lxc_handler *, struct lxc_netdev *); -static int shutdown_none(struct lxc_handler *, struct lxc_netdev *); - -static instantiate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = { - [LXC_NET_VETH] = shutdown_veth, - [LXC_NET_MACVLAN] = shutdown_macvlan, - [LXC_NET_VLAN] = shutdown_vlan, - [LXC_NET_PHYS] = shutdown_phys, - [LXC_NET_EMPTY] = shutdown_empty, - [LXC_NET_NONE] = shutdown_none, -}; - static struct mount_opt mount_opt[] = { { "async", 1, MS_SYNCHRONOUS }, { "atime", 1, MS_NOATIME }, @@ -376,6 +355,57 @@ static struct caps_opt caps_opt[] = {}; #endif +static struct limit_opt limit_opt[] = { +#ifdef RLIMIT_AS + { "as", RLIMIT_AS }, +#endif +#ifdef RLIMIT_CORE + { "core", RLIMIT_CORE }, +#endif +#ifdef RLIMIT_CPU + { "cpu", RLIMIT_CPU }, +#endif +#ifdef RLIMIT_DATA + { "data", RLIMIT_DATA }, +#endif +#ifdef RLIMIT_FSIZE + { "fsize", RLIMIT_FSIZE }, +#endif +#ifdef RLIMIT_LOCKS + { "locks", RLIMIT_LOCKS }, +#endif +#ifdef RLIMIT_MEMLOCK + { "memlock", RLIMIT_MEMLOCK }, +#endif +#ifdef RLIMIT_MSGQUEUE + { "msgqueue", RLIMIT_MSGQUEUE }, +#endif +#ifdef RLIMIT_NICE + { "nice", RLIMIT_NICE }, +#endif +#ifdef RLIMIT_NOFILE + { "nofile", RLIMIT_NOFILE }, +#endif +#ifdef RLIMIT_NPROC + { "nproc", RLIMIT_NPROC }, +#endif +#ifdef RLIMIT_RSS + { "rss", RLIMIT_RSS }, +#endif +#ifdef RLIMIT_RTPRIO + { "rtprio", RLIMIT_RTPRIO }, +#endif +#ifdef RLIMIT_RTTIME + { "rttime", RLIMIT_RTTIME }, +#endif +#ifdef RLIMIT_SIGPENDING + { "sigpending", RLIMIT_SIGPENDING }, +#endif +#ifdef RLIMIT_STACK + { "stack", RLIMIT_STACK }, +#endif +}; + static int run_buffer(char *buffer) { struct lxc_popen_FILE *f; @@ -466,8 +496,7 @@ return run_buffer(buffer); } -static int run_script(const char *name, const char *section, const char *script, - ...) +int run_script(const char *name, const char *section, const char *script, ...) { int ret; char *buffer, *p; @@ -518,49 +547,6 @@ return run_buffer(buffer); } -static int mount_rootfs_dir(const char *rootfs, const char *target, - const char *options) -{ - unsigned long mntflags; - char *mntdata; - int ret; - - if (parse_mntopts(options, &mntflags, &mntdata) < 0) { - free(mntdata); - return -1; - } - - ret = mount(rootfs, target, "none", MS_BIND | MS_REC | mntflags, mntdata); - free(mntdata); - - return ret; -} - -static int lxc_mount_rootfs_file(const char *rootfs, const char *target, - const char *options) -{ - int ret, loopfd; - char path[MAXPATHLEN]; - - loopfd = lxc_prepare_loop_dev(rootfs, path, LO_FLAGS_AUTOCLEAR); - if (loopfd < 0) - return -1; - DEBUG("prepared loop device \"%s\"", path); - - ret = mount_unknown_fs(path, target, options); - close(loopfd); - - DEBUG("mounted rootfs \"%s\" on loop device \"%s\" via loop device \"%s\"", rootfs, target, path); - - return ret; -} - -static int mount_rootfs_block(const char *rootfs, const char *target, - const char *options) -{ - return mount_unknown_fs(rootfs, target, options); -} - /* * pin_rootfs * if rootfs is a directory, then open ${rootfs}/lxc.hold for writing for @@ -608,7 +594,7 @@ * If we are asking to remount something, make sure that any * NOEXEC etc are honored. */ -static unsigned long add_required_remount_flags(const char *s, const char *d, +unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags) { #ifdef HAVE_STATVFS @@ -748,16 +734,16 @@ * :mixed, because then the container can't remount it read-write. */ if (cg_flags == LXC_AUTO_CGROUP_NOSPEC || cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC) { int has_sys_admin = 0; - if (!lxc_list_empty(&conf->keepcaps)) { + + if (!lxc_list_empty(&conf->keepcaps)) has_sys_admin = in_caplist(CAP_SYS_ADMIN, &conf->keepcaps); - } else { + else has_sys_admin = !in_caplist(CAP_SYS_ADMIN, &conf->caps); - } - if (cg_flags == LXC_AUTO_CGROUP_NOSPEC) { + + if (cg_flags == LXC_AUTO_CGROUP_NOSPEC) cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_RW : LXC_AUTO_CGROUP_MIXED; - } else { + else cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_FULL_RW : LXC_AUTO_CGROUP_FULL_MIXED; - } } if (!cgroup_mount(conf->rootfs.path ? conf->rootfs.mount : "", handler, cg_flags)) { @@ -769,49 +755,6 @@ return 0; } -static int mount_rootfs(const char *rootfs, const char *target, const char *options) -{ - char absrootfs[MAXPATHLEN]; - struct stat s; - int i; - - typedef int (*rootfs_cb)(const char *, const char *, const char *); - - struct rootfs_type { - int type; - rootfs_cb cb; - } rtfs_type[] = { - { S_IFDIR, mount_rootfs_dir }, - { S_IFBLK, mount_rootfs_block }, - { S_IFREG, lxc_mount_rootfs_file }, - }; - - if (!realpath(rootfs, absrootfs)) { - SYSERROR("Failed to get real path for \"%s\".", rootfs); - return -1; - } - - if (access(absrootfs, F_OK)) { - SYSERROR("The rootfs \"%s\" is not accessible.", absrootfs); - return -1; - } - - if (stat(absrootfs, &s)) { - SYSERROR("Failed to stat the rootfs \"%s\".", absrootfs); - return -1; - } - - for (i = 0; i < sizeof(rtfs_type)/sizeof(rtfs_type[0]); i++) { - if (!__S_ISTYPE(s.st_mode, rtfs_type[i].type)) - continue; - - return rtfs_type[i].cb(absrootfs, target, options); - } - - ERROR("Unsupported rootfs type for rootfs \"%s\".", absrootfs); - return -1; -} - static int setup_utsname(struct utsname *utsname) { if (!utsname) @@ -874,9 +817,7 @@ return 0; } -/* - * Build a space-separate list of ptys to pass to systemd. - */ +/* Build a space-separate list of ptys to pass to systemd. */ static bool append_ptyname(char **pp, char *name) { char *p; @@ -897,88 +838,242 @@ return true; } -static int setup_tty(struct lxc_conf *conf) +static int lxc_setup_ttys(struct lxc_conf *conf) { + int i, ret; const struct lxc_tty_info *tty_info = &conf->tty_info; char *ttydir = conf->ttydir; char path[MAXPATHLEN], lxcpath[MAXPATHLEN]; - int i, ret; if (!conf->rootfs.path) return 0; for (i = 0; i < tty_info->nbtty; i++) { - struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; ret = snprintf(path, sizeof(path), "/dev/tty%d", i + 1); - if (ret >= sizeof(path)) { - ERROR("pathname too long for ttys"); + if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; - } + if (ttydir) { /* create dev/lxc/tty%d" */ - ret = snprintf(lxcpath, sizeof(lxcpath), "/dev/%s/tty%d", ttydir, i + 1); - if (ret >= sizeof(lxcpath)) { - ERROR("pathname too long for ttys"); + ret = snprintf(lxcpath, sizeof(lxcpath), + "/dev/%s/tty%d", ttydir, i + 1); + if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; - } + ret = creat(lxcpath, 0660); - if (ret==-1 && errno != EEXIST) { - SYSERROR("error creating %s", lxcpath); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create \"%s\"", lxcpath); return -1; } if (ret >= 0) close(ret); + ret = unlink(path); - if (ret && errno != ENOENT) { - SYSERROR("error unlinking %s", path); + if (ret < 0 && errno != ENOENT) { + SYSERROR("Failed to unlink \"%s\"", path); return -1; } - if (mount(pty_info->name, lxcpath, "none", MS_BIND, 0)) { - WARN("failed to mount '%s'->'%s'", + ret = mount(pty_info->name, lxcpath, "none", MS_BIND, 0); + if (ret < 0) { + WARN("Failed to bind mount \"%s\" onto \"%s\"", pty_info->name, path); continue; } + DEBUG("bind mounted \"%s\" onto \"%s\"", pty_info->name, + path); - ret = snprintf(lxcpath, sizeof(lxcpath), "%s/tty%d", ttydir, i+1); - if (ret >= sizeof(lxcpath)) { - ERROR("tty pathname too long"); + ret = snprintf(lxcpath, sizeof(lxcpath), "%s/tty%d", + ttydir, i + 1); + if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; - } + ret = symlink(lxcpath, path); - if (ret) { - SYSERROR("failed to create symlink for tty %d", i+1); + if (ret < 0) { + SYSERROR("Failed to create symlink \"%s\" -> \"%s\"", + path, lxcpath); return -1; } } else { - /* If we populated /dev, then we need to create /dev/ttyN */ - if (access(path, F_OK)) { + /* If we populated /dev, then we need to create + * /dev/ttyN + */ + ret = access(path, F_OK); + if (ret < 0) { ret = creat(path, 0660); - if (ret==-1) { - SYSERROR("error creating %s", path); + if (ret < 0) { + SYSERROR("Failed to create \"%s\"", path); /* this isn't fatal, continue */ } else { close(ret); } } - if (mount(pty_info->name, path, "none", MS_BIND, 0)) { - SYSERROR("failed to mount '%s'->'%s'", pty_info->name, path); + + ret = mount(pty_info->name, path, "none", MS_BIND, 0); + if (ret < 0) { + SYSERROR("Failed to mount '%s'->'%s'", pty_info->name, path); continue; } + + DEBUG("Bind mounted \"%s\" onto \"%s\"", pty_info->name, + path); } + if (!append_ptyname(&conf->pty_names, pty_info->name)) { ERROR("Error setting up container_ttys string"); return -1; } } - INFO("%d tty(s) has been setup", tty_info->nbtty); + INFO("Finished setting up %d /dev/tty device(s)", tty_info->nbtty); + return 0; +} + +int lxc_allocate_ttys(const char *name, struct lxc_conf *conf) +{ + struct lxc_tty_info *tty_info = &conf->tty_info; + int i, ret; + + /* no tty in the configuration */ + if (!conf->tty) + return 0; + + tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty); + if (!tty_info->pty_info) { + SYSERROR("failed to allocate struct *pty_info"); + return -ENOMEM; + } + + for (i = 0; i < conf->tty; i++) { + struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; + + process_lock(); + ret = openpty(&pty_info->master, &pty_info->slave, + pty_info->name, NULL, NULL); + process_unlock(); + if (ret) { + SYSERROR("failed to create pty device number %d", i); + tty_info->nbtty = i; + lxc_delete_tty(tty_info); + return -ENOTTY; + } + + DEBUG("allocated pty \"%s\" with master fd %d and slave fd %d", + pty_info->name, pty_info->master, pty_info->slave); + + /* Prevent leaking the file descriptors to the container */ + ret = fcntl(pty_info->master, F_SETFD, FD_CLOEXEC); + if (ret < 0) + WARN("failed to set FD_CLOEXEC flag on master fd %d of " + "pty device \"%s\": %s", + pty_info->master, pty_info->name, strerror(errno)); + + ret = fcntl(pty_info->slave, F_SETFD, FD_CLOEXEC); + if (ret < 0) + WARN("failed to set FD_CLOEXEC flag on slave fd %d of " + "pty device \"%s\": %s", + pty_info->slave, pty_info->name, strerror(errno)); + + pty_info->busy = 0; + } + + tty_info->nbtty = conf->tty; + INFO("finished allocating %d pts devices", conf->tty); return 0; } +void lxc_delete_tty(struct lxc_tty_info *tty_info) +{ + int i; + + for (i = 0; i < tty_info->nbtty; i++) { + struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; + + close(pty_info->master); + close(pty_info->slave); + } + + free(tty_info->pty_info); + tty_info->pty_info = NULL; + tty_info->nbtty = 0; +} + +static int lxc_send_ttys_to_parent(struct lxc_handler *handler) +{ + int i; + struct lxc_conf *conf = handler->conf; + struct lxc_tty_info *tty_info = &conf->tty_info; + int sock = handler->data_sock[0]; + int ret = -1; + + if (!conf->tty) + return 0; + + for (i = 0; i < conf->tty; i++) { + int ttyfds[2]; + struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; + + ttyfds[0] = pty_info->master; + ttyfds[1] = pty_info->slave; + + ret = lxc_abstract_unix_send_fds(sock, ttyfds, 2, NULL, 0); + if (ret < 0) + break; + + TRACE("Send pty \"%s\" with master fd %d and slave fd %d to " + "parent", pty_info->name, pty_info->master, pty_info->slave); + } + + if (ret < 0) + ERROR("Failed to send %d ttys to parent: %s", conf->tty, + strerror(errno)); + else + TRACE("Sent %d ttys to parent", conf->tty); + + return ret; +} + +static int lxc_create_ttys(struct lxc_handler *handler) +{ + int ret = -1; + struct lxc_conf *conf = handler->conf; + + ret = lxc_allocate_ttys(handler->name, conf); + if (ret < 0) { + ERROR("Failed to allocate ttys"); + goto on_error; + } + + ret = lxc_send_ttys_to_parent(handler); + if (ret < 0) { + ERROR("Failed to send ttys to parent"); + goto on_error; + } + + if (!conf->is_execute) { + ret = lxc_setup_ttys(conf); + if (ret < 0) { + ERROR("Failed to setup ttys"); + goto on_error; + } + } + + if (conf->pty_names) { + ret = setenv("container_ttys", conf->pty_names, 1); + if (ret < 0) + SYSERROR("Failed to set \"container_ttys=%s\"", conf->pty_names); + } + + ret = 0; + +on_error: + lxc_delete_tty(&conf->tty_info); + + return ret; +} static int setup_rootfs_pivot_root(const char *rootfs) { @@ -1041,58 +1136,56 @@ return -1; } -/* - * Just create a path for /dev under $lxcpath/$name and in rootfs - * If we hit an error, log it but don't fail yet. +/* Just create a path for /dev under $lxcpath/$name and in rootfs If we hit an + * error, log it but don't fail yet. */ -static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs, const char *lxcpath) +static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs, + const char *lxcpath) { int ret; size_t clen; char *path; - INFO("Mounting container /dev"); + INFO("Preparing \"/dev\""); /* $(rootfs->mount) + "/dev/pts" + '\0' */ clen = (rootfs->path ? strlen(rootfs->mount) : 0) + 9; path = alloca(clen); ret = snprintf(path, clen, "%s/dev", rootfs->path ? rootfs->mount : ""); - if (ret < 0 || ret >= clen) + if (ret < 0 || (size_t)ret >= clen) return -1; if (!dir_exists(path)) { - WARN("No /dev in container."); - WARN("Proceeding without autodev setup"); + WARN("\"/dev\" directory does not exist. Proceeding without " + "autodev being set up"); return 0; } ret = safe_mount("none", path, "tmpfs", 0, "size=500000,mode=755", - rootfs->path ? rootfs->mount : NULL); - if (ret != 0) { - SYSERROR("Failed mounting tmpfs onto %s\n", path); + rootfs->path ? rootfs->mount : NULL); + if (ret < 0) { + SYSERROR("Failed to mount tmpfs on \"%s\"", path); return -1; } - - INFO("Mounted tmpfs onto %s", path); + INFO("Mounted tmpfs on \"%s\"", path); ret = snprintf(path, clen, "%s/dev/pts", rootfs->path ? rootfs->mount : ""); - if (ret < 0 || ret >= clen) + if (ret < 0 || (size_t)ret >= clen) return -1; - /* - * If we are running on a devtmpfs mapping, dev/pts may already exist. + /* If we are running on a devtmpfs mapping, dev/pts may already exist. * If not, then create it and exit if that fails... */ if (!dir_exists(path)) { ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if (ret) { - SYSERROR("Failed to create /dev/pts in container"); + if (ret < 0) { + SYSERROR("Failed to create directory \"%s\"", path); return -1; } } - INFO("Mounted container /dev"); + INFO("Prepared \"/dev\""); return 0; } @@ -1104,12 +1197,12 @@ }; static const struct lxc_devs lxc_devs[] = { - { "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 }, - { "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 }, - { "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 }, - { "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 }, - { "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 }, - { "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 }, + { "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 }, + { "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 }, + { "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 }, + { "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 }, + { "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 }, + { "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 }, }; static int lxc_fill_autodev(const struct lxc_rootfs *rootfs) @@ -1119,29 +1212,30 @@ int i; mode_t cmask; - ret = snprintf(path, MAXPATHLEN, "%s/dev", rootfs->path ? rootfs->mount : ""); - if (ret < 0 || ret >= MAXPATHLEN) { - ERROR("Error calculating container /dev location"); + ret = snprintf(path, MAXPATHLEN, "%s/dev", + rootfs->path ? rootfs->mount : ""); + if (ret < 0 || ret >= MAXPATHLEN) return -1; - } /* ignore, just don't try to fill in */ if (!dir_exists(path)) return 0; - INFO("populating container /dev"); + INFO("Populating \"/dev\""); + cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH); for (i = 0; i < sizeof(lxc_devs) / sizeof(lxc_devs[0]); i++) { const struct lxc_devs *d = &lxc_devs[i]; - ret = snprintf(path, MAXPATHLEN, "%s/dev/%s", rootfs->path ? rootfs->mount : "", d->name); + ret = snprintf(path, MAXPATHLEN, "%s/dev/%s", + rootfs->path ? rootfs->mount : "", d->name); if (ret < 0 || ret >= MAXPATHLEN) return -1; ret = mknod(path, d->mode, makedev(d->maj, d->min)); if (ret < 0) { - char hostpath[MAXPATHLEN]; FILE *pathfile; + char hostpath[MAXPATHLEN]; if (errno == EEXIST) { DEBUG("\"%s\" device already existed", path); @@ -1154,30 +1248,38 @@ ret = snprintf(hostpath, MAXPATHLEN, "/dev/%s", d->name); if (ret < 0 || ret >= MAXPATHLEN) return -1; + pathfile = fopen(path, "wb"); if (!pathfile) { - SYSERROR("Failed to create device mount target '%s'", path); + SYSERROR("Failed to create file \"%s\"", path); return -1; } fclose(pathfile); - if (safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL) != 0) { - SYSERROR("Failed bind mounting device %s from host into container", d->name); + + ret = safe_mount(hostpath, path, 0, MS_BIND, NULL, + rootfs->path ? rootfs->mount : NULL); + if (ret < 0) { + SYSERROR("Failed to bind mount \"%s\" from " + "host into container", + d->name); return -1; } - DEBUG("bind mounted \"%s\" onto \"%s\"", hostpath, path); + DEBUG("Bind mounted \"%s\" onto \"%s\"", hostpath, + path); } else { - DEBUG("created device node \"%s\"", path); + DEBUG("Created device node \"%s\"", path); } } umask(cmask); - INFO("populated container /dev"); + INFO("Populated \"/dev\""); return 0; } -static int setup_rootfs(struct lxc_conf *conf) +static int lxc_setup_rootfs(struct lxc_conf *conf) { - struct bdev *bdev; + int ret; + struct lxc_storage *bdev; const struct lxc_rootfs *rootfs; rootfs = &conf->rootfs; @@ -1195,18 +1297,17 @@ return -1; } - /* First try mounting rootfs using a bdev. */ - bdev = bdev_init(conf, rootfs->path, rootfs->mount, rootfs->options); - if (bdev && !bdev->ops->mount(bdev)) { - bdev_put(bdev); - DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\".", + bdev = storage_init(conf, rootfs->path, rootfs->mount, rootfs->options); + if (!bdev) { + ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); - return 0; + return -1; } - if (bdev) - bdev_put(bdev); - if (mount_rootfs(rootfs->path, rootfs->mount, rootfs->options)) { + + ret = bdev->ops->mount(bdev); + storage_put(bdev); + if (ret < 0) { ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); @@ -1216,6 +1317,7 @@ DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\".", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); + return 0; } @@ -1338,7 +1440,8 @@ static int lxc_setup_devpts(int num_pts) { int ret; - const char *devpts_mntopts = "newinstance,ptmxmode=0666,mode=0620,gid=5"; + const char *default_devpts_mntopts = "newinstance,ptmxmode=0666,mode=0620,gid=5"; + char devpts_mntopts[256]; if (!num_pts) { DEBUG("no new devpts instance will be mounted since no pts " @@ -1346,6 +1449,11 @@ return 0; } + ret = snprintf(devpts_mntopts, sizeof(devpts_mntopts), "%s,max=%d", + default_devpts_mntopts, num_pts); + if (ret < 0 || (size_t)ret >= sizeof(devpts_mntopts)) + return -1; + /* Unmount old devpts instance. */ ret = access("/dev/pts/ptmx", F_OK); if (!ret) { @@ -1370,6 +1478,7 @@ SYSERROR("failed to mount new devpts instance"); return -1; } + DEBUG("mount new devpts instance with options \"%s\"", devpts_mntopts); /* Remove any pre-existing /dev/ptmx file. */ ret = access("/dev/ptmx", F_OK); @@ -1460,6 +1569,7 @@ } else { DEBUG("cleared all (%d) mounts from \"%s\"", ret, path); } + ret = unlink(path); if (ret < 0) { SYSERROR("error unlinking %s", path); @@ -1511,7 +1621,7 @@ SYSERROR("failed with errno %d to create %s", errno, path); return -errno; } - DEBUG("created directory for console and tty devices at \%s\"", path); + DEBUG("Created directory for console and tty devices at \"%s\"", path); ret = snprintf(lxcpath, sizeof(lxcpath), "%s/dev/%s/console", rootfs->mount, ttydir); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) @@ -1636,33 +1746,6 @@ return lxc_setup_ttydir_console(rootfs, console, ttydir); } -static int setup_kmsg(const struct lxc_rootfs *rootfs, - const struct lxc_console *console) -{ - char kpath[MAXPATHLEN]; - int ret; - - if (!rootfs->path) - return 0; - ret = snprintf(kpath, sizeof(kpath), "%s/dev/kmsg", rootfs->mount); - if (ret < 0 || ret >= sizeof(kpath)) - return -1; - - ret = unlink(kpath); - if (ret && errno != ENOENT) { - SYSERROR("error unlinking %s", kpath); - return -1; - } - - ret = symlink("console", kpath); - if (ret) { - SYSERROR("failed to create symlink for kmsg"); - return -1; - } - - return 0; -} - static void parse_mntopt(char *opt, unsigned long *flags, char **data) { struct mount_opt *mo; @@ -1751,174 +1834,201 @@ static int mount_entry(const char *fsname, const char *target, const char *fstype, unsigned long mountflags, - const char *data, int optional, int dev, const char *rootfs) + const char *data, int optional, int dev, + const char *rootfs) { + int ret; #ifdef HAVE_STATVFS struct statvfs sb; #endif - if (safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs)) { + ret = safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, + rootfs); + if (ret < 0) { if (optional) { - INFO("failed to mount '%s' on '%s' (optional): %s", fsname, - target, strerror(errno)); + INFO("Failed to mount \"%s\" on \"%s\" (optional): %s", + fsname, target, strerror(errno)); return 0; } - else { - SYSERROR("failed to mount '%s' on '%s'", fsname, target); - return -1; - } + + SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname, target); + return -1; } if ((mountflags & MS_REMOUNT) || (mountflags & MS_BIND)) { - DEBUG("remounting %s on %s to respect bind or remount options", - fsname ? fsname : "(none)", target ? target : "(none)"); unsigned long rqd_flags = 0; + + DEBUG("Remounting \"%s\" on \"%s\" to respect bind or remount " + "options", + fsname ? fsname : "(none)", target ? target : "(none)"); + if (mountflags & MS_RDONLY) rqd_flags |= MS_RDONLY; #ifdef HAVE_STATVFS if (statvfs(fsname, &sb) == 0) { unsigned long required_flags = rqd_flags; + if (sb.f_flag & MS_NOSUID) required_flags |= MS_NOSUID; + if (sb.f_flag & MS_NODEV && !dev) required_flags |= MS_NODEV; + if (sb.f_flag & MS_RDONLY) required_flags |= MS_RDONLY; + if (sb.f_flag & MS_NOEXEC) required_flags |= MS_NOEXEC; - DEBUG("(at remount) flags for %s was %lu, required extra flags are %lu", fsname, sb.f_flag, required_flags); - /* - * If this was a bind mount request, and required_flags + + DEBUG("Flags for \"%s\" were %lu, required extra flags " + "are %lu", fsname, sb.f_flag, required_flags); + + /* If this was a bind mount request, and required_flags * does not have any flags which are not already in - * mountflags, then skip the remount + * mountflags, then skip the remount. */ if (!(mountflags & MS_REMOUNT)) { - if (!(required_flags & ~mountflags) && rqd_flags == 0) { - DEBUG("mountflags already was %lu, skipping remount", - mountflags); + if (!(required_flags & ~mountflags) && + rqd_flags == 0) { + DEBUG("Mountflags already were %lu, " + "skipping remount", mountflags); goto skipremount; } } + mountflags |= required_flags; } #endif - if (mount(fsname, target, fstype, - mountflags | MS_REMOUNT, data) < 0) { + ret = mount(fsname, target, fstype, mountflags | MS_REMOUNT, data); + if (ret < 0) { if (optional) { - INFO("failed to mount '%s' on '%s' (optional): %s", - fsname, target, strerror(errno)); + INFO("Failed to mount \"%s\" on \"%s\" " + "(optional): %s", fsname, target, + strerror(errno)); return 0; } - else { - SYSERROR("failed to mount '%s' on '%s'", - fsname, target); - return -1; - } + + SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname, target); + return -1; } } #ifdef HAVE_STATVFS skipremount: #endif - DEBUG("mounted '%s' on '%s', type '%s'", fsname, target, fstype); + DEBUG("Mounted \"%s\" on \"%s\" with filesystem type \"%s\"", fsname, + target, fstype); return 0; } -/* - * Remove 'optional', 'create=dir', and 'create=file' from mntopt - */ +/* Remove "optional", "create=dir", and "create=file" from mntopt */ static void cull_mntent_opt(struct mntent *mntent) { int i; - char *p, *p2; - char *list[] = {"create=dir", - "create=file", - "optional", - NULL }; + char *list[] = {"create=dir", "create=file", "optional", NULL}; - for (i=0; list[i]; i++) { - if (!(p = strstr(mntent->mnt_opts, list[i]))) + for (i = 0; list[i]; i++) { + char *p, *p2; + + p = strstr(mntent->mnt_opts, list[i]); + if (!p) continue; + p2 = strchr(p, ','); if (!p2) { /* no more mntopts, so just chop it here */ *p = '\0'; continue; } - memmove(p, p2+1, strlen(p2+1)+1); + + memmove(p, p2 + 1, strlen(p2 + 1) + 1); } } static int mount_entry_create_dir_file(const struct mntent *mntent, - const char* path, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path) + const char *path, + const struct lxc_rootfs *rootfs, + const char *lxc_name, + const char *lxc_path) { - char *pathdirname = NULL; int ret = 0; - FILE *pathfile = NULL; - if (strncmp(mntent->mnt_type, "overlay", 7) == 0) { - if (ovl_mkdir(mntent, rootfs, lxc_name, lxc_path) < 0) - return -1; - } else if (strncmp(mntent->mnt_type, "aufs", 4) == 0) { - if (aufs_mkdir(mntent, rootfs, lxc_name, lxc_path) < 0) - return -1; - } + if (!strncmp(mntent->mnt_type, "overlay", 7)) + ret = ovl_mkdir(mntent, rootfs, lxc_name, lxc_path); + else if (!strncmp(mntent->mnt_type, "aufs", 4)) + ret = aufs_mkdir(mntent, rootfs, lxc_name, lxc_path); + if (ret < 0) + return -1; if (hasmntopt(mntent, "create=dir")) { - if (mkdir_p(path, 0755) < 0) { - WARN("Failed to create mount target '%s'", path); - ret = -1; + ret = mkdir_p(path, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", path); + return -1; } } if (hasmntopt(mntent, "create=file") && access(path, F_OK)) { - pathdirname = strdup(path); - pathdirname = dirname(pathdirname); - if (mkdir_p(pathdirname, 0755) < 0) { - WARN("Failed to create target directory"); - } - pathfile = fopen(path, "wb"); - if (!pathfile) { - WARN("Failed to create mount target '%s'", path); - ret = -1; - } else { - fclose(pathfile); + int fd; + char *p1, *p2; + + p1 = strdup(path); + if (!p1) + return -1; + + p2 = dirname(p1); + + ret = mkdir_p(p2, 0755); + free(p1); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", path); + return -1; } + + fd = open(path, O_CREAT, 0644); + if (fd < 0) + return -1; + close(fd); } - free(pathdirname); - return ret; + + return 0; } /* rootfs, lxc_name, and lxc_path can be NULL when the container is created * without a rootfs. */ static inline int mount_entry_on_generic(struct mntent *mntent, - const char* path, const struct lxc_rootfs *rootfs, - const char *lxc_name, const char *lxc_path) + const char *path, + const struct lxc_rootfs *rootfs, + const char *lxc_name, + const char *lxc_path) { + int ret; unsigned long mntflags; char *mntdata; - int ret; - bool optional = hasmntopt(mntent, "optional") != NULL; - bool dev = hasmntopt(mntent, "dev") != NULL; - + bool dev, optional; char *rootfs_path = NULL; + + optional = hasmntopt(mntent, "optional") != NULL; + dev = hasmntopt(mntent, "dev") != NULL; + if (rootfs && rootfs->path) rootfs_path = rootfs->mount; - ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name, lxc_path); - - if (ret < 0) - return optional ? 0 : -1; + ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name, + lxc_path); + if (ret < 0) { + if (optional) + return 0; + return -1; + } cull_mntent_opt(mntent); - if (parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata) < 0) { - free(mntdata); + ret = parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata); + if (ret < 0) return -1; - } ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags, mntdata, optional, dev, rootfs_path); @@ -1929,20 +2039,18 @@ static inline int mount_entry_on_systemfs(struct mntent *mntent) { - char path[MAXPATHLEN]; int ret; + char path[MAXPATHLEN]; /* For containers created without a rootfs all mounts are treated as - * absolute paths starting at / on the host. */ + * absolute paths starting at / on the host. + */ if (mntent->mnt_dir[0] != '/') ret = snprintf(path, sizeof(path), "/%s", mntent->mnt_dir); else ret = snprintf(path, sizeof(path), "%s", mntent->mnt_dir); - - if (ret < 0 || ret >= sizeof(path)) { - ERROR("path name too long"); + if (ret < 0 || ret >= sizeof(path)) return -1; - } return mount_entry_on_generic(mntent, path, NULL, NULL, NULL); } @@ -1952,21 +2060,21 @@ const char *lxc_name, const char *lxc_path) { + int offset; char *aux; - char path[MAXPATHLEN]; - int r, ret = 0, offset; const char *lxcpath; + char path[MAXPATHLEN]; + int ret = 0; lxcpath = lxc_global_config_value("lxc.lxcpath"); - if (!lxcpath) { - ERROR("Out of memory"); + if (!lxcpath) return -1; - } - /* if rootfs->path is a blockdev path, allow container fstab to - * use $lxcpath/CN/rootfs as the target prefix */ - r = snprintf(path, MAXPATHLEN, "%s/%s/rootfs", lxcpath, lxc_name); - if (r < 0 || r >= MAXPATHLEN) + /* If rootfs->path is a blockdev path, allow container fstab to use + * //rootfs" as the target prefix. + */ + ret = snprintf(path, MAXPATHLEN, "%s/%s/rootfs", lxcpath, lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) goto skipvarlib; aux = strstr(mntent->mnt_dir, path); @@ -1978,19 +2086,15 @@ skipvarlib: aux = strstr(mntent->mnt_dir, rootfs->path); if (!aux) { - WARN("ignoring mount point '%s'", mntent->mnt_dir); + WARN("Ignoring mount point \"%s\"", mntent->mnt_dir); return ret; } offset = strlen(rootfs->path); skipabs: - - r = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->mount, - aux + offset); - if (r < 0 || r >= MAXPATHLEN) { - WARN("pathnme too long for '%s'", mntent->mnt_dir); + ret = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->mount, aux + offset); + if (ret < 0 || ret >= MAXPATHLEN) return -1; - } return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } @@ -2013,57 +2117,123 @@ return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } -static int mount_file_entries(const struct lxc_rootfs *rootfs, FILE *file, - const char *lxc_name, const char *lxc_path) +/* This logs a NOTICE() when a user specifies mounts that would conflict with + * devices liblxc sets up automatically. + */ +static void log_notice_on_conflict(const struct lxc_conf *conf, const char *src, + const char *dest) +{ + char *clean_mnt_fsname, *clean_mnt_dir, *tmp; + bool needs_warning = false; + + clean_mnt_fsname = lxc_deslashify(src); + if (!clean_mnt_fsname) + return; + + clean_mnt_dir = lxc_deslashify(dest); + if (!clean_mnt_dir) { + free(clean_mnt_fsname); + return; + } + + tmp = clean_mnt_dir; + if (*tmp == '/') + tmp++; + + if (strncmp(src, "/dev", 4) || strncmp(tmp, "dev", 3)) { + free(clean_mnt_dir); + free(clean_mnt_fsname); + return; + } + + if (!conf->autodev && !conf->pts && !conf->tty && + (!conf->console.path || !strcmp(conf->console.path, "none"))) { + free(clean_mnt_dir); + free(clean_mnt_fsname); + return; + } + + if (!strcmp(tmp, "dev") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/pts") && (conf->autodev > 0 || conf->pts > 0)) + needs_warning = true; + else if (!strcmp(tmp, "dev/ptmx") && (conf->autodev > 0 || conf->pts > 0)) + needs_warning = true; + else if (!strcmp(tmp, "dev/pts/ptmx") && (conf->autodev > 0 || conf->pts > 0)) + needs_warning = true; + else if (!strcmp(tmp, "dev/null") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/zero") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/full") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/urandom") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/random") && conf->autodev > 0) + needs_warning = true; + else if (!strcmp(tmp, "dev/tty") && conf->autodev > 0) + needs_warning = true; + else if (!strncmp(tmp, "dev/tty", 7) && (conf->autodev > 0 || conf->tty > 0)) + needs_warning = true; + + if (needs_warning) + NOTICE("Requesting to mount \"%s\" on \"%s\" while requesting " + "automatic device setup under \"/dev\"", + clean_mnt_fsname, clean_mnt_dir); + + free(clean_mnt_dir); + free(clean_mnt_fsname); +} + +static int mount_file_entries(const struct lxc_conf *conf, + const struct lxc_rootfs *rootfs, FILE *file, + const char *lxc_name, const char *lxc_path) { struct mntent mntent; char buf[4096]; int ret = -1; while (getmntent_r(file, &mntent, buf, sizeof(buf))) { + log_notice_on_conflict(conf, mntent.mnt_fsname, mntent.mnt_dir); - if (!rootfs->path) { - if (mount_entry_on_systemfs(&mntent)) - goto out; - continue; - } - - /* We have a separate root, mounts are relative to it */ - if (mntent.mnt_dir[0] != '/') { - if (mount_entry_on_relative_rootfs(&mntent, rootfs, lxc_name, lxc_path)) - goto out; - continue; - } - - if (mount_entry_on_absolute_rootfs(&mntent, rootfs, lxc_name, lxc_path)) - goto out; + if (!rootfs->path) + ret = mount_entry_on_systemfs(&mntent); + else if (mntent.mnt_dir[0] != '/') + ret = mount_entry_on_relative_rootfs(&mntent, rootfs, + lxc_name, lxc_path); + else + ret = mount_entry_on_absolute_rootfs(&mntent, rootfs, + lxc_name, lxc_path); + if (ret < 0) + return -1; } - ret = 0; - INFO("mount points have been setup"); -out: + INFO("Set up mount entries"); return ret; } -static int setup_mount(const struct lxc_rootfs *rootfs, const char *fstab, - const char *lxc_name, const char *lxc_path) +static int setup_mount(const struct lxc_conf *conf, + const struct lxc_rootfs *rootfs, const char *fstab, + const char *lxc_name, const char *lxc_path) { - FILE *file; + FILE *f; int ret; if (!fstab) return 0; - file = setmntent(fstab, "r"); - if (!file) { - SYSERROR("failed to use '%s'", fstab); + f = setmntent(fstab, "r"); + if (!f) { + SYSERROR("Failed to open \"%s\"", fstab); return -1; } - ret = mount_file_entries(rootfs, file, lxc_name, lxc_path); + ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); + if (ret < 0) + ERROR("Failed to set up mount entries"); - endmntent(file); + endmntent(f); return ret; } @@ -2072,55 +2242,59 @@ int ret; char *mount_entry; struct lxc_list *iterator; - FILE *file; + FILE *f; int fd = -1; fd = memfd_create("lxc_mount_file", MFD_CLOEXEC); if (fd < 0) { if (errno != ENOSYS) return NULL; - file = tmpfile(); + f = tmpfile(); + TRACE("Created temporary mount file"); } else { - file = fdopen(fd, "r+"); + f = fdopen(fd, "r+"); + TRACE("Created anonymous mount file"); } - if (!file) { - int saved_errno = errno; + if (!f) { + SYSERROR("Could not create mount file"); if (fd != -1) close(fd); - ERROR("Could not create mount entry file: %s.", strerror(saved_errno)); return NULL; } lxc_list_for_each(iterator, mount) { mount_entry = iterator->elem; - ret = fprintf(file, "%s\n", mount_entry); + ret = fprintf(f, "%s\n", mount_entry); if (ret < strlen(mount_entry)) - WARN("Could not write mount entry to anonymous mount file."); + WARN("Could not write mount entry to mount file"); } - if (fseek(file, 0, SEEK_SET) < 0) { - fclose(file); + ret = fseek(f, 0, SEEK_SET); + if (ret < 0) { + SYSERROR("Failed to seek mount file"); + fclose(f); return NULL; } - return file; + return f; } -static int setup_mount_entries(const struct lxc_rootfs *rootfs, +static int setup_mount_entries(const struct lxc_conf *conf, + const struct lxc_rootfs *rootfs, struct lxc_list *mount, const char *lxc_name, const char *lxc_path) { - FILE *file; + FILE *f; int ret; - file = make_anonymous_mount_file(mount); - if (!file) + f = make_anonymous_mount_file(mount); + if (!f) return -1; - ret = mount_file_entries(rootfs, file, lxc_name, lxc_path); + ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); - fclose(file); + fclose(f); return ret; } @@ -2216,7 +2390,7 @@ if (numcaps <= 0 || numcaps > 200) return -1; - // caplist[i] is 1 if we keep capability i + /* caplist[i] is 1 if we keep capability i */ int *caplist = alloca(numcaps * sizeof(int)); memset(caplist, 0, numcaps * sizeof(int)); @@ -2252,328 +2426,45 @@ return 0; } -static int setup_hw_addr(char *hwaddr, const char *ifname) -{ - struct sockaddr sockaddr; - struct ifreq ifr; - int ret, fd, saved_errno; - - ret = lxc_convert_mac(hwaddr, &sockaddr); - if (ret) { - ERROR("mac address '%s' conversion failed : %s", - hwaddr, strerror(-ret)); - return -1; - } - - memcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_name[IFNAMSIZ-1] = '\0'; - memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr)); - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - ERROR("socket failure : %s", strerror(errno)); - return -1; - } - - ret = ioctl(fd, SIOCSIFHWADDR, &ifr); - saved_errno = errno; - close(fd); - if (ret) - ERROR("ioctl failure : %s", strerror(saved_errno)); - - DEBUG("mac address '%s' on '%s' has been setup", hwaddr, ifr.ifr_name); - - return ret; -} - -static int setup_ipv4_addr(struct lxc_list *ip, int ifindex) -{ - struct lxc_list *iterator; - struct lxc_inetdev *inetdev; - int err; - - lxc_list_for_each(iterator, ip) { - - inetdev = iterator->elem; - - err = lxc_ipv4_addr_add(ifindex, &inetdev->addr, - &inetdev->bcast, inetdev->prefix); - if (err) { - ERROR("failed to setup_ipv4_addr ifindex %d : %s", - ifindex, strerror(-err)); - return -1; - } - } - - return 0; -} - -static int setup_ipv6_addr(struct lxc_list *ip, int ifindex) -{ - struct lxc_list *iterator; - struct lxc_inet6dev *inet6dev; - int err; - - lxc_list_for_each(iterator, ip) { - - inet6dev = iterator->elem; +static int parse_resource(const char *res) { + size_t i; + int resid = -1; - err = lxc_ipv6_addr_add(ifindex, &inet6dev->addr, - &inet6dev->mcast, &inet6dev->acast, - inet6dev->prefix); - if (err) { - ERROR("failed to setup_ipv6_addr ifindex %d : %s", - ifindex, strerror(-err)); - return -1; - } + for (i = 0; i < sizeof(limit_opt)/sizeof(limit_opt[0]); ++i) { + if (strcmp(res, limit_opt[i].name) == 0) + return limit_opt[i].value; } - return 0; + /* try to see if it's numeric, so the user may specify + * resources that the running kernel knows about but + * we don't */ + if (lxc_safe_int(res, &resid) == 0) + return resid; + return -1; } -static int setup_netdev(struct lxc_netdev *netdev) -{ - char ifname[IFNAMSIZ]; - char *current_ifname = ifname; - int err; - - /* empty network namespace */ - if (!netdev->ifindex) { - if (netdev->flags & IFF_UP) { - err = lxc_netdev_up("lo"); - if (err) { - ERROR("failed to set the loopback up : %s", - strerror(-err)); - return -1; - } - } - if (netdev->type != LXC_NET_VETH) - return 0; - netdev->ifindex = if_nametoindex(netdev->name); - } - - /* get the new ifindex in case of physical netdev */ - if (netdev->type == LXC_NET_PHYS) { - if (!(netdev->ifindex = if_nametoindex(netdev->link))) { - ERROR("failed to get ifindex for %s", - netdev->link); - return -1; - } - } - - /* retrieve the name of the interface */ - if (!if_indextoname(netdev->ifindex, current_ifname)) { - ERROR("no interface corresponding to index '%d'", - netdev->ifindex); - return -1; - } - - /* default: let the system to choose one interface name */ - if (!netdev->name) - netdev->name = netdev->type == LXC_NET_PHYS ? - netdev->link : "eth%d"; - - /* rename the interface name */ - if (strcmp(ifname, netdev->name) != 0) { - err = lxc_netdev_rename_by_name(ifname, netdev->name); - if (err) { - ERROR("failed to rename %s->%s : %s", ifname, netdev->name, - strerror(-err)); - return -1; - } - } - - /* Re-read the name of the interface because its name has changed - * and would be automatically allocated by the system - */ - if (!if_indextoname(netdev->ifindex, current_ifname)) { - ERROR("no interface corresponding to index '%d'", - netdev->ifindex); - return -1; - } - - /* set a mac address */ - if (netdev->hwaddr) { - if (setup_hw_addr(netdev->hwaddr, current_ifname)) { - ERROR("failed to setup hw address for '%s'", - current_ifname); - return -1; - } - } - - /* setup ipv4 addresses on the interface */ - if (setup_ipv4_addr(&netdev->ipv4, netdev->ifindex)) { - ERROR("failed to setup ip addresses for '%s'", - ifname); - return -1; - } - - /* setup ipv6 addresses on the interface */ - if (setup_ipv6_addr(&netdev->ipv6, netdev->ifindex)) { - ERROR("failed to setup ipv6 addresses for '%s'", - ifname); - return -1; - } - - /* set the network device up */ - if (netdev->flags & IFF_UP) { - int err; - - err = lxc_netdev_up(current_ifname); - if (err) { - ERROR("failed to set '%s' up : %s", current_ifname, - strerror(-err)); - return -1; - } - - /* the network is up, make the loopback up too */ - err = lxc_netdev_up("lo"); - if (err) { - ERROR("failed to set the loopback up : %s", - strerror(-err)); - return -1; - } - } - - /* We can only set up the default routes after bringing - * up the interface, sine bringing up the interface adds - * the link-local routes and we can't add a default - * route if the gateway is not reachable. */ - - /* setup ipv4 gateway on the interface */ - if (netdev->ipv4_gateway) { - if (!(netdev->flags & IFF_UP)) { - ERROR("Cannot add ipv4 gateway for %s when not bringing up the interface", ifname); - return -1; - } - - if (lxc_list_empty(&netdev->ipv4)) { - ERROR("Cannot add ipv4 gateway for %s when not assigning an address", ifname); - return -1; - } - - err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); - if (err) { - err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway); - if (err) { - ERROR("failed to add ipv4 dest for '%s': %s", - ifname, strerror(-err)); - } - - err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); - if (err) { - ERROR("failed to setup ipv4 gateway for '%s': %s", - ifname, strerror(-err)); - if (netdev->ipv4_gateway_auto) { - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); - ERROR("tried to set autodetected ipv4 gateway '%s'", buf); - } - return -1; - } - } - } +int setup_resource_limits(struct lxc_list *limits, pid_t pid) { + struct lxc_list *it; + struct lxc_limit *lim; + int resid; - /* setup ipv6 gateway on the interface */ - if (netdev->ipv6_gateway) { - if (!(netdev->flags & IFF_UP)) { - ERROR("Cannot add ipv6 gateway for %s when not bringing up the interface", ifname); - return -1; - } + lxc_list_for_each(it, limits) { + lim = it->elem; - if (lxc_list_empty(&netdev->ipv6) && !IN6_IS_ADDR_LINKLOCAL(netdev->ipv6_gateway)) { - ERROR("Cannot add ipv6 gateway for %s when not assigning an address", ifname); + resid = parse_resource(lim->resource); + if (resid < 0) { + ERROR("unknown resource %s", lim->resource); return -1; } - err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); - if (err) { - err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway); - if (err) { - ERROR("failed to add ipv6 dest for '%s': %s", - ifname, strerror(-err)); - } - - err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); - if (err) { - ERROR("failed to setup ipv6 gateway for '%s': %s", - ifname, strerror(-err)); - if (netdev->ipv6_gateway_auto) { - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); - ERROR("tried to set autodetected ipv6 gateway '%s'", buf); - } - return -1; - } - } - } - - DEBUG("'%s' has been setup", current_ifname); - - return 0; -} - -static int setup_network(struct lxc_list *network) -{ - struct lxc_list *iterator; - struct lxc_netdev *netdev; - - lxc_list_for_each(iterator, network) { - - netdev = iterator->elem; - - if (setup_netdev(netdev)) { - ERROR("failed to setup netdev"); + if (prlimit(pid, resid, &lim->limit, NULL) != 0) { + ERROR("failed to set limit %s: %s", lim->resource, strerror(errno)); return -1; } } - - if (!lxc_list_empty(network)) - INFO("network has been setup"); - return 0; } -/* try to move physical nics to the init netns */ -void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf) -{ - int i, oldfd; - char ifname[IFNAMSIZ]; - - if (netnsfd < 0 || conf->num_savednics == 0) - return; - - INFO("Running to reset %d nic names.", conf->num_savednics); - - oldfd = lxc_preserve_ns(getpid(), "net"); - if (oldfd < 0) { - SYSERROR("Failed to open monitor netns fd."); - return; - } - - if (setns(netnsfd, 0) != 0) { - SYSERROR("Failed to enter container netns to reset nics"); - close(oldfd); - return; - } - for (i=0; inum_savednics; i++) { - struct saved_nic *s = &conf->saved_nics[i]; - /* retrieve the name of the interface */ - if (!if_indextoname(s->ifindex, ifname)) { - WARN("no interface corresponding to index '%d'", s->ifindex); - continue; - } - if (lxc_netdev_move_by_name(ifname, 1, s->orig_name)) - WARN("Error moving nic name:%s back to host netns", ifname); - free(s->orig_name); - } - conf->num_savednics = 0; - - if (setns(oldfd, 0) != 0) - SYSERROR("Failed to re-enter monitor's netns"); - close(oldfd); -} - static char *default_rootfs_mount = LXCROOTFSMOUNT; struct lxc_conf *lxc_conf_init(void) @@ -2581,14 +2472,14 @@ struct lxc_conf *new; int i; - new = malloc(sizeof(*new)); + new = malloc(sizeof(*new)); if (!new) { - ERROR("lxc_conf_init : %m"); + ERROR("lxc_conf_init : %s", strerror(errno)); return NULL; } memset(new, 0, sizeof(*new)); - new->loglevel = LXC_LOG_PRIORITY_NOTSET; + new->loglevel = LXC_LOG_LEVEL_NOTSET; new->personality = -1; new->autodev = 1; new->console.log_path = NULL; @@ -2605,11 +2496,10 @@ new->nbd_idx = -1; new->rootfs.mount = strdup(default_rootfs_mount); if (!new->rootfs.mount) { - ERROR("lxc_conf_init : %m"); + ERROR("lxc_conf_init : %s", strerror(errno)); free(new); return NULL; } - new->kmsg = 0; new->logfd = -1; lxc_list_init(&new->cgroup); lxc_list_init(&new->network); @@ -2620,6 +2510,7 @@ lxc_list_init(&new->includes); lxc_list_init(&new->aliens); lxc_list_init(&new->environment); + lxc_list_init(&new->limits); for (i=0; ihooks[i]); lxc_list_init(&new->groups); @@ -2634,663 +2525,60 @@ * default to running as UID/GID 0 when using lxc-execute */ new->init_uid = 0; new->init_gid = 0; + memset(&new->cgroup_meta, 0, sizeof(struct lxc_cgroup)); return new; } -static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) +static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, + size_t buf_size) { - char veth1buf[IFNAMSIZ], *veth1; - char veth2buf[IFNAMSIZ], *veth2; - int bridge_index, err; - unsigned int mtu = 0; - - if (netdev->priv.veth_attr.pair) { - veth1 = netdev->priv.veth_attr.pair; - if (handler->conf->reboot) - lxc_netdev_delete_by_name(veth1); - } else { - err = snprintf(veth1buf, sizeof(veth1buf), "vethXXXXXX"); - if (err >= sizeof(veth1buf)) { /* can't *really* happen, but... */ - ERROR("veth1 name too long"); - return -1; - } - veth1 = lxc_mkifname(veth1buf); - if (!veth1) { - ERROR("failed to allocate a temporary name"); - return -1; - } - /* store away for deconf */ - memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ); - } + char path[MAXPATHLEN]; + int fd, ret; - snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX"); - veth2 = lxc_mkifname(veth2buf); - if (!veth2) { - ERROR("failed to allocate a temporary name"); - goto out_delete; + ret = snprintf(path, MAXPATHLEN, "/proc/%d/%cid_map", pid, + idtype == ID_TYPE_UID ? 'u' : 'g'); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("failed to create path \"%s\"", path); + return -E2BIG; } - err = lxc_veth_create(veth1, veth2); - if (err) { - ERROR("failed to create veth pair (%s and %s): %s", veth1, veth2, - strerror(-err)); - goto out_delete; + fd = open(path, O_WRONLY); + if (fd < 0) { + SYSERROR("failed to open \"%s\"", path); + return -1; } - /* changing the high byte of the mac address to 0xfe, the bridge interface - * will always keep the host's mac address and not take the mac address - * of a container */ - err = setup_private_host_hw_addr(veth1); - if (err) { - ERROR("failed to change mac address of host interface '%s': %s", - veth1, strerror(-err)); - goto out_delete; + errno = 0; + ret = lxc_write_nointr(fd, buf, buf_size); + if (ret != buf_size) { + SYSERROR("failed to write %cid mapping to \"%s\"", + idtype == ID_TYPE_UID ? 'u' : 'g', path); + close(fd); + return -1; } + close(fd); - netdev->ifindex = if_nametoindex(veth2); - if (!netdev->ifindex) { - ERROR("failed to retrieve the index for %s", veth2); - goto out_delete; - } + return 0; +} - if (netdev->mtu) { - if (lxc_safe_uint(netdev->mtu, &mtu) < 0) - WARN("Failed to parse mtu from."); - else - INFO("Retrieved mtu %d", mtu); - } else if (netdev->link) { - bridge_index = if_nametoindex(netdev->link); - if (bridge_index) { - mtu = netdev_get_mtu(bridge_index); - INFO("Retrieved mtu %d from %s", mtu, netdev->link); - } else { - mtu = netdev_get_mtu(netdev->ifindex); - INFO("Retrieved mtu %d from %s", mtu, veth2); - } - } +/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. + * + * @return 1 if functional binary was found + * @return 0 if binary exists but is lacking privilege + * @return -ENOENT if binary does not exist + * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID + * + */ +static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) +{ + char *path; + int ret; + struct stat st; + int fret = 0; - if (mtu) { - err = lxc_netdev_set_mtu(veth1, mtu); - if (!err) - err = lxc_netdev_set_mtu(veth2, mtu); - if (err) { - ERROR("failed to set mtu '%i' for veth pair (%s and %s): %s", - mtu, veth1, veth2, strerror(-err)); - goto out_delete; - } - } - - if (netdev->link) { - err = lxc_bridge_attach(handler->lxcpath, handler->name, netdev->link, veth1); - if (err) { - ERROR("failed to attach '%s' to the bridge '%s': %s", - veth1, netdev->link, strerror(-err)); - goto out_delete; - } - INFO("Attached '%s': to the bridge '%s': ", veth1, netdev->link); - } - - err = lxc_netdev_up(veth1); - if (err) { - ERROR("failed to set %s up : %s", veth1, strerror(-err)); - goto out_delete; - } - - if (netdev->upscript) { - err = run_script(handler->name, "net", netdev->upscript, "up", - "veth", veth1, (char*) NULL); - if (err) - goto out_delete; - } - - DEBUG("instantiated veth '%s/%s', index is '%d'", - veth1, veth2, netdev->ifindex); - - return 0; - -out_delete: - lxc_netdev_delete_by_name(veth1); - if (!netdev->priv.veth_attr.pair) - free(veth1); - free(veth2); - return -1; -} - -static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - char *veth1; - int err; - - if (netdev->priv.veth_attr.pair) - veth1 = netdev->priv.veth_attr.pair; - else - veth1 = netdev->priv.veth_attr.veth1; - - if (netdev->downscript) { - err = run_script(handler->name, "net", netdev->downscript, - "down", "veth", veth1, (char*) NULL); - if (err) - return -1; - } - return 0; -} - -static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - char peerbuf[IFNAMSIZ], *peer; - int err; - - if (!netdev->link) { - ERROR("no link specified for macvlan netdev"); - return -1; - } - - err = snprintf(peerbuf, sizeof(peerbuf), "mcXXXXXX"); - if (err >= sizeof(peerbuf)) - return -1; - - peer = lxc_mkifname(peerbuf); - if (!peer) { - ERROR("failed to make a temporary name"); - return -1; - } - - err = lxc_macvlan_create(netdev->link, peer, - netdev->priv.macvlan_attr.mode); - if (err) { - ERROR("failed to create macvlan interface '%s' on '%s' : %s", - peer, netdev->link, strerror(-err)); - goto out; - } - - netdev->ifindex = if_nametoindex(peer); - if (!netdev->ifindex) { - ERROR("failed to retrieve the index for %s", peer); - goto out; - } - - if (netdev->upscript) { - err = run_script(handler->name, "net", netdev->upscript, "up", - "macvlan", netdev->link, (char*) NULL); - if (err) - goto out; - } - - DEBUG("instantiated macvlan '%s', index is '%d' and mode '%d'", - peer, netdev->ifindex, netdev->priv.macvlan_attr.mode); - - return 0; -out: - lxc_netdev_delete_by_name(peer); - free(peer); - return -1; -} - -static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - int err; - - if (netdev->downscript) { - err = run_script(handler->name, "net", netdev->downscript, - "down", "macvlan", netdev->link, - (char*) NULL); - if (err) - return -1; - } - return 0; -} - -/* XXX: merge with instantiate_macvlan */ -static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - char peer[IFNAMSIZ]; - int err; - static uint16_t vlan_cntr = 0; - unsigned int mtu = 0; - - if (!netdev->link) { - ERROR("no link specified for vlan netdev"); - return -1; - } - - err = snprintf(peer, sizeof(peer), "vlan%d-%d", netdev->priv.vlan_attr.vid, vlan_cntr++); - if (err >= sizeof(peer)) { - ERROR("peer name too long"); - return -1; - } - - err = lxc_vlan_create(netdev->link, peer, netdev->priv.vlan_attr.vid); - if (err) { - ERROR("failed to create vlan interface '%s' on '%s' : %s", - peer, netdev->link, strerror(-err)); - return -1; - } - - netdev->ifindex = if_nametoindex(peer); - if (!netdev->ifindex) { - ERROR("failed to retrieve the ifindex for %s", peer); - lxc_netdev_delete_by_name(peer); - return -1; - } - - DEBUG("instantiated vlan '%s', ifindex is '%d'", " vlan1000", - netdev->ifindex); - if (netdev->mtu) { - if (lxc_safe_uint(netdev->mtu, &mtu) < 0) { - ERROR("Failed to retrieve mtu from: '%d'/'%s'.", - netdev->ifindex, netdev->name); - return -1; - } - err = lxc_netdev_set_mtu(peer, mtu); - if (err) { - ERROR("failed to set mtu '%s' for %s : %s", - netdev->mtu, peer, strerror(-err)); - lxc_netdev_delete_by_name(peer); - return -1; - } - } - - return 0; -} - -static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - return 0; -} - -static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - if (!netdev->link) { - ERROR("no link specified for the physical interface"); - return -1; - } - - netdev->ifindex = if_nametoindex(netdev->link); - if (!netdev->ifindex) { - ERROR("failed to retrieve the index for %s", netdev->link); - return -1; - } - - if (netdev->upscript) { - int err; - err = run_script(handler->name, "net", netdev->upscript, - "up", "phys", netdev->link, (char*) NULL); - if (err) - return -1; - } - - return 0; -} - -static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - int err; - - if (netdev->downscript) { - err = run_script(handler->name, "net", netdev->downscript, - "down", "phys", netdev->link, (char*) NULL); - if (err) - return -1; - } - return 0; -} - -static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - netdev->ifindex = 0; - return 0; -} - -static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - netdev->ifindex = 0; - if (netdev->upscript) { - int err; - err = run_script(handler->name, "net", netdev->upscript, - "up", "empty", (char*) NULL); - if (err) - return -1; - } - return 0; -} - -static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - int err; - - if (netdev->downscript) { - err = run_script(handler->name, "net", netdev->downscript, - "down", "empty", (char*) NULL); - if (err) - return -1; - } - return 0; -} - -static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev) -{ - return 0; -} - -int lxc_requests_empty_network(struct lxc_handler *handler) -{ - struct lxc_list *network = &handler->conf->network; - struct lxc_list *iterator; - struct lxc_netdev *netdev; - bool found_none = false, found_nic = false; - - if (lxc_list_empty(network)) - return 0; - - lxc_list_for_each(iterator, network) { - - netdev = iterator->elem; - - if (netdev->type == LXC_NET_NONE) - found_none = true; - else - found_nic = true; - } - if (found_none && !found_nic) - return 1; - return 0; -} - -int lxc_create_network(struct lxc_handler *handler) -{ - struct lxc_list *network = &handler->conf->network; - struct lxc_list *iterator; - struct lxc_netdev *netdev; - int am_root = (getuid() == 0); - - if (!am_root) - return 0; - - lxc_list_for_each(iterator, network) { - - netdev = iterator->elem; - - if (netdev->type < 0 || netdev->type > LXC_NET_MAXCONFTYPE) { - ERROR("invalid network configuration type '%d'", - netdev->type); - return -1; - } - - if (netdev_conf[netdev->type](handler, netdev)) { - ERROR("failed to create netdev"); - return -1; - } - - } - - return 0; -} - -bool lxc_delete_network(struct lxc_handler *handler) -{ - int ret; - struct lxc_list *network = &handler->conf->network; - struct lxc_list *iterator; - struct lxc_netdev *netdev; - bool deleted_all = true; - - lxc_list_for_each(iterator, network) { - netdev = iterator->elem; - - if (netdev->ifindex != 0 && netdev->type == LXC_NET_PHYS) { - if (lxc_netdev_rename_by_index(netdev->ifindex, netdev->link)) - WARN("Failed to rename interface with index %d " - "to its initial name \"%s\".", - netdev->ifindex, netdev->link); - continue; - } - - if (netdev_deconf[netdev->type](handler, netdev)) { - WARN("Failed to destroy netdev"); - } - - /* Recent kernel remove the virtual interfaces when the network - * namespace is destroyed but in case we did not moved the - * interface to the network namespace, we have to destroy it - */ - if (netdev->ifindex != 0) { - ret = lxc_netdev_delete_by_index(netdev->ifindex); - if (-ret == ENODEV) { - INFO("Interface \"%s\" with index %d already " - "deleted or existing in different network " - "namespace.", - netdev->name ? netdev->name : "(null)", - netdev->ifindex); - } else if (ret < 0) { - deleted_all = false; - WARN("Failed to remove interface \"%s\" with " - "index %d: %s.", - netdev->name ? netdev->name : "(null)", - netdev->ifindex, strerror(-ret)); - } else { - INFO("Removed interface \"%s\" with index %d.", - netdev->name ? netdev->name : "(null)", - netdev->ifindex); - } - } - - /* Explicitly delete host veth device to prevent lingering - * devices. We had issues in LXD around this. - */ - if (netdev->type == LXC_NET_VETH && !am_unpriv()) { - char *hostveth; - if (netdev->priv.veth_attr.pair) { - hostveth = netdev->priv.veth_attr.pair; - ret = lxc_netdev_delete_by_name(hostveth); - if (ret < 0) { - WARN("Failed to remove interface \"%s\" from host: %s.", hostveth, strerror(-ret)); - } else { - INFO("Removed interface \"%s\" from host.", hostveth); - } - } else if (strlen(netdev->priv.veth_attr.veth1) > 0) { - hostveth = netdev->priv.veth_attr.veth1; - ret = lxc_netdev_delete_by_name(hostveth); - if (ret < 0) { - WARN("Failed to remove \"%s\" from host: %s.", hostveth, strerror(-ret)); - } else { - INFO("Removed interface \"%s\" from host.", hostveth); - memset((void *)&netdev->priv.veth_attr.veth1, 0, sizeof(netdev->priv.veth_attr.veth1)); - } - } - } - } - - return deleted_all; -} - -#define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic" - -/* lxc-user-nic returns "interface_name:interface_name\n" */ -#define MAX_BUFFER_SIZE IFNAMSIZ * 2 + 2 -static int unpriv_assign_nic(const char *lxcpath, char *lxcname, - struct lxc_netdev *netdev, pid_t pid) -{ - pid_t child; - int bytes, pipefd[2]; - char *token, *saveptr = NULL; - char buffer[MAX_BUFFER_SIZE]; - char netdev_link[IFNAMSIZ + 1]; - - if (netdev->type != LXC_NET_VETH) { - ERROR("nic type %d not support for unprivileged use", - netdev->type); - return -1; - } - - if (pipe(pipefd) < 0) { - SYSERROR("pipe failed"); - return -1; - } - - child = fork(); - if (child < 0) { - SYSERROR("fork"); - close(pipefd[0]); - close(pipefd[1]); - return -1; - } - - if (child == 0) { // child - /* Call lxc-user-nic pid type bridge. */ - int ret; - char pidstr[LXC_NUMSTRLEN64]; - - close(pipefd[0]); /* Close the read-end of the pipe. */ - - /* Redirect stdout to write-end of the pipe. */ - ret = dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); /* Close the write-end of the pipe. */ - if (ret < 0) { - SYSERROR("Failed to dup2() to redirect stdout to pipe file descriptor."); - exit(EXIT_FAILURE); - } - - if (netdev->link) - strncpy(netdev_link, netdev->link, IFNAMSIZ); - else - strncpy(netdev_link, "none", IFNAMSIZ); - - ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid); - if (ret < 0 || ret >= LXC_NUMSTRLEN64) - exit(EXIT_FAILURE); - pidstr[LXC_NUMSTRLEN64 - 1] = '\0'; - - INFO("Execing lxc-user-nic %s %s %s veth %s %s", lxcpath, - lxcname, pidstr, netdev_link, netdev->name); - execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, lxcpath, lxcname, - pidstr, "veth", netdev_link, netdev->name, NULL); - - SYSERROR("Failed to exec lxc-user-nic."); - exit(EXIT_FAILURE); - } - - /* close the write-end of the pipe */ - close(pipefd[1]); - - bytes = read(pipefd[0], &buffer, MAX_BUFFER_SIZE); - if (bytes < 0) - SYSERROR("Failed to read from pipe file descriptor."); - buffer[bytes - 1] = '\0'; - - if (wait_for_pid(child) != 0) { - close(pipefd[0]); - return -1; - } - - /* close the read-end of the pipe */ - close(pipefd[0]); - - /* fill netdev->name field */ - token = strtok_r(buffer, ":", &saveptr); - if (!token) - return -1; - - netdev->name = malloc(IFNAMSIZ + 1); - if (!netdev->name) { - SYSERROR("Failed to allocate memory."); - return -1; - } - memset(netdev->name, 0, IFNAMSIZ + 1); - strncpy(netdev->name, token, IFNAMSIZ); - - /* fill netdev->veth_attr.pair field */ - token = strtok_r(NULL, ":", &saveptr); - if (!token) - return -1; - - netdev->priv.veth_attr.pair = strdup(token); - if (!netdev->priv.veth_attr.pair) { - ERROR("Failed to allocate memory."); - return -1; - } - - return 0; -} - -int lxc_assign_network(const char *lxcpath, char *lxcname, - struct lxc_list *network, pid_t pid) -{ - struct lxc_list *iterator; - struct lxc_netdev *netdev; - char ifname[IFNAMSIZ]; - int am_root = (getuid() == 0); - int err; - - lxc_list_for_each(iterator, network) { - - netdev = iterator->elem; - - if (netdev->type == LXC_NET_VETH && !am_root) { - if (unpriv_assign_nic(lxcpath, lxcname, netdev, pid)) - return -1; - // lxc-user-nic has moved the nic to the new ns. - // unpriv_assign_nic() fills in netdev->name. - // netdev->ifindex will be filed in at setup_netdev. - continue; - } - - /* empty network namespace, nothing to move */ - if (!netdev->ifindex) - continue; - - /* retrieve the name of the interface */ - if (!if_indextoname(netdev->ifindex, ifname)) { - ERROR("no interface corresponding to index '%d'", netdev->ifindex); - return -1; - } - - err = lxc_netdev_move_by_name(ifname, pid, NULL); - if (err) { - ERROR("failed to move '%s' to the container : %s", - netdev->link, strerror(-err)); - return -1; - } - - DEBUG("move '%s'/'%s' to '%d': .", ifname, netdev->name, pid); - } - - return 0; -} - -static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, - size_t buf_size) -{ - char path[PATH_MAX]; - int ret, closeret; - FILE *f; - - ret = snprintf(path, PATH_MAX, "/proc/%d/%cid_map", pid, idtype == ID_TYPE_UID ? 'u' : 'g'); - if (ret < 0 || ret >= PATH_MAX) { - fprintf(stderr, "%s: path name too long\n", __func__); - return -E2BIG; - } - f = fopen(path, "w"); - if (!f) { - perror("open"); + if (cap != CAP_SETUID && cap != CAP_SETGID) return -EINVAL; - } - ret = fwrite(buf, buf_size, 1, f); - if (ret < 0) - SYSERROR("writing id mapping"); - closeret = fclose(f); - if (closeret) - SYSERROR("writing id mapping"); - return ret < 0 ? ret : closeret; -} - -/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. */ -static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) -{ - char *path; - int ret; - struct stat st; - int fret = 0; path = on_path(binary, NULL); if (!path) @@ -3344,18 +2632,35 @@ return fret; } +int lxc_map_ids_exec_wrapper(void *args) +{ + execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL); + return -1; +} + int lxc_map_ids(struct lxc_list *idmap, pid_t pid) { struct id_map *map; struct lxc_list *iterator; enum idtype type; + char u_or_g; char *pos; - int euid; - int ret = 0, use_shadow = 0; - int uidmap = 0, gidmap = 0; - char *buf = NULL; - - euid = geteuid(); + int fill, left; + char cmd_output[MAXPATHLEN]; + /* strlen("new@idmap") = 9 + * + + * strlen(" ") = 1 + * + + * LXC_NUMSTRLEN64 + * + + * strlen(" ") = 1 + * + * We add some additional space to make sure that we really have + * LXC_IDMAPLEN bytes available for our the {g,u]id mapping. + */ + char mapbuf[9 + 1 + LXC_NUMSTRLEN64 + 1 + LXC_IDMAPLEN] = {0}; + int ret = 0, uidmap = 0, gidmap = 0; + bool use_shadow = false, had_entry = false; /* If new{g,u}idmap exists, that is, if shadow is handing out subuid * ranges, then insist that root also reserve ranges in subuid. This @@ -3363,32 +2668,36 @@ * range by shadow. */ uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID); + if (uidmap == -ENOENT) + WARN("newuidmap binary is missing"); + else if (!uidmap) + WARN("newuidmap is lacking necessary privileges"); + gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID); + if (gidmap == -ENOENT) + WARN("newgidmap binary is missing"); + else if (!gidmap) + WARN("newgidmap is lacking necessary privileges"); + if (uidmap > 0 && gidmap > 0) { DEBUG("Functional newuidmap and newgidmap binary found."); use_shadow = true; - } else if (uidmap == -ENOENT && gidmap == -ENOENT && !euid) { - DEBUG("No newuidmap and newgidmap binary found. Trying to " - "write directly with euid 0."); - use_shadow = false; } else { - DEBUG("Either one or both of the newuidmap and newgidmap " - "binaries do not exist or are missing necessary " - "privilege."); - return -1; + /* In case unprivileged users run application containers via + * execute() or a start*() there are valid cases where they may + * only want to map their own {g,u}id. Let's not block them from + * doing so by requiring geteuid() == 0. + */ + DEBUG("No newuidmap and newgidmap binary found. Trying to " + "write directly with euid %d.", geteuid()); } - for (type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) { - int left, fill; - bool had_entry = false; - if (!buf) { - buf = pos = malloc(LXC_IDMAPLEN); - if (!buf) - return -ENOMEM; - } - pos = buf; + for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID; + type++, u_or_g = 'g') { + pos = mapbuf; + if (use_shadow) - pos += sprintf(buf, "new%cidmap %d", type == ID_TYPE_UID ? 'u' : 'g', pid); + pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid); lxc_list_for_each(iterator, idmap) { /* The kernel only takes <= 4k for writes to @@ -3400,7 +2709,7 @@ had_entry = true; - left = LXC_IDMAPLEN - (pos - buf); + left = LXC_IDMAPLEN - (pos - mapbuf); fill = snprintf(pos, left, "%s%lu %lu %lu%s", use_shadow ? " " : "", map->nsid, map->hostid, map->range, @@ -3413,22 +2722,32 @@ if (!had_entry) continue; - if (!use_shadow) { - ret = write_id_mapping(type, pid, buf, pos - buf); + /* Try to catch the ouput of new{g,u}idmap to make debugging + * easier. + */ + if (use_shadow) { + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_map_ids_exec_wrapper, + (void *)mapbuf); + if (ret < 0) { + ERROR("new%cidmap failed to write mapping \"%s\": %s", + u_or_g, cmd_output, mapbuf); + return -1; + } + TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf); } else { - left = LXC_IDMAPLEN - (pos - buf); - fill = snprintf(pos, left, "\n"); - if (fill <= 0 || fill >= left) - SYSERROR("Too many {g,u}id mappings defined."); - pos += fill; - ret = system(buf); + ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf); + if (ret < 0) { + ERROR("Failed to write mapping: %s", mapbuf); + return -1; + } + TRACE("Wrote mapping \"%s\"", mapbuf); } - if (ret) - break; + + memset(mapbuf, 0, sizeof(mapbuf)); } - free(buf); - return ret; + return 0; } /* @@ -3454,148 +2773,42 @@ return false; } -int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype) -{ - struct lxc_list *it; - struct id_map *map; - lxc_list_for_each(it, &conf->id_map) { - map = it->elem; - if (map->idtype != idtype) - continue; - if (id >= map->hostid && id < map->hostid + map->range) - return (id - map->hostid) + map->nsid; - } - return -1; -} - -int find_unmapped_nsuid(struct lxc_conf *conf, enum idtype idtype) -{ - struct lxc_list *it; - struct id_map *map; - unsigned int freeid = 0; -again: - lxc_list_for_each(it, &conf->id_map) { - map = it->elem; - if (map->idtype != idtype) - continue; - if (freeid >= map->nsid && freeid < map->nsid + map->range) { - freeid = map->nsid + map->range; - goto again; - } - } - return freeid; -} - -int lxc_find_gateway_addresses(struct lxc_handler *handler) -{ - struct lxc_list *network = &handler->conf->network; - struct lxc_list *iterator; - struct lxc_netdev *netdev; - int link_index; - - lxc_list_for_each(iterator, network) { - netdev = iterator->elem; - - if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto) - continue; - - if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) { - ERROR("gateway = auto only supported for " - "veth and macvlan"); - return -1; - } - - if (!netdev->link) { - ERROR("gateway = auto needs a link interface"); - return -1; - } - - link_index = if_nametoindex(netdev->link); - if (!link_index) - return -EINVAL; - - if (netdev->ipv4_gateway_auto) { - if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) { - ERROR("failed to automatically find ipv4 gateway " - "address from link interface '%s'", netdev->link); - return -1; - } - } - - if (netdev->ipv6_gateway_auto) { - if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) { - ERROR("failed to automatically find ipv6 gateway " - "address from link interface '%s'", netdev->link); - return -1; - } - } - } - - return 0; -} - -int lxc_create_tty(const char *name, struct lxc_conf *conf) -{ - struct lxc_tty_info *tty_info = &conf->tty_info; - int i, ret; - - /* no tty in the configuration */ - if (!conf->tty) - return 0; - - tty_info->pty_info = - malloc(sizeof(*tty_info->pty_info)*conf->tty); - if (!tty_info->pty_info) { - SYSERROR("failed to allocate pty_info"); - return -1; - } - - for (i = 0; i < conf->tty; i++) { - - struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; - - process_lock(); - ret = openpty(&pty_info->master, &pty_info->slave, - pty_info->name, NULL, NULL); - process_unlock(); - if (ret) { - SYSERROR("failed to create pty #%d", i); - tty_info->nbtty = i; - lxc_delete_tty(tty_info); - return -1; - } - - DEBUG("allocated pty '%s' (%d/%d)", - pty_info->name, pty_info->master, pty_info->slave); - - /* Prevent leaking the file descriptors to the container */ - fcntl(pty_info->master, F_SETFD, FD_CLOEXEC); - fcntl(pty_info->slave, F_SETFD, FD_CLOEXEC); - - pty_info->busy = 0; - } - - tty_info->nbtty = conf->tty; - - INFO("tty's configured"); - - return 0; -} - -void lxc_delete_tty(struct lxc_tty_info *tty_info) +int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype) { - int i; - - for (i = 0; i < tty_info->nbtty; i++) { - struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; + struct lxc_list *it; + struct id_map *map; + lxc_list_for_each(it, &conf->id_map) { + map = it->elem; + if (map->idtype != idtype) + continue; + if (id >= map->hostid && id < map->hostid + map->range) + return (id - map->hostid) + map->nsid; + } + return -1; +} - close(pty_info->master); - close(pty_info->slave); +int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype) +{ + struct lxc_list *it; + struct id_map *map; + unsigned int freeid = 0; +again: + lxc_list_for_each(it, &conf->id_map) { + map = it->elem; + if (map->idtype != idtype) + continue; + if (freeid >= map->nsid && freeid < map->nsid + map->range) { + freeid = map->nsid + map->range; + goto again; + } } + return freeid; +} - free(tty_info->pty_info); - tty_info->pty_info = NULL; - tty_info->nbtty = 0; +int chown_mapped_root_exec_wrapper(void *args) +{ + execvp("lxc-usernsexec", args); + return -1; } /* @@ -3608,42 +2821,44 @@ */ int chown_mapped_root(char *path, struct lxc_conf *conf) { - uid_t rootuid; - gid_t rootgid; - pid_t pid; + uid_t rootuid, rootgid; unsigned long val; - char *chownpath = path; + int hostuid, hostgid, ret; + struct stat sb; + char map1[100], map2[100], map3[100], map4[100], map5[100]; + char ugid[100]; + char *args1[] = {"lxc-usernsexec", + "-m", map1, + "-m", map2, + "-m", map3, + "-m", map5, + "--", "chown", ugid, path, + NULL}; + char *args2[] = {"lxc-usernsexec", + "-m", map1, + "-m", map2, + "-m", map3, + "-m", map4, + "-m", map5, + "--", "chown", ugid, path, + NULL}; + char cmd_output[MAXPATHLEN]; + + hostuid = geteuid(); + hostgid = getegid(); if (!get_mapped_rootid(conf, ID_TYPE_UID, &val)) { - ERROR("No mapping for container root"); + ERROR("No uid mapping for container root"); return -1; } - rootuid = (uid_t) val; + rootuid = (uid_t)val; if (!get_mapped_rootid(conf, ID_TYPE_GID, &val)) { - ERROR("No mapping for container root"); + ERROR("No gid mapping for container root"); return -1; } - rootgid = (gid_t) val; + rootgid = (gid_t)val; - /* - * In case of overlay, we want only the writeable layer - * to be chowned - */ - if (strncmp(path, "overlayfs:", 10) == 0 || strncmp(path, "aufs:", 5) == 0) { - chownpath = strchr(path, ':'); - if (!chownpath) { - ERROR("Bad overlay path: %s", path); - return -1; - } - chownpath = strchr(chownpath+1, ':'); - if (!chownpath) { - ERROR("Bad overlay path: %s", path); - return -1; - } - chownpath++; - } - path = chownpath; - if (geteuid() == 0) { + if (hostuid == 0) { if (chown(path, rootuid, rootgid) < 0) { ERROR("Error chowning %s", path); return -1; @@ -3651,109 +2866,106 @@ return 0; } - if (rootuid == geteuid()) { - // nothing to do - INFO("%s: container root is our uid; no need to chown" ,__func__); + if (rootuid == hostuid) { + /* nothing to do */ + INFO("Container root is our uid; no need to chown"); return 0; } - pid = fork(); - if (pid < 0) { - SYSERROR("Failed forking"); + /* save the current gid of "path" */ + if (stat(path, &sb) < 0) { + ERROR("Error stat %s", path); return -1; } - if (!pid) { - int hostuid = geteuid(), hostgid = getegid(), ret; - struct stat sb; - char map1[100], map2[100], map3[100], map4[100], map5[100]; - char ugid[100]; - char *args1[] = { "lxc-usernsexec", "-m", map1, "-m", map2, - "-m", map3, "-m", map5, - "--", "chown", ugid, path, NULL }; - char *args2[] = { "lxc-usernsexec", "-m", map1, "-m", map2, - "-m", map3, "-m", map4, "-m", map5, - "--", "chown", ugid, path, NULL }; - - // save the current gid of "path" - if (stat(path, &sb) < 0) { - ERROR("Error stat %s", path); - return -1; - } - /* - * A file has to be group-owned by a gid mapped into the - * container, or the container won't be privileged over it. - */ - if (sb.st_uid == geteuid() && - mapped_hostid(sb.st_gid, conf, ID_TYPE_GID) < 0 && - chown(path, -1, hostgid) < 0) { - ERROR("Failed chgrping %s", path); - return -1; - } + /* Update the path argument in case this was overlayfs. */ + args1[sizeof(args1) / sizeof(args1[0]) - 2] = path; + args2[sizeof(args2) / sizeof(args2[0]) - 2] = path; - // "u:0:rootuid:1" - ret = snprintf(map1, 100, "u:0:%d:1", rootuid); - if (ret < 0 || ret >= 100) { - ERROR("Error uid printing map string"); - return -1; - } + /* + * A file has to be group-owned by a gid mapped into the + * container, or the container won't be privileged over it. + */ + DEBUG("trying to chown \"%s\" to %d", path, hostgid); + if (sb.st_uid == hostuid && + mapped_hostid(sb.st_gid, conf, ID_TYPE_GID) < 0 && + chown(path, -1, hostgid) < 0) { + ERROR("Failed chgrping %s", path); + return -1; + } - // "u:hostuid:hostuid:1" - ret = snprintf(map2, 100, "u:%d:%d:1", hostuid, hostuid); - if (ret < 0 || ret >= 100) { - ERROR("Error uid printing map string"); - return -1; - } + /* "u:0:rootuid:1" */ + ret = snprintf(map1, 100, "u:0:%d:1", rootuid); + if (ret < 0 || ret >= 100) { + ERROR("Error uid printing map string"); + return -1; + } - // "g:0:rootgid:1" - ret = snprintf(map3, 100, "g:0:%d:1", rootgid); - if (ret < 0 || ret >= 100) { - ERROR("Error gid printing map string"); - return -1; - } + /* "u:hostuid:hostuid:1" */ + ret = snprintf(map2, 100, "u:%d:%d:1", hostuid, hostuid); + if (ret < 0 || ret >= 100) { + ERROR("Error uid printing map string"); + return -1; + } - // "g:pathgid:rootgid+pathgid:1" - ret = snprintf(map4, 100, "g:%d:%d:1", (gid_t)sb.st_gid, - rootgid + (gid_t)sb.st_gid); - if (ret < 0 || ret >= 100) { - ERROR("Error gid printing map string"); - return -1; - } + /* "g:0:rootgid:1" */ + ret = snprintf(map3, 100, "g:0:%d:1", rootgid); + if (ret < 0 || ret >= 100) { + ERROR("Error gid printing map string"); + return -1; + } - // "g:hostgid:hostgid:1" - ret = snprintf(map5, 100, "g:%d:%d:1", hostgid, hostgid); - if (ret < 0 || ret >= 100) { - ERROR("Error gid printing map string"); - return -1; - } + /* "g:pathgid:rootgid+pathgid:1" */ + ret = snprintf(map4, 100, "g:%d:%d:1", (gid_t)sb.st_gid, + rootgid + (gid_t)sb.st_gid); + if (ret < 0 || ret >= 100) { + ERROR("Error gid printing map string"); + return -1; + } - // "0:pathgid" (chown) - ret = snprintf(ugid, 100, "0:%d", (gid_t)sb.st_gid); - if (ret < 0 || ret >= 100) { - ERROR("Error owner printing format string for chown"); - return -1; - } + /* "g:hostgid:hostgid:1" */ + ret = snprintf(map5, 100, "g:%d:%d:1", hostgid, hostgid); + if (ret < 0 || ret >= 100) { + ERROR("Error gid printing map string"); + return -1; + } - if (hostgid == sb.st_gid) - ret = execvp("lxc-usernsexec", args1); - else - ret = execvp("lxc-usernsexec", args2); - SYSERROR("Failed executing usernsexec"); - exit(1); + /* "0:pathgid" (chown) */ + ret = snprintf(ugid, 100, "0:%d", (gid_t)sb.st_gid); + if (ret < 0 || ret >= 100) { + ERROR("Error owner printing format string for chown"); + return -1; } - return wait_for_pid(pid); + + if (hostgid == sb.st_gid) + ret = run_command(cmd_output, sizeof(cmd_output), + chown_mapped_root_exec_wrapper, + (void *)args1); + else + ret = run_command(cmd_output, sizeof(cmd_output), + chown_mapped_root_exec_wrapper, + (void *)args2); + if (ret < 0) + ERROR("lxc-usernsexec failed: %s", cmd_output); + + return ret; } -int ttys_shift_ids(struct lxc_conf *c) +int lxc_ttys_shift_ids(struct lxc_conf *c) { if (lxc_list_empty(&c->id_map)) return 0; - if (strcmp(c->console.name, "") !=0 && chown_mapped_root(c->console.name, c) < 0) { - ERROR("Failed to chown %s", c->console.name); + if (!strcmp(c->console.name, "")) + return 0; + + if (chown_mapped_root(c->console.name, c) < 0) { + ERROR("failed to chown console \"%s\"", c->console.name); return -1; } + TRACE("chowned console \"%s\"", c->console.name); + return 0; } @@ -3887,7 +3099,7 @@ return -1; } - if (setup_rootfs(conf)) { + if (lxc_setup_rootfs(conf)) { ERROR("failed to setup rootfs for '%s'", name); return -1; } @@ -3921,50 +3133,9 @@ return true; } -static int send_fd(int sock, int fd) -{ - int ret = lxc_abstract_unix_send_fd(sock, fd, NULL, 0); - - - if (ret < 0) { - SYSERROR("Error sending tty fd to parent"); - return -1; - } - - return 0; -} - -static int send_ttys_to_parent(struct lxc_handler *handler) -{ - struct lxc_conf *conf = handler->conf; - const struct lxc_tty_info *tty_info = &conf->tty_info; - int i; - int sock = handler->ttysock[0]; - - for (i = 0; i < tty_info->nbtty; i++) { - struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; - if (send_fd(sock, pty_info->slave) < 0) - goto bad; - close(pty_info->slave); - pty_info->slave = -1; - if (send_fd(sock, pty_info->master) < 0) - goto bad; - close(pty_info->master); - pty_info->master = -1; - } - - close(handler->ttysock[0]); - close(handler->ttysock[1]); - - return 0; - -bad: - ERROR("Error writing tty fd to parent"); - return -1; -} - int lxc_setup(struct lxc_handler *handler) { + int ret; const char *name = handler->name; struct lxc_conf *lxc_conf = handler->conf; const char *lxcpath = handler->lxcpath; @@ -3981,11 +3152,16 @@ } } - if (setup_network(&lxc_conf->network)) { + if (lxc_setup_network_in_child_namespaces(lxc_conf, &lxc_conf->network)) { ERROR("failed to setup the network for '%s'", name); return -1; } + if (lxc_network_send_name_and_ifindex_to_parent(handler) < 0) { + ERROR("Failed to network device names and ifindices to parent"); + return -1; + } + if (lxc_conf->autodev > 0) { if (mount_autodev(name, &lxc_conf->rootfs, lxcpath)) { ERROR("failed to mount /dev in the container"); @@ -4001,12 +3177,12 @@ return -1; } - if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) { + if (setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) { ERROR("failed to setup the mounts for '%s'", name); return -1; } - if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(&lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) { + if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) { ERROR("failed to setup the mount entries for '%s'", name); return -1; } @@ -4037,6 +3213,7 @@ ERROR("failed to run autodev hooks for container '%s'.", name); return -1; } + if (lxc_fill_autodev(&lxc_conf->rootfs)) { ERROR("failed to populate /dev in the container"); return -1; @@ -4048,11 +3225,6 @@ return -1; } - if (lxc_conf->kmsg) { - if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) // don't fail - ERROR("failed to setup kmsg for '%s'", name); - } - if (!lxc_conf->is_execute && setup_dev_symlinks(&lxc_conf->rootfs)) { ERROR("failed to setup /dev symlinks for '%s'", name); return -1; @@ -4074,25 +3246,9 @@ return -1; } - if (lxc_create_tty(name, lxc_conf)) { - ERROR("failed to create the ttys"); - return -1; - } - - if (send_ttys_to_parent(handler) < 0) { - ERROR("failure sending console info to parent"); - return -1; - } - - - if (!lxc_conf->is_execute && setup_tty(lxc_conf)) { - ERROR("failed to setup the ttys for '%s'", name); + ret = lxc_create_ttys(handler); + if (ret < 0) return -1; - } - - if (lxc_conf->pty_names && setenv("container_ttys", lxc_conf->pty_names, 1)) - SYSERROR("failed to set environment variable for container ptys"); - if (setup_personality(lxc_conf->personality)) { ERROR("failed to setup personality"); @@ -4113,7 +3269,7 @@ return -1; } - NOTICE("'%s' is setup.", name); + NOTICE("Container \"%s\" is set up", name); return 0; } @@ -4154,101 +3310,9 @@ return 0; } -static void lxc_remove_nic(struct lxc_list *it) -{ - struct lxc_netdev *netdev = it->elem; - struct lxc_list *it2,*next; - - lxc_list_del(it); - - free(netdev->link); - free(netdev->name); - if (netdev->type == LXC_NET_VETH) - free(netdev->priv.veth_attr.pair); - free(netdev->upscript); - free(netdev->hwaddr); - free(netdev->mtu); - free(netdev->ipv4_gateway); - free(netdev->ipv6_gateway); - lxc_list_for_each_safe(it2, &netdev->ipv4, next) { - lxc_list_del(it2); - free(it2->elem); - free(it2); - } - lxc_list_for_each_safe(it2, &netdev->ipv6, next) { - lxc_list_del(it2); - free(it2->elem); - free(it2); - } - free(netdev); - free(it); -} - -/* we get passed in something like '0', '0.ipv4' or '1.ipv6' */ -int lxc_clear_nic(struct lxc_conf *c, const char *key) -{ - char *p1; - int ret, idx, i; - struct lxc_list *it; - struct lxc_netdev *netdev; - - p1 = strchr(key, '.'); - if (!p1 || *(p1+1) == '\0') - p1 = NULL; - - ret = sscanf(key, "%d", &idx); - if (ret != 1) return -1; - if (idx < 0) - return -1; - - i = 0; - lxc_list_for_each(it, &c->network) { - if (i == idx) - break; - i++; - } - if (i < idx) // we don't have that many nics defined - return -1; - - if (!it || !it->elem) - return -1; - - netdev = it->elem; - - if (!p1) { - lxc_remove_nic(it); - } else if (strcmp(p1, ".ipv4") == 0) { - struct lxc_list *it2,*next; - lxc_list_for_each_safe(it2, &netdev->ipv4, next) { - lxc_list_del(it2); - free(it2->elem); - free(it2); - } - } else if (strcmp(p1, ".ipv6") == 0) { - struct lxc_list *it2,*next; - lxc_list_for_each_safe(it2, &netdev->ipv6, next) { - lxc_list_del(it2); - free(it2->elem); - free(it2); - } - } - else return -1; - - return 0; -} - -int lxc_clear_config_network(struct lxc_conf *c) -{ - struct lxc_list *it,*next; - lxc_list_for_each_safe(it, &c->network, next) { - lxc_remove_nic(it); - } - return 0; -} - int lxc_clear_config_caps(struct lxc_conf *c) { - struct lxc_list *it,*next; + struct lxc_list *it, *next; lxc_list_for_each_safe(it, &c->caps, next) { lxc_list_del(it); @@ -4312,6 +3376,34 @@ return 0; } +int lxc_clear_limits(struct lxc_conf *c, const char *key) +{ + struct lxc_list *it, *next; + bool all = false; + const char *k = NULL; + + if (strcmp(key, "lxc.limit") == 0 + || strcmp(key, "lxc.prlimit")) + all = true; + else if (strncmp(key, "lxc.limit.", sizeof("lxc.limit.")-1) == 0) + k = key + sizeof("lxc.limit.")-1; + else if (strncmp(key, "lxc.prlimit.", sizeof("lxc.prlimit.")-1) == 0) + k = key + sizeof("lxc.prlimit.")-1; + else + return -1; + + lxc_list_for_each_safe(it, &c->limits, next) { + struct lxc_limit *lim = it->elem; + if (!all && strcmp(lim->resource, k) != 0) + continue; + lxc_list_del(it); + free(lim->resource); + free(lim); + free(it); + } + return 0; +} + int lxc_clear_groups(struct lxc_conf *c) { struct lxc_list *it,*next; @@ -4336,7 +3428,6 @@ return 0; } - int lxc_clear_mount_entries(struct lxc_conf *c) { struct lxc_list *it,*next; @@ -4387,17 +3478,6 @@ return 0; } -static void lxc_clear_saved_nics(struct lxc_conf *conf) -{ - int i; - - if (!conf->saved_nics) - return; - for (i=0; i < conf->num_savednics; i++) - free(conf->saved_nics[i].orig_name); - free(conf->saved_nics); -} - static inline void lxc_clear_aliens(struct lxc_conf *conf) { struct lxc_list *it,*next; @@ -4409,7 +3489,7 @@ } } -static inline void lxc_clear_includes(struct lxc_conf *conf) +void lxc_clear_includes(struct lxc_conf *conf) { struct lxc_list *it,*next; @@ -4442,7 +3522,8 @@ free(conf->init_cmd); free(conf->unexpanded_config); free(conf->pty_names); - lxc_clear_config_network(conf); + free(conf->syslog); + lxc_free_networks(&conf->network); free(conf->lsm_aa_profile); free(conf->lsm_se_context); lxc_seccomp_free(conf); @@ -4451,17 +3532,20 @@ lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf, "lxc.hook"); lxc_clear_mount_entries(conf); - lxc_clear_saved_nics(conf); lxc_clear_idmaps(conf); lxc_clear_groups(conf); lxc_clear_includes(conf); lxc_clear_aliens(conf); lxc_clear_environment(conf); + lxc_clear_limits(conf, "lxc.prlimit"); + free(conf->cgroup_meta.dir); + free(conf->cgroup_meta.controllers); free(conf); } struct userns_fn_data { int (*fn)(void *); + const char *fn_name; void *arg; int p[2]; }; @@ -4470,108 +3554,112 @@ { struct userns_fn_data *d = data; char c; - // we're not sharing with the parent any more, if it was a thread + /* Close write end of the pipe. */ close(d->p[1]); + + /* Wait for parent to finish establishing a new mapping in the user + * namespace we are executing in. + */ if (read(d->p[0], &c, 1) != 1) return -1; + + /* Close read end of the pipe. */ close(d->p[0]); + + if (d->fn_name) + TRACE("calling function \"%s\"", d->fn_name); + /* Call function to run. */ return d->fn(d->arg); } -/* - * Add ID_TYPE_UID/ID_TYPE_GID entries to an existing lxc_conf, - * if they are not already there. - */ -static struct lxc_list *idmap_add_id(struct lxc_conf *conf, - uid_t uid, gid_t gid) +static struct id_map *mapped_hostid_entry(struct lxc_conf *conf, unsigned id, + enum idtype idtype) { - int hostuid_mapped = mapped_hostid(uid, conf, ID_TYPE_UID); - int hostgid_mapped = mapped_hostid(gid, conf, ID_TYPE_GID); - struct lxc_list *new = NULL, *tmp, *it, *next; - struct id_map *entry; + struct lxc_list *it; + struct id_map *map; + struct id_map *retmap = NULL; - new = malloc(sizeof(*new)); - if (!new) { - ERROR("Out of memory building id map"); - return NULL; - } - lxc_list_init(new); + lxc_list_for_each(it, &conf->id_map) { + map = it->elem; + if (map->idtype != idtype) + continue; - if (hostuid_mapped < 0) { - hostuid_mapped = find_unmapped_nsuid(conf, ID_TYPE_UID); - if (hostuid_mapped < 0) - goto err; - tmp = malloc(sizeof(*tmp)); - if (!tmp) - goto err; - entry = malloc(sizeof(*entry)); - if (!entry) { - free(tmp); - goto err; - } - tmp->elem = entry; - entry->idtype = ID_TYPE_UID; - entry->nsid = hostuid_mapped; - entry->hostid = (unsigned long) uid; - entry->range = 1; - lxc_list_add_tail(new, tmp); - } - if (hostgid_mapped < 0) { - hostgid_mapped = find_unmapped_nsuid(conf, ID_TYPE_GID); - if (hostgid_mapped < 0) - goto err; - tmp = malloc(sizeof(*tmp)); - if (!tmp) - goto err; - entry = malloc(sizeof(*entry)); - if (!entry) { - free(tmp); - goto err; - } - tmp->elem = entry; - entry->idtype = ID_TYPE_GID; - entry->nsid = hostgid_mapped; - entry->hostid = (unsigned long) gid; - entry->range = 1; - lxc_list_add_tail(new, tmp); - } - lxc_list_for_each_safe(it, &conf->id_map, next) { - tmp = malloc(sizeof(*tmp)); - if (!tmp) - goto err; - entry = malloc(sizeof(*entry)); - if (!entry) { - free(tmp); - goto err; - } - memset(entry, 0, sizeof(*entry)); - memcpy(entry, it->elem, sizeof(*entry)); - tmp->elem = entry; - lxc_list_add_tail(new, tmp); + if (id >= map->hostid && id < map->hostid + map->range) { + retmap = map; + break; + } } - return new; + if (!retmap) + return NULL; -err: - ERROR("Out of memory building a new uid/gid map"); - if (new) - lxc_free_idmap(new); - free(new); - return NULL; + retmap = malloc(sizeof(*retmap)); + if (!retmap) + return NULL; + + memcpy(retmap, map, sizeof(*retmap)); + return retmap; } /* - * Run a function in a new user namespace. - * The caller's euid/egid will be mapped in if it is not already. + * Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already + * existing one or establish a new one. + */ +static struct id_map *idmap_add(struct lxc_conf *conf, uid_t id, enum idtype type) +{ + int hostid_mapped; + struct id_map *entry = NULL; + + /* Reuse existing mapping. */ + entry = mapped_hostid_entry(conf, id, type); + if (entry) + return entry; + + /* Find new mapping. */ + hostid_mapped = find_unmapped_nsid(conf, type); + if (hostid_mapped < 0) { + DEBUG("failed to find free mapping for id %d", id); + return NULL; + } + + entry = malloc(sizeof(*entry)); + if (!entry) + return NULL; + + entry->idtype = type; + entry->nsid = hostid_mapped; + entry->hostid = (unsigned long)id; + entry->range = 1; + + return entry; +} + +/* Run a function in a new user namespace. + * The caller's euid/egid will be mapped if it is not already. + * Afaict, userns_exec_1() is only used to operate based on privileges for the + * user's own {g,u}id on the host and for the container root's unmapped {g,u}id. + * This means we require only to establish a mapping from: + * - the container root {g,u}id as seen from the host > user's host {g,u}id + * - the container root -> some sub{g,u}id + * The former we add, if the user did not specifiy a mapping. The latter we + * retrieve from the ontainer's configured {g,u}id mappings as it must have been + * there to start the container in the first place. */ -int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data) +int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, + const char *fn_name) { - int ret, pid; + pid_t pid; + uid_t euid, egid; struct userns_fn_data d; - char c = '1'; int p[2]; - struct lxc_list *idmap; + struct lxc_list *it; + struct id_map *map; + char c = '1'; + int ret = -1; + struct lxc_list *idmap = NULL, *tmplist = NULL; + struct id_map *container_root_uid = NULL, *container_root_gid = NULL, + *host_uid_map = NULL, *host_gid_map = NULL; ret = pipe(p); if (ret < 0) { @@ -4579,44 +3667,186 @@ return -1; } d.fn = fn; + d.fn_name = fn_name; d.arg = data; d.p[0] = p[0]; d.p[1] = p[1]; + + /* Clone child in new user namespace. */ pid = lxc_clone(run_userns_fn, &d, CLONE_NEWUSER); - if (pid < 0) - goto err; + if (pid < 0) { + ERROR("failed to clone child process in new user namespace"); + goto on_error; + } + close(p[0]); p[0] = -1; - if ((idmap = idmap_add_id(conf, geteuid(), getegid())) == NULL) { - ERROR("Error adding self to container uid/gid map"); - goto err; + euid = geteuid(); + egid = getegid(); + + /* Find container root. */ + lxc_list_for_each(it, &conf->id_map) { + map = it->elem; + + if (map->nsid != 0) + continue; + + if (map->idtype == ID_TYPE_UID && container_root_uid == NULL) { + container_root_uid = malloc(sizeof(*container_root_uid)); + if (!container_root_uid) + goto on_error; + container_root_uid->idtype = map->idtype; + container_root_uid->hostid = map->hostid; + container_root_uid->nsid = 0; + container_root_uid->range = map->range; + + /* Check if container root mapping contains a mapping + * for user's uid. + */ + if (euid >= map->hostid && euid < map->hostid + map->range) + host_uid_map = container_root_uid; + } else if (map->idtype == ID_TYPE_GID && container_root_gid == NULL) { + container_root_gid = malloc(sizeof(*container_root_gid)); + if (!container_root_gid) + goto on_error; + container_root_gid->idtype = map->idtype; + container_root_gid->hostid = map->hostid; + container_root_gid->nsid = 0; + container_root_gid->range = map->range; + + /* Check if container root mapping contains a mapping + * for user's gid. + */ + if (egid >= map->hostid && egid < map->hostid + map->range) + host_gid_map = container_root_gid; + } + + /* Found container root. */ + if (container_root_uid && container_root_gid) + break; + } + + /* This is actually checked earlier but it can't hurt. */ + if (!container_root_uid || !container_root_gid) { + ERROR("no mapping for container root found"); + goto on_error; + } + + /* Check whether the {g,u}id of the user has a mapping. */ + if (!host_uid_map) + host_uid_map = idmap_add(conf, euid, ID_TYPE_UID); + + if (!host_gid_map) + host_gid_map = idmap_add(conf, egid, ID_TYPE_GID); + + if (!host_uid_map) { + DEBUG("failed to find mapping for uid %d", euid); + goto on_error; + } + + if (!host_gid_map) { + DEBUG("failed to find mapping for gid %d", egid); + goto on_error; + } + + /* Allocate new {g,u}id map list. */ + idmap = malloc(sizeof(*idmap)); + if (!idmap) + goto on_error; + lxc_list_init(idmap); + + /* Add container root to the map. */ + tmplist = malloc(sizeof(*tmplist)); + if (!tmplist) + goto on_error; + lxc_list_add_elem(tmplist, container_root_uid); + lxc_list_add_tail(idmap, tmplist); + + if (host_uid_map && (host_uid_map != container_root_uid)) { + /* idmap will now keep track of that memory. */ + container_root_uid = NULL; + + /* Add container root to the map. */ + tmplist = malloc(sizeof(*tmplist)); + if (!tmplist) + goto on_error; + lxc_list_add_elem(tmplist, host_uid_map); + lxc_list_add_tail(idmap, tmplist); + } + /* idmap will now keep track of that memory. */ + container_root_uid = NULL; + /* idmap will now keep track of that memory. */ + host_uid_map = NULL; + + tmplist = malloc(sizeof(*tmplist)); + if (!tmplist) + goto on_error; + lxc_list_add_elem(tmplist, container_root_gid); + lxc_list_add_tail(idmap, tmplist); + + if (host_gid_map && (host_gid_map != container_root_gid)) { + /* idmap will now keep track of that memory. */ + container_root_gid = NULL; + + tmplist = malloc(sizeof(*tmplist)); + if (!tmplist) + goto on_error; + lxc_list_add_elem(tmplist, host_gid_map); + lxc_list_add_tail(idmap, tmplist); + } + /* idmap will now keep track of that memory. */ + container_root_gid = NULL; + /* idmap will now keep track of that memory. */ + host_gid_map = NULL; + + if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE || + conf->loglevel == LXC_LOG_LEVEL_TRACE) { + lxc_list_for_each(it, idmap) { + map = it->elem; + TRACE("establishing %cid mapping for \"%d\" in new " + "user namespace: nsuid %lu - hostid %lu - range " + "%lu", + (map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, + map->nsid, map->hostid, map->range); + } } + /* Set up {g,u}id mapping for user namespace of child process. */ ret = lxc_map_ids(idmap, pid); - lxc_free_idmap(idmap); - free(idmap); - if (ret) { - ERROR("Error setting up child mappings"); - goto err; + if (ret < 0) { + ERROR("error setting up {g,u}id mappings for child process " + "\"%d\"", + pid); + goto on_error; } - // kick the child + /* Tell child to proceed. */ if (write(p[1], &c, 1) != 1) { - SYSERROR("writing to pipe to child"); - goto err; + SYSERROR("failed telling child process \"%d\" to proceed", pid); + goto on_error; } + /* Wait for child to finish. */ ret = wait_for_pid(pid); - close(p[1]); - return ret; +on_error: + if (idmap) + lxc_free_idmap(idmap); + if (container_root_uid) + free(container_root_uid); + if (container_root_gid) + free(container_root_gid); + if (host_uid_map && (host_uid_map != container_root_uid)) + free(host_uid_map); + if (host_gid_map && (host_gid_map != container_root_gid)) + free(host_gid_map); -err: if (p[0] != -1) close(p[0]); close(p[1]); - return -1; + + return ret; } /* not thread-safe, do not use from api without first forking */ @@ -4742,8 +3972,8 @@ ERROR("To pass uid mappings to lxc-create, you could create"); ERROR("~/.config/lxc/default.conf:"); ERROR("lxc.include = %s", LXC_DEFAULT_CONFIG); - ERROR("lxc.id_map = u 0 %u %u", uid, urange); - ERROR("lxc.id_map = g 0 %u %u", gid, grange); + ERROR("lxc.idmap = u 0 %u %u", uid, urange); + ERROR("lxc.idmap = g 0 %u %u", gid, grange); free(gname); free(uname); diff -Nru lxc-2.0.8/src/lxc/conf.h lxc-2.1.0/src/lxc/conf.h --- lxc-2.0.8/src/lxc/conf.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/conf.h 2017-09-06 02:32:37.000000000 +0000 @@ -26,10 +26,13 @@ #include "config.h" #include -#include #include +#include #include #include +#if HAVE_SYS_RESOURCE_H +#include +#endif #include #include "list.h" @@ -43,110 +46,47 @@ #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; - unsigned int prefix; -}; - -struct lxc_route { - struct in_addr addr; -}; - -/* - * Defines the structure to configure an ipv6 address - * @flags : set the address up - * @address : ipv6 address - * @broadcast : ipv6 broadcast address - * @mask : network mask - */ -struct lxc_inet6dev { - struct in6_addr addr; - struct in6_addr mcast; - struct in6_addr acast; - unsigned int prefix; -}; - -struct lxc_route6 { - struct in6_addr addr; -}; - -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, passthru */ -}; - -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. + * 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 + * + * @controllers : The controllers to use for this container. + * @dir : The name of the directory containing the container's cgroup. + * Not that this is a per-container setting. */ struct lxc_cgroup { - char *subsystem; - char *value; + union { + /* information about a specific controller */ + struct /* controller */ { + char *subsystem; + char *value; + }; + + /* meta information about cgroup configuration */ + struct /* meta */ { + char *controllers; + char *dir; + }; + }; +}; + +#if !HAVE_SYS_RESOURCE_H +# define RLIM_INFINITY ((unsigned long)-1) +struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; +#endif +/* + * Defines a structure to configure resource limits to set via setrlimit(). + * @resource : the resource name in lowercase without the RLIMIT_ prefix + * @limit : the limit to set + */ +struct lxc_limit { + char *resource; + struct rlimit limit; }; enum idtype { @@ -156,10 +96,10 @@ /* * 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 + * lxc.idmap = u 0 9800 100 + * lxc.idmap = u 1000 9900 100 + * lxc.idmap = g 0 9800 100 + * lxc.idmap = 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. @@ -263,7 +203,6 @@ /* * 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 @@ -280,36 +219,38 @@ * @lsm_se_context : selinux type to switch to or NULL */ enum lxchooks { - LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, - LXCHOOK_START, LXCHOOK_STOP, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, LXCHOOK_DESTROY, - NUM_LXC_HOOKS}; -extern char *lxchook_names[NUM_LXC_HOOKS]; - -struct saved_nic { - int ifindex; - char *orig_name; + LXCHOOK_PRESTART, + LXCHOOK_PREMOUNT, + LXCHOOK_MOUNT, + LXCHOOK_AUTODEV, + LXCHOOK_START, + LXCHOOK_STOP, + LXCHOOK_POSTSTOP, + LXCHOOK_CLONE, + LXCHOOK_DESTROY, + NUM_LXC_HOOKS }; +extern char *lxchook_names[NUM_LXC_HOOKS]; + struct lxc_conf { int is_execute; char *fstab; unsigned int tty; unsigned int pts; int reboot; - int need_utmp_watch; signed long personality; struct utsname *utsname; struct lxc_list cgroup; struct 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; - char *pty_names; // comma-separated list of lxc.tty pty names + /* Comma-separated list of lxc.tty.max pty names. */ + char *pty_names; struct lxc_console console; struct lxc_rootfs rootfs; char *ttydir; @@ -320,25 +261,24 @@ unsigned int lsm_aa_allow_incomplete; char *lsm_se_context; int tmp_umount_proc; - char *seccomp; // filename with the seccomp rules + char *seccomp; /* filename with the seccomp rules */ #if HAVE_SCMP_FILTER_CTX scmp_filter_ctx seccomp_ctx; #endif int maincmd_fd; - unsigned int autodev; // if 1, mount and fill a /dev at start - int haltsignal; // signal used to halt container - int rebootsignal; // signal used to reboot container - int stopsignal; // signal used to hard stop container - unsigned int kmsg; // if 1, create /dev/kmsg symlink - char *rcfile; // Copy of the top level rcfile we read - - // Logfile and logleve can be set in a container config file. - // Those function as defaults. The defaults can be overriden - // by command line. However we don't want the command line - // specified values to be saved on c->save_config(). So we - // store the config file specified values here. - char *logfile; // the logfile as specifed in config - int loglevel; // loglevel as specifed in config (if any) + unsigned int autodev; /* if 1, mount and fill a /dev at start */ + int haltsignal; /* signal used to halt container */ + int rebootsignal; /* signal used to reboot container */ + int stopsignal; /* signal used to hard stop container */ + char *rcfile; /* Copy of the top level rcfile we read */ + + /* Logfile and logleve can be set in a container config file. Those + * function as defaults. The defaults can be overriden by command line. + * However we don't want the command line specified values to be saved + * on c->save_config(). So we store the config file specified values + * here. */ + char *logfile; /* the logfile as specifed in config */ + int loglevel; /* loglevel as specifed in config (if any) */ int logfd; int inherit_ns_fd[LXC_NS_MAX]; @@ -378,6 +318,29 @@ /* indicator if the container will be destroyed on shutdown */ unsigned int ephemeral; + + /* The facility to pass to syslog. Let's users establish as what type of + * program liblxc is supposed to write to the syslog. */ + char *syslog; + + /* Whether PR_SET_NO_NEW_PRIVS will be set for the container. */ + bool no_new_privs; + + /* RLIMIT_* limits */ + struct lxc_list limits; + + /* REMOVE IN LXC 3.0 + * Indicator whether the current config file we're using contained any + * legacy configuration keys. + */ + bool contains_legacy_key; + + /* Contains generic info about the cgroup configuration for this + * container. Note that struct lxc_cgroup contains a union. It is only + * valid to access the members of the anonymous "meta" struct within + * that union. + */ + struct lxc_cgroup cgroup_meta; }; #ifdef HAVE_TLS @@ -386,32 +349,15 @@ extern struct lxc_conf *current_config; #endif -int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, - const char *lxcpath, char *argv[]); - +extern int 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 bool lxc_delete_network(struct lxc_handler *handler); -extern int lxc_assign_network(const char *lxcpath, char *lxcname, - 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); @@ -421,30 +367,30 @@ extern int lxc_clear_idmaps(struct lxc_conf *c); extern int lxc_clear_groups(struct lxc_conf *c); extern int lxc_clear_environment(struct lxc_conf *c); +extern int lxc_clear_limits(struct lxc_conf *c, const char *key); extern int lxc_delete_autodev(struct lxc_handler *handler); - +extern void lxc_clear_includes(struct lxc_conf *conf); extern int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath); - -/* - * Configure the container from inside - */ - -struct cgroup_process_info; extern int lxc_setup(struct lxc_handler *handler); - -extern 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 setup_resource_limits(struct lxc_list *limits, pid_t pid); +extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype); +extern int mapped_hostid(unsigned id, struct lxc_conf *conf, + enum idtype idtype); extern int chown_mapped_root(char *path, struct lxc_conf *conf); -extern int ttys_shift_ids(struct lxc_conf *c); -extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data); +extern int lxc_ttys_shift_ids(struct lxc_conf *c); +extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, + const char *fn_name); extern int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata); extern void tmp_proc_unmount(struct lxc_conf *lxc_conf); -void remount_all_slave(void); +extern void remount_all_slave(void); extern void suggest_default_idmap(void); -FILE *make_anonymous_mount_file(struct lxc_list *mount); -struct lxc_list *sort_cgroup_settings(struct lxc_list* cgroup_settings); -#endif +extern FILE *make_anonymous_mount_file(struct lxc_list *mount); +extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings); +extern unsigned long add_required_remount_flags(const char *s, const char *d, + unsigned long flags); +extern int run_script(const char *name, const char *section, const char *script, + ...); + +#endif /* __LXC_CONF_H */ diff -Nru lxc-2.0.8/src/lxc/confile.c lxc-2.1.0/src/lxc/confile.c --- lxc-2.0.8/src/lxc/confile.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile.c 2017-09-06 02:32:37.000000000 +0000 @@ -1,10 +1,11 @@ /* * lxc: linux Container library - * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano + * Serge Hallyn + * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,34 +21,46 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + #define _GNU_SOURCE +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include #include -#include -#include -#include -#include -#include -#include "bdev.h" -#include "parse.h" +#include "conf.h" #include "config.h" #include "confile.h" -#include "utils.h" +#include "confile_legacy.h" +#include "confile_utils.h" #include "log.h" -#include "conf.h" -#include "network.h" #include "lxcseccomp.h" +#include "network.h" +#include "parse.h" +#include "storage.h" +#include "utils.h" + +#if HAVE_IFADDRS_H +#include +#else +#include <../include/ifaddrs.h> +#endif #if HAVE_SYS_PERSONALITY_H #include @@ -55,135 +68,191 @@ lxc_log_define(lxc_confile, lxc); -static int config_personality(const char *, const char *, struct lxc_conf *); -static int config_pts(const char *, const char *, struct lxc_conf *); -static int config_tty(const char *, const char *, struct lxc_conf *); -static int config_ttydir(const char *, const char *, struct lxc_conf *); -static int config_kmsg(const char *, const char *, struct lxc_conf *); -static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *); -static int config_lsm_aa_incomplete(const char *, const char *, struct lxc_conf *); -static int config_lsm_se_context(const char *, const char *, struct lxc_conf *); -static int config_cgroup(const char *, const char *, struct lxc_conf *); -static int config_idmap(const char *, const char *, struct lxc_conf *); -static int config_loglevel(const char *, const char *, struct lxc_conf *); -static int config_logfile(const char *, const char *, struct lxc_conf *); -static int config_mount(const char *, const char *, struct lxc_conf *); -static int config_mount_auto(const char *, const char *, struct lxc_conf *); -static int config_fstab(const char *, const char *, struct lxc_conf *); -static int config_rootfs(const char *, const char *, struct lxc_conf *); -static int config_rootfs_mount(const char *, const char *, struct lxc_conf *); -static int config_rootfs_options(const char *, const char *, struct lxc_conf *); -static int config_rootfs_backend(const char *, const char *, struct lxc_conf *); -static int config_pivotdir(const char *, const char *, struct lxc_conf *); -static int config_utsname(const char *, const char *, struct lxc_conf *); -static int config_hook(const char *, const char *, struct lxc_conf *lxc_conf); -static int config_network(const char *, const char *, struct lxc_conf *); -static int config_network_type(const char *, const char *, struct lxc_conf *); -static int config_network_flags(const char *, const char *, struct lxc_conf *); -static int config_network_link(const char *, const char *, struct lxc_conf *); -static int config_network_name(const char *, const char *, struct lxc_conf *); -static int config_network_veth_pair(const char *, const char *, struct lxc_conf *); -static int config_network_macvlan_mode(const char *, const char *, struct lxc_conf *); -static int config_network_hwaddr(const char *, const char *, struct lxc_conf *); -static int config_network_vlan_id(const char *, const char *, struct lxc_conf *); -static int config_network_mtu(const char *, const char *, struct lxc_conf *); -static int config_network_ipv4(const char *, const char *, struct lxc_conf *); -static int config_network_ipv4_gateway(const char *, const char *, struct lxc_conf *); -static int config_network_script_up(const char *, const char *, struct lxc_conf *); -static int config_network_script_down(const char *, const char *, struct lxc_conf *); -static int config_network_ipv6(const char *, const char *, struct lxc_conf *); -static int config_network_ipv6_gateway(const char *, const char *, struct lxc_conf *); -static int config_cap_drop(const char *, const char *, struct lxc_conf *); -static int config_cap_keep(const char *, const char *, struct lxc_conf *); -static int config_console(const char *, const char *, struct lxc_conf *); -static int config_console_logfile(const char *, const char *, struct lxc_conf *); -static int config_seccomp(const char *, const char *, struct lxc_conf *); -static int config_includefile(const char *, const char *, struct lxc_conf *); -static int config_network_nic(const char *, const char *, struct lxc_conf *); -static int config_autodev(const char *, const char *, struct lxc_conf *); -static int config_haltsignal(const char *, const char *, struct lxc_conf *); -static int config_rebootsignal(const char *, const char *, struct lxc_conf *); -static int config_stopsignal(const char *, const char *, struct lxc_conf *); -static int config_start(const char *, const char *, struct lxc_conf *); -static int config_monitor(const char *, const char *, struct lxc_conf *); -static int config_group(const char *, const char *, struct lxc_conf *); -static int config_environment(const char *, const char *, struct lxc_conf *); -static int config_init_cmd(const char *, const char *, struct lxc_conf *); -static int config_init_uid(const char *, const char *, struct lxc_conf *); -static int config_init_gid(const char *, const char *, struct lxc_conf *); -static int config_ephemeral(const char *, const char *, struct lxc_conf *); +#define lxc_config_define(name) \ + static int set_config_##name(const char *, const char *, \ + struct lxc_conf *, void *); \ + static int get_config_##name(const char *, char *, int, \ + struct lxc_conf *, void *); \ + static int clr_config_##name(const char *, struct lxc_conf *, void *); + +lxc_config_define(personality); +lxc_config_define(pty_max); +lxc_config_define(tty_max); +lxc_config_define(tty_dir); +lxc_config_define(apparmor_profile); +lxc_config_define(apparmor_allow_incomplete); +lxc_config_define(selinux_context); +lxc_config_define(cgroup_controller); +lxc_config_define(cgroup_dir); +lxc_config_define(idmaps); +lxc_config_define(log_level); +lxc_config_define(log_file); +lxc_config_define(mount); +lxc_config_define(mount_auto); +lxc_config_define(mount_fstab); +lxc_config_define(rootfs_mount); +lxc_config_define(rootfs_options); +lxc_config_define(rootfs_backend); +lxc_config_define(rootfs_path); +lxc_config_define(uts_name); +lxc_config_define(hooks); +lxc_config_define(net_type); +lxc_config_define(net_flags); +lxc_config_define(net_link); +lxc_config_define(net_name); +lxc_config_define(net_veth_pair); +lxc_config_define(net_macvlan_mode); +lxc_config_define(net_hwaddr); +lxc_config_define(net_vlan_id); +lxc_config_define(net_mtu); +lxc_config_define(net_ipv4_address); +lxc_config_define(net_ipv4_gateway); +lxc_config_define(net_script_up); +lxc_config_define(net_script_down); +lxc_config_define(net_ipv6_address); +lxc_config_define(net_ipv6_gateway); +lxc_config_define(net_nic); +lxc_config_define(net); +lxc_config_define(cap_drop); +lxc_config_define(cap_keep); +lxc_config_define(console_logfile); +lxc_config_define(console_path); +lxc_config_define(seccomp_profile); +lxc_config_define(includefiles); +lxc_config_define(autodev); +lxc_config_define(signal_halt); +lxc_config_define(signal_reboot); +lxc_config_define(signal_stop); +lxc_config_define(start); +lxc_config_define(monitor); +lxc_config_define(group); +lxc_config_define(environment); +lxc_config_define(init_cmd); +lxc_config_define(init_uid); +lxc_config_define(init_gid); +lxc_config_define(ephemeral); +lxc_config_define(log_syslog); +lxc_config_define(no_new_privs); +lxc_config_define(prlimit); static struct lxc_config_t config[] = { - - { "lxc.arch", config_personality }, - { "lxc.pts", config_pts }, - { "lxc.tty", config_tty }, - { "lxc.devttydir", config_ttydir }, - { "lxc.kmsg", config_kmsg }, - { "lxc.aa_profile", config_lsm_aa_profile }, - { "lxc.aa_allow_incomplete", config_lsm_aa_incomplete }, - { "lxc.se_context", config_lsm_se_context }, - { "lxc.cgroup", config_cgroup }, - { "lxc.id_map", config_idmap }, - { "lxc.loglevel", config_loglevel }, - { "lxc.logfile", config_logfile }, - { "lxc.mount.entry", config_mount }, - { "lxc.mount.auto", config_mount_auto }, - { "lxc.mount", config_fstab }, - { "lxc.rootfs.mount", config_rootfs_mount }, - { "lxc.rootfs.options", config_rootfs_options }, - { "lxc.rootfs.backend", config_rootfs_backend }, - { "lxc.rootfs", config_rootfs }, - { "lxc.pivotdir", config_pivotdir }, - { "lxc.utsname", config_utsname }, - { "lxc.hook.pre-start", config_hook }, - { "lxc.hook.pre-mount", config_hook }, - { "lxc.hook.mount", config_hook }, - { "lxc.hook.autodev", config_hook }, - { "lxc.hook.start", config_hook }, - { "lxc.hook.stop", config_hook }, - { "lxc.hook.post-stop", config_hook }, - { "lxc.hook.clone", config_hook }, - { "lxc.hook.destroy", config_hook }, - { "lxc.hook", config_hook }, - { "lxc.network.type", config_network_type }, - { "lxc.network.flags", config_network_flags }, - { "lxc.network.link", config_network_link }, - { "lxc.network.name", config_network_name }, - { "lxc.network.macvlan.mode", config_network_macvlan_mode }, - { "lxc.network.veth.pair", config_network_veth_pair }, - { "lxc.network.script.up", config_network_script_up }, - { "lxc.network.script.down", config_network_script_down }, - { "lxc.network.hwaddr", config_network_hwaddr }, - { "lxc.network.mtu", config_network_mtu }, - { "lxc.network.vlan.id", config_network_vlan_id }, - { "lxc.network.ipv4.gateway", config_network_ipv4_gateway }, - { "lxc.network.ipv4", config_network_ipv4 }, - { "lxc.network.ipv6.gateway", config_network_ipv6_gateway }, - { "lxc.network.ipv6", config_network_ipv6 }, - /* config_network_nic must come after all other 'lxc.network.*' entries */ - { "lxc.network.", config_network_nic }, - { "lxc.network", config_network }, - { "lxc.cap.drop", config_cap_drop }, - { "lxc.cap.keep", config_cap_keep }, - { "lxc.console.logfile", config_console_logfile }, - { "lxc.console", config_console }, - { "lxc.seccomp", config_seccomp }, - { "lxc.include", config_includefile }, - { "lxc.autodev", config_autodev }, - { "lxc.haltsignal", config_haltsignal }, - { "lxc.rebootsignal", config_rebootsignal }, - { "lxc.stopsignal", config_stopsignal }, - { "lxc.start.auto", config_start }, - { "lxc.start.delay", config_start }, - { "lxc.start.order", config_start }, - { "lxc.monitor.unshare", config_monitor }, - { "lxc.group", config_group }, - { "lxc.environment", config_environment }, - { "lxc.init_cmd", config_init_cmd }, - { "lxc.init_uid", config_init_uid }, - { "lxc.init_gid", config_init_gid }, - { "lxc.ephemeral", config_ephemeral }, + /* REMOVE in LXC 3.0 */ + { "lxc.arch", false, set_config_personality, get_config_personality, clr_config_personality, }, + { "lxc.apparmor.profile", false, set_config_apparmor_profile, get_config_apparmor_profile, clr_config_apparmor_profile, }, + { "lxc.apparmor.allow_incomplete", false, set_config_apparmor_allow_incomplete, get_config_apparmor_allow_incomplete, clr_config_apparmor_allow_incomplete, }, + { "lxc.autodev", false, set_config_autodev, get_config_autodev, clr_config_autodev, }, + { "lxc.cap.drop", false, set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, }, + { "lxc.cap.keep", false, set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, }, + { "lxc.cgroup.dir", false, set_config_cgroup_dir, get_config_cgroup_dir, clr_config_cgroup_dir, }, + { "lxc.cgroup", false, set_config_cgroup_controller, get_config_cgroup_controller, clr_config_cgroup_controller, }, + { "lxc.console.logfile", false, set_config_console_logfile, get_config_console_logfile, clr_config_console_logfile, }, + { "lxc.console.path", false, set_config_console_path, get_config_console_path, clr_config_console_path, }, + { "lxc.environment", false, set_config_environment, get_config_environment, clr_config_environment, }, + { "lxc.ephemeral", false, set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, }, + { "lxc.group", false, set_config_group, get_config_group, clr_config_group, }, + { "lxc.hook.autodev", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.clone", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.destroy", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.post-stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.pre-start", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.pre-mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.start", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook.stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.hook", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, + { "lxc.idmap", false, set_config_idmaps, get_config_idmaps, clr_config_idmaps, }, + { "lxc.include", false, set_config_includefiles, get_config_includefiles, clr_config_includefiles, }, + { "lxc.init.cmd", false, set_config_init_cmd, get_config_init_cmd, clr_config_init_cmd, }, + { "lxc.init.gid", false, set_config_init_gid, get_config_init_gid, clr_config_init_gid, }, + { "lxc.init.uid", false, set_config_init_uid, get_config_init_uid, clr_config_init_uid, }, + { "lxc.log.file", false, set_config_log_file, get_config_log_file, clr_config_log_file, }, + { "lxc.log.level", false, set_config_log_level, get_config_log_level, clr_config_log_level, }, + { "lxc.log.syslog", false, set_config_log_syslog, get_config_log_syslog, clr_config_log_syslog, }, + { "lxc.monitor.unshare", false, set_config_monitor, get_config_monitor, clr_config_monitor, }, + { "lxc.mount.auto", false, set_config_mount_auto, get_config_mount_auto, clr_config_mount_auto, }, + { "lxc.mount.entry", false, set_config_mount, get_config_mount, clr_config_mount, }, + { "lxc.mount.fstab", false, set_config_mount_fstab, get_config_mount_fstab, clr_config_mount_fstab, }, + + /* [START]: REMOVE IN LXC 3.0 */ + { "lxc.network.type", true, set_config_network_legacy_type, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.flags", true, set_config_network_legacy_flags, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.link", true, set_config_network_legacy_link, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.name", true, set_config_network_legacy_name, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.macvlan.mode", true, set_config_network_legacy_macvlan_mode, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.veth.pair", true, set_config_network_legacy_veth_pair, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.script.up", true, set_config_network_legacy_script_up, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.script.down", true, set_config_network_legacy_script_down, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.hwaddr", true, set_config_network_legacy_hwaddr, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.mtu", true, set_config_network_legacy_mtu, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.vlan.id", true, set_config_network_legacy_vlan_id, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.ipv4.gateway", true, set_config_network_legacy_ipv4_gateway, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.ipv4", true, set_config_network_legacy_ipv4, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.ipv6.gateway", true, set_config_network_legacy_ipv6_gateway, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.ipv6", true, set_config_network_legacy_ipv6, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network.", true, set_config_network_legacy_nic, get_config_network_legacy_item, clr_config_network_legacy_item, }, + { "lxc.network", true, set_config_network_legacy, get_config_network_legacy, clr_config_network_legacy, }, + /* [END]: REMOVE IN LXC 3.0 */ + + { "lxc.net.flags", false, set_config_net_flags, get_config_net_flags, clr_config_net_flags, }, + { "lxc.net.hwaddr", false, set_config_net_hwaddr, get_config_net_hwaddr, clr_config_net_hwaddr, }, + { "lxc.net.ipv4.address", false, set_config_net_ipv4_address, get_config_net_ipv4_address, clr_config_net_ipv4_address, }, + { "lxc.net.ipv4.gateway", false, set_config_net_ipv4_gateway, get_config_net_ipv4_gateway, clr_config_net_ipv4_gateway, }, + { "lxc.net.ipv6.address", false, set_config_net_ipv6_address, get_config_net_ipv6_address, clr_config_net_ipv6_address, }, + { "lxc.net.ipv6.gateway", false, set_config_net_ipv6_gateway, get_config_net_ipv6_gateway, clr_config_net_ipv6_gateway, }, + { "lxc.net.link", false, set_config_net_link, get_config_net_link, clr_config_net_link, }, + { "lxc.net.macvlan.mode", false, set_config_net_macvlan_mode, get_config_net_macvlan_mode, clr_config_net_macvlan_mode, }, + { "lxc.net.mtu", false, set_config_net_mtu, get_config_net_mtu, clr_config_net_mtu, }, + { "lxc.net.name", false, set_config_net_name, get_config_net_name, clr_config_net_name, }, + { "lxc.net.script.down", false, set_config_net_script_down, get_config_net_script_down, clr_config_net_script_down, }, + { "lxc.net.script.up", false, set_config_net_script_up, get_config_net_script_up, clr_config_net_script_up, }, + { "lxc.net.type", false, set_config_net_type, get_config_net_type, clr_config_net_type, }, + { "lxc.net.vlan.id", false, set_config_net_vlan_id, get_config_net_vlan_id, clr_config_net_vlan_id, }, + { "lxc.net.veth.pair", false, set_config_net_veth_pair, get_config_net_veth_pair, clr_config_net_veth_pair, }, + { "lxc.net.", false, set_config_net_nic, get_config_net_nic, clr_config_net_nic, }, + { "lxc.net", false, set_config_net, get_config_net, clr_config_net, }, + { "lxc.no_new_privs", false, set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, }, + { "lxc.prlimit", false, set_config_prlimit, get_config_prlimit, clr_config_prlimit, }, + { "lxc.pty.max", false, set_config_pty_max, get_config_pty_max, clr_config_pty_max, }, + { "lxc.rootfs.mount", false, set_config_rootfs_mount, get_config_rootfs_mount, clr_config_rootfs_mount, }, + { "lxc.rootfs.options", false, set_config_rootfs_options, get_config_rootfs_options, clr_config_rootfs_options, }, + { "lxc.rootfs.path", false, set_config_rootfs_path, get_config_rootfs_path, clr_config_rootfs_path, }, + { "lxc.seccomp.profile", false, set_config_seccomp_profile, get_config_seccomp_profile, clr_config_seccomp_profile, }, + { "lxc.selinux.context", false, set_config_selinux_context, get_config_selinux_context, clr_config_selinux_context, }, + { "lxc.signal.halt", false, set_config_signal_halt, get_config_signal_halt, clr_config_signal_halt, }, + { "lxc.signal.reboot", false, set_config_signal_reboot, get_config_signal_reboot, clr_config_signal_reboot, }, + { "lxc.signal.stop", false, set_config_signal_stop, get_config_signal_stop, clr_config_signal_stop, }, + { "lxc.start.auto", false, set_config_start, get_config_start, clr_config_start, }, + { "lxc.start.delay", false, set_config_start, get_config_start, clr_config_start, }, + { "lxc.start.order", false, set_config_start, get_config_start, clr_config_start, }, + { "lxc.tty.dir", false, set_config_tty_dir, get_config_tty_dir, clr_config_tty_dir, }, + { "lxc.tty.max", false, set_config_tty_max, get_config_tty_max, clr_config_tty_max, }, + { "lxc.uts.name", false, set_config_uts_name, get_config_uts_name, clr_config_uts_name, }, + + /* [START]: REMOVE IN LXC 3.0 */ + { "lxc.pts", true, set_config_pty_max, get_config_pty_max, clr_config_pty_max, }, + { "lxc.devttydir", true, set_config_tty_dir, get_config_tty_dir, clr_config_tty_dir, }, + { "lxc.tty", true, set_config_tty_max, get_config_tty_max, clr_config_tty_max, }, + { "lxc.aa_profile", true, set_config_lsm_aa_profile, get_config_lsm_aa_profile, clr_config_lsm_aa_profile, }, + { "lxc.aa_allow_incomplete", true, set_config_lsm_aa_incomplete, get_config_lsm_aa_incomplete, clr_config_lsm_aa_incomplete, }, + { "lxc.se_context", true, set_config_lsm_se_context, get_config_lsm_se_context, clr_config_lsm_se_context, }, + { "lxc.id_map", true, set_config_idmaps, get_config_idmaps, clr_config_idmaps, }, + { "lxc.mount", true, set_config_mount_fstab, get_config_mount_fstab, clr_config_mount_fstab, }, + { "lxc.rootfs.backend", true, set_config_rootfs_backend, get_config_rootfs_backend, clr_config_rootfs_backend, }, + { "lxc.rootfs", true, set_config_rootfs_path, get_config_rootfs_path, clr_config_rootfs_path, }, + { "lxc.utsname", true, set_config_uts_name, get_config_uts_name, clr_config_uts_name, }, + { "lxc.seccomp", true, set_config_seccomp_profile, get_config_seccomp_profile, clr_config_seccomp_profile, }, + { "lxc.console", true, set_config_console_path, get_config_console_path, clr_config_console_path, }, + { "lxc.haltsignal", true, set_config_signal_halt, get_config_signal_halt, clr_config_signal_halt, }, + { "lxc.rebootsignal", true, set_config_signal_reboot, get_config_signal_reboot, clr_config_signal_reboot, }, + { "lxc.stopsignal", true, set_config_signal_stop, get_config_signal_stop, clr_config_signal_stop, }, + { "lxc.syslog", true, set_config_log_syslog, get_config_log_syslog, clr_config_log_syslog, }, + { "lxc.loglevel", true, set_config_log_level, get_config_log_level, clr_config_log_level, }, + { "lxc.logfile", true, set_config_log_file, get_config_log_file, clr_config_log_file, }, + { "lxc.init_cmd", true, set_config_init_cmd, get_config_init_cmd, clr_config_init_cmd, }, + { "lxc.init_uid", true, set_config_init_uid, get_config_init_uid, clr_config_init_uid, }, + { "lxc.init_gid", true, set_config_init_gid, get_config_init_gid, clr_config_init_gid, }, + { "lxc.limit", true, set_config_limit, get_config_limit, clr_config_limit, }, + /* [END]: REMOVE IN LXC 3.0 */ }; struct signame { @@ -192,556 +261,310 @@ }; static const struct signame signames[] = { - { SIGHUP, "HUP" }, - { SIGINT, "INT" }, - { SIGQUIT, "QUIT" }, - { SIGILL, "ILL" }, - { SIGABRT, "ABRT" }, - { SIGFPE, "FPE" }, - { SIGKILL, "KILL" }, - { SIGSEGV, "SEGV" }, - { SIGPIPE, "PIPE" }, - { SIGALRM, "ALRM" }, - { SIGTERM, "TERM" }, - { SIGUSR1, "USR1" }, - { SIGUSR2, "USR2" }, - { SIGCHLD, "CHLD" }, - { SIGCONT, "CONT" }, - { SIGSTOP, "STOP" }, - { SIGTSTP, "TSTP" }, - { SIGTTIN, "TTIN" }, - { SIGTTOU, "TTOU" }, + { SIGHUP, "HUP" }, + { SIGINT, "INT" }, + { SIGQUIT, "QUIT" }, + { SIGILL, "ILL" }, + { SIGABRT, "ABRT" }, + { SIGFPE, "FPE" }, + { SIGKILL, "KILL" }, + { SIGSEGV, "SEGV" }, + { SIGPIPE, "PIPE" }, + { SIGALRM, "ALRM" }, + { SIGTERM, "TERM" }, + { SIGUSR1, "USR1" }, + { SIGUSR2, "USR2" }, + { SIGCHLD, "CHLD" }, + { SIGCONT, "CONT" }, + { SIGSTOP, "STOP" }, + { SIGTSTP, "TSTP" }, + { SIGTTIN, "TTIN" }, + { SIGTTOU, "TTOU" }, #ifdef SIGTRAP - { SIGTRAP, "TRAP" }, + { SIGTRAP, "TRAP" }, #endif #ifdef SIGIOT - { SIGIOT, "IOT" }, + { SIGIOT, "IOT" }, #endif #ifdef SIGEMT - { SIGEMT, "EMT" }, + { SIGEMT, "EMT" }, #endif #ifdef SIGBUS - { SIGBUS, "BUS" }, + { SIGBUS, "BUS" }, #endif #ifdef SIGSTKFLT { SIGSTKFLT, "STKFLT" }, #endif #ifdef SIGCLD - { SIGCLD, "CLD" }, + { SIGCLD, "CLD" }, #endif #ifdef SIGURG - { SIGURG, "URG" }, + { SIGURG, "URG" }, #endif #ifdef SIGXCPU - { SIGXCPU, "XCPU" }, + { SIGXCPU, "XCPU" }, #endif #ifdef SIGXFSZ - { SIGXFSZ, "XFSZ" }, + { SIGXFSZ, "XFSZ" }, #endif #ifdef SIGVTALRM { SIGVTALRM, "VTALRM" }, #endif #ifdef SIGPROF - { SIGPROF, "PROF" }, + { SIGPROF, "PROF" }, #endif #ifdef SIGWINCH - { SIGWINCH, "WINCH" }, + { SIGWINCH, "WINCH" }, #endif #ifdef SIGIO - { SIGIO, "IO" }, + { SIGIO, "IO" }, #endif #ifdef SIGPOLL - { SIGPOLL, "POLL" }, + { SIGPOLL, "POLL" }, #endif #ifdef SIGINFO - { SIGINFO, "INFO" }, + { SIGINFO, "INFO" }, #endif #ifdef SIGLOST - { SIGLOST, "LOST" }, + { SIGLOST, "LOST" }, #endif #ifdef SIGPWR - { SIGPWR, "PWR" }, + { SIGPWR, "PWR" }, #endif #ifdef SIGUNUSED { SIGUNUSED, "UNUSED" }, #endif #ifdef SIGSYS - { SIGSYS, "SYS" }, + { SIGSYS, "SYS" }, #endif }; -static const size_t config_size = sizeof(config)/sizeof(struct lxc_config_t); +static const size_t config_size = sizeof(config) / sizeof(struct lxc_config_t); -extern struct lxc_config_t *lxc_getconfig(const char *key) +struct lxc_config_t *lxc_get_config(const char *key) { size_t i; for (i = 0; i < config_size; i++) - if (!strncmp(config[i].name, key, - strlen(config[i].name))) + if (!strncmp(config[i].name, key, strlen(config[i].name))) return &config[i]; - return NULL; -} - -#define strprint(str, inlen, ...) \ - do { \ - len = snprintf(str, inlen, ##__VA_ARGS__); \ - if (len < 0) { SYSERROR("snprintf"); return -1; }; \ - fulllen += len; \ - if (inlen > 0) { \ - if (str) str += len; \ - inlen -= len; \ - if (inlen < 0) inlen = 0; \ - } \ - } while (0); - -int lxc_listconfigs(char *retv, int inlen) -{ - size_t i; - int fulllen = 0, len; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - for (i = 0; i < config_size; i++) { - char *s = config[i].name; - if (s[strlen(s)-1] == '.') - continue; - strprint(retv, inlen, "%s\n", s); - } - return fulllen; -} - -static int config_string_item(char **conf_item, const char *value) -{ - char *new_value; - - if (!value || strlen(value) == 0) { - free(*conf_item); - *conf_item = NULL; - return 0; - } - - new_value = strdup(value); - if (!new_value) { - SYSERROR("failed to strdup '%s': %m", value); - return -1; - } - - free(*conf_item); - *conf_item = new_value; - return 0; -} - -static int config_string_item_max(char **conf_item, const char *value, - size_t max) -{ - if (strlen(value) >= max) { - ERROR("%s is too long (>= %lu)", value, (unsigned long)max); - return -1; - } - - return config_string_item(conf_item, value); -} - -static int config_path_item(char **conf_item, const char *value) -{ - return config_string_item_max(conf_item, value, PATH_MAX); -} - -/* - * config entry is something like "lxc.network.0.ipv4" - * the key 'lxc.network.' was found. So we make sure next - * comes an integer, find the right callback (by rewriting - * the key), and call it. - */ -static int config_network_nic(const char *key, const char *value, - struct lxc_conf *lxc_conf) -{ - char *copy = strdup(key), *p; - int ret = -1; - struct lxc_config_t *config; - - if (!copy) { - SYSERROR("failed to allocate memory"); - return -1; - } - /* - * ok we know that to get here we've got "lxc.network." - * and it isn't any of the other network entries. So - * after the second . should come an integer (# of defined - * nic) followed by a valid entry. - */ - if (*(key+12) < '0' || *(key+12) > '9') - goto out; - p = strchr(key+12, '.'); - if (!p) - goto out; - strcpy(copy+12, p+1); - config = lxc_getconfig(copy); - if (!config) { - ERROR("unknown key %s", key); - goto out; - } - ret = config->cb(key, value, lxc_conf); -out: - free(copy); - return ret; + return NULL; } -static int config_network(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if (value && strlen(value)) { - ERROR("lxc.network must not have a value"); + if (!lxc_config_value_empty(value)) { + ERROR("lxc.net must not have a value"); return -1; } - return lxc_clear_config_network(lxc_conf); + return clr_config_net(key, lxc_conf, data); } -static int macvlan_mode(int *valuep, const char *value); - -static int config_network_type(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_type(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - struct lxc_list *network = &lxc_conf->network; struct lxc_netdev *netdev; - struct lxc_list *list; - if (!value || strlen(value) == 0) - return lxc_clear_config_network(lxc_conf); + if (lxc_config_value_empty(value)) + return clr_config_net_type(key, lxc_conf, data); - netdev = malloc(sizeof(*netdev)); - if (!netdev) { - SYSERROR("failed to allocate memory"); + if (!data) return -1; - } - - memset(netdev, 0, sizeof(*netdev)); - lxc_list_init(&netdev->ipv4); - lxc_list_init(&netdev->ipv6); - - list = malloc(sizeof(*list)); - if (!list) { - SYSERROR("failed to allocate memory"); - free(netdev); + else + netdev = data; + if (!netdev) return -1; - } - - lxc_list_init(list); - list->elem = netdev; - lxc_list_add_tail(network, list); - - if (!strcmp(value, "veth")) + if (!strcmp(value, "veth")) { netdev->type = LXC_NET_VETH; - else if (!strcmp(value, "macvlan")) { + } else if (!strcmp(value, "macvlan")) { netdev->type = LXC_NET_MACVLAN; - macvlan_mode(&netdev->priv.macvlan_attr.mode, "private"); - } - else if (!strcmp(value, "vlan")) + lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, + "private"); + } else if (!strcmp(value, "vlan")) { netdev->type = LXC_NET_VLAN; - else if (!strcmp(value, "phys")) + } else if (!strcmp(value, "phys")) { netdev->type = LXC_NET_PHYS; - else if (!strcmp(value, "empty")) + } else if (!strcmp(value, "empty")) { netdev->type = LXC_NET_EMPTY; - else if (!strcmp(value, "none")) + } else if (!strcmp(value, "none")) { netdev->type = LXC_NET_NONE; - else { + } else { ERROR("invalid network type %s", value); return -1; } - return 0; -} - -static int config_ip_prefix(struct in_addr *addr) -{ - if (IN_CLASSA(addr->s_addr)) - return 32 - IN_CLASSA_NSHIFT; - if (IN_CLASSB(addr->s_addr)) - return 32 - IN_CLASSB_NSHIFT; - if (IN_CLASSC(addr->s_addr)) - return 32 - IN_CLASSC_NSHIFT; return 0; } -/* - * if you have p="lxc.network.0.link", pass it p+12 - * to get back '0' (the index of the nic) - */ -static int get_network_netdev_idx(const char *key) -{ - int ret, idx; - - if (*key < '0' || *key > '9') - return -1; - ret = sscanf(key, "%d", &idx); - if (ret != 1) - return -1; - return idx; -} - -/* - * if you have p="lxc.network.0", pass this p+12 and it will return - * the netdev of the first configured nic - */ -static struct lxc_netdev *get_netdev_from_key(const char *key, - struct lxc_list *network) -{ - int i = 0, idx = get_network_netdev_idx(key); - struct lxc_netdev *netdev = NULL; - struct lxc_list *it; - if (idx == -1) - return NULL; - lxc_list_for_each(it, network) { - if (idx == i++) { - netdev = it->elem; - break; - } - } - return netdev; -} - -extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, - char *retv, int inlen) +static int set_config_net_flags(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - int fulllen = 0, len; - netdev = get_netdev_from_key(key+12, &c->network); + if (lxc_config_value_empty(value)) + return clr_config_net_flags(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); + netdev->flags |= IFF_UP; - strprint(retv, inlen, "type\n"); - strprint(retv, inlen, "script.up\n"); - strprint(retv, inlen, "script.down\n"); - if (netdev->type != LXC_NET_EMPTY) { - strprint(retv, inlen, "flags\n"); - strprint(retv, inlen, "link\n"); - strprint(retv, inlen, "name\n"); - strprint(retv, inlen, "hwaddr\n"); - strprint(retv, inlen, "mtu\n"); - strprint(retv, inlen, "ipv6\n"); - strprint(retv, inlen, "ipv6.gateway\n"); - strprint(retv, inlen, "ipv4\n"); - strprint(retv, inlen, "ipv4.gateway\n"); - } - switch(netdev->type) { - case LXC_NET_VETH: - strprint(retv, inlen, "veth.pair\n"); - break; - case LXC_NET_MACVLAN: - strprint(retv, inlen, "macvlan.mode\n"); - break; - case LXC_NET_VLAN: - strprint(retv, inlen, "vlan.id\n"); - break; - case LXC_NET_PHYS: - break; - } - return fulllen; + return 0; } -static struct lxc_netdev *network_netdev(const char *key, const char *value, - struct lxc_list *network) +static int create_matched_ifnames(const char *value, struct lxc_conf *lxc_conf, + struct lxc_netdev *netdev) { - struct lxc_netdev *netdev = NULL; - - if (lxc_list_empty(network)) { - ERROR("network is not created for '%s' = '%s' option", - key, value); - return NULL; - } - - if (get_network_netdev_idx(key+12) == -1) - netdev = lxc_list_last_elem(network); - else - netdev = get_netdev_from_key(key+12, network); + struct ifaddrs *ifaddr, *ifa; + int n; + int ret = 0; + const char *type_key = "lxc.net.type"; + const char *link_key = "lxc.net.link"; + const char *tmpvalue = "phys"; - if (!netdev) { - ERROR("no network device defined for '%s' = '%s' option", - key, value); - return NULL; + if (getifaddrs(&ifaddr) == -1) { + SYSERROR("Get network interfaces failed"); + return -1; } - return netdev; -} - -static int network_ifname(char **valuep, const char *value) -{ - return config_string_item_max(valuep, value, IFNAMSIZ); -} - -#ifndef MACVLAN_MODE_PRIVATE -# define MACVLAN_MODE_PRIVATE 1 -#endif - -#ifndef MACVLAN_MODE_VEPA -# define MACVLAN_MODE_VEPA 2 -#endif - -#ifndef MACVLAN_MODE_BRIDGE -# define MACVLAN_MODE_BRIDGE 4 -#endif - -#ifndef MACVLAN_MODE_PASSTHRU -# define MACVLAN_MODE_PASSTHRU 8 -#endif - -static int macvlan_mode(int *valuep, const char *value) -{ - struct mc_mode { - char *name; - int mode; - } m[] = { - { "private", MACVLAN_MODE_PRIVATE }, - { "vepa", MACVLAN_MODE_VEPA }, - { "bridge", MACVLAN_MODE_BRIDGE }, - { "passthru", MACVLAN_MODE_PASSTHRU }, - }; - - size_t i; - - for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) { - if (strcmp(m[i].name, value)) + for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_PACKET) continue; - *valuep = m[i].mode; - return 0; - } - - return -1; -} - -static int rand_complete_hwaddr(char *hwaddr) -{ - const char hex[] = "0123456789abcdef"; - char *curs = hwaddr; - -#ifndef HAVE_RAND_R - randseed(true); -#else - unsigned int seed=randseed(false); -#endif - while (*curs != '\0' && *curs != '\n') - { - if ( *curs == 'x' || *curs == 'X' ) { - if (curs - hwaddr == 1) { - //ensure address is unicast -#ifdef HAVE_RAND_R - *curs = hex[rand_r(&seed) & 0x0E]; - } else { - *curs = hex[rand_r(&seed) & 0x0F]; -#else - *curs = hex[rand() & 0x0E]; + if (!strncmp(value, ifa->ifa_name, strlen(value) - 1)) { + ret = set_config_net_type(type_key, tmpvalue, lxc_conf, + netdev); + if (!ret) { + ret = set_config_net_link( + link_key, ifa->ifa_name, lxc_conf, netdev); + if (ret) { + ERROR("failed to create matched ifnames"); + break; + } } else { - *curs = hex[rand() & 0x0F]; -#endif + ERROR("failed to create matched ifnames"); + break; } } - curs++; } - return 0; + + freeifaddrs(ifaddr); + ifaddr = NULL; + + return ret; } -static int config_network_flags(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_link(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; + int ret = 0; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_link(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - netdev->flags |= IFF_UP; + if (value[strlen(value) - 1] == '+' && netdev->type == LXC_NET_PHYS) + ret = create_matched_ifnames(value, lxc_conf, netdev); + else + ret = network_ifname(netdev->link, value); - return 0; + return ret; } -static int config_network_link(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_name(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_name(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - return network_ifname(&netdev->link, value); + return network_ifname(netdev->name, value); } -static int config_network_name(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_veth_pair(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_veth_pair(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - return network_ifname(&netdev->name, value); + return network_ifname(netdev->priv.veth_attr.pair, value); } -static int config_network_veth_pair(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_macvlan_mode(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_macvlan_mode(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - if (netdev->type != LXC_NET_VETH) { - ERROR("Invalid veth pair for a non-veth netdev"); - return -1; - } - return network_ifname(&netdev->priv.veth_attr.pair, value); + return lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, value); } -static int config_network_macvlan_mode(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_hwaddr(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; + char *new_value; + + if (lxc_config_value_empty(value)) + return clr_config_net_hwaddr(key, lxc_conf, data); - netdev = network_netdev(key, value, &lxc_conf->network); + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - if (netdev->type != LXC_NET_MACVLAN) { - ERROR("Invalid macvlan.mode for a non-macvlan netdev"); + new_value = strdup(value); + if (!new_value) return -1; - } - return macvlan_mode(&netdev->priv.macvlan_attr.mode, value); -} - -static int config_network_hwaddr(const char *key, const char *value, - struct lxc_conf *lxc_conf) -{ - struct lxc_netdev *netdev; - char *new_value = strdup(value); - if (!new_value) { - SYSERROR("failed to strdup '%s': %m", value); - return -1; - } rand_complete_hwaddr(new_value); - netdev = network_netdev(key, new_value, &lxc_conf->network); - if (!netdev) { - free(new_value); - return -1; - }; - - if (!new_value || strlen(new_value) == 0) { + if (lxc_config_value_empty(new_value)) { free(new_value); netdev->hwaddr = NULL; return 0; @@ -751,62 +574,75 @@ return 0; } -static int config_network_vlan_id(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_vlan_id(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + int ret; struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); - if (!netdev) - return -1; + if (lxc_config_value_empty(value)) + return clr_config_net_vlan_id(key, lxc_conf, data); - if (netdev->type != LXC_NET_VLAN) { - ERROR("Invalid vlan.id for a non-macvlan netdev"); + if (!data) return -1; - } - if (get_u16(&netdev->priv.vlan_attr.vid, value, 0)) + else + netdev = data; + if (!netdev) + return -1; + + ret = get_u16(&netdev->priv.vlan_attr.vid, value, 0); + if (ret < 0) return -1; return 0; } -static int config_network_mtu(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_mtu(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_mtu(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; - return config_string_item(&netdev->mtu, value); + return set_config_string_item(&netdev->mtu, value); } -static int config_network_ipv4(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_ipv4_address(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + int ret; struct lxc_netdev *netdev; struct lxc_inetdev *inetdev; struct lxc_list *list; - char *cursor, *slash, *addr = NULL, *bcast = NULL, *prefix = NULL; + char *cursor, *slash; + char *addr = NULL, *bcast = NULL, *prefix = NULL; - if (!value || !strlen(value)) - return lxc_clear_config_item(lxc_conf, key); + if (lxc_config_value_empty(value)) + return clr_config_net_ipv4_address(key, lxc_conf, data); - netdev = network_netdev(key, value, &lxc_conf->network); + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; inetdev = malloc(sizeof(*inetdev)); - if (!inetdev) { - SYSERROR("failed to allocate ipv4 address"); + if (!inetdev) return -1; - } + memset(inetdev, 0, sizeof(*inetdev)); list = malloc(sizeof(*list)); if (!list) { - SYSERROR("failed to allocate memory"); free(inetdev); return -1; } @@ -816,7 +652,6 @@ addr = strdup(value); if (!addr) { - ERROR("no address specified"); free(inetdev); free(list); return -1; @@ -834,37 +669,42 @@ prefix = slash + 1; } - if (!inet_pton(AF_INET, addr, &inetdev->addr)) { - SYSERROR("invalid ipv4 address: %s", value); + ret = inet_pton(AF_INET, addr, &inetdev->addr); + if (!ret || ret < 0) { + SYSERROR("Invalid ipv4 address \"%s\"", value); free(inetdev); free(addr); free(list); return -1; } - if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { - SYSERROR("invalid ipv4 broadcast address: %s", value); - free(inetdev); - free(list); - free(addr); - return -1; + if (bcast) { + ret = inet_pton(AF_INET, bcast, &inetdev->bcast); + if (!ret || ret < 0) { + SYSERROR("Invalid ipv4 broadcast address \"%s\"", value); + free(inetdev); + free(list); + free(addr); + return -1; + } + } - /* no prefix specified, determine it from the network class */ + /* No prefix specified, determine it from the network class. */ if (prefix) { - if (lxc_safe_uint(prefix, &inetdev->prefix) < 0) + ret = lxc_safe_uint(prefix, &inetdev->prefix); + if (ret < 0) return -1; } else { inetdev->prefix = config_ip_prefix(&inetdev->addr); } - /* if no broadcast address, let compute one from the - * prefix and address + /* If no broadcast address, let compute one from the + * prefix and address. */ if (!bcast) { inetdev->bcast.s_addr = inetdev->addr.s_addr; - inetdev->bcast.s_addr |= - htonl(INADDR_BROADCAST >> inetdev->prefix); + inetdev->bcast.s_addr |= htonl(INADDR_BROADCAST >> inetdev->prefix); } lxc_list_add_tail(&netdev->ipv4, list); @@ -873,33 +713,37 @@ return 0; } -static int config_network_ipv4_gateway(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_ipv4_gateway(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_ipv4_gateway(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; free(netdev->ipv4_gateway); - if (!value || strlen(value) == 0) { - netdev->ipv4_gateway = NULL; - } else if (!strcmp(value, "auto")) { + if (!strcmp(value, "auto")) { netdev->ipv4_gateway = NULL; netdev->ipv4_gateway_auto = true; } else { + int ret; struct in_addr *gw; gw = malloc(sizeof(*gw)); - if (!gw) { - SYSERROR("failed to allocate ipv4 gateway address"); + if (!gw) return -1; - } - if (!inet_pton(AF_INET, value, gw)) { - SYSERROR("invalid ipv4 gateway address: %s", value); + ret = inet_pton(AF_INET, value, gw); + if (!ret || ret < 0) { + SYSERROR("Invalid ipv4 gateway address \"%s\"", value); free(gw); return -1; } @@ -911,32 +755,33 @@ return 0; } -static int config_network_ipv6(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_ipv6_address(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + int ret; struct lxc_netdev *netdev; struct lxc_inet6dev *inet6dev; struct lxc_list *list; - char *slash,*valdup; - char *netmask; + char *slash, *valdup, *netmask; - if (!value || !strlen(value)) - return lxc_clear_config_item(lxc_conf, key); + if (lxc_config_value_empty(value)) + return clr_config_net_ipv6_address(key, lxc_conf, data); - netdev = network_netdev(key, value, &lxc_conf->network); + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; inet6dev = malloc(sizeof(*inet6dev)); - if (!inet6dev) { - SYSERROR("failed to allocate ipv6 address"); + if (!inet6dev) return -1; - } + memset(inet6dev, 0, sizeof(*inet6dev)); list = malloc(sizeof(*list)); if (!list) { - SYSERROR("failed to allocate memory"); free(inet6dev); return -1; } @@ -946,7 +791,6 @@ valdup = strdup(value); if (!valdup) { - ERROR("no address specified"); free(list); free(inet6dev); return -1; @@ -961,8 +805,9 @@ return -1; } - if (!inet_pton(AF_INET6, valdup, &inet6dev->addr)) { - SYSERROR("invalid ipv6 address: %s", valdup); + ret = inet_pton(AF_INET6, valdup, &inet6dev->addr); + if (!ret || ret < 0) { + SYSERROR("Invalid ipv6 address \"%s\"", valdup); free(list); free(inet6dev); free(valdup); @@ -975,33 +820,37 @@ return 0; } -static int config_network_ipv6_gateway(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_ipv6_gateway(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_ipv6_gateway(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) return -1; free(netdev->ipv6_gateway); - if (!value || strlen(value) == 0) { - netdev->ipv6_gateway = NULL; - } else if (!strcmp(value, "auto")) { + if (!strcmp(value, "auto")) { netdev->ipv6_gateway = NULL; netdev->ipv6_gateway_auto = true; } else { + int ret; struct in6_addr *gw; gw = malloc(sizeof(*gw)); - if (!gw) { - SYSERROR("failed to allocate ipv6 gateway address"); + if (!gw) return -1; - } - if (!inet_pton(AF_INET6, value, gw)) { - SYSERROR("invalid ipv6 gateway address: %s", value); + ret = inet_pton(AF_INET6, value, gw); + if (!ret || ret < 0) { + SYSERROR("Invalid ipv6 gateway address \"%s\"", value); free(gw); return -1; } @@ -1013,28 +862,40 @@ return 0; } -static int config_network_script_up(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_script_up(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_script_up(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) - return -1; + return -1; - return config_string_item(&netdev->upscript, value); + return set_config_string_item(&netdev->upscript, value); } -static int config_network_script_down(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_net_script_down(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev; - netdev = network_netdev(key, value, &lxc_conf->network); + if (lxc_config_value_empty(value)) + return clr_config_net_script_down(key, lxc_conf, data); + + if (!data) + return -1; + else + netdev = data; if (!netdev) - return -1; + return -1; - return config_string_item(&netdev->downscript, value); + return set_config_string_item(&netdev->downscript, value); } static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) @@ -1046,164 +907,203 @@ free(hook); return -1; } + hooklist->elem = hook; lxc_list_add_tail(&lxc_conf->hooks[which], hooklist); return 0; } -static int config_seccomp(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_seccomp_profile(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_path_item(&lxc_conf->seccomp, value); + return set_config_path_item(&lxc_conf->seccomp, value); } -static int config_init_cmd(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_init_cmd(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_path_item(&lxc_conf->init_cmd, value); + return set_config_path_item(&lxc_conf->init_cmd, value); } -static int config_init_uid(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_init_uid(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { unsigned int init_uid; + if (lxc_config_value_empty(value)) { + lxc_conf->init_uid = 0; + return 0; + } + if (lxc_safe_uint(value, &init_uid) < 0) return -1; + lxc_conf->init_uid = init_uid; return 0; } -static int config_init_gid(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_init_gid(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { unsigned int init_gid; + if (lxc_config_value_empty(value)) { + lxc_conf->init_gid = 0; + return 0; + } + if (lxc_safe_uint(value, &init_gid) < 0) return -1; + lxc_conf->init_gid = init_gid; return 0; } -static int config_hook(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_hooks(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *copy; - if (!value || strlen(value) == 0) + if (lxc_config_value_empty(value)) return lxc_clear_hooks(lxc_conf, key); - if (strcmp(key, "lxc.hook") == 0) { - ERROR("lxc.hook cannot take a value"); + if (strcmp(key + 4, "hook") == 0) { + ERROR("lxc.hook must not have a value"); return -1; } + copy = strdup(value); - if (!copy) { - SYSERROR("failed to dup string '%s'", value); + if (!copy) return -1; - } - if (strcmp(key, "lxc.hook.pre-start") == 0) + + if (strcmp(key + 9, "pre-start") == 0) return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); - else if (strcmp(key, "lxc.hook.pre-mount") == 0) + else if (strcmp(key + 9, "pre-mount") == 0) return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy); - else if (strcmp(key, "lxc.hook.autodev") == 0) + else if (strcmp(key + 9, "autodev") == 0) return add_hook(lxc_conf, LXCHOOK_AUTODEV, copy); - else if (strcmp(key, "lxc.hook.mount") == 0) + else if (strcmp(key + 9, "mount") == 0) return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); - else if (strcmp(key, "lxc.hook.start") == 0) + else if (strcmp(key + 9, "start") == 0) return add_hook(lxc_conf, LXCHOOK_START, copy); - else if (strcmp(key, "lxc.hook.stop") == 0) + else if (strcmp(key + 9, "stop") == 0) return add_hook(lxc_conf, LXCHOOK_STOP, copy); - else if (strcmp(key, "lxc.hook.post-stop") == 0) + else if (strcmp(key + 9, "post-stop") == 0) return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); - else if (strcmp(key, "lxc.hook.clone") == 0) + else if (strcmp(key + 9, "clone") == 0) return add_hook(lxc_conf, LXCHOOK_CLONE, copy); - else if (strcmp(key, "lxc.hook.destroy") == 0) + else if (strcmp(key + 9, "destroy") == 0) return add_hook(lxc_conf, LXCHOOK_DESTROY, copy); - SYSERROR("Unknown key: %s", key); + free(copy); return -1; } -static int config_personality(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_personality(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { signed long personality = lxc_config_parse_arch(value); if (personality >= 0) lxc_conf->personality = personality; else - WARN("unsupported personality '%s'", value); + WARN("Unsupported personality \"%s\"", value); return 0; } -static int config_pts(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_pty_max(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + if (lxc_config_value_empty(value)) { + lxc_conf->pts = 0; + return 0; + } + if (lxc_safe_uint(value, &lxc_conf->pts) < 0) return -1; return 0; } -static int config_start(const char *key, const char *value, - struct lxc_conf *lxc_conf) +/* We only need to check whether the first byte of the key after the lxc.start. + * prefix matches our expectations since they fortunately all start with a + * different letter. If anything was wrong with the key we would have already + * noticed when the callback was called. + */ +static int set_config_start(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if(strcmp(key, "lxc.start.auto") == 0) { + bool is_empty; + + is_empty = lxc_config_value_empty(value); + + if (*(key + 10) == 'a') { /* lxc.start.auto */ + if (is_empty) { + lxc_conf->start_auto = 0; + return 0; + } + if (lxc_safe_uint(value, &lxc_conf->start_auto) < 0) return -1; + if (lxc_conf->start_auto > 1) return -1; + return 0; + } else if (*(key + 10) == 'd') { /* lxc.start.delay */ + if (is_empty) { + lxc_conf->start_delay = 0; + return 0; + } + + return lxc_safe_uint(value, &lxc_conf->start_delay); + } else if (*(key + 10) == 'o') { /* lxc.start.order */ + if (is_empty) { + lxc_conf->start_order = 0; + return 0; + } + + return lxc_safe_int(value, &lxc_conf->start_order); } - else if (strcmp(key, "lxc.start.delay") == 0) { - if (lxc_safe_uint(value, &lxc_conf->start_delay) < 0) - return -1; - return 0; - } - else if (strcmp(key, "lxc.start.order") == 0) { - if (lxc_safe_int(value, &lxc_conf->start_order) < 0) - return -1; - return 0; - } - SYSERROR("Unknown key: %s", key); + return -1; } -static int config_monitor(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_monitor(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if(strcmp(key, "lxc.monitor.unshare") == 0) { - if (lxc_safe_uint(value, &lxc_conf->monitor_unshare) < 0) - return -1; + if (lxc_config_value_empty(value)) { + lxc_conf->monitor_unshare = 0; return 0; } - SYSERROR("Unknown key: %s", key); + + if (strcmp(key + 12, "unshare") == 0) + return lxc_safe_uint(value, &lxc_conf->monitor_unshare); + return -1; } -static int config_group(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_group(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *groups, *groupptr, *sptr, *token; struct lxc_list *grouplist; int ret = -1; - if (!strlen(value)) + if (lxc_config_value_empty(value)) return lxc_clear_groups(lxc_conf); groups = strdup(value); - if (!groups) { - SYSERROR("failed to dup '%s'", value); + if (!groups) return -1; - } - /* in case several groups are specified in a single line - * split these groups in a single element for the list */ - for (groupptr = groups;;groupptr = NULL) { + /* In case several groups are specified in a single line split these + * groups in a single element for the list. + */ + for (groupptr = groups;; groupptr = NULL) { token = strtok_r(groupptr, " \t", &sptr); if (!token) { ret = 0; @@ -1211,14 +1111,11 @@ } grouplist = malloc(sizeof(*grouplist)); - if (!grouplist) { - SYSERROR("failed to allocate groups list"); + if (!grouplist) break; - } grouplist->elem = strdup(token); if (!grouplist->elem) { - SYSERROR("failed to dup '%s'", token); free(grouplist); break; } @@ -1227,110 +1124,114 @@ } free(groups); - return ret; } -static int config_environment(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_environment(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct lxc_list *list_item = NULL; - if (!strlen(value)) + if (lxc_config_value_empty(value)) return lxc_clear_environment(lxc_conf); list_item = malloc(sizeof(*list_item)); if (!list_item) - goto freak_out; + goto on_error; list_item->elem = strdup(value); if (!list_item->elem) - goto freak_out; + goto on_error; lxc_list_add_tail(&lxc_conf->environment, list_item); return 0; -freak_out: +on_error: free(list_item); - return -1; } -static int config_tty(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_tty_max(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if (lxc_safe_uint(value, &lxc_conf->tty) < 0) - return -1; + if (lxc_config_value_empty(value)) { + lxc_conf->tty = 0; + return 0; + } - return 0; + return lxc_safe_uint(value, &lxc_conf->tty); } -static int config_ttydir(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_tty_dir(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_string_item_max(&lxc_conf->ttydir, value, NAME_MAX+1); + return set_config_string_item_max(&lxc_conf->ttydir, value, + NAME_MAX + 1); } -static int config_kmsg(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_apparmor_profile(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if (lxc_safe_uint(value, &lxc_conf->kmsg) < 0) - return -1; - - if (lxc_conf->kmsg > 1) - return -1; - - return 0; + return set_config_string_item(&lxc_conf->lsm_aa_profile, value); } -static int config_lsm_aa_profile(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_apparmor_allow_incomplete(const char *key, + const char *value, + struct lxc_conf *lxc_conf, + void *data) { - return config_string_item(&lxc_conf->lsm_aa_profile, value); -} + if (lxc_config_value_empty(value)) { + lxc_conf->lsm_aa_allow_incomplete = 0; + return 0; + } -static int config_lsm_aa_incomplete(const char *key, const char *value, - struct lxc_conf *lxc_conf) -{ if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_incomplete) < 0) return -1; - if (lxc_conf->lsm_aa_allow_incomplete > 1) { - ERROR("Wrong value for lxc.lsm_aa_allow_incomplete. Can only be set to 0 or 1"); + if (lxc_conf->lsm_aa_allow_incomplete > 1) return -1; - } return 0; } -static int config_lsm_se_context(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_selinux_context(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_string_item(&lxc_conf->lsm_se_context, value); + return set_config_string_item(&lxc_conf->lsm_se_context, value); } -static int config_logfile(const char *key, const char *value, - struct lxc_conf *c) +static int set_config_log_file(const char *key, const char *value, + struct lxc_conf *c, void *data) { int ret; - // store these values in the lxc_conf, and then try to set for - // actual current logging. - ret = config_path_item(&c->logfile, value); + if (lxc_config_value_empty(value)) { + free(c->logfile); + c->logfile = NULL; + return 0; + } + + /* Store these values in the lxc_conf, and then try to set for actual + * current logging. + */ + ret = set_config_path_item(&c->logfile, value); if (ret == 0) ret = lxc_log_set_file(&c->logfd, c->logfile); + return ret; } -static int config_loglevel(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_log_level(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { int newlevel; - if (!value || strlen(value) == 0) + if (lxc_config_value_empty(value)) { + lxc_conf->loglevel = LXC_LOG_LEVEL_NOTSET; return 0; + } if (value[0] >= '0' && value[0] <= '9') { if (lxc_safe_int(value, &newlevel) < 0) @@ -1338,57 +1239,63 @@ } else { newlevel = lxc_log_priority_to_int(value); } - // store these values in the lxc_conf, and then try to set for - // actual current logging. + + /* Store these values in the lxc_conf, and then try to set for actual + * current logging. + */ lxc_conf->loglevel = newlevel; return lxc_log_set_level(&lxc_conf->loglevel, newlevel); } -static int config_autodev(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_autodev(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + if (lxc_config_value_empty(value)) { + lxc_conf->autodev = 0; + return 0; + } + if (lxc_safe_uint(value, &lxc_conf->autodev) < 0) return -1; - if (lxc_conf->autodev > 1) { - ERROR("Wrong value for lxc.autodev. Can only be set to 0 or 1"); + if (lxc_conf->autodev > 1) return -1; - } return 0; } static int sig_num(const char *sig) { - int n; - char *endp = NULL; + unsigned int signum; - errno = 0; - n = strtol(sig, &endp, 10); - if (sig == endp || n < 0 || errno != 0) + if (lxc_safe_uint(sig, &signum) < 0) return -1; - return n; + + return signum; } static int rt_sig_num(const char *signame) { - int sig_n = 0; - int rtmax = 0; + int rtmax = 0, sig_n = 0; if (strncasecmp(signame, "max-", 4) == 0) { rtmax = 1; } + signame += 4; if (!isdigit(*signame)) return -1; + sig_n = sig_num(signame); sig_n = rtmax ? SIGRTMAX - sig_n : SIGRTMIN + sig_n; if (sig_n > SIGRTMAX || sig_n < SIGRTMIN) return -1; + return sig_n; } -static int sig_parse(const char *signame) { +static int sig_parse(const char *signame) +{ size_t n; if (isdigit(*signame)) { @@ -1398,62 +1305,83 @@ if (strncasecmp(signame, "rt", 2) == 0) return rt_sig_num(signame + 2); for (n = 0; n < sizeof(signames) / sizeof((signames)[0]); n++) { - if (strcasecmp (signames[n].name, signame) == 0) + if (strcasecmp(signames[n].name, signame) == 0) return signames[n].num; } } + return -1; } -static int config_haltsignal(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_signal_halt(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - int sig_n = sig_parse(value); + int sig_n; + if (lxc_config_value_empty(value)) { + lxc_conf->haltsignal = 0; + return 0; + } + + sig_n = sig_parse(value); if (sig_n < 0) return -1; + lxc_conf->haltsignal = sig_n; return 0; } -static int config_rebootsignal(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_signal_reboot(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - int sig_n = sig_parse(value); + int sig_n; + if (lxc_config_value_empty(value)) { + lxc_conf->rebootsignal = 0; + return 0; + } + + sig_n = sig_parse(value); if (sig_n < 0) return -1; + lxc_conf->rebootsignal = sig_n; return 0; } -static int config_stopsignal(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_signal_stop(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - int sig_n = sig_parse(value); + int sig_n; + if (lxc_config_value_empty(value)) { + lxc_conf->stopsignal = 0; + return 0; + } + + sig_n = sig_parse(value); if (sig_n < 0) return -1; + lxc_conf->stopsignal = sig_n; return 0; } -static int config_cgroup(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_cgroup_controller(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - char *token = "lxc.cgroup."; char *subkey; + char *token = "lxc.cgroup."; struct lxc_list *cglist = NULL; struct lxc_cgroup *cgelem = NULL; - if (!value || strlen(value) == 0) + if (lxc_config_value_empty(value)) return lxc_clear_cgroups(lxc_conf, key); subkey = strstr(key, token); - if (!subkey) return -1; @@ -1488,128 +1416,218 @@ out: free(cglist); - if (cgelem) { free(cgelem->subsystem); - free(cgelem->value); - free(cgelem); } return -1; } -static int config_idmap(const char *key, const char *value, struct lxc_conf *lxc_conf) +static int set_config_cgroup_dir(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - char *token = "lxc.id_map"; - char *subkey; - struct lxc_list *idmaplist = NULL; - struct id_map *idmap = NULL; - unsigned long hostid, nsid, range; - char type; - int ret; + if (lxc_config_value_empty(value)) + return clr_config_cgroup_dir(key, lxc_conf, NULL); - if (!value || strlen(value) == 0) - return lxc_clear_idmaps(lxc_conf); + return set_config_string_item(&lxc_conf->cgroup_meta.dir, value); +} - subkey = strstr(key, token); +static int set_config_prlimit(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *iter; + struct rlimit limit; + unsigned long limit_value; + struct lxc_list *limlist = NULL; + struct lxc_limit *limelem = NULL; - if (!subkey) + if (lxc_config_value_empty(value)) + return lxc_clear_limits(lxc_conf, key); + + if (strncmp(key, "lxc.prlimit.", sizeof("lxc.prlimit.") - 1) != 0) return -1; - if (!strlen(subkey)) + key += sizeof("lxc.prlimit.") - 1; + + /* soft limit comes first in the value */ + if (!parse_limit_value(&value, &limit_value)) return -1; + limit.rlim_cur = limit_value; + + /* skip spaces and a colon */ + while (isspace(*value)) + ++value; + + if (*value == ':') + ++value; + else if (*value) /* any other character is an error here */ + return -1; + + while (isspace(*value)) + ++value; + + /* optional hard limit */ + if (*value) { + if (!parse_limit_value(&value, &limit_value)) + return -1; + limit.rlim_max = limit_value; + + /* check for trailing garbage */ + while (isspace(*value)) + ++value; + + if (*value) + return -1; + } else { + /* a single value sets both hard and soft limit */ + limit.rlim_max = limit.rlim_cur; + } + + /* find existing list element */ + lxc_list_for_each(iter, &lxc_conf->limits) + { + limelem = iter->elem; + if (!strcmp(key, limelem->resource)) { + limelem->limit = limit; + return 0; + } + } + + /* allocate list element */ + limlist = malloc(sizeof(*limlist)); + if (!limlist) + goto out; + + limelem = malloc(sizeof(*limelem)); + if (!limelem) + goto out; + memset(limelem, 0, sizeof(*limelem)); + + limelem->resource = strdup(key); + if (!limelem->resource) + goto out; + limelem->limit = limit; + + limlist->elem = limelem; + + lxc_list_add_tail(&lxc_conf->limits, limlist); + + return 0; + +out: + free(limlist); + if (limelem) { + free(limelem->resource); + free(limelem); + } + return -1; +} + +static int set_config_idmaps(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + unsigned long hostid, nsid, range; + char type; + int ret; + struct lxc_list *idmaplist = NULL; + struct id_map *idmap = NULL; + + if (lxc_config_value_empty(value)) + return lxc_clear_idmaps(lxc_conf); idmaplist = malloc(sizeof(*idmaplist)); if (!idmaplist) - goto out; + goto on_error; idmap = malloc(sizeof(*idmap)); if (!idmap) - goto out; + goto on_error; memset(idmap, 0, sizeof(*idmap)); - ret = sscanf(value, "%c %lu %lu %lu", &type, &nsid, &hostid, &range); - if (ret != 4) - goto out; + ret = parse_idmaps(value, &type, &nsid, &hostid, &range); + if (ret < 0) + goto on_error; - INFO("read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range); + INFO("Read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range); if (type == 'u') idmap->idtype = ID_TYPE_UID; else if (type == 'g') idmap->idtype = ID_TYPE_GID; else - goto out; + goto on_error; idmap->hostid = hostid; idmap->nsid = nsid; idmap->range = range; - idmaplist->elem = idmap; lxc_list_add_tail(&lxc_conf->id_map, idmaplist); + idmap = NULL; return 0; -out: +on_error: free(idmaplist); - - if (idmap) { - free(idmap); - } + free(idmap); return -1; } -static int config_fstab(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_mount_fstab(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if (!value || strlen(value) == 0) + if (lxc_config_value_empty(value)) { + clr_config_mount_fstab(key, lxc_conf, NULL); return -1; - return config_path_item(&lxc_conf->fstab, value); + } + + return set_config_path_item(&lxc_conf->fstab, value); } -static int config_mount_auto(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_mount_auto(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *autos, *autoptr, *sptr, *token; - static struct { const char *token; int mask; int flag; } allowed_auto_mounts[] = { - { "proc", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, - { "proc:mixed", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, - { "proc:rw", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW }, - { "sys", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, - { "sys:ro", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, - { "sys:mixed", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, - { "sys:rw", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW }, - { "cgroup", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC }, - { "cgroup:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED }, - { "cgroup:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO }, - { "cgroup:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW }, - { "cgroup-full", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC }, - { "cgroup-full:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED }, - { "cgroup-full:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO }, - { "cgroup-full:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW }, - /* NB: For adding anything that is just a single on/off, but has - * no options: keep mask and flag identical and just define the - * enum value as an unused bit so far - */ - { NULL, 0 } - }; int i; int ret = -1; + static struct { + const char *token; + int mask; + int flag; + } allowed_auto_mounts[] = { + { "proc", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, + { "proc:mixed", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, + { "proc:rw", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW }, + { "sys", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, + { "sys:ro", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, + { "sys:mixed", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, + { "sys:rw", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW }, + { "cgroup", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC }, + { "cgroup:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED }, + { "cgroup:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO }, + { "cgroup:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW }, + { "cgroup-full", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC }, + { "cgroup-full:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED }, + { "cgroup-full:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO }, + { "cgroup-full:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW }, + /* For adding anything that is just a single on/off, but has no + * options: keep mask and flag identical and just define the enum + * value as an unused bit so far + */ + { NULL, 0, 0 } + }; - if (!value || strlen(value) == 0) { + if (lxc_config_value_empty(value)) { lxc_conf->auto_mounts = 0; return 0; } autos = strdup(value); - if (!autos) { - SYSERROR("failed to dup '%s'", value); + if (!autos) return -1; - } - for (autoptr = autos; ; autoptr = NULL) { + for (autoptr = autos;; autoptr = NULL) { token = strtok_r(autoptr, " \t", &sptr); if (!token) { ret = 0; @@ -1622,7 +1640,7 @@ } if (!allowed_auto_mounts[i].token) { - ERROR("Invalid filesystem to automount: %s", token); + ERROR("Invalid filesystem to automount \"%s\"", token); break; } @@ -1631,17 +1649,16 @@ } free(autos); - return ret; } -static int config_mount(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_mount(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *mntelem; struct lxc_list *mntlist; - if (!value || strlen(value) == 0) + if (lxc_config_value_empty(value)) return lxc_clear_mount_entries(lxc_conf); mntlist = malloc(sizeof(*mntlist)); @@ -1660,25 +1677,24 @@ return 0; } -static int config_cap_keep(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_cap_keep(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *keepcaps, *keepptr, *sptr, *token; struct lxc_list *keeplist; int ret = -1; - if (!strlen(value)) + if (lxc_config_value_empty(value)) return lxc_clear_config_keepcaps(lxc_conf); keepcaps = strdup(value); - if (!keepcaps) { - SYSERROR("failed to dup '%s'", value); + if (!keepcaps) return -1; - } - /* in case several capability keep is specified in a single line - * split these caps in a single element for the list */ - for (keepptr = keepcaps;;keepptr = NULL) { + /* In case several capability keep is specified in a single line + * split these caps in a single element for the list. + */ + for (keepptr = keepcaps;; keepptr = NULL) { token = strtok_r(keepptr, " \t", &sptr); if (!token) { ret = 0; @@ -1689,14 +1705,11 @@ lxc_clear_config_keepcaps(lxc_conf); keeplist = malloc(sizeof(*keeplist)); - if (!keeplist) { - SYSERROR("failed to allocate keepcap list"); + if (!keeplist) break; - } keeplist->elem = strdup(token); if (!keeplist->elem) { - SYSERROR("failed to dup '%s'", token); free(keeplist); break; } @@ -1709,25 +1722,24 @@ return ret; } -static int config_cap_drop(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_cap_drop(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { char *dropcaps, *dropptr, *sptr, *token; struct lxc_list *droplist; int ret = -1; - if (!strlen(value)) + if (lxc_config_value_empty(value)) return lxc_clear_config_caps(lxc_conf); dropcaps = strdup(value); - if (!dropcaps) { - SYSERROR("failed to dup '%s'", value); + if (!dropcaps) return -1; - } - /* in case several capability drop is specified in a single line - * split these caps in a single element for the list */ - for (dropptr = dropcaps;;dropptr = NULL) { + /* In case several capability drop is specified in a single line + * split these caps in a single element for the list. + */ + for (dropptr = dropcaps;; dropptr = NULL) { token = strtok_r(dropptr, " \t", &sptr); if (!token) { ret = 0; @@ -1735,14 +1747,11 @@ } droplist = malloc(sizeof(*droplist)); - if (!droplist) { - SYSERROR("failed to allocate drop list"); + if (!droplist) break; - } droplist->elem = strdup(token); if (!droplist->elem) { - SYSERROR("failed to dup '%s'", token); free(droplist); break; } @@ -1755,45 +1764,16 @@ return ret; } -static int config_console(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_console_path(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_path_item(&lxc_conf->console.path, value); + return set_config_path_item(&lxc_conf->console.path, value); } -static int config_console_logfile(const char *key, const char *value, - struct lxc_conf *lxc_conf) -{ - return config_path_item(&lxc_conf->console.log_path, value); -} - -/* - * If we find a lxc.network.hwaddr in the original config file, - * we expand it in the unexpanded_config, so that after a save_config - * we store the hwaddr for re-use. - * This is only called when reading the config file, not when executing - * a lxc.include. - * 'x' and 'X' are substituted in-place. - */ -static void update_hwaddr(const char *line) +static int set_config_console_logfile(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - char *p; - - line += lxc_char_left_gc(line, strlen(line)); - if (line[0] == '#') - return; - if (strncmp(line, "lxc.network.hwaddr", 18) != 0) - return; - p = strchr(line, '='); - if (!p) - return; // let config_network_hwaddr raise the error - p++; - while (isblank(*p)) - p++; - if (!*p) - return; - - rand_complete_hwaddr(p); + return set_config_path_item(&lxc_conf->console.log_path, value); } int append_unexp_config_line(const char *line, struct lxc_conf *conf) @@ -1803,9 +1783,11 @@ update_hwaddr(line); while (conf->unexpanded_alloced <= len + linelen + 2) { - char *tmp = realloc(conf->unexpanded_config, conf->unexpanded_alloced + 1024); + char *tmp = realloc(conf->unexpanded_config, + conf->unexpanded_alloced + 1024); if (!tmp) return -1; + if (!conf->unexpanded_config) *tmp = '\0'; conf->unexpanded_config = tmp; @@ -1813,10 +1795,11 @@ } strcat(conf->unexpanded_config, line); conf->unexpanded_len += linelen; - if (line[linelen-1] != '\n') { + if (line[linelen - 1] != '\n') { strcat(conf->unexpanded_config, "\n"); conf->unexpanded_len++; } + return 0; } @@ -1825,13 +1808,12 @@ struct dirent *direntp; DIR *dir; char path[MAXPATHLEN]; - int ret = -1, len; + int len; + int ret = -1; dir = opendir(dirp); - if (!dir) { - SYSERROR("failed to open '%s'", dirp); + if (!dir) return -1; - } while ((direntp = readdir(dir))) { const char *fnam; @@ -1846,11 +1828,11 @@ continue; len = strlen(fnam); - if (len < 6 || strncmp(fnam+len-5, ".conf", 5) != 0) + if (len < 6 || strncmp(fnam + len - 5, ".conf", 5) != 0) continue; + len = snprintf(path, MAXPATHLEN, "%s/%s", dirp, fnam); if (len < 0 || len >= MAXPATHLEN) { - ERROR("lxc.include filename too long under '%s'", dirp); ret = -1; goto out; } @@ -1862,75 +1844,64 @@ ret = 0; out: - if (closedir(dir)) - WARN("lxc.include dir: failed to close directory"); + closedir(dir); return ret; } -static int config_includefile(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_includefiles(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + if (lxc_config_value_empty(value)) { + clr_config_includefiles(key, lxc_conf, NULL); + return 0; + } + if (is_dir(value)) return do_includedir(value, lxc_conf); return lxc_config_read(value, lxc_conf, true); } -static int config_rootfs(const char *key, const char *value, - struct lxc_conf *lxc_conf) -{ - return config_path_item(&lxc_conf->rootfs.path, value); -} - -static int config_rootfs_mount(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_rootfs_path(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_path_item(&lxc_conf->rootfs.mount, value); + return set_config_path_item(&lxc_conf->rootfs.path, value); } -static int config_rootfs_options(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_rootfs_mount(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - return config_string_item(&lxc_conf->rootfs.options, value); + return set_config_path_item(&lxc_conf->rootfs.mount, value); } -static int config_rootfs_backend(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_rootfs_options(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - if (strlen(value) == 0) { - free(lxc_conf->rootfs.bdev_type); - lxc_conf->rootfs.bdev_type = NULL; - } - if (!is_valid_bdev_type(value)) { - ERROR("Bad rootfs.backend: '%s'", value); - return -1; - } - - return config_string_item(&lxc_conf->rootfs.bdev_type, value); + return set_config_string_item(&lxc_conf->rootfs.options, value); } -static int config_pivotdir(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_rootfs_backend(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { - WARN("lxc.pivotdir is ignored. It will soon become an error."); return 0; } -static int config_utsname(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_uts_name(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { struct utsname *utsname; + if (lxc_config_value_empty(value)) { + clr_config_uts_name(key, lxc_conf, NULL); + return 0; + } + utsname = malloc(sizeof(*utsname)); - if (!utsname) { - SYSERROR("failed to allocate memory"); + if (!utsname) return -1; - } if (strlen(value) >= sizeof(utsname->nodename)) { - ERROR("node name '%s' is too long", - value); free(utsname); return -1; } @@ -1950,25 +1921,20 @@ static int parse_line(char *buffer, void *data) { struct lxc_config_t *config; - char *line, *linep; - char *dot; - char *key; - char *value; - int ret = 0; + char *dot, *key, *line, *linep, *value; struct parse_line_conf *plc = data; + int ret = 0; if (lxc_is_line_empty(buffer)) return 0; - /* we have to dup the buffer otherwise, at the re-exec for - * reboot we modified the original string on the stack by - * replacing '=' by '\0' below + /* We have to dup the buffer otherwise, at the re-exec for reboot we + * modified the original string on the stack by replacing '=' by '\0' + * below. */ linep = line = strdup(buffer); - if (!line) { - SYSERROR("failed to allocate memory for '%s'", buffer); + if (!line) return -1; - } if (!plc->from_include) if ((ret = append_unexp_config_line(line, plc->conf))) @@ -2002,20 +1968,35 @@ value[lxc_char_right_gc(value, strlen(value))] = '\0'; if (*value == '\'' || *value == '\"') { - size_t len = strlen(value); - if (len > 1 && value[len-1] == *value) { - value[len-1] = '\0'; + size_t len; + + len = strlen(value); + if (len > 1 && value[len - 1] == *value) { + value[len - 1] = '\0'; value++; } } - config = lxc_getconfig(key); + config = lxc_get_config(key); if (!config) { - ERROR("unknown key %s", key); + ERROR("Unknown configuration key \"%s\"", key); goto out; } - ret = config->cb(key, value, plc->conf); + /* [START]: REMOVE IN LXC 3.0 */ + if (config->is_legacy_key && !plc->conf->contains_legacy_key) { + plc->conf->contains_legacy_key = true; + /* Warn the user once loud and clear that there is at least one + * legacy configuration item in the configuration file and then + * an update is required. + */ + fprintf(stderr, "The configuration file contains legacy " + "configuration keys.\nPlease update your " + "configuration file!\n"); + } + /* [END]: REMOVE IN LXC 3.0 */ + + ret = config->set(key, value, plc->conf, NULL); out: free(linep); @@ -2034,23 +2015,24 @@ int lxc_config_read(const char *file, struct lxc_conf *conf, bool from_include) { + int ret; struct parse_line_conf c; c.conf = conf; c.from_include = from_include; - if( access(file, R_OK) == -1 ) { + ret = access(file, R_OK); + if (ret < 0) return -1; - } - /* Catch only the top level config file name in the structure */ - if( ! conf->rcfile ) - conf->rcfile = strdup( file ); + /* Catch only the top level config file name in the structure. */ + if (!conf->rcfile) + conf->rcfile = strdup(file); return lxc_file_for_each_line(file, parse_line, &c); } -int lxc_config_define_add(struct lxc_list *defines, char* arg) +int lxc_config_define_add(struct lxc_list *defines, char *arg) { struct lxc_list *dent; @@ -2065,7 +2047,7 @@ int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf) { - struct lxc_list *it,*next; + struct lxc_list *it, *next; int ret = 0; lxc_list_for_each(it, defines) { @@ -2084,48 +2066,47 @@ signed long lxc_config_parse_arch(const char *arch) { - #if HAVE_SYS_PERSONALITY_H +#if HAVE_SYS_PERSONALITY_H + size_t i; struct per_name { char *name; unsigned long per; } pername[] = { - { "x86", PER_LINUX32 }, - { "linux32", PER_LINUX32 }, - { "i386", PER_LINUX32 }, - { "i486", PER_LINUX32 }, - { "i586", PER_LINUX32 }, - { "i686", PER_LINUX32 }, - { "athlon", PER_LINUX32 }, - { "mips", PER_LINUX32 }, - { "mipsel", PER_LINUX32 }, - { "ppc", PER_LINUX32 }, - { "arm", PER_LINUX32 }, - { "armv7l", PER_LINUX32 }, - { "armhf", PER_LINUX32 }, - { "armel", PER_LINUX32 }, - { "powerpc", PER_LINUX32 }, - { "linux64", PER_LINUX }, - { "x86_64", PER_LINUX }, - { "amd64", PER_LINUX }, - { "mips64", PER_LINUX }, - { "mips64el", PER_LINUX }, - { "ppc64", PER_LINUX }, - { "ppc64le", PER_LINUX }, - { "ppc64el", PER_LINUX }, - { "powerpc64", PER_LINUX }, - { "s390x", PER_LINUX }, - { "aarch64", PER_LINUX }, - { "arm64", PER_LINUX }, + { "x86", PER_LINUX32 }, + { "linux32", PER_LINUX32 }, + { "i386", PER_LINUX32 }, + { "i486", PER_LINUX32 }, + { "i586", PER_LINUX32 }, + { "i686", PER_LINUX32 }, + { "athlon", PER_LINUX32 }, + { "mips", PER_LINUX32 }, + { "mipsel", PER_LINUX32 }, + { "ppc", PER_LINUX32 }, + { "arm", PER_LINUX32 }, + { "armv7l", PER_LINUX32 }, + { "armhf", PER_LINUX32 }, + { "armel", PER_LINUX32 }, + { "powerpc", PER_LINUX32 }, + { "linux64", PER_LINUX }, + { "x86_64", PER_LINUX }, + { "amd64", PER_LINUX }, + { "mips64", PER_LINUX }, + { "mips64el", PER_LINUX }, + { "ppc64", PER_LINUX }, + { "ppc64le", PER_LINUX }, + { "ppc64el", PER_LINUX }, + { "powerpc64", PER_LINUX }, + { "s390x", PER_LINUX }, + { "aarch64", PER_LINUX }, + { "arm64", PER_LINUX }, }; size_t len = sizeof(pername) / sizeof(pername[0]); - size_t i; - for (i = 0; i < len; i++) { if (!strcmp(pername[i].name, arch)) - return pername[i].per; + return pername[i].per; } - #endif +#endif return -1; } @@ -2134,29 +2115,32 @@ { char *token, *saveptr = NULL; int i, aflag; - struct { const char *token; int flag; } all_privs[] = { - { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, - { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, - { "LSM", LXC_ATTACH_LSM_EXEC }, - { NULL, 0 } + struct { + const char *token; + int flag; + } all_privs[] = { + { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, + { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, + { "LSM", LXC_ATTACH_LSM_EXEC }, + { NULL, 0 } }; if (!flaglist) { - /* for the sake of backward compatibility, drop all privileges - if none is specified */ - for (i = 0; all_privs[i].token; i++) { + /* For the sake of backward compatibility, drop all privileges + * if none is specified. + */ + for (i = 0; all_privs[i].token; i++) *flags |= all_privs[i].flag; - } + return 0; } token = strtok_r(flaglist, "|", &saveptr); while (token) { aflag = -1; - for (i = 0; all_privs[i].token; i++) { + for (i = 0; all_privs[i].token; i++) if (!strcmp(all_privs[i].token, token)) aflag = all_privs[i].flag; - } if (aflag < 0) return -1; @@ -2164,494 +2148,38 @@ token = strtok_r(NULL, "|", &saveptr); } - return 0; -} - -static int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) -{ - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - return snprintf(retv, inlen, "%d", v); -} - -static int lxc_get_arch_entry(struct lxc_conf *c, char *retv, int inlen) -{ - int fulllen = 0; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - #if HAVE_SYS_PERSONALITY_H - int len = 0; - - switch(c->personality) { - case PER_LINUX32: strprint(retv, inlen, "i686"); break; - case PER_LINUX: strprint(retv, inlen, "x86_64"); break; - default: break; - } - #endif - - return fulllen; -} - -/* - * If you ask for a specific cgroup value, i.e. lxc.cgroup.devices.list, - * then just the value(s) will be printed. Since there still could be - * more than one, it is newline-separated. - * (Maybe that's ambigous, since some values, i.e. devices.list, will - * already have newlines?) - * If you ask for 'lxc.cgroup", then all cgroup entries will be printed, - * in 'lxc.cgroup.subsystem.key = value' format. - */ -static int lxc_get_cgroup_entry(struct lxc_conf *c, char *retv, int inlen, - const char *key) -{ - int fulllen = 0, len; - int all = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - if (strcmp(key, "all") == 0) - all = 1; - - lxc_list_for_each(it, &c->cgroup) { - struct lxc_cgroup *cg = it->elem; - if (all) { - strprint(retv, inlen, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); - } else if (strcmp(cg->subsystem, key) == 0) { - strprint(retv, inlen, "%s\n", cg->value); - } - } - return fulllen; -} - -static int lxc_get_item_hooks(struct lxc_conf *c, char *retv, int inlen, - const char *key) -{ - char *subkey; - int len, fulllen = 0, found = -1; - struct lxc_list *it; - int i; - - /* "lxc.hook.mount" */ - subkey = strchr(key, '.'); - if (subkey) subkey = strchr(subkey+1, '.'); - if (!subkey) - return -1; - subkey++; - if (!*subkey) - return -1; - for (i=0; ihooks[found]) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_item_groups(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->groups) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_item_environment(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->environment) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_item_cap_drop(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->caps) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_item_cap_keep(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->keepcaps) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->mount_list) { - strprint(retv, inlen, "%s\n", (char *)it->elem); - } - return fulllen; -} - -static int lxc_get_auto_mounts(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - const char *sep = ""; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - if (!(c->auto_mounts & LXC_AUTO_ALL_MASK)) - return 0; - - switch (c->auto_mounts & LXC_AUTO_PROC_MASK) { - case LXC_AUTO_PROC_MIXED: strprint(retv, inlen, "%sproc:mixed", sep); sep = " "; break; - case LXC_AUTO_PROC_RW: strprint(retv, inlen, "%sproc:rw", sep); sep = " "; break; - default: break; - } - switch (c->auto_mounts & LXC_AUTO_SYS_MASK) { - case LXC_AUTO_SYS_RO: strprint(retv, inlen, "%ssys:ro", sep); sep = " "; break; - case LXC_AUTO_SYS_RW: strprint(retv, inlen, "%ssys:rw", sep); sep = " "; break; - case LXC_AUTO_SYS_MIXED: strprint(retv, inlen, "%ssys:mixed", sep); sep = " "; break; - default: break; - } - switch (c->auto_mounts & LXC_AUTO_CGROUP_MASK) { - case LXC_AUTO_CGROUP_NOSPEC: strprint(retv, inlen, "%scgroup", sep); sep = " "; break; - case LXC_AUTO_CGROUP_MIXED: strprint(retv, inlen, "%scgroup:mixed", sep); sep = " "; break; - case LXC_AUTO_CGROUP_RO: strprint(retv, inlen, "%scgroup:ro", sep); sep = " "; break; - case LXC_AUTO_CGROUP_RW: strprint(retv, inlen, "%scgroup:rw", sep); sep = " "; break; - case LXC_AUTO_CGROUP_FULL_NOSPEC: strprint(retv, inlen, "%scgroup-full", sep); sep = " "; break; - case LXC_AUTO_CGROUP_FULL_MIXED: strprint(retv, inlen, "%scgroup-full:mixed", sep); sep = " "; break; - case LXC_AUTO_CGROUP_FULL_RO: strprint(retv, inlen, "%scgroup-full:ro", sep); sep = " "; break; - case LXC_AUTO_CGROUP_FULL_RW: strprint(retv, inlen, "%scgroup-full:rw", sep); sep = " "; break; - default: break; - } - - return fulllen; -} - -/* - * lxc.network.0.XXX, where XXX can be: name, type, link, flags, type, - * macvlan.mode, veth.pair, vlan, ipv4, ipv6, script.up, hwaddr, mtu, - * ipv4.gateway, ipv6.gateway. ipvX.gateway can return 'auto' instead - * of an address. ipv4 and ipv6 return lists (newline-separated). - * things like veth.pair return '' if invalid (i.e. if called for vlan - * type). - */ -static int lxc_get_item_nic(struct lxc_conf *c, char *retv, int inlen, - const char *key) -{ - char *p1; - int len, fulllen = 0; - struct lxc_netdev *netdev; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - p1 = strchr(key, '.'); - if (!p1 || *(p1+1) == '\0') return -1; - p1++; - - netdev = get_netdev_from_key(key, &c->network); - if (!netdev) - return -1; - if (strcmp(p1, "name") == 0) { - if (netdev->name) - strprint(retv, inlen, "%s", netdev->name); - } else if (strcmp(p1, "type") == 0) { - strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); - } else if (strcmp(p1, "link") == 0) { - if (netdev->link) - strprint(retv, inlen, "%s", netdev->link); - } else if (strcmp(p1, "flags") == 0) { - if (netdev->flags & IFF_UP) - strprint(retv, inlen, "up"); - } else if (strcmp(p1, "script.up") == 0) { - if (netdev->upscript) - strprint(retv, inlen, "%s", netdev->upscript); - } else if (strcmp(p1, "script.down") == 0) { - if (netdev->downscript) - strprint(retv, inlen, "%s", netdev->downscript); - } else if (strcmp(p1, "hwaddr") == 0) { - if (netdev->hwaddr) - strprint(retv, inlen, "%s", netdev->hwaddr); - } else if (strcmp(p1, "mtu") == 0) { - if (netdev->mtu) - strprint(retv, inlen, "%s", netdev->mtu); - } else if (strcmp(p1, "macvlan.mode") == 0) { - if (netdev->type == LXC_NET_MACVLAN) { - const char *mode; - switch (netdev->priv.macvlan_attr.mode) { - case MACVLAN_MODE_PRIVATE: mode = "private"; break; - case MACVLAN_MODE_VEPA: mode = "vepa"; break; - case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; - case MACVLAN_MODE_PASSTHRU: mode = "passthru"; break; - default: mode = "(invalid)"; break; - } - strprint(retv, inlen, "%s", mode); - } - } else if (strcmp(p1, "veth.pair") == 0) { - if (netdev->type == LXC_NET_VETH) { - strprint(retv, inlen, "%s", - netdev->priv.veth_attr.pair ? - netdev->priv.veth_attr.pair : - netdev->priv.veth_attr.veth1); - } - } else if (strcmp(p1, "vlan") == 0) { - if (netdev->type == LXC_NET_VLAN) { - strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); - } - } else if (strcmp(p1, "ipv4.gateway") == 0) { - if (netdev->ipv4_gateway_auto) { - strprint(retv, inlen, "auto"); - } else if (netdev->ipv4_gateway) { - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); - strprint(retv, inlen, "%s", buf); - } - } else if (strcmp(p1, "ipv4") == 0) { - struct lxc_list *it2; - lxc_list_for_each(it2, &netdev->ipv4) { - struct lxc_inetdev *i = it2->elem; - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); - strprint(retv, inlen, "%s/%d\n", buf, i->prefix); - } - } else if (strcmp(p1, "ipv6.gateway") == 0) { - if (netdev->ipv6_gateway_auto) { - strprint(retv, inlen, "auto"); - } else if (netdev->ipv6_gateway) { - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); - strprint(retv, inlen, "%s", buf); - } - } else if (strcmp(p1, "ipv6") == 0) { - struct lxc_list *it2; - lxc_list_for_each(it2, &netdev->ipv6) { - struct lxc_inet6dev *i = it2->elem; - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); - strprint(retv, inlen, "%s/%d\n", buf, i->prefix); - } - } - return fulllen; -} - -static int lxc_get_item_network(struct lxc_conf *c, char *retv, int inlen) -{ - int len, fulllen = 0; - struct lxc_list *it; - - if (!retv) - inlen = 0; - else - memset(retv, 0, inlen); - - lxc_list_for_each(it, &c->network) { - struct lxc_netdev *n = it->elem; - const char *t = lxc_net_type_to_str(n->type); - strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); - } - return fulllen; -} -int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv, - int inlen) -{ - const char *v = NULL; - - if (strcmp(key, "lxc.mount.entry") == 0) - return lxc_get_mount_entries(c, retv, inlen); - else if (strcmp(key, "lxc.mount.auto") == 0) - return lxc_get_auto_mounts(c, retv, inlen); - else if (strcmp(key, "lxc.mount") == 0) - v = c->fstab; - else if (strcmp(key, "lxc.tty") == 0) - return lxc_get_conf_int(c, retv, inlen, c->tty); - else if (strcmp(key, "lxc.pts") == 0) - return lxc_get_conf_int(c, retv, inlen, c->pts); - else if (strcmp(key, "lxc.devttydir") == 0) - v = c->ttydir; - else if (strcmp(key, "lxc.arch") == 0) - return lxc_get_arch_entry(c, retv, inlen); - else if (strcmp(key, "lxc.aa_profile") == 0) - v = c->lsm_aa_profile; - else if (strcmp(key, "lxc.aa_allow_incomplete") == 0) - return lxc_get_conf_int(c, retv, inlen, c->lsm_aa_allow_incomplete); - else if (strcmp(key, "lxc.se_context") == 0) - v = c->lsm_se_context; - else if (strcmp(key, "lxc.logfile") == 0) - v = c->logfile; - else if (strcmp(key, "lxc.loglevel") == 0) - v = lxc_log_priority_to_string(c->loglevel); - else if (strcmp(key, "lxc.cgroup") == 0) // all cgroup info - return lxc_get_cgroup_entry(c, retv, inlen, "all"); - else if (strncmp(key, "lxc.cgroup.", 11) == 0) // specific cgroup info - return lxc_get_cgroup_entry(c, retv, inlen, key + 11); - else if (strcmp(key, "lxc.utsname") == 0) - v = c->utsname ? c->utsname->nodename : NULL; - else if (strcmp(key, "lxc.console.logfile") == 0) - v = c->console.log_path; - else if (strcmp(key, "lxc.console") == 0) - v = c->console.path; - else if (strcmp(key, "lxc.rootfs.mount") == 0) - v = c->rootfs.mount; - else if (strcmp(key, "lxc.rootfs.backend") == 0) - v = c->rootfs.bdev_type; - else if (strcmp(key, "lxc.rootfs.options") == 0) - v = c->rootfs.options; - else if (strcmp(key, "lxc.rootfs") == 0) - v = c->rootfs.path; - else if (strcmp(key, "lxc.cap.drop") == 0) - return lxc_get_item_cap_drop(c, retv, inlen); - else if (strcmp(key, "lxc.cap.keep") == 0) - return lxc_get_item_cap_keep(c, retv, inlen); - else if (strncmp(key, "lxc.hook", 8) == 0) - return lxc_get_item_hooks(c, retv, inlen, key); - else if (strcmp(key, "lxc.network") == 0) - return lxc_get_item_network(c, retv, inlen); - else if (strncmp(key, "lxc.network.", 12) == 0) - return lxc_get_item_nic(c, retv, inlen, key + 12); - else if (strcmp(key, "lxc.start.auto") == 0) - return lxc_get_conf_int(c, retv, inlen, c->start_auto); - else if (strcmp(key, "lxc.start.delay") == 0) - return lxc_get_conf_int(c, retv, inlen, c->start_delay); - else if (strcmp(key, "lxc.start.order") == 0) - return lxc_get_conf_int(c, retv, inlen, c->start_order); - else if (strcmp(key, "lxc.monitor.unshare") == 0) - return lxc_get_conf_int(c, retv, inlen, c->monitor_unshare); - else if (strcmp(key, "lxc.group") == 0) - return lxc_get_item_groups(c, retv, inlen); - else if (strcmp(key, "lxc.seccomp") == 0) - v = c->seccomp; - else if (strcmp(key, "lxc.environment") == 0) - return lxc_get_item_environment(c, retv, inlen); - else if (strcmp(key, "lxc.init_cmd") == 0) - v = c->init_cmd; - else if (strcmp(key, "lxc.init_uid") == 0) - return lxc_get_conf_int(c, retv, inlen, c->init_uid); - else if (strcmp(key, "lxc.init_gid") == 0) - return lxc_get_conf_int(c, retv, inlen, c->init_gid); - else if (strcmp(key, "lxc.ephemeral") == 0) - return lxc_get_conf_int(c, retv, inlen, c->ephemeral); - else return -1; - - if (!v) - return 0; - if (retv && inlen >= strlen(v) + 1) - strncpy(retv, v, strlen(v)+1); - return strlen(v); -} - -int lxc_clear_config_item(struct lxc_conf *c, const char *key) -{ - if (strcmp(key, "lxc.network") == 0) - return lxc_clear_config_network(c); - else if (strncmp(key, "lxc.network.", 12) == 0) - return lxc_clear_nic(c, key + 12); - else if (strcmp(key, "lxc.cap.drop") == 0) - return lxc_clear_config_caps(c); - else if (strcmp(key, "lxc.cap.keep") == 0) - return lxc_clear_config_keepcaps(c); - else if (strncmp(key, "lxc.cgroup", 10) == 0) - return lxc_clear_cgroups(c, key); - else if (strcmp(key, "lxc.mount.entry") == 0) - return lxc_clear_mount_entries(c); - else if (strcmp(key, "lxc.mount.auto") == 0) - return lxc_clear_automounts(c); - else if (strncmp(key, "lxc.hook", 8) == 0) - return lxc_clear_hooks(c, key); - else if (strncmp(key, "lxc.group", 9) == 0) - return lxc_clear_groups(c); - else if (strncmp(key, "lxc.environment", 15) == 0) - return lxc_clear_environment(c); - else if (strncmp(key, "lxc.id_map", 10) == 0) - return lxc_clear_idmaps(c); - return -1; + return 0; } -/* - * writing out a confile. - */ +/* Write out a configuration file. */ void write_config(FILE *fout, struct lxc_conf *c) { - size_t len = c->unexpanded_len; int ret; + size_t len = c->unexpanded_len; if (!len) return; + ret = fwrite(c->unexpanded_config, 1, len, fout); if (ret != len) - SYSERROR("Error writing configuration file"); + SYSERROR("Failed to write configuration file"); } -bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, const char *v) +bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, + const char *v) { int ret; - size_t len = strlen(key) + strlen(v) + 4; - char *tmp = alloca(len); + size_t len; + char *tmp; - ret = snprintf(tmp, len, "%s = %s", key, v); + len = strlen(key) + strlen(v) + 4; + tmp = alloca(len); + + if (lxc_config_value_empty(v)) + ret = snprintf(tmp, len, "%s =", key); + else + ret = snprintf(tmp, len, "%s = %s", key, v); if (ret < 0 || ret >= len) return false; @@ -2662,12 +2190,15 @@ return true; } -void clear_unexp_config_line(struct lxc_conf *conf, const char *key, bool rm_subkeys) +void clear_unexp_config_line(struct lxc_conf *conf, const char *key, + bool rm_subkeys) { - char *lstart = conf->unexpanded_config, *lend; + char *lend; + char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return; + while (*lstart) { lend = strchr(lstart, '\n'); char v; @@ -2691,7 +2222,7 @@ *lstart = '\0'; return; } - memmove(lstart, lend, strlen(lend)+1); + memmove(lstart, lend, strlen(lend) + 1); } } @@ -2699,58 +2230,65 @@ const char *newpath, const char *oldname, const char *newname, const char *ovldir) { - const char *key = "lxc.mount.entry"; int ret; + char *lend, *newdir, *olddir, *p, *q; + size_t newdirlen, olddirlen; char *lstart = conf->unexpanded_config; - char *lend; - char *p; - char *q; - size_t newdirlen = strlen(ovldir) + strlen(newpath) + strlen(newname) + 2; - size_t olddirlen = strlen(ovldir) + strlen(oldpath) + strlen(oldname) + 2; - char *olddir = alloca(olddirlen + 1); - char *newdir = alloca(newdirlen + 1); - - ret = snprintf(olddir, olddirlen + 1, "%s=%s/%s", ovldir, oldpath, oldname); - if (ret < 0 || ret >= olddirlen + 1) { - ERROR("Bug in %s", __func__); + const char *key = "lxc.mount.entry"; + + olddirlen = strlen(ovldir) + strlen(oldpath) + strlen(oldname) + 2; + olddir = alloca(olddirlen + 1); + ret = snprintf(olddir, olddirlen + 1, "%s=%s/%s", ovldir, oldpath, + oldname); + if (ret < 0 || ret >= olddirlen + 1) return false; - } - ret = snprintf(newdir, newdirlen + 1, "%s=%s/%s", ovldir, newpath, newname); - if (ret < 0 || ret >= newdirlen + 1) { - ERROR("Bug in %s", __func__); + + newdirlen = strlen(ovldir) + strlen(newpath) + strlen(newname) + 2; + newdir = alloca(newdirlen + 1); + ret = snprintf(newdir, newdirlen + 1, "%s=%s/%s", ovldir, newpath, + newname); + if (ret < 0 || ret >= newdirlen + 1) return false; - } + if (!conf->unexpanded_config) return true; + while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; + if (strncmp(lstart, key, strlen(key)) != 0) - goto next; + goto next; + p = strchr(lstart + strlen(key), '='); if (!p) - goto next; + goto next; p++; + while (isblank(*p)) p++; + if (p >= lend) - goto next; - /* Whenever an lxc.mount.entry entry is found in a line we check - * if the substring " overlay" or the substring " aufs" is - * present before doing any further work. We check for " - * overlay" and " aufs" since both substrings need to have at - * least one space before them in a valid overlay - * lxc.mount.entry (/A B overlay). When the space before is - * missing it is very likely that these substrings are part of a - * path or something else. (Checking q >= lend ensures that we - * only count matches in the current line.) */ - if ((!(q = strstr(p, " overlay")) || q >= lend) && (!(q = strstr(p, " aufs")) || q >= lend)) - goto next; + goto next; + + /* Whenever an lxc.mount.entry entry is found in a line we check + * if the substring " overlay" or the substring " aufs" is + * present before doing any further work. We check for " + * overlay" and " aufs" since both substrings need to have at + * least one space before them in a valid overlay + * lxc.mount.entry (/A B overlay). When the space before is + * missing it is very likely that these substrings are part of a + * path or something else. (Checking q >= lend ensures that we + * only count matches in the current line.) */ + if ((!(q = strstr(p, " overlay")) || q >= lend) && + (!(q = strstr(p, " aufs")) || q >= lend)) + goto next; + if (!(q = strstr(p, olddir)) || (q >= lend)) - goto next; + goto next; /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { @@ -2768,16 +2306,17 @@ size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = q - conf->unexpanded_config; + new = realloc(conf->unexpanded_config, newlen + 1); - if (!new) { - ERROR("Out of memory"); + if (!new) return false; - } + conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); - /* move over the remainder to make room for the newdir */ + /* Move over the remainder to make room for the newdir. + */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); @@ -2785,9 +2324,10 @@ memcpy(new + poffset, newdir, newdirlen); lend += diff; } -next: - lstart = lend; + next: + lstart = lend; } + return true; } @@ -2795,44 +2335,51 @@ const char *newpath, const char *oldname, const char *newname) { - const char *key = "lxc.hook"; int ret; - char *lstart = conf->unexpanded_config, *lend, *p; - size_t newdirlen = strlen(newpath) + strlen(newname) + 1; - size_t olddirlen = strlen(oldpath) + strlen(oldname) + 1; - char *olddir = alloca(olddirlen + 1); - char *newdir = alloca(newdirlen + 1); - + char *lend, *newdir, *olddir, *p; + char *lstart = conf->unexpanded_config; + size_t newdirlen, olddirlen; + const char *key = "lxc.hook"; + + olddirlen = strlen(oldpath) + strlen(oldname) + 1; + olddir = alloca(olddirlen + 1); ret = snprintf(olddir, olddirlen + 1, "%s/%s", oldpath, oldname); - if (ret < 0 || ret >= olddirlen + 1) { - ERROR("Bug in %s", __func__); + if (ret < 0 || ret >= olddirlen + 1) return false; - } + + newdirlen = strlen(newpath) + strlen(newname) + 1; + newdir = alloca(newdirlen + 1); ret = snprintf(newdir, newdirlen + 1, "%s/%s", newpath, newname); - if (ret < 0 || ret >= newdirlen + 1) { - ERROR("Bug in %s", __func__); + if (ret < 0 || ret >= newdirlen + 1) return false; - } + if (!conf->unexpanded_config) return true; + while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; + if (strncmp(lstart, key, strlen(key)) != 0) - goto next; + goto next; + p = strchr(lstart + strlen(key), '='); if (!p) - goto next; + goto next; p++; + while (isblank(*p)) p++; - if (p >= lend) - goto next; + + if (p >= lend) + goto next; + if (strncmp(p, olddir, strlen(olddir)) != 0) - goto next; + goto next; + /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { size_t diff = olddirlen - newdirlen; @@ -2849,16 +2396,17 @@ size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = p - conf->unexpanded_config; + new = realloc(conf->unexpanded_config, newlen + 1); - if (!new) { - ERROR("Out of memory"); + if (!new) return false; - } + conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); - /* move over the remainder to make room for the newdir */ + /* Move over the remainder to make room for the newdir. + */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); @@ -2866,52 +2414,34 @@ memcpy(new + poffset, newdir, newdirlen); lend += diff; } -next: - lstart = lend; + next: + lstart = lend; } - return true; -} -#define DO(cmd) { \ - if (!(cmd)) { \ - ERROR("Error writing to new config"); \ - return false; \ - } \ + return true; } -static bool new_hwaddr(char *hwaddr) -{ - int ret; - - /* COMMENT(brauner): Initialize random number generator. */ - (void)randseed(true); - - ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255, - rand() % 255, rand() % 255); - if (ret < 0 || ret >= 18) { - SYSERROR("Failed to call snprintf()."); - return false; +#define DO(cmd) \ + { \ + if (!(cmd)) { \ + ERROR("Error writing to new config"); \ + return false; \ + } \ } - return true; -} - -/* - * This is called only from clone. - * We wish to update all hwaddrs in the unexpanded config file. We - * can't/don't want to update any which come from lxc.includes (there - * shouldn't be any). - * We can't just walk the c->lxc-conf->network list because that includes - * netifs from the include files. So we update the ones which we find in - * the unexp config file, then find the original macaddr in the - * conf->network, and update that to the same value. +/* This is called only from clone. We wish to update all hwaddrs in the + * unexpanded config file. We can't/don't want to update any which come from + * lxc.includes (there shouldn't be any). + * We can't just walk the c->lxc-conf->network list because that includes netifs + * from the include files. So we update the ones which we find in the unexp + * config file, then find the original macaddr in the conf->network, and update + * that to the same value. */ bool network_new_hwaddrs(struct lxc_conf *conf) { + char *lend, *p, *p2; struct lxc_list *it; - - const char *key = "lxc.network.hwaddr"; - char *lstart = conf->unexpanded_config, *lend, *p, *p2; + char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return true; @@ -2925,12 +2455,12 @@ else lend++; - if (strncmp(lstart, key, strlen(key)) != 0) { + if (!lxc_config_net_hwaddr(lstart)) { lstart = lend; continue; } - p = strchr(lstart+strlen(key), '='); + p = strchr(lstart, '='); if (!p) { lstart = lend; continue; @@ -2945,7 +2475,8 @@ p2 = p; while (*p2 && !isblank(*p2) && *p2 != '\n') p2++; - if (p2-p != 17) { + + if ((p2 - p) != 17) { WARN("Bad hwaddr entry"); lstart = lend; continue; @@ -2959,6 +2490,7 @@ memcpy(p, newhwaddr, 17); lxc_list_for_each(it, &conf->network) { struct lxc_netdev *n = it->elem; + if (n->hwaddr && memcmp(oldhwaddr, n->hwaddr, 17) == 0) memcpy(n->hwaddr, newhwaddr, 17); } @@ -2969,17 +2501,2021 @@ return true; } -static int config_ephemeral(const char *key, const char *value, - struct lxc_conf *lxc_conf) +static int set_config_ephemeral(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) { + if (lxc_config_value_empty(value)) { + lxc_conf->ephemeral = 0; + return 0; + } + if (lxc_safe_uint(value, &lxc_conf->ephemeral) < 0) return -1; - if (lxc_conf->ephemeral > 1) { - ERROR("Wrong value for lxc.ephemeral. Can only be set to 0 or 1"); + if (lxc_conf->ephemeral > 1) + return -1; + + return 0; +} + +static int set_config_log_syslog(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + int facility; + + if (lxc_conf->syslog) { + free(lxc_conf->syslog); + lxc_conf->syslog = NULL; + } + + if (lxc_config_value_empty(value)) + return 0; + + facility = lxc_syslog_priority_to_int(value); + if (facility == -EINVAL) + return -1; + + lxc_log_syslog(facility); + return set_config_string_item(&lxc_conf->syslog, value); +} + +static int set_config_no_new_privs(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + unsigned int v; + + if (lxc_config_value_empty(value)) { + lxc_conf->no_new_privs = false; + return 0; + } + + if (lxc_safe_uint(value, &v) < 0) + return -1; + + if (v > 1) + return -1; + + lxc_conf->no_new_privs = v ? true : false; + + return 0; +} + +/* Callbacks to get configuration items. */ +static int get_config_personality(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + +#if HAVE_SYS_PERSONALITY_H + int len = 0; + + switch (c->personality) { + case PER_LINUX32: + strprint(retv, inlen, "i686"); + break; + case PER_LINUX: + strprint(retv, inlen, "x86_64"); + break; + default: + break; + } +#endif + + return fulllen; +} + +static int get_config_pty_max(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->pts); +} + +static int get_config_tty_max(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->tty); +} + +static int get_config_tty_dir(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->ttydir); +} + +static int get_config_apparmor_profile(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->lsm_aa_profile); +} + +static int get_config_apparmor_allow_incomplete(const char *key, char *retv, + int inlen, struct lxc_conf *c, + void *data) +{ + return lxc_get_conf_int(c, retv, inlen, + c->lsm_aa_allow_incomplete); +} + +static int get_config_selinux_context(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->lsm_se_context); +} + +/* If you ask for a specific cgroup value, i.e. lxc.cgroup.devices.list, then + * just the value(s) will be printed. Since there still could be more than one, + * it is newline-separated. + * (Maybe that's ambigous, since some values, i.e. devices.list, will already + * have newlines?) + * If you ask for 'lxc.cgroup", then all cgroup entries will be printed, in + * 'lxc.cgroup.subsystem.key = value' format. + */ +static int get_config_cgroup_controller(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + struct lxc_list *it; + int len; + int fulllen = 0; + bool get_all = false; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!strcmp(key, "lxc.cgroup")) + get_all = true; + else if (!strncmp(key, "lxc.cgroup.", 11)) + key += 11; + else return -1; + + lxc_list_for_each(it, &c->cgroup) { + struct lxc_cgroup *cg = it->elem; + + if (get_all) { + strprint(retv, inlen, "lxc.cgroup.%s = %s\n", + cg->subsystem, cg->value); + } else if (!strcmp(cg->subsystem, key)) { + strprint(retv, inlen, "%s\n", cg->value); + } + } + + return fulllen; +} + +static int get_config_cgroup_dir(const char *key, char *retv, int inlen, + struct lxc_conf *lxc_conf, void *data) +{ + int len; + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "%s", lxc_conf->cgroup_meta.dir); + + return fulllen; +} + +static int get_config_idmaps(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + struct lxc_list *it; + int len, listlen, ret; + int fulllen = 0; +/* "u 1000 1000000 65536" + * + * let's render this as + * + * sizeof(char) + * + + * sizeof(" ") + * + + * sizeof(uint64_t) + * + + * sizeof(" ") + * + + * sizeof(uint64_t) + * + + * sizeof(" ") + * + + * sizeof(uint64_t) + * + + * \0 + */ +#define __LXC_IDMAP_STR_BUF (3 * LXC_NUMSTRLEN64 + 3 + 1 + 1) + char buf[__LXC_IDMAP_STR_BUF]; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + listlen = lxc_list_len(&c->id_map); + lxc_list_for_each(it, &c->id_map) + { + struct id_map *map = it->elem; + ret = snprintf(buf, __LXC_IDMAP_STR_BUF, "%c %lu %lu %lu", + (map->idtype == ID_TYPE_UID) ? 'u' : 'g', + map->nsid, map->hostid, map->range); + if (ret < 0 || ret >= __LXC_IDMAP_STR_BUF) + return -1; + + strprint(retv, inlen, "%s%s", buf, (listlen-- > 1) ? "\n" : ""); + } + return fulllen; +} + +static int get_config_log_level(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + const char *v; + v = lxc_log_priority_to_string(c->loglevel); + return lxc_get_conf_str(retv, inlen, v); +} + +static int get_config_log_file(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->logfile); +} + +static int get_config_mount_fstab(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->fstab); +} + +static int get_config_mount_auto(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + const char *sep = ""; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!(c->auto_mounts & LXC_AUTO_ALL_MASK)) + return 0; + + switch (c->auto_mounts & LXC_AUTO_PROC_MASK) { + case LXC_AUTO_PROC_MIXED: + strprint(retv, inlen, "%sproc:mixed", sep); + sep = " "; + break; + case LXC_AUTO_PROC_RW: + strprint(retv, inlen, "%sproc:rw", sep); + sep = " "; + break; + default: + break; + } + + switch (c->auto_mounts & LXC_AUTO_SYS_MASK) { + case LXC_AUTO_SYS_RO: + strprint(retv, inlen, "%ssys:ro", sep); + sep = " "; + break; + case LXC_AUTO_SYS_RW: + strprint(retv, inlen, "%ssys:rw", sep); + sep = " "; + break; + case LXC_AUTO_SYS_MIXED: + strprint(retv, inlen, "%ssys:mixed", sep); + sep = " "; + break; + default: + break; + } + + switch (c->auto_mounts & LXC_AUTO_CGROUP_MASK) { + case LXC_AUTO_CGROUP_NOSPEC: + strprint(retv, inlen, "%scgroup", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_MIXED: + strprint(retv, inlen, "%scgroup:mixed", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_RO: + strprint(retv, inlen, "%scgroup:ro", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_RW: + strprint(retv, inlen, "%scgroup:rw", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_FULL_NOSPEC: + strprint(retv, inlen, "%scgroup-full", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_FULL_MIXED: + strprint(retv, inlen, "%scgroup-full:mixed", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_FULL_RO: + strprint(retv, inlen, "%scgroup-full:ro", sep); + sep = " "; + break; + case LXC_AUTO_CGROUP_FULL_RW: + strprint(retv, inlen, "%scgroup-full:rw", sep); + sep = " "; + break; + default: + break; + } + + return fulllen; +} + +static int get_config_mount(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->mount_list) { + strprint(retv, inlen, "%s\n", (char *)it->elem); } + return fulllen; +} + +static int get_config_rootfs_path(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->rootfs.path); +} + +static int get_config_rootfs_mount(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->rootfs.mount); +} + +static int get_config_rootfs_options(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->rootfs.options); +} + +static int get_config_rootfs_backend(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ return 0; } +static int get_config_uts_name(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str( + retv, inlen, + c->utsname ? c->utsname->nodename : NULL); +} + +static int get_config_hooks(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + char *subkey; + int len, fulllen = 0, found = -1; + struct lxc_list *it; + int i; + + subkey = strchr(key, '.'); + if (subkey) + subkey = strchr(subkey + 1, '.'); + if (!subkey) + return -1; + subkey++; + if (!*subkey) + return -1; + for (i = 0; i < NUM_LXC_HOOKS; i++) { + if (strcmp(lxchook_names[i], subkey) == 0) { + found = i; + break; + } + } + if (found == -1) + return -1; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->hooks[found]) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int get_config_net(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); + } + + return fulllen; +} + +static int get_config_cap_drop(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->caps) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +static int get_config_cap_keep(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->keepcaps) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +static int get_config_console_path(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->console.path); +} + +static int get_config_console_logfile(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->console.log_path); +} + +static int get_config_seccomp_profile(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->seccomp); +} + +static int get_config_autodev(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->autodev); +} + +static int get_config_signal_halt(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->haltsignal); +} + +static int get_config_signal_reboot(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->rebootsignal); +} + +static int get_config_signal_stop(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->stopsignal); +} + +static int get_config_start(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + if (strcmp(key + 10, "auto") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_auto); + else if (strcmp(key + 10, "delay") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_delay); + else if (strcmp(key + 10, "order") == 0) + return lxc_get_conf_int(c, retv, inlen, c->start_order); + + return -1; +} + +static int get_config_log_syslog(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->syslog); +} + +static int get_config_monitor(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->monitor_unshare); +} + +static int get_config_group(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->groups) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +static int get_config_environment(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->environment) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +static int get_config_init_cmd(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->init_cmd); +} + +static int get_config_init_uid(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->init_uid); +} + +static int get_config_init_gid(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->init_gid); +} + +static int get_config_ephemeral(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->ephemeral); +} + +static int get_config_no_new_privs(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, c->no_new_privs); +} + +/* If you ask for a specific value, i.e. lxc.prlimit.nofile, then just the value + * will be printed. If you ask for 'lxc.prlimit', then all limit entries will be + * printed, in 'lxc.prlimit.resource = value' format. + */ +static int get_config_prlimit(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int fulllen = 0, len; + bool get_all = false; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!strcmp(key, "lxc.prlimit")) + get_all = true; + else if (strncmp(key, "lxc.prlimit.", 12) == 0) + key += 12; + else + return -1; + + lxc_list_for_each(it, &c->limits) { + char buf[LXC_NUMSTRLEN64 * 2 + 2]; /* 2 colon separated 64 bit + integers or the word + 'unlimited' */ + int partlen; + struct lxc_limit *lim = it->elem; + + if (lim->limit.rlim_cur == RLIM_INFINITY) { + memcpy(buf, "unlimited", sizeof("unlimited")); + partlen = sizeof("unlimited") - 1; + } else { + partlen = sprintf(buf, "%" PRIu64, + (uint64_t)lim->limit.rlim_cur); + } + if (lim->limit.rlim_cur != lim->limit.rlim_max) { + if (lim->limit.rlim_max == RLIM_INFINITY) + memcpy(buf + partlen, ":unlimited", + sizeof(":unlimited")); + else + sprintf(buf + partlen, ":%" PRIu64, + (uint64_t)lim->limit.rlim_max); + } + + if (get_all) { + strprint(retv, inlen, "lxc.prlimit.%s = %s\n", + lim->resource, buf); + } else if (!strcmp(lim->resource, key)) { + strprint(retv, inlen, "%s", buf); + } + } + + return fulllen; +} + +/* Callbacks to clear config items. */ +static inline int clr_config_personality(const char *key, struct lxc_conf *c, + void *data) +{ + c->personality = -1; + return 0; +} + +static inline int clr_config_pty_max(const char *key, struct lxc_conf *c, + void *data) +{ + c->pts = 0; + return 0; +} + +static inline int clr_config_tty_max(const char *key, struct lxc_conf *c, + void *data) +{ + c->tty = 0; + return 0; +} + +static inline int clr_config_tty_dir(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->ttydir); + c->ttydir = NULL; + return 0; +} + +static inline int clr_config_apparmor_profile(const char *key, + struct lxc_conf *c, void *data) +{ + free(c->lsm_aa_profile); + c->lsm_aa_profile = NULL; + return 0; +} + +static inline int clr_config_apparmor_allow_incomplete(const char *key, + struct lxc_conf *c, + void *data) +{ + c->lsm_aa_allow_incomplete = 0; + return 0; +} + +static inline int clr_config_selinux_context(const char *key, + struct lxc_conf *c, void *data) +{ + free(c->lsm_se_context); + c->lsm_se_context = NULL; + return 0; +} + +static inline int clr_config_cgroup_controller(const char *key, + struct lxc_conf *c, void *data) +{ + return lxc_clear_cgroups(c, key); +} + +static int clr_config_cgroup_dir(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + if (lxc_conf->cgroup_meta.dir) { + free(lxc_conf->cgroup_meta.dir); + lxc_conf->cgroup_meta.dir = NULL; + } + + return 0; +} + +static inline int clr_config_idmaps(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_idmaps(c); +} + +static inline int clr_config_log_level(const char *key, struct lxc_conf *c, + void *data) +{ + c->loglevel = LXC_LOG_LEVEL_NOTSET; + return 0; +} + +static inline int clr_config_log_file(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->logfile); + c->logfile = NULL; + return 0; +} + +static inline int clr_config_mount(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_mount_entries(c); +} + +static inline int clr_config_mount_auto(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_automounts(c); +} + +static inline int clr_config_mount_fstab(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->fstab); + c->fstab = NULL; + return 0; +} + +static inline int clr_config_rootfs_path(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->rootfs.path); + c->rootfs.path = NULL; + return 0; +} + +static inline int clr_config_rootfs_mount(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->rootfs.mount); + c->rootfs.mount = NULL; + return 0; +} + +static inline int clr_config_rootfs_options(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->rootfs.options); + c->rootfs.options = NULL; + return 0; +} + +static inline int clr_config_rootfs_backend(const char *key, struct lxc_conf *c, + void *data) +{ + return 0; +} + +static inline int clr_config_uts_name(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->utsname); + c->utsname = NULL; + return 0; +} + +static inline int clr_config_hooks(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_hooks(c, key); +} + +static inline int clr_config_net(const char *key, struct lxc_conf *c, + void *data) +{ + lxc_free_networks(&c->network); + + return 0; +} + +static inline int clr_config_cap_drop(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_config_caps(c); +} + +static inline int clr_config_cap_keep(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_config_keepcaps(c); +} + +static inline int clr_config_console_path(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->console.path); + c->console.path = NULL; + return 0; +} + +static inline int clr_config_console_logfile(const char *key, + struct lxc_conf *c, void *data) +{ + free(c->console.log_path); + c->console.log_path = NULL; + return 0; +} + +static inline int clr_config_seccomp_profile(const char *key, + struct lxc_conf *c, void *data) +{ + free(c->seccomp); + c->seccomp = NULL; + return 0; +} + +static inline int clr_config_autodev(const char *key, struct lxc_conf *c, + void *data) +{ + c->autodev = 1; + return 0; +} + +static inline int clr_config_signal_halt(const char *key, struct lxc_conf *c, + void *data) +{ + c->haltsignal = 0; + return 0; +} + +static inline int clr_config_signal_reboot(const char *key, struct lxc_conf *c, + void *data) +{ + c->rebootsignal = 0; + return 0; +} + +static inline int clr_config_signal_stop(const char *key, struct lxc_conf *c, + void *data) +{ + c->stopsignal = 0; + return 0; +} + +static inline int clr_config_start(const char *key, struct lxc_conf *c, + void *data) +{ + if (strcmp(key + 10, "auto") == 0) + c->start_auto = 0; + else if (strcmp(key + 10, "delay") == 0) + c->start_delay = 0; + else if (strcmp(key + 10, "order") == 0) + c->start_order = 0; + + return 0; +} + +static inline int clr_config_log_syslog(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->syslog); + c->syslog = NULL; + return 0; +} + +static inline int clr_config_monitor(const char *key, struct lxc_conf *c, + void *data) +{ + c->monitor_unshare = 0; + return 0; +} + +static inline int clr_config_group(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_groups(c); +} + +static inline int clr_config_environment(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_environment(c); +} + +static inline int clr_config_init_cmd(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->init_cmd); + c->init_cmd = NULL; + return 0; +} + +static inline int clr_config_init_uid(const char *key, struct lxc_conf *c, + void *data) +{ + c->init_uid = 0; + return 0; +} + +static inline int clr_config_init_gid(const char *key, struct lxc_conf *c, + void *data) +{ + c->init_gid = 0; + return 0; +} + +static inline int clr_config_ephemeral(const char *key, struct lxc_conf *c, + void *data) +{ + c->ephemeral = 0; + return 0; +} + +static inline int clr_config_no_new_privs(const char *key, struct lxc_conf *c, + void *data) +{ + c->no_new_privs = false; + return 0; +} + +static inline int clr_config_prlimit(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_limits(c, key); +} + +static inline int clr_config_includefiles(const char *key, struct lxc_conf *c, + void *data) +{ + lxc_clear_includes(c); + return 0; +} + +static int get_config_includefiles(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return -ENOSYS; +} + +static struct lxc_config_t *get_network_config_ops(const char *key, + struct lxc_conf *lxc_conf, + ssize_t *idx, + char **deindexed_key) +{ + int ret; + unsigned int tmpidx; + size_t numstrlen; + char *copy, *idx_start, *idx_end; + struct lxc_config_t *config = NULL; + + /* check that this is a sensible network key */ + if (strncmp("lxc.net.", key, 8)) { + ERROR("Invalid network configuration key \"%s\"", key); + return NULL; + } + + copy = strdup(key); + if (!copy) { + ERROR("Failed to duplicate string \"%s\"", key); + return NULL; + } + + /* lxc.net. */ + if (!isdigit(*(key + 8))) { + ERROR("Failed to detect digit in string \"%s\"", key + 8); + goto on_error; + } + + /* beginning of index string */ + idx_start = (copy + 7); + *idx_start = '\0'; + + /* end of index string */ + idx_end = strchr((copy + 8), '.'); + if (idx_end) + *idx_end = '\0'; + + /* parse current index */ + ret = lxc_safe_uint((idx_start + 1), &tmpidx); + if (ret < 0) { + ERROR("Failed to parse usigned integer from string \"%s\": %s", + idx_start + 1, strerror(-ret)); + *idx = ret; + goto on_error; + } + + /* This, of course is utterly nonsensical on so many levels, but + * better safe than sorry. + * (Checking for INT_MAX here is intentional.) + */ + if (tmpidx == INT_MAX) { + SYSERROR("Number of configured networks would overflow the " + "counter"); + goto on_error; + } + *idx = tmpidx; + + numstrlen = strlen((idx_start + 1)); + + /* repair configuration key */ + *idx_start = '.'; + + /* lxc.net.. */ + if (idx_end) { + *idx_end = '.'; + + memmove(copy + 8, idx_end + 1, strlen(idx_end + 1)); + copy[strlen(key) - numstrlen + 1] = '\0'; + + config = lxc_get_config(copy); + if (!config) { + ERROR("Unknown network configuration key \"%s\"", key); + goto on_error; + } + } + + if (deindexed_key) + *deindexed_key = copy; + + return config; + +on_error: + free(copy); + return NULL; +} + +/* Config entry is something like "lxc.net.0.ipv4" the key 'lxc.net.' was + * found. So we make sure next comes an integer, find the right callback (by + * rewriting the key), and call it. + */ +static int set_config_net_nic(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + int ret; + const char *idxstring; + struct lxc_config_t *config; + struct lxc_netdev *netdev; + ssize_t idx = -1; + char *deindexed_key = NULL; + + idxstring = key + 8; + if (!isdigit(*idxstring)) + return -1; + + if (lxc_config_value_empty(value)) + return clr_config_net_nic(key, lxc_conf, data); + + config = get_network_config_ops(key, lxc_conf, &idx, &deindexed_key); + if (!config || idx < 0) + return -1; + + netdev = lxc_get_netdev_by_idx(lxc_conf, (unsigned int)idx, true); + if (!netdev) { + free(deindexed_key); + return -1; + } + + ret = config->set(deindexed_key, value, lxc_conf, netdev); + free(deindexed_key); + return ret; +} + +static int clr_config_net_nic(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + int ret; + const char *idxstring; + struct lxc_config_t *config; + struct lxc_netdev *netdev; + ssize_t idx = -1; + char *deindexed_key = NULL; + + idxstring = key + 8; + if (!isdigit(*idxstring)) + return -1; + + /* The left conjunct is pretty self-explanatory. The right conjunct + * checks whether the two pointers are equal. If they are we know that + * this is not a key that is namespaced any further and so we are + * supposed to clear the whole network. + */ + if (isdigit(*idxstring) && (strrchr(key, '.') == (idxstring - 1))) { + unsigned int rmnetdevidx; + + if (lxc_safe_uint(idxstring, &rmnetdevidx) < 0) + return -1; + + /* Remove network from network list. */ + lxc_remove_nic_by_idx(lxc_conf, rmnetdevidx); + return 0; + } + + config = get_network_config_ops(key, lxc_conf, &idx, &deindexed_key); + if (!config || idx < 0) + return -1; + + netdev = lxc_get_netdev_by_idx(lxc_conf, (unsigned int)idx, false); + if (!netdev) { + free(deindexed_key); + return -1; + } + + ret = config->clr(deindexed_key, lxc_conf, netdev); + free(deindexed_key); + return ret; +} + +static int clr_config_net_type(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->type = -1; + + return 0; +} + +static int clr_config_net_name(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->name[0] = '\0'; + + return 0; +} + +static int clr_config_net_flags(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->flags = 0; + + return 0; +} + +static int clr_config_net_link(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->link[0] = '\0'; + + return 0; +} + +static int clr_config_net_macvlan_mode(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_MACVLAN) + return 0; + + netdev->priv.macvlan_attr.mode = -1; + + return 0; +} + +static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->priv.veth_attr.pair[0] = '\0'; + + return 0; +} + +static int clr_config_net_script_up(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->upscript); + netdev->upscript = NULL; + + return 0; +} + +static int clr_config_net_script_down(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->downscript); + netdev->downscript = NULL; + + return 0; +} + +static int clr_config_net_hwaddr(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->hwaddr); + netdev->hwaddr = NULL; + + return 0; +} + +static int clr_config_net_mtu(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->mtu); + netdev->mtu = NULL; + + return 0; +} + +static int clr_config_net_vlan_id(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + netdev->priv.vlan_attr.vid = 0; + + return 0; +} + +static int clr_config_net_ipv4_gateway(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->ipv4_gateway); + netdev->ipv4_gateway = NULL; + + return 0; +} + +static int clr_config_net_ipv4_address(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + struct lxc_list *cur, *next; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + lxc_list_for_each_safe(cur, &netdev->ipv4, next) { + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + + return 0; +} + +static int clr_config_net_ipv6_gateway(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + free(netdev->ipv6_gateway); + netdev->ipv6_gateway = NULL; + + return 0; +} + +static int clr_config_net_ipv6_address(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + struct lxc_list *cur, *next; + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + lxc_list_for_each_safe(cur, &netdev->ipv6, next) { + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + + return 0; +} + +static int get_config_net_nic(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int ret; + const char *idxstring; + struct lxc_config_t *config; + struct lxc_netdev *netdev; + ssize_t idx = -1; + char *deindexed_key = NULL; + + idxstring = key + 8; + if (!isdigit(*idxstring)) + return -1; + + config = get_network_config_ops(key, c, &idx, &deindexed_key); + if (!config || idx < 0) + return -1; + + netdev = lxc_get_netdev_by_idx(c, (unsigned int)idx, false); + if (!netdev) { + free(deindexed_key); + return -1; + } + + ret = config->get(deindexed_key, retv, inlen, c, netdev); + free(deindexed_key); + return ret; +} + +static int get_config_net_type(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); + + return fulllen; +} + +static int get_config_net_flags(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->flags & IFF_UP) + strprint(retv, inlen, "up"); + + return fulllen; +} + +static int get_config_net_link(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->link[0] != '\0') + strprint(retv, inlen, "%s", netdev->link); + + return fulllen; +} + +static int get_config_net_name(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->name[0] != '\0') + strprint(retv, inlen, "%s", netdev->name); + + return fulllen; +} + +static int get_config_net_macvlan_mode(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + const char *mode; + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_MACVLAN) + return 0; + + switch (netdev->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: + mode = "private"; + break; + case MACVLAN_MODE_VEPA: + mode = "vepa"; + break; + case MACVLAN_MODE_BRIDGE: + mode = "bridge"; + break; + case MACVLAN_MODE_PASSTHRU: + mode = "passthru"; + break; + default: + mode = "(invalid)"; + break; + } + + strprint(retv, inlen, "%s", mode); + + return fulllen; +} + +static int get_config_net_veth_pair(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_VETH) + return 0; + + strprint(retv, inlen, "%s", + netdev->priv.veth_attr.pair[0] != '\0' + ? netdev->priv.veth_attr.pair + : netdev->priv.veth_attr.veth1); + + return fulllen; +} + +static int get_config_net_script_up(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->upscript) + strprint(retv, inlen, "%s", netdev->upscript); + + return fulllen; +} + +static int get_config_net_script_down(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->downscript) + strprint(retv, inlen, "%s", netdev->downscript); + + return fulllen; +} + +static int get_config_net_hwaddr(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->hwaddr) + strprint(retv, inlen, "%s", netdev->hwaddr); + + return fulllen; +} + +static int get_config_net_mtu(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->mtu) + strprint(retv, inlen, "%s", netdev->mtu); + + return fulllen; +} + +static int get_config_net_vlan_id(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_VLAN) + return 0; + + strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); + + return fulllen; +} + +static int get_config_net_ipv4_gateway(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + char buf[INET_ADDRSTRLEN]; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->ipv4_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv4_gateway) { + inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + + return fulllen; +} + +static int get_config_net_ipv4_address(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + size_t listlen; + char buf[INET_ADDRSTRLEN]; + struct lxc_netdev *netdev; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + listlen = lxc_list_len(&netdev->ipv4); + lxc_list_for_each(it, &netdev->ipv4) { + struct lxc_inetdev *i = it->elem; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s/%u%s", buf, i->prefix, + (listlen-- > 1) ? "\n" : ""); + } + + return fulllen; +} + +static int get_config_net_ipv6_gateway(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + char buf[INET6_ADDRSTRLEN]; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + if (netdev->ipv6_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv6_gateway) { + inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + + return fulllen; +} + +static int get_config_net_ipv6_address(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + size_t listlen; + char buf[INET6_ADDRSTRLEN]; + struct lxc_netdev *netdev; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!data) + return -1; + else + netdev = data; + if (!netdev) + return -1; + + listlen = lxc_list_len(&netdev->ipv6); + lxc_list_for_each(it, &netdev->ipv6) { + struct lxc_inet6dev *i = it->elem; + inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s/%u%s", buf, i->prefix, + (listlen-- > 1) ? "\n" : ""); + } + + return fulllen; +} + +int lxc_list_config_items(char *retv, int inlen) +{ + size_t i; + int len; + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + for (i = 0; i < config_size; i++) { + char *s = config[i].name; + + if (s[strlen(s) - 1] == '.') + continue; + + strprint(retv, inlen, "%s\n", s); + } + + return fulllen; +} + +int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv, + int inlen) +{ + int len; + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!strcmp(key, "lxc.apparmor")) { + strprint(retv, inlen, "allow_incomplete\n"); + strprint(retv, inlen, "profile\n"); + } else if (!strcmp(key, "lxc.cgroup")) { + strprint(retv, inlen, "dir\n"); + } else if (!strcmp(key, "lxc.selinux")) { + strprint(retv, inlen, "context\n"); + } else if (!strcmp(key, "lxc.mount")) { + strprint(retv, inlen, "auto\n"); + strprint(retv, inlen, "entry\n"); + strprint(retv, inlen, "fstab\n"); + } else if (!strcmp(key, "lxc.rootfs")) { + strprint(retv, inlen, "mount\n"); + strprint(retv, inlen, "options\n"); + strprint(retv, inlen, "path\n"); + } else if (!strcmp(key, "lxc.uts")) { + strprint(retv, inlen, "name\n"); + } else if (!strcmp(key, "lxc.hook")) { + strprint(retv, inlen, "autodev\n"); + strprint(retv, inlen, "clone\n"); + strprint(retv, inlen, "destroy\n"); + strprint(retv, inlen, "mount\n"); + strprint(retv, inlen, "post-stop\n"); + strprint(retv, inlen, "pre-mount\n"); + strprint(retv, inlen, "pre-start\n"); + strprint(retv, inlen, "start\n"); + strprint(retv, inlen, "stop\n"); + } else if (!strcmp(key, "lxc.cap")) { + strprint(retv, inlen, "drop\n"); + strprint(retv, inlen, "keep\n"); + } else if (!strcmp(key, "lxc.console")) { + strprint(retv, inlen, "logfile\n"); + strprint(retv, inlen, "path\n"); + } else if (!strcmp(key, "lxc.seccomp")) { + strprint(retv, inlen, "profile\n"); + } else if (!strcmp(key, "lxc.signal")) { + strprint(retv, inlen, "halt\n"); + strprint(retv, inlen, "reboot\n"); + strprint(retv, inlen, "stop\n"); + } else if (!strcmp(key, "lxc.start")) { + strprint(retv, inlen, "auto\n"); + strprint(retv, inlen, "delay\n"); + strprint(retv, inlen, "order\n"); + } else if (!strcmp(key, "lxc.monitor")) { + strprint(retv, inlen, "unshare\n"); + } else { + fulllen = -1; + } + + return fulllen; +} + +int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen) +{ + int len; + const char *idxstring; + struct lxc_netdev *netdev; + int fulllen = 0; + ssize_t idx = -1; + + idxstring = key + 8; + if (!isdigit(*idxstring)) + return -1; + + (void)get_network_config_ops(key, c, &idx, NULL); + if (idx < 0) + return -1; + + netdev = lxc_get_netdev_by_idx(c, (unsigned int)idx, false); + if (!netdev) + return -1; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "type\n"); + strprint(retv, inlen, "script.up\n"); + strprint(retv, inlen, "script.down\n"); + if (netdev->type != LXC_NET_EMPTY) { + strprint(retv, inlen, "flags\n"); + strprint(retv, inlen, "link\n"); + strprint(retv, inlen, "name\n"); + strprint(retv, inlen, "hwaddr\n"); + strprint(retv, inlen, "mtu\n"); + strprint(retv, inlen, "ipv6.address\n"); + strprint(retv, inlen, "ipv6.gateway\n"); + strprint(retv, inlen, "ipv4.address\n"); + strprint(retv, inlen, "ipv4.gateway\n"); + } + + switch (netdev->type) { + case LXC_NET_VETH: + strprint(retv, inlen, "veth.pair\n"); + break; + case LXC_NET_MACVLAN: + strprint(retv, inlen, "macvlan.mode\n"); + break; + case LXC_NET_VLAN: + strprint(retv, inlen, "vlan.id\n"); + break; + case LXC_NET_PHYS: + break; + } + + return fulllen; +} diff -Nru lxc-2.0.8/src/lxc/confile.h lxc-2.1.0/src/lxc/confile.h --- lxc-2.0.8/src/lxc/confile.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile.h 2017-09-06 02:32:37.000000000 +0000 @@ -5,6 +5,8 @@ * * Authors: * Daniel Lezcano + * Serge Hallyn + * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,45 +26,93 @@ #ifndef __LXC_CONFILE_H #define __LXC_CONFILE_H +#include #include #include -#include struct lxc_conf; struct lxc_list; -typedef int (*config_cb)(const char *, const char *, struct lxc_conf *); +/* Callback prototype to set a configuration item. + * Must be implemented when adding a new configuration key. + */ +typedef int (*config_set_cb)(const char *key, const char *value, + struct lxc_conf *conf, void *data); + +/* Callback prototype to get a configuration item. + * Must be implemented when adding a new configuration key. + */ +typedef int (*config_get_cb)(const char *key, char *value, int inlen, + struct lxc_conf *conf, void *data); + +/* Callback prototype to clear a configuration item. + * Must be implemented when adding a new configuration key. + */ +typedef int (*config_clr_cb)(const char *key, struct lxc_conf *conf, + void *data); + struct lxc_config_t { char *name; - config_cb cb; + bool is_legacy_key; /* REMOVE in LXC 3.0 */ + config_set_cb set; + config_get_cb get; + config_clr_cb clr; }; -extern struct lxc_config_t *lxc_getconfig(const char *key); -extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, char *retv, int inlen); -extern int lxc_listconfigs(char *retv, int inlen); -extern int lxc_config_read(const char *file, struct lxc_conf *conf, bool from_include); +/* Get the jump table entry for the given configuration key. */ +extern struct lxc_config_t *lxc_get_config(const char *key); + +/* List all available config items. */ +extern int lxc_list_config_items(char *retv, int inlen); + +/* Given a configuration key namespace (e.g. lxc.apparmor) list all associated + * subkeys for that namespace. + * Must be implemented when adding a new configuration key. + */ +extern int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv, + int inlen); + +/* List all configuration items associated with a given network. For example + * pass "lxc.net.[i]" to retrieve all configuration items associated with + * the network associated with index [i]. + */ +extern int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, + int inlen); + +extern int lxc_config_read(const char *file, struct lxc_conf *conf, + bool from_include); + extern int append_unexp_config_line(const char *line, struct lxc_conf *conf); extern int lxc_config_define_add(struct lxc_list *defines, char* arg); + extern int lxc_config_define_load(struct lxc_list *defines, struct lxc_conf *conf); /* needed for lxc-attach */ extern signed long lxc_config_parse_arch(const char *arch); + extern int lxc_fill_elevated_privileges(char *flaglist, int *flags); -extern int lxc_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); -extern bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, const char *v); +extern bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, + const char *v); /* These are used when cloning a container */ -extern void clear_unexp_config_line(struct lxc_conf *conf, const char *key, bool rm_subkeys); +extern void clear_unexp_config_line(struct lxc_conf *conf, const char *key, + bool rm_subkeys); + extern bool clone_update_unexp_hooks(struct lxc_conf *conf, const char *oldpath, - const char *newpath, const char *oldname, const char *newmame); + const char *newpath, const char *oldname, + const char *newmame); + bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname, const char *ovldir); + extern bool network_new_hwaddrs(struct lxc_conf *conf); -#endif + +#endif /* __LXC_CONFILE_H */ diff -Nru lxc-2.0.8/src/lxc/confile_legacy.c lxc-2.1.0/src/lxc/confile_legacy.c --- lxc-2.0.8/src/lxc/confile_legacy.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile_legacy.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,1243 @@ +/* + * lxc: linux Container library + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ +#include +#include +#include +#include +#include +#include +#include +#include /* Required for PRIu64 to work. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "config.h" +#include "confile.h" +#include "confile_utils.h" +#include "confile_legacy.h" +#include "utils.h" +#include "log.h" +#include "conf.h" +#include "network.h" +#include "lxcseccomp.h" +#include "storage.h" + +#if HAVE_IFADDRS_H +#include +#else +#include <../include/ifaddrs.h> +#endif + +lxc_log_define(lxc_confile_legacy, lxc); + +/* + * Config entry is something like "lxc.network.0.ipv4" the key 'lxc.network.' + * was found. So we make sure next comes an integer, find the right callback + * (by rewriting the key), and call it. + */ +int set_config_network_legacy_nic(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + char *copy = strdup(key), *p; + int ret = -1; + struct lxc_config_t *config; + + if (!copy) { + SYSERROR("failed to allocate memory"); + return -1; + } + /* + * Ok we know that to get here we've got "lxc.network." + * and it isn't any of the other network entries. So + * after the second . Should come an integer (# of defined + * nic) followed by a valid entry. + */ + if (*(key + 12) < '0' || *(key + 12) > '9') + goto out; + + p = strchr(key + 12, '.'); + if (!p) + goto out; + + strcpy(copy + 12, p + 1); + config = lxc_get_config(copy); + if (!config) { + ERROR("unknown key %s", key); + goto out; + } + ret = config->set(key, value, lxc_conf, NULL); + +out: + free(copy); + return ret; +} + +static void lxc_remove_nic(struct lxc_list *it) +{ + struct lxc_netdev *netdev = it->elem; + struct lxc_list *it2,*next; + + lxc_list_del(it); + + free(netdev->upscript); + free(netdev->downscript); + free(netdev->hwaddr); + free(netdev->mtu); + free(netdev->ipv4_gateway); + free(netdev->ipv6_gateway); + lxc_list_for_each_safe(it2, &netdev->ipv4, next) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + lxc_list_for_each_safe(it2, &netdev->ipv6, next) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + free(netdev); + free(it); +} + +static int lxc_clear_config_network(struct lxc_conf *c) +{ + struct lxc_list *it,*next; + lxc_list_for_each_safe(it, &c->network, next) { + lxc_remove_nic(it); + } + return 0; +} + +int set_config_network_legacy(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + if (!lxc_config_value_empty(value)) { + ERROR("lxc.network must not have a value"); + return -1; + } + + return lxc_clear_config_network(lxc_conf); +} + +int set_config_network_legacy_type(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *network = &lxc_conf->network; + struct lxc_netdev *netdev, *prevnetdev; + struct lxc_list *list; + + if (lxc_config_value_empty(value)) + return lxc_clear_config_network(lxc_conf); + + netdev = malloc(sizeof(*netdev)); + if (!netdev) { + SYSERROR("failed to allocate memory"); + return -1; + } + + memset(netdev, 0, sizeof(*netdev)); + lxc_list_init(&netdev->ipv4); + lxc_list_init(&netdev->ipv6); + + netdev->name[0] = '\0'; + netdev->link[0] = '\0'; + memset(&netdev->priv, 0, sizeof(netdev->priv)); + /* I'm not completely sure if the memset takes care to zero the arrays + * in the union as well. So let's make extra sure and set the first byte + * to zero so that we don't have any surprises. + */ + netdev->priv.veth_attr.pair[0] = '\0'; + netdev->priv.veth_attr.veth1[0] = '\0'; + + list = malloc(sizeof(*list)); + if (!list) { + SYSERROR("failed to allocate memory"); + free(netdev); + return -1; + } + + lxc_list_init(list); + list->elem = netdev; + + /* We maintain a negative count for legacy networks. */ + netdev->idx = -1; + if (!lxc_list_empty(network)) { + prevnetdev = lxc_list_last_elem(network); + netdev->idx = prevnetdev->idx; + if (netdev->idx == INT_MIN) { + ERROR("number of requested networks would underflow " + "counter"); + free(netdev); + free(list); + return -1; + } + netdev->idx--; + } + + lxc_list_add_tail(network, list); + + if (!strcmp(value, "veth")) + netdev->type = LXC_NET_VETH; + else if (!strcmp(value, "macvlan")) { + netdev->type = LXC_NET_MACVLAN; + lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, "private"); + } else if (!strcmp(value, "vlan")) + netdev->type = LXC_NET_VLAN; + else if (!strcmp(value, "phys")) + netdev->type = LXC_NET_PHYS; + else if (!strcmp(value, "empty")) + netdev->type = LXC_NET_EMPTY; + else if (!strcmp(value, "none")) + netdev->type = LXC_NET_NONE; + else { + ERROR("invalid network type %s", value); + return -1; + } + return 0; +} + +/* + * If you have p="lxc.network.0.link", pass it p+12 + * to get back '0' (the index of the nic). + */ +static int get_network_netdev_idx(const char *key) +{ + int ret, idx; + + if (*key < '0' || *key > '9') + return EINVAL; + + ret = sscanf(key, "%d", &idx); + if (ret != 1) + return EINVAL; + + /* Since we've implemented the new network parser legacy networks are + * recorded using a negative index starting from -1. To preserve the old + * behavior we need this function to return the appropriate negative + * index. + */ + return -(++idx); +} + +/* + * If you have p="lxc.network.0", pass this p+12 and it will return + * the netdev of the first configured nic. + */ +static struct lxc_netdev *get_netdev_from_key(const char *key, + struct lxc_list *network) +{ + int idx; + struct lxc_list *it; + struct lxc_netdev *netdev = NULL; + + idx = get_network_netdev_idx(key); + if (idx == EINVAL) + return NULL; + + lxc_list_for_each(it, network) { + netdev = it->elem; + if (idx == netdev->idx) + return netdev; + } + + return NULL; +} + +int lxc_list_nicconfigs_legacy(struct lxc_conf *c, const char *key, char *retv, + int inlen) +{ + struct lxc_netdev *netdev; + int len; + int fulllen = 0; + + netdev = get_netdev_from_key(key + 12, &c->network); + if (!netdev) + return -1; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "type\n"); + strprint(retv, inlen, "script.up\n"); + strprint(retv, inlen, "script.down\n"); + if (netdev->type != LXC_NET_EMPTY) { + strprint(retv, inlen, "flags\n"); + strprint(retv, inlen, "link\n"); + strprint(retv, inlen, "name\n"); + strprint(retv, inlen, "hwaddr\n"); + strprint(retv, inlen, "mtu\n"); + strprint(retv, inlen, "ipv6\n"); + strprint(retv, inlen, "ipv6.gateway\n"); + strprint(retv, inlen, "ipv4\n"); + strprint(retv, inlen, "ipv4.gateway\n"); + } + + switch (netdev->type) { + case LXC_NET_VETH: + strprint(retv, inlen, "veth.pair\n"); + break; + case LXC_NET_MACVLAN: + strprint(retv, inlen, "macvlan.mode\n"); + break; + case LXC_NET_VLAN: + strprint(retv, inlen, "vlan.id\n"); + break; + case LXC_NET_PHYS: + break; + } + + return fulllen; +} + +static struct lxc_netdev *network_netdev(const char *key, const char *value, + struct lxc_list *network) +{ + struct lxc_netdev *netdev = NULL; + + if (lxc_list_empty(network)) { + ERROR("network is not created for '%s' = '%s' option", key, + value); + return NULL; + } + + if (get_network_netdev_idx(key + 12) == EINVAL) + netdev = lxc_list_last_elem(network); + else + netdev = get_netdev_from_key(key + 12, network); + + if (!netdev) { + ERROR("no network device defined for '%s' = '%s' option", key, + value); + return NULL; + } + + return netdev; +} + +int set_config_network_legacy_flags(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + netdev->flags |= IFF_UP; + + return 0; +} + +static int create_matched_ifnames(const char *value, struct lxc_conf *lxc_conf, + struct lxc_netdev *netdev) +{ + struct ifaddrs *ifaddr, *ifa; + int n; + int ret = 0; + const char *type_key = "lxc.network.type"; + const char *link_key = "lxc.network.link"; + const char *tmpvalue = "phys"; + + if (getifaddrs(&ifaddr) == -1) { + SYSERROR("Get network interfaces failed"); + return -1; + } + + for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_PACKET) + continue; + + if (!strncmp(value, ifa->ifa_name, strlen(value) - 1)) { + ret = set_config_network_legacy_type(type_key, tmpvalue, + lxc_conf, netdev); + if (!ret) { + ret = set_config_network_legacy_link( + link_key, ifa->ifa_name, lxc_conf, netdev); + if (ret) { + ERROR("failed to create matched ifnames"); + break; + } + } else { + ERROR("failed to create matched ifnames"); + break; + } + } + } + + freeifaddrs(ifaddr); + ifaddr = NULL; + + return ret; +} + +int set_config_network_legacy_link(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + struct lxc_list *it; + int ret = 0; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + if (value[strlen(value) - 1] == '+' && netdev->type == LXC_NET_PHYS) { + /* Get the last network list and remove it. */ + it = lxc_conf->network.prev; + if (((struct lxc_netdev *)(it->elem))->type != LXC_NET_PHYS) { + ERROR("lxc config cannot support string pattern " + "matching for this link type"); + return -1; + } + + lxc_list_del(it); + free(it); + ret = create_matched_ifnames(value, lxc_conf, NULL); + } else { + ret = network_ifname(netdev->link, value); + } + + return ret; +} + +int set_config_network_legacy_name(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + return network_ifname(netdev->name, value); +} + +int set_config_network_legacy_veth_pair(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_VETH) { + ERROR("Invalid veth pair for a non-veth netdev"); + return -1; + } + + return network_ifname(netdev->priv.veth_attr.pair, value); +} + +int set_config_network_legacy_macvlan_mode(const char *key, const char *value, + struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_MACVLAN) { + ERROR("Invalid macvlan.mode for a non-macvlan netdev"); + return -1; + } + + return lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, value); +} + +int set_config_network_legacy_hwaddr(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + char *new_value; + + new_value = strdup(value); + if (!new_value) { + SYSERROR("failed to strdup \"%s\"", value); + return -1; + } + rand_complete_hwaddr(new_value); + + netdev = network_netdev(key, new_value, &lxc_conf->network); + if (!netdev) { + free(new_value); + return -1; + }; + + if (lxc_config_value_empty(new_value)) { + free(new_value); + netdev->hwaddr = NULL; + return 0; + } + + netdev->hwaddr = new_value; + return 0; +} + +int set_config_network_legacy_vlan_id(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_VLAN) { + ERROR("Invalid vlan.id for a non-macvlan netdev"); + return -1; + } + + if (get_u16(&netdev->priv.vlan_attr.vid, value, 0)) + return -1; + + return 0; +} + +int set_config_network_legacy_mtu(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + return set_config_string_item(&netdev->mtu, value); +} + +int set_config_network_legacy_ipv4(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + struct lxc_inetdev *inetdev; + struct lxc_list *list; + char *cursor, *slash; + char *addr = NULL, *bcast = NULL, *prefix = NULL; + + if (lxc_config_value_empty(value)) + return clr_config_network_legacy_item(key, lxc_conf, NULL); + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + inetdev = malloc(sizeof(*inetdev)); + if (!inetdev) { + SYSERROR("failed to allocate ipv4 address"); + return -1; + } + memset(inetdev, 0, sizeof(*inetdev)); + + list = malloc(sizeof(*list)); + if (!list) { + SYSERROR("failed to allocate memory"); + free(inetdev); + return -1; + } + + lxc_list_init(list); + list->elem = inetdev; + + addr = strdup(value); + if (!addr) { + ERROR("no address specified"); + free(inetdev); + free(list); + return -1; + } + + cursor = strstr(addr, " "); + if (cursor) { + *cursor = '\0'; + bcast = cursor + 1; + } + + slash = strstr(addr, "/"); + if (slash) { + *slash = '\0'; + prefix = slash + 1; + } + + if (!inet_pton(AF_INET, addr, &inetdev->addr)) { + SYSERROR("invalid ipv4 address: %s", value); + free(inetdev); + free(addr); + free(list); + return -1; + } + + if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { + SYSERROR("invalid ipv4 broadcast address: %s", value); + free(inetdev); + free(list); + free(addr); + return -1; + } + + /* No prefix specified, determine it from the network class. */ + if (prefix) { + if (lxc_safe_uint(prefix, &inetdev->prefix) < 0) + return -1; + } else { + inetdev->prefix = config_ip_prefix(&inetdev->addr); + } + + /* If no broadcast address, let compute one from the + * prefix and address. + */ + if (!bcast) { + inetdev->bcast.s_addr = inetdev->addr.s_addr; + inetdev->bcast.s_addr |= + htonl(INADDR_BROADCAST >> inetdev->prefix); + } + + lxc_list_add_tail(&netdev->ipv4, list); + + free(addr); + return 0; +} + +int set_config_network_legacy_ipv4_gateway(const char *key, const char *value, + struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + free(netdev->ipv4_gateway); + + if (lxc_config_value_empty(value)) { + netdev->ipv4_gateway = NULL; + } else if (!strcmp(value, "auto")) { + netdev->ipv4_gateway = NULL; + netdev->ipv4_gateway_auto = true; + } else { + struct in_addr *gw; + + gw = malloc(sizeof(*gw)); + if (!gw) { + SYSERROR("failed to allocate ipv4 gateway address"); + return -1; + } + + if (!inet_pton(AF_INET, value, gw)) { + SYSERROR("invalid ipv4 gateway address: %s", value); + free(gw); + return -1; + } + + netdev->ipv4_gateway = gw; + netdev->ipv4_gateway_auto = false; + } + + return 0; +} + +int set_config_network_legacy_ipv6(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + struct lxc_inet6dev *inet6dev; + struct lxc_list *list; + char *slash, *valdup, *netmask; + + if (lxc_config_value_empty(value)) + return clr_config_network_legacy_item(key, lxc_conf, NULL); + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + inet6dev = malloc(sizeof(*inet6dev)); + if (!inet6dev) { + SYSERROR("failed to allocate ipv6 address"); + return -1; + } + memset(inet6dev, 0, sizeof(*inet6dev)); + + list = malloc(sizeof(*list)); + if (!list) { + SYSERROR("failed to allocate memory"); + free(inet6dev); + return -1; + } + + lxc_list_init(list); + list->elem = inet6dev; + + valdup = strdup(value); + if (!valdup) { + ERROR("no address specified"); + free(list); + free(inet6dev); + return -1; + } + + inet6dev->prefix = 64; + slash = strstr(valdup, "/"); + if (slash) { + *slash = '\0'; + netmask = slash + 1; + if (lxc_safe_uint(netmask, &inet6dev->prefix) < 0) + return -1; + } + + if (!inet_pton(AF_INET6, valdup, &inet6dev->addr)) { + SYSERROR("invalid ipv6 address: %s", valdup); + free(list); + free(inet6dev); + free(valdup); + return -1; + } + + lxc_list_add_tail(&netdev->ipv6, list); + + free(valdup); + return 0; +} + +int set_config_network_legacy_ipv6_gateway(const char *key, const char *value, + struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + free(netdev->ipv6_gateway); + + if (lxc_config_value_empty(value)) { + netdev->ipv6_gateway = NULL; + } else if (!strcmp(value, "auto")) { + netdev->ipv6_gateway = NULL; + netdev->ipv6_gateway_auto = true; + } else { + struct in6_addr *gw; + + gw = malloc(sizeof(*gw)); + if (!gw) { + SYSERROR("failed to allocate ipv6 gateway address"); + return -1; + } + + if (!inet_pton(AF_INET6, value, gw)) { + SYSERROR("invalid ipv6 gateway address: %s", value); + free(gw); + return -1; + } + + netdev->ipv6_gateway = gw; + netdev->ipv6_gateway_auto = false; + } + + return 0; +} + +int set_config_network_legacy_script_up(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + return set_config_string_item(&netdev->upscript, value); +} + +int set_config_network_legacy_script_down(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev; + + netdev = network_netdev(key, value, &lxc_conf->network); + if (!netdev) + return -1; + + return set_config_string_item(&netdev->downscript, value); +} + +int get_config_network_legacy(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); + } + + return fulllen; +} + +/* + * lxc.network.0.XXX, where XXX can be: name, type, link, flags, type, + * macvlan.mode, veth.pair, vlan, ipv4, ipv6, script.up, hwaddr, mtu, + * ipv4.gateway, ipv6.gateway. ipvX.gateway can return 'auto' instead + * of an address. ipv4 and ipv6 return lists (newline-separated). + * things like veth.pair return '' if invalid (i.e. if called for vlan + * type). + */ +int get_config_network_legacy_item(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + char *p1; + int len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!strncmp(key, "lxc.network.", 12)) + key += 12; + else + return -1; + + p1 = strchr(key, '.'); + if (!p1 || *(p1 + 1) == '\0') + return -1; + p1++; + + netdev = get_netdev_from_key(key, &c->network); + if (!netdev) + return -1; + if (strcmp(p1, "name") == 0) { + if (netdev->name[0] != '\0') + strprint(retv, inlen, "%s", netdev->name); + } else if (strcmp(p1, "type") == 0) { + strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); + } else if (strcmp(p1, "link") == 0) { + if (netdev->link[0] != '\0') + strprint(retv, inlen, "%s", netdev->link); + } else if (strcmp(p1, "flags") == 0) { + if (netdev->flags & IFF_UP) + strprint(retv, inlen, "up"); + } else if (strcmp(p1, "script.up") == 0) { + if (netdev->upscript) + strprint(retv, inlen, "%s", netdev->upscript); + } else if (strcmp(p1, "script.down") == 0) { + if (netdev->downscript) + strprint(retv, inlen, "%s", netdev->downscript); + } else if (strcmp(p1, "hwaddr") == 0) { + if (netdev->hwaddr) + strprint(retv, inlen, "%s", netdev->hwaddr); + } else if (strcmp(p1, "mtu") == 0) { + if (netdev->mtu) + strprint(retv, inlen, "%s", netdev->mtu); + } else if (strcmp(p1, "macvlan.mode") == 0) { + if (netdev->type == LXC_NET_MACVLAN) { + const char *mode; + switch (netdev->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: + mode = "private"; + break; + case MACVLAN_MODE_VEPA: + mode = "vepa"; + break; + case MACVLAN_MODE_BRIDGE: + mode = "bridge"; + break; + case MACVLAN_MODE_PASSTHRU: + mode = "passthru"; + break; + default: + mode = "(invalid)"; + break; + } + strprint(retv, inlen, "%s", mode); + } + } else if (strcmp(p1, "veth.pair") == 0) { + if (netdev->type == LXC_NET_VETH) { + strprint(retv, inlen, "%s", + netdev->priv.veth_attr.pair[0] != '\0' + ? netdev->priv.veth_attr.pair + : netdev->priv.veth_attr.veth1); + } + } else if (strcmp(p1, "vlan") == 0) { + if (netdev->type == LXC_NET_VLAN) { + strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); + } + } else if (strcmp(p1, "ipv4.gateway") == 0) { + if (netdev->ipv4_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv4_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv4_gateway, buf, + sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv4") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv4) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s/%u\n", buf, i->prefix); + } + } else if (strcmp(p1, "ipv6.gateway") == 0) { + if (netdev->ipv6_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv6_gateway) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, + sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv6") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv6) { + struct lxc_inet6dev *i = it2->elem; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s/%u\n", buf, i->prefix); + } + } + return fulllen; +} + +/* we get passed in something like '0', '0.ipv4' or '1.ipv6' */ +static int lxc_clear_nic(struct lxc_conf *c, const char *key) +{ + char *p1; + int idx; + struct lxc_list *it = NULL; + struct lxc_netdev *netdev = NULL; + + if (lxc_list_empty(&c->network)) { + ERROR("network is not created for %s", key); + return -1; + } + + if ((idx = get_network_netdev_idx(key)) == EINVAL) + netdev = lxc_list_last_elem(&c->network); + else { + lxc_list_for_each(it, &c->network) { + netdev = it->elem; + if (idx == netdev->idx) + break; + netdev = NULL; + } + } + if (!netdev) + return -1; + + p1 = strchr(key, '.'); + if (!p1 || *(p1+1) == '\0') + p1 = NULL; + + if (!p1 && it) { + lxc_remove_nic(it); + } else if (strcmp(p1, ".ipv4") == 0) { + struct lxc_list *it2,*next; + lxc_list_for_each_safe(it2, &netdev->ipv4, next) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + } else if (strcmp(p1, ".ipv6") == 0) { + struct lxc_list *it2,*next; + lxc_list_for_each_safe(it2, &netdev->ipv6, next) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + } + else return -1; + + return 0; +} + +inline int clr_config_network_legacy_item(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_nic(c, key + 12); +} + +inline int clr_config_network_legacy(const char *key, struct lxc_conf *c, void *data) +{ + return lxc_clear_config_network(c); +} + +inline int clr_config_lsm_aa_profile(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->lsm_aa_profile); + c->lsm_aa_profile = NULL; + return 0; +} + +inline int clr_config_lsm_aa_incomplete(const char *key, struct lxc_conf *c, + void *data) +{ + c->lsm_aa_allow_incomplete = 0; + return 0; +} + +int get_config_lsm_aa_profile(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->lsm_aa_profile); +} + +int get_config_lsm_aa_incomplete(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_int(c, retv, inlen, + c->lsm_aa_allow_incomplete); +} + +int set_config_lsm_aa_profile(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + return set_config_string_item(&lxc_conf->lsm_aa_profile, value); +} + +int set_config_lsm_aa_incomplete(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + /* Set config value to default. */ + if (lxc_config_value_empty(value)) { + lxc_conf->lsm_aa_allow_incomplete = 0; + return 0; + } + + /* Parse new config value. */ + if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_incomplete) < 0) + return -1; + + if (lxc_conf->lsm_aa_allow_incomplete > 1) { + ERROR("Wrong value for lxc.lsm_aa_allow_incomplete. Can only " + "be set to 0 or 1"); + return -1; + } + + return 0; +} + +int set_config_lsm_se_context(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + return set_config_string_item(&lxc_conf->lsm_se_context, value); +} + +int get_config_lsm_se_context(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return lxc_get_conf_str(retv, inlen, c->lsm_se_context); +} + +inline int clr_config_lsm_se_context(const char *key, struct lxc_conf *c, + void *data) +{ + free(c->lsm_se_context); + c->lsm_se_context = NULL; + return 0; +} + +extern int set_config_limit(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *iter; + struct rlimit limit; + unsigned long limit_value; + struct lxc_list *limlist = NULL; + struct lxc_limit *limelem = NULL; + + if (lxc_config_value_empty(value)) + return lxc_clear_limits(lxc_conf, key); + + if (strncmp(key, "lxc.limit.", sizeof("lxc.limit.") - 1) != 0) + return -1; + + key += sizeof("lxc.limit.") - 1; + + /* soft limit comes first in the value */ + if (!parse_limit_value(&value, &limit_value)) + return -1; + limit.rlim_cur = limit_value; + + /* skip spaces and a colon */ + while (isspace(*value)) + ++value; + + if (*value == ':') + ++value; + else if (*value) /* any other character is an error here */ + return -1; + + while (isspace(*value)) + ++value; + + /* optional hard limit */ + if (*value) { + if (!parse_limit_value(&value, &limit_value)) + return -1; + limit.rlim_max = limit_value; + + /* check for trailing garbage */ + while (isspace(*value)) + ++value; + + if (*value) + return -1; + } else { + /* a single value sets both hard and soft limit */ + limit.rlim_max = limit.rlim_cur; + } + + /* find existing list element */ + lxc_list_for_each(iter, &lxc_conf->limits) + { + limelem = iter->elem; + if (!strcmp(key, limelem->resource)) { + limelem->limit = limit; + return 0; + } + } + + /* allocate list element */ + limlist = malloc(sizeof(*limlist)); + if (!limlist) + goto out; + + limelem = malloc(sizeof(*limelem)); + if (!limelem) + goto out; + memset(limelem, 0, sizeof(*limelem)); + + limelem->resource = strdup(key); + if (!limelem->resource) + goto out; + limelem->limit = limit; + + limlist->elem = limelem; + + lxc_list_add_tail(&lxc_conf->limits, limlist); + + return 0; + +out: + free(limlist); + if (limelem) { + free(limelem->resource); + free(limelem); + } + return -1; +} + +/* + * If you ask for a specific value, i.e. lxc.limit.nofile, then just the value + * will be printed. If you ask for 'lxc.limit', then all limit entries will be + * printed, in 'lxc.limit.resource = value' format. + */ +extern int get_config_limit(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int fulllen = 0, len; + bool get_all = false; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!strcmp(key, "lxc.limit")) + get_all = true; + else if (strncmp(key, "lxc.limit.", 10) == 0) + key += 10; + else + return -1; + + lxc_list_for_each(it, &c->limits) { + char buf[LXC_NUMSTRLEN64 * 2 + 2]; /* 2 colon separated 64 bit + integers or the word + 'unlimited' */ + int partlen; + struct lxc_limit *lim = it->elem; + + if (lim->limit.rlim_cur == RLIM_INFINITY) { + memcpy(buf, "unlimited", sizeof("unlimited")); + partlen = sizeof("unlimited") - 1; + } else { + partlen = sprintf(buf, "%" PRIu64, + (uint64_t)lim->limit.rlim_cur); + } + if (lim->limit.rlim_cur != lim->limit.rlim_max) { + if (lim->limit.rlim_max == RLIM_INFINITY) { + memcpy(buf + partlen, ":unlimited", + sizeof(":unlimited")); + } else { + sprintf(buf + partlen, ":%" PRIu64, + (uint64_t)lim->limit.rlim_max); + } + } + + if (get_all) { + strprint(retv, inlen, "lxc.limit.%s = %s\n", + lim->resource, buf); + } else if (strcmp(lim->resource, key) == 0) { + strprint(retv, inlen, "%s", buf); + } + } + + return fulllen; +} + +extern int clr_config_limit(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_limits(c, key); +} diff -Nru lxc-2.0.8/src/lxc/confile_legacy.h lxc-2.1.0/src/lxc/confile_legacy.h --- lxc-2.0.8/src/lxc/confile_legacy.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile_legacy.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * 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_LEGACY_H +#define __LXC_CONFILE_LEGACY_H + +#include +#include +#include + +struct lxc_conf; +struct lxc_list; + +extern int set_config_network_legacy_type(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_flags(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_link(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_name(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_veth_pair(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_macvlan_mode(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_hwaddr(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_vlan_id(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_mtu(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_ipv4(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_ipv4_gateway(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_script_up(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_script_down(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_ipv6(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_ipv6_gateway(const char *, const char *, + struct lxc_conf *, void *); +extern int set_config_network_legacy_nic(const char *, const char *, + struct lxc_conf *, void *); +extern int get_config_network_legacy_item(const char *, char *, int, + struct lxc_conf *, void *); +extern int clr_config_network_legacy_item(const char *, struct lxc_conf *, + void *); + +extern int lxc_list_nicconfigs_legacy(struct lxc_conf *c, const char *key, + char *retv, int inlen); +extern int lxc_listconfigs(char *retv, int inlen); + +extern bool network_new_hwaddrs(struct lxc_conf *conf); + +#define lxc_config_legacy_define(name) \ + extern int set_config_##name(const char *, const char *, \ + struct lxc_conf *, void *); \ + extern int get_config_##name(const char *, char *, int, \ + struct lxc_conf *, void *); \ + extern int clr_config_##name(const char *, struct lxc_conf *, \ + void *); + +lxc_config_legacy_define(network_legacy); +lxc_config_legacy_define(lsm_aa_profile); +lxc_config_legacy_define(lsm_aa_incomplete); +lxc_config_legacy_define(lsm_se_context); +lxc_config_legacy_define(limit); + +#endif /* __LXC_CONFILE_LEGACY_H */ diff -Nru lxc-2.0.8/src/lxc/confile_utils.c lxc-2.1.0/src/lxc/confile_utils.c --- lxc-2.0.8/src/lxc/confile_utils.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile_utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,705 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "conf.h" +#include "confile.h" +#include "confile_utils.h" +#include "error.h" +#include "log.h" +#include "list.h" +#include "network.h" +#include "parse.h" +#include "utils.h" + +lxc_log_define(lxc_confile_utils, lxc); + +int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, + unsigned long *hostid, unsigned long *range) +{ + int ret = -1; + unsigned long tmp_hostid, tmp_nsid, tmp_range; + char tmp_type; + char *window, *slide; + char *dup = NULL; + + /* Duplicate string. */ + dup = strdup(idmap); + if (!dup) + goto on_error; + + /* A prototypical idmap entry would be: "u 1000 1000000 65536" */ + + /* align */ + slide = window = dup; + /* skip whitespace */ + slide += strspn(slide, " \t\r"); + if (slide != window && *slide == '\0') + goto on_error; + + /* Validate type. */ + if (*slide != 'u' && *slide != 'g') + goto on_error; + /* Assign type. */ + tmp_type = *slide; + + /* move beyond type */ + slide++; + /* align */ + window = slide; + /* Validate that only whitespace follows. */ + slide += strspn(slide, " \t\r"); + /* There must be whitespace. */ + if (slide == window) + goto on_error; + + /* Mark beginning of nsuid. */ + window = slide; + /* Validate that non-whitespace follows. */ + slide += strcspn(slide, " \t\r"); + /* There must be non-whitespace. */ + if (slide == window || *slide == '\0') + goto on_error; + /* Mark end of nsuid. */ + *slide = '\0'; + + /* Parse nsuid. */ + if (lxc_safe_ulong(window, &tmp_nsid) < 0) + goto on_error; + + /* Move beyond \0. */ + slide++; + /* align */ + window = slide; + /* Validate that only whitespace follows. */ + slide += strspn(slide, " \t\r"); + /* If there was only one whitespace then we whiped it with our \0 above. + * So only ensure that we're not at the end of the string. + */ + if (*slide == '\0') + goto on_error; + + /* Mark beginning of hostid. */ + window = slide; + /* Validate that non-whitespace follows. */ + slide += strcspn(slide, " \t\r"); + /* There must be non-whitespace. */ + if (slide == window || *slide == '\0') + goto on_error; + /* Mark end of nsuid. */ + *slide = '\0'; + + /* Parse hostid. */ + if (lxc_safe_ulong(window, &tmp_hostid) < 0) + goto on_error; + + /* Move beyond \0. */ + slide++; + /* align */ + window = slide; + /* Validate that only whitespace follows. */ + slide += strspn(slide, " \t\r"); + /* If there was only one whitespace then we whiped it with our \0 above. + * So only ensure that we're not at the end of the string. + */ + if (*slide == '\0') + goto on_error; + + /* Mark beginning of range. */ + window = slide; + /* Validate that non-whitespace follows. */ + slide += strcspn(slide, " \t\r"); + /* There must be non-whitespace. */ + if (slide == window) + goto on_error; + + /* The range is the last valid entry we expect. So make sure that there + * is not trailing garbage and if there is, error out. + */ + if (*(slide + strspn(slide, " \t\r\n")) != '\0') + goto on_error; + /* Mark end of range. */ + *slide = '\0'; + + /* Parse range. */ + if (lxc_safe_ulong(window, &tmp_range) < 0) + goto on_error; + + *type = tmp_type; + *nsid = tmp_nsid; + *hostid = tmp_hostid; + *range = tmp_range; + + /* Yay, we survived. */ + ret = 0; + +on_error: + free(dup); + + return ret; +} + +bool lxc_config_value_empty(const char *value) +{ + if (value && strlen(value) > 0) + return false; + + return true; +} + +struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail) +{ + struct lxc_list *newlist; + struct lxc_netdev *netdev = NULL; + + /* network does not exist */ + netdev = malloc(sizeof(*netdev)); + if (!netdev) + return NULL; + + memset(netdev, 0, sizeof(*netdev)); + lxc_list_init(&netdev->ipv4); + lxc_list_init(&netdev->ipv6); + netdev->name[0] = '\0'; + netdev->link[0] = '\0'; + memset(&netdev->priv, 0, sizeof(netdev->priv)); + /* I'm not completely sure if the memset takes care to zero the arrays + * in the union as well. So let's make extra sure and set the first byte + * to zero so that we don't have any surprises. + */ + netdev->priv.veth_attr.pair[0] = '\0'; + netdev->priv.veth_attr.veth1[0] = '\0'; + + /* give network a unique index */ + netdev->idx = idx; + + /* prepare new list */ + newlist = malloc(sizeof(*newlist)); + if (!newlist) { + free(netdev); + return NULL; + } + + lxc_list_init(newlist); + newlist->elem = netdev; + + if (tail) + lxc_list_add_tail(networks, newlist); + else + lxc_list_add(networks, newlist); + return netdev; +} + +/* Takes care of finding the correct netdev struct in the networks list or + * allocates a new one if it couldn't be found. + */ +struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf, + unsigned int idx, bool allocate) +{ + struct lxc_netdev *netdev = NULL; + struct lxc_list *networks = &conf->network; + struct lxc_list *insert = networks; + + /* lookup network */ + if (!lxc_list_empty(networks)) { + lxc_list_for_each(insert, networks) { + netdev = insert->elem; + if (netdev->idx == idx) + return netdev; + else if (netdev->idx > idx) + break; + } + } + + if (!allocate) + return NULL; + + return lxc_network_add(insert, idx, true); +} + +void lxc_log_configured_netdevs(const struct lxc_conf *conf) +{ + struct lxc_netdev *netdev; + struct lxc_list *it = (struct lxc_list *)&conf->network;; + + if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) && + (lxc_log_get_level() != LXC_LOG_LEVEL_TRACE)) + return; + + if (lxc_list_empty(it)) { + TRACE("container has no networks configured"); + return; + } + + lxc_list_for_each(it, &conf->network) { + struct lxc_list *cur, *next; + struct lxc_inetdev *inet4dev; + struct lxc_inet6dev *inet6dev; + char bufinet4[INET_ADDRSTRLEN], bufinet6[INET6_ADDRSTRLEN]; + + netdev = it->elem; + + TRACE("index: %zd", netdev->idx); + TRACE("ifindex: %d", netdev->ifindex); + switch (netdev->type) { + case LXC_NET_VETH: + TRACE("type: veth"); + if (netdev->priv.veth_attr.pair[0] != '\0') + TRACE("veth pair: %s", + netdev->priv.veth_attr.pair); + if (netdev->priv.veth_attr.veth1[0] != '\0') + TRACE("veth1 : %s", + netdev->priv.veth_attr.veth1); + if (netdev->priv.veth_attr.ifindex > 0) + TRACE("host side ifindex for veth device: %d", + netdev->priv.veth_attr.ifindex); + break; + case LXC_NET_MACVLAN: + TRACE("type: macvlan"); + if (netdev->priv.macvlan_attr.mode > 0) { + char *macvlan_mode; + macvlan_mode = lxc_macvlan_flag_to_mode( + netdev->priv.macvlan_attr.mode); + TRACE("macvlan mode: %s", + macvlan_mode ? macvlan_mode + : "(invalid mode)"); + } + break; + case LXC_NET_VLAN: + TRACE("type: vlan"); + TRACE("vlan id: %d", netdev->priv.vlan_attr.vid); + break; + case LXC_NET_PHYS: + TRACE("type: phys"); + if (netdev->priv.phys_attr.ifindex > 0) { + TRACE("host side ifindex for phys device: %d", + netdev->priv.phys_attr.ifindex); + } + break; + case LXC_NET_EMPTY: + TRACE("type: empty"); + break; + case LXC_NET_NONE: + TRACE("type: none"); + break; + default: + ERROR("invalid network type %d", netdev->type); + return; + } + + if (netdev->type != LXC_NET_EMPTY) { + TRACE("flags: %s", + netdev->flags == IFF_UP ? "up" : "none"); + if (netdev->link[0] != '\0') + TRACE("link: %s", netdev->link); + if (netdev->name[0] != '\0') + TRACE("name: %s", netdev->name); + if (netdev->hwaddr) + TRACE("hwaddr: %s", netdev->hwaddr); + if (netdev->mtu) + TRACE("mtu: %s", netdev->mtu); + if (netdev->upscript) + TRACE("upscript: %s", netdev->upscript); + if (netdev->downscript) + TRACE("downscript: %s", netdev->downscript); + + TRACE("ipv4 gateway auto: %s", + netdev->ipv4_gateway_auto ? "true" : "false"); + + if (netdev->ipv4_gateway) { + inet_ntop(AF_INET, netdev->ipv4_gateway, + bufinet4, sizeof(bufinet4)); + TRACE("ipv4 gateway: %s", bufinet4); + } + + lxc_list_for_each_safe(cur, &netdev->ipv4, next) { + inet4dev = cur->elem; + inet_ntop(AF_INET, &inet4dev->addr, bufinet4, + sizeof(bufinet4)); + TRACE("ipv4 addr: %s", bufinet4); + } + + TRACE("ipv6 gateway auto: %s", + netdev->ipv6_gateway_auto ? "true" : "false"); + if (netdev->ipv6_gateway) { + inet_ntop(AF_INET6, netdev->ipv6_gateway, + bufinet6, sizeof(bufinet6)); + TRACE("ipv6 gateway: %s", bufinet6); + } + lxc_list_for_each_safe(cur, &netdev->ipv6, next) { + inet6dev = cur->elem; + inet_ntop(AF_INET6, &inet6dev->addr, bufinet6, + sizeof(bufinet6)); + TRACE("ipv6 addr: %s", bufinet6); + } + } + } +} + +static void lxc_free_netdev(struct lxc_netdev *netdev) +{ + struct lxc_list *cur, *next; + + free(netdev->upscript); + free(netdev->downscript); + free(netdev->hwaddr); + free(netdev->mtu); + + free(netdev->ipv4_gateway); + lxc_list_for_each_safe(cur, &netdev->ipv4, next) { + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + + free(netdev->ipv6_gateway); + lxc_list_for_each_safe(cur, &netdev->ipv6, next) { + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + + free(netdev); +} + +bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx) +{ + struct lxc_list *cur, *next; + struct lxc_netdev *netdev; + bool found = false; + + lxc_list_for_each_safe(cur, &conf->network, next) { + netdev = cur->elem; + if (netdev->idx != idx) + continue; + + lxc_list_del(cur); + found = true; + break; + } + + if (!found) + return false; + + lxc_free_netdev(netdev); + free(cur); + + return true; +} + +void lxc_free_networks(struct lxc_list *networks) +{ + struct lxc_list *cur, *next; + struct lxc_netdev *netdev; + + lxc_list_for_each_safe(cur, networks, next) { + netdev = cur->elem; + lxc_free_netdev(netdev); + free(cur); + } + + /* prevent segfaults */ + lxc_list_init(networks); +} + +static struct macvlan_mode { + char *name; + int mode; +} macvlan_mode[] = { + { "private", MACVLAN_MODE_PRIVATE }, + { "vepa", MACVLAN_MODE_VEPA }, + { "bridge", MACVLAN_MODE_BRIDGE }, + { "passthru", MACVLAN_MODE_PASSTHRU }, +}; + +int lxc_macvlan_mode_to_flag(int *mode, const char *value) +{ + size_t i; + + for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) { + if (strcmp(macvlan_mode[i].name, value)) + continue; + + *mode = macvlan_mode[i].mode; + return 0; + } + + return -1; +} + +char *lxc_macvlan_flag_to_mode(int mode) +{ + size_t i; + + for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) { + if (macvlan_mode[i].mode == mode) + continue; + + return macvlan_mode[i].name; + } + + return NULL; +} + +int set_config_string_item(char **conf_item, const char *value) +{ + char *new_value; + + if (lxc_config_value_empty(value)) { + free(*conf_item); + *conf_item = NULL; + return 0; + } + + new_value = strdup(value); + if (!new_value) { + SYSERROR("failed to duplicate string \"%s\"", value); + return -1; + } + + free(*conf_item); + *conf_item = new_value; + return 0; +} + +int set_config_string_item_max(char **conf_item, const char *value, size_t max) +{ + if (strlen(value) >= max) { + ERROR("%s is too long (>= %lu)", value, (unsigned long)max); + return -1; + } + + return set_config_string_item(conf_item, value); +} + +int set_config_path_item(char **conf_item, const char *value) +{ + return set_config_string_item_max(conf_item, value, PATH_MAX); +} + +int config_ip_prefix(struct in_addr *addr) +{ + if (IN_CLASSA(addr->s_addr)) + return 32 - IN_CLASSA_NSHIFT; + if (IN_CLASSB(addr->s_addr)) + return 32 - IN_CLASSB_NSHIFT; + if (IN_CLASSC(addr->s_addr)) + return 32 - IN_CLASSC_NSHIFT; + + return 0; +} + +int network_ifname(char *valuep, const char *value) +{ + if (strlen(value) >= IFNAMSIZ) { + ERROR("Network devie name \"%s\" is too long (>= %zu)", value, + (size_t)IFNAMSIZ); + } + + strcpy(valuep, value); + return 0; +} + +int rand_complete_hwaddr(char *hwaddr) +{ + const char hex[] = "0123456789abcdef"; + char *curs = hwaddr; + +#ifndef HAVE_RAND_R + randseed(true); +#else + unsigned int seed; + + seed = randseed(false); +#endif + while (*curs != '\0' && *curs != '\n') { + if (*curs == 'x' || *curs == 'X') { + if (curs - hwaddr == 1) { + /* ensure address is unicast */ +#ifdef HAVE_RAND_R + *curs = hex[rand_r(&seed) & 0x0E]; + } else { + *curs = hex[rand_r(&seed) & 0x0F]; +#else + *curs = hex[rand() & 0x0E]; + } else { + *curs = hex[rand() & 0x0F]; +#endif + } + } + curs++; + } + return 0; +} + +bool lxc_config_net_hwaddr(const char *line) +{ + char *copy, *p; + + if (strncmp(line, "lxc.net", 7) != 0) + return false; + if (strncmp(line, "lxc.network.hwaddr", 18) == 0) + return true; + + /* We have to dup the line, if line is something like + * "lxc.net.[i].xxx = xxxxx ", we need to remove + * '[i]' and compare its key with 'lxc.net.hwaddr'*/ + copy = strdup(line); + if (!copy) { + SYSERROR("failed to allocate memory"); + return false; + } + if (*(copy + 8) >= '0' && *(copy + 8) <= '9') { + p = strchr(copy + 8, '.'); + if (!p) { + free(copy); + return false; + } + /* strlen("hwaddr") = 6 */ + strncpy(copy + 8, p + 1, 6); + copy[8 + 6] = '\0'; + } + if (strncmp(copy, "lxc.net.hwaddr", 14) == 0) { + free(copy); + return true; + } + free(copy); + + /* We have to dup the line second time, if line is something like + * "lxc.network.[i].xxx = xxxxx ", we need to remove + * '[i]' and compare its key with 'lxc.network.hwaddr'*/ + copy = strdup(line); + if (!copy) { + SYSERROR("failed to allocate memory"); + return false; + } + if (*(copy + 12) >= '0' && *(copy + 12) <= '9') { + p = strchr(copy + 12, '.'); + if (!p) { + free(copy); + return false; + } + /* strlen("hwaddr") = 6 */ + strncpy(copy + 12, p + 1, 6); + copy[12 + 6] = '\0'; + } + if (strncmp(copy, "lxc.network.hwaddr", 18) == 0) { + free(copy); + return true; + } + + free(copy); + return false; +} + +/* + * If we find a lxc.net.[i].hwaddr or lxc.network.hwaddr in the original config + * file, we expand it in the unexpanded_config, so that after a save_config we + * store the hwaddr for re-use. + * This is only called when reading the config file, not when executing a + * lxc.include. + * 'x' and 'X' are substituted in-place. + */ +void update_hwaddr(const char *line) +{ + char *p; + + line += lxc_char_left_gc(line, strlen(line)); + if (line[0] == '#') + return; + + if (!lxc_config_net_hwaddr(line)) + return; + + /* Let config_net_hwaddr raise the error. */ + p = strchr(line, '='); + if (!p) + return; + p++; + + while (isblank(*p)) + p++; + + if (!*p) + return; + + rand_complete_hwaddr(p); +} + +bool new_hwaddr(char *hwaddr) +{ + int ret; + + (void)randseed(true); + + ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255, + rand() % 255, rand() % 255); + if (ret < 0 || ret >= 18) { + SYSERROR("Failed to call snprintf()."); + return false; + } + + return true; +} + +int lxc_get_conf_str(char *retv, int inlen, const char *value) +{ + if (!value) + return 0; + if (retv && inlen >= strlen(value) + 1) + strncpy(retv, value, strlen(value) + 1); + + return strlen(value); +} + +int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) +{ + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + return snprintf(retv, inlen, "%d", v); +} + +bool parse_limit_value(const char **value, unsigned long *res) +{ + char *endptr = NULL; + + if (strncmp(*value, "unlimited", sizeof("unlimited") - 1) == 0) { + *res = RLIM_INFINITY; + *value += sizeof("unlimited") - 1; + return true; + } + + errno = 0; + *res = strtoul(*value, &endptr, 10); + if (errno || !endptr) + return false; + *value = endptr; + + return true; +} diff -Nru lxc-2.0.8/src/lxc/confile_utils.h lxc-2.1.0/src/lxc/confile_utils.h --- lxc-2.0.8/src/lxc/confile_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/confile_utils.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,88 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __LXC_CONFILE_UTILS_H +#define __LXC_CONFILE_UTILS_H + +#include + +#include "conf.h" +#include "confile_utils.h" + +#ifndef MACVLAN_MODE_PRIVATE +#define MACVLAN_MODE_PRIVATE 1 +#endif + +#ifndef MACVLAN_MODE_VEPA +#define MACVLAN_MODE_VEPA 2 +#endif + +#ifndef MACVLAN_MODE_BRIDGE +#define MACVLAN_MODE_BRIDGE 4 +#endif + +#ifndef MACVLAN_MODE_PASSTHRU +#define MACVLAN_MODE_PASSTHRU 8 +#endif + +#define strprint(str, inlen, ...) \ + do { \ + len = snprintf(str, inlen, ##__VA_ARGS__); \ + if (len < 0) { \ + SYSERROR("failed to create string"); \ + return -1; \ + }; \ + fulllen += len; \ + if (inlen > 0) { \ + if (str) \ + str += len; \ + inlen -= len; \ + if (inlen < 0) \ + inlen = 0; \ + } \ + } while (0); + +extern int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, + unsigned long *hostid, unsigned long *range); + +extern bool lxc_config_value_empty(const char *value); +extern struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, + bool tail); +extern struct lxc_netdev * +lxc_get_netdev_by_idx(struct lxc_conf *conf, unsigned int idx, bool allocate); +extern void lxc_log_configured_netdevs(const struct lxc_conf *conf); +extern bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx); +extern void lxc_free_networks(struct lxc_list *networks); +extern int lxc_macvlan_mode_to_flag(int *mode, const char *value); +extern char *lxc_macvlan_flag_to_mode(int mode); + +extern int set_config_string_item(char **conf_item, const char *value); +extern int set_config_string_item_max(char **conf_item, const char *value, + size_t max); +extern int set_config_path_item(char **conf_item, const char *value); +extern int config_ip_prefix(struct in_addr *addr); +extern int network_ifname(char *valuep, const char *value); +extern int rand_complete_hwaddr(char *hwaddr); +extern bool lxc_config_net_hwaddr(const char *line); +extern void update_hwaddr(const char *line); +extern bool new_hwaddr(char *hwaddr); +extern int lxc_get_conf_str(char *retv, int inlen, const char *value); +extern int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v); +extern bool parse_limit_value(const char **value, unsigned long *res); +#endif /* __LXC_CONFILE_UTILS_H */ diff -Nru lxc-2.0.8/src/lxc/console.c lxc-2.1.0/src/lxc/console.c --- lxc-2.0.8/src/lxc/console.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/console.c 2017-09-06 02:32:37.000000000 +0000 @@ -121,6 +121,11 @@ ts->masterfd = dstfd; ts->sigfd = -1; + if (!isatty(srcfd)) { + INFO("fd %d does not refer to a tty device", srcfd); + return ts; + } + /* add tty to list to be scanned at SIGWINCH time */ lxc_list_add_elem(&ts->node, ts); lxc_list_add_tail(&lxc_ttys, &ts->node); @@ -128,30 +133,33 @@ sigemptyset(&mask); sigaddset(&mask, SIGWINCH); if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) { - SYSERROR("failed to block SIGWINCH."); + SYSERROR("failed to block SIGWINCH"); ts->sigfd = -1; + lxc_list_del(&ts->node); return ts; } ts->sigfd = signalfd(-1, &mask, 0); if (ts->sigfd < 0) { - SYSERROR("failed to get signalfd."); + SYSERROR("failed to create signal fd"); sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); ts->sigfd = -1; + lxc_list_del(&ts->node); return ts; } - DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd); + DEBUG("process %d created signal fd %d to handle SIGWINCH events", getpid(), ts->sigfd); return ts; } void lxc_console_sigwinch_fini(struct lxc_tty_state *ts) { - if (ts->sigfd >= 0) + if (ts->sigfd >= 0) { close(ts->sigfd); + lxc_list_del(&ts->node); + sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); + } - lxc_list_del(&ts->node); - sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); free(ts); } @@ -292,7 +300,7 @@ static void lxc_console_peer_proxy_free(struct lxc_console *console) { - if (console->tty_state && console->tty_state->sigfd != -1) { + if (console->tty_state) { lxc_console_sigwinch_fini(console->tty_state); console->tty_state = NULL; } @@ -441,7 +449,7 @@ console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600)); if (console->peer < 0) { - ERROR("failed to open \"%s\"", path); + ERROR("failed to open \"%s\": %s", path, strerror(errno)); return -ENOTTY; } DEBUG("using \"%s\" as peer tty device", path); @@ -661,16 +669,17 @@ struct lxc_epoll_descr descr; struct termios oldtios; struct lxc_tty_state *ts; + int istty = 0; - if (!isatty(stdinfd)) { - ERROR("stdin is not a tty"); - return -1; - } - - ret = lxc_setup_tios(stdinfd, &oldtios); - if (ret) { - ERROR("failed to setup tios"); - return -1; + istty = isatty(stdinfd); + if (istty) { + ret = lxc_setup_tios(stdinfd, &oldtios); + if (ret) { + ERROR("failed to setup terminal properties"); + return -1; + } + } else { + INFO("fd %d does not refer to a tty device", stdinfd); } ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); @@ -697,9 +706,12 @@ ts->escape = escape; ts->winch_proxy = c->name; ts->winch_proxy_lxcpath = c->config_path; + ts->stdoutfd = stdoutfd; - lxc_console_winsz(stdinfd, masterfd); - lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); + if (istty) { + lxc_console_winsz(stdinfd, masterfd); + lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); + } ret = lxc_mainloop_open(&descr); if (ret) { @@ -741,14 +753,15 @@ err4: lxc_mainloop_close(&descr); err3: - if (ts->sigfd != -1) - lxc_console_sigwinch_fini(ts); + lxc_console_sigwinch_fini(ts); err2: close(masterfd); close(ttyfd); err1: - tcsetattr(stdinfd, TCSAFLUSH, &oldtios); + if (istty) { + if (tcsetattr(stdinfd, TCSAFLUSH, &oldtios) < 0) + WARN("failed to reset terminal properties: %s.", strerror(errno)); + } return ret; } - diff -Nru lxc-2.0.8/src/lxc/console.h lxc-2.1.0/src/lxc/console.h --- lxc-2.0.8/src/lxc/console.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/console.h 2017-09-06 02:32:37.000000000 +0000 @@ -110,7 +110,7 @@ extern void lxc_console_sigwinch(int sig); /* - * Connect to one of the ptys given to the container via lxc.tty. + * Connect to one of the ptys given to the container via lxc.tty.max. * - 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 @@ -123,10 +123,10 @@ int escape); /* - * Allocate one of the ptys given to the container via lxc.tty. Returns an open - * fd to the allocated pty. + * Allocate one of the ptys given to the container via lxc.tty.max. 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. + * the range specified by lxc.tty.max to allocate a specific pty. */ extern int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd); diff -Nru lxc-2.0.8/src/lxc/criu.c lxc-2.1.0/src/lxc/criu.c --- lxc-2.0.8/src/lxc/criu.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/criu.c 2017-09-06 02:32:37.000000000 +0000 @@ -34,7 +34,6 @@ #include "config.h" -#include "bdev.h" #include "cgroup.h" #include "conf.h" #include "commands.h" @@ -43,6 +42,7 @@ #include "lxc.h" #include "lxclock.h" #include "network.h" +#include "storage.h" #include "utils.h" #if IS_BIONIC @@ -83,7 +83,7 @@ /* The path that is bind mounted from /dev/console, if any. We don't * want to use `--ext-mount-map auto`'s result here because the pts * device may have a different path (e.g. if the pty number is - * different) on the target host. NULL if lxc.console = "none". + * different) on the target host. NULL if lxc.console.path = "none". */ char *console_name; @@ -106,8 +106,8 @@ f = fopen(path, "r"); if (!f) { /* This means we're coming from a liblxc which didn't export - * the tty info. In this case they had to have lxc.console = - * none, so there's no problem restoring. + * the tty info. In this case they had to have lxc.console.path + * = * none, so there's no problem restoring. */ if (errno == ENOENT) return 0; @@ -126,6 +126,47 @@ return 0; } +static int cmp_version(const char *v1, const char *v2) +{ + int ret; + int oct_v1[3], oct_v2[3]; + + memset(oct_v1, -1, sizeof(oct_v1)); + memset(oct_v2, -1, sizeof(oct_v2)); + + ret = sscanf(v1, "%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2]); + if (ret < 1) + return -1; + + ret = sscanf(v2, "%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2]); + if (ret < 1) + return -1; + + /* Major version is greater. */ + if (oct_v1[0] > oct_v2[0]) + return 1; + + if (oct_v1[0] < oct_v2[0]) + return -1; + + /* Minor number is greater.*/ + if (oct_v1[1] > oct_v2[1]) + return 1; + + if (oct_v1[1] < oct_v2[1]) + return -1; + + /* Patch number is greater. */ + if (oct_v1[2] > oct_v2[2]) + return 1; + + /* Patch numbers are equal. */ + if (oct_v1[2] == oct_v2[2]) + return 0; + + return -1; +} + static void exec_criu(struct criu_opts *opts) { char **argv, log[PATH_MAX]; @@ -263,7 +304,7 @@ for (i = 0; i < cgroup_num_hierarchies(); i++) { char **controllers = NULL, *fullname; - char *path; + char *path, *tmp; if (!cgroup_get_hierarchies(i, &controllers)) { ERROR("failed to get hierarchy %d", i); @@ -296,11 +337,15 @@ } } - if (!lxc_deslashify(&path)) { - ERROR("failed to deslashify %s", path); + tmp = lxc_deslashify(path); + if (!tmp) { + ERROR("Failed to remove extraneous slashes from \"%s\"", + path); free(path); goto err; } + free(path); + path = tmp; fullname = lxc_string_join(",", (const char **) controllers, false); if (!fullname) { @@ -408,6 +453,7 @@ if (opts->user->predump_dir) { DECLARE_ARG("--prev-images-dir"); DECLARE_ARG(opts->user->predump_dir); + DECLARE_ARG("--track-mem"); } if (opts->user->pageserver_address && opts->user->pageserver_port) { @@ -449,7 +495,7 @@ if (tty_info[0]) { if (opts->console_fd < 0) { - ERROR("lxc.console configured on source host but not target"); + ERROR("lxc.console.path configured on source host but not target"); goto err; } @@ -495,7 +541,7 @@ struct lxc_netdev *n = it->elem; bool external_not_veth; - if (strcmp(opts->criu_version, CRIU_EXTERNAL_NOT_VETH) >= 0) { + if (cmp_version(opts->criu_version, CRIU_EXTERNAL_NOT_VETH) >= 0) { /* Since criu version 2.8 the usage of --veth-pair * has been deprecated: * git tag --contains f2037e6d3445fc400 @@ -519,7 +565,7 @@ case LXC_NET_VETH: veth = n->priv.veth_attr.pair; - if (n->link) { + if (n->link[0] != '\0') { if (external_not_veth) fmt = "veth[%s]:%s@%s"; else @@ -538,7 +584,7 @@ goto err; break; case LXC_NET_MACVLAN: - if (!n->link) { + if (n->link[0] == '\0') { ERROR("no host interface for macvlan %s", n->name); goto err; } @@ -760,11 +806,13 @@ snprintf(template, sizeof(template), "vethXXXXXX"); - if (!netdev->priv.veth_attr.pair) - netdev->priv.veth_attr.pair = lxc_mkifname(template); + if (netdev->priv.veth_attr.pair[0] == '\0' && + netdev->priv.veth_attr.veth1[0] == '\0') { + if (!lxc_mkifname(template)) + goto out_unlock; - if (!netdev->priv.veth_attr.pair) - goto out_unlock; + strcpy(netdev->priv.veth_attr.veth1, template); + } } has_error = false; @@ -774,8 +822,9 @@ return !has_error; } -// do_restore never returns, the calling process is used as the -// monitor process. do_restore calls exit() if it fails. +/* do_restore never returns, the calling process is used as the monitor process. + * do_restore calls exit() if it fails. + */ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_opts *opts, char *criu_version) { pid_t pid; @@ -796,10 +845,13 @@ close(fd); } - handler = lxc_init(c->name, c->lxc_conf, c->config_path); + handler = lxc_init_handler(c->name, c->lxc_conf, c->config_path, false); if (!handler) goto out; + if (lxc_init(c->name, handler) < 0) + goto out; + if (!cgroup_init(handler)) { ERROR("failed initing cgroups"); goto out_fini_handler; @@ -1206,7 +1258,7 @@ if (pid == 0) { close(pipefd[0]); - // this never returns + /* this never returns */ do_restore(c, pipefd[1], opts, criu_version); } @@ -1219,9 +1271,10 @@ goto err_wait; } - // If the criu process was killed or exited nonzero, wait() for the - // handler, since the restore process died. Otherwise, we don't need to - // wait, since the child becomes the monitor process. + /* If the criu process was killed or exited nonzero, wait() for the + * handler, since the restore process died. Otherwise, we don't need to + * wait, since the child becomes the monitor process. + */ if (!WIFEXITED(status) || WEXITSTATUS(status)) goto err_wait; return true; diff -Nru lxc-2.0.8/src/lxc/error.c lxc-2.1.0/src/lxc/error.c --- lxc-2.0.8/src/lxc/error.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/error.c 2017-09-06 02:32:37.000000000 +0000 @@ -46,12 +46,12 @@ if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret) - INFO("Child <%d> ended on error (%d).", pid, 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); + INFO("Child <%d> ended on signal (%d)", pid, signal); } return ret; diff -Nru lxc-2.0.8/src/lxc/execute.c lxc-2.1.0/src/lxc/execute.c --- lxc-2.0.8/src/lxc/execute.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/execute.c 2017-09-06 02:32:37.000000000 +0000 @@ -111,16 +111,15 @@ }; int lxc_execute(const char *name, char *const argv[], int quiet, - struct lxc_conf *conf, const char *lxcpath, bool backgrounded) + struct lxc_handler *handler, const char *lxcpath, + bool backgrounded) { - struct execute_args args = { - .argv = argv, - .quiet = quiet - }; + struct execute_args args = {.argv = argv, .quiet = quiet}; - if (lxc_check_inherited(conf, false, -1)) + if (lxc_check_inherited(handler->conf, false, &handler->conf->maincmd_fd, 1)) return -1; - conf->is_execute = 1; - return __lxc_start(name, conf, &execute_start_ops, &args, lxcpath, backgrounded); + handler->conf->is_execute = 1; + return __lxc_start(name, handler, &execute_start_ops, &args, lxcpath, + backgrounded); } diff -Nru lxc-2.0.8/src/lxc/initutils.c lxc-2.1.0/src/lxc/initutils.c --- lxc-2.0.8/src/lxc/initutils.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/initutils.c 2017-09-06 02:32:37.000000000 +0000 @@ -30,10 +30,10 @@ { /* the umount may fail */ if (umount(target)) - WARN("failed to unmount %s : %s", target, strerror(errno)); + WARN("Failed to unmount %s : %s", target, strerror(errno)); if (mount(source, target, type, 0, NULL)) { - ERROR("failed to mount %s : %s", target, strerror(errno)); + ERROR("Failed to mount %s : %s", target, strerror(errno)); return -1; } @@ -45,26 +45,26 @@ extern void lxc_setup_fs(void) { if (mount_fs("proc", "/proc", "proc")) - INFO("failed to remount proc"); + INFO("Failed to remount proc"); /* if /dev has been populated by us, /dev/shm does not exist */ if (access("/dev/shm", F_OK) && mkdir("/dev/shm", 0777)) - INFO("failed to create /dev/shm"); + INFO("Failed to create /dev/shm"); /* if we can't mount /dev/shm, continue anyway */ if (mount_fs("shmfs", "/dev/shm", "tmpfs")) - INFO("failed to mount /dev/shm"); + INFO("Failed to mount /dev/shm"); /* If we were able to mount /dev/shm, then /dev exists */ /* Sure, but it's read-only per config :) */ if (access("/dev/mqueue", F_OK) && mkdir("/dev/mqueue", 0666)) { - DEBUG("failed to create '/dev/mqueue'"); + DEBUG("Failed to create '/dev/mqueue'"); return; } /* continue even without posix message queue support */ if (mount_fs("mqueue", "/dev/mqueue", "mqueue")) - INFO("failed to mount /dev/mqueue"); + INFO("Failed to mount /dev/mqueue"); } static char *copy_global_config_value(char *p) diff -Nru lxc-2.0.8/src/lxc/list.h lxc-2.1.0/src/lxc/list.h --- lxc-2.0.8/src/lxc/list.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/list.h 2017-09-06 02:32:37.000000000 +0000 @@ -24,45 +24,44 @@ #ifndef __LXC_LIST_H #define __LXC_LIST_H +#include + struct lxc_list { void *elem; struct lxc_list *next; struct lxc_list *prev; }; -#define lxc_init_list(l) { .next = l, .prev = l } +#define lxc_init_list(l) \ + { \ + .next = l, .prev = l \ + } /* * Iterate through an lxc list. An example for an idiom would be: * * struct lxc_list *iterator; - * type *tmp; // where "type" can be an int, char * etc. * lxc_list_for_each(iterator, list) { + * type *tmp; * tmp = iterator->elem; - * // Do stuff with tmp. * } - * free(iterator); */ -#define lxc_list_for_each(__iterator, __list) \ - for (__iterator = (__list)->next; \ - __iterator != __list; \ +#define lxc_list_for_each(__iterator, __list) \ + for (__iterator = (__list)->next; __iterator != __list; \ __iterator = __iterator->next) -/* - * Iterate safely through an lxc list. An example for an appropriate use case +/* Iterate safely through an lxc list. An example for an appropriate use case * would be: * - * struct lxc_list *iterator; - * lxc_list_for_each_safe(iterator, list, list->next) { - * tmp = iterator->elem; - * // Do stuff with tmp. + * struct lxc_list *cur, *next; + * lxc_list_for_each_safe(cur, list, next) { + * type *tmp; + * tmp = cur->elem; * } - * free(iterator); */ -#define lxc_list_for_each_safe(__iterator, __list, __next) \ - for (__iterator = (__list)->next, __next = __iterator->next; \ - __iterator != __list; \ - __iterator = __next, __next = __next->next) +#define lxc_list_for_each_safe(__iterator, __list, __next) \ + for (__iterator = (__list)->next, __next = __iterator->next; \ + __iterator != __list; __iterator = __next, __next = __next->next) /* Initalize list. */ static inline void lxc_list_init(struct lxc_list *list) @@ -72,7 +71,8 @@ } /* Add an element to a list. See lxc_list_add() and lxc_list_add_tail() for an - * idiom. */ + * idiom. + */ static inline void lxc_list_add_elem(struct lxc_list *list, void *elem) { list->elem = elem; @@ -97,8 +97,7 @@ } /* Workhorse to be called from lxc_list_add() and lxc_list_add_tail(). */ -static inline void __lxc_list_add(struct lxc_list *new, - struct lxc_list *prev, +static inline void __lxc_list_add(struct lxc_list *new, struct lxc_list *prev, struct lxc_list *next) { next->prev = new; @@ -107,8 +106,7 @@ prev->next = new; } -/* - * Idiom to add an element to the beginning of an lxc list: +/* Idiom to add an element to the beginning of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) @@ -121,8 +119,7 @@ __lxc_list_add(list, head, head->next); } -/* - * Idiom to add an element to the end of an lxc list: +/* Idiom to add an element to the end of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) @@ -136,14 +133,13 @@ __lxc_list_add(list, head->prev, head); } -/* - * Idiom to free an lxc list: - * - * lxc_list_for_each_safe(iterator, list, list->next) { - * lxc_list_del(iterator); - * free(iterator); +/* Idiom to remove an element from a list: + * struct lxc_list *cur, *next; + * lxc_list_for_each_safe(cur, list, next) { + * lxc_list_del(cur); + * free(cur->elem); + * free(cur); * } - * free(iterator); */ static inline void lxc_list_del(struct lxc_list *list) { @@ -160,6 +156,7 @@ { size_t i = 0; struct lxc_list *iter; + lxc_list_for_each(iter, list) { i++; } @@ -167,4 +164,4 @@ return i; } -#endif +#endif /* __LXC_LIST_H */ diff -Nru lxc-2.0.8/src/lxc/log.c lxc-2.1.0/src/lxc/log.c --- lxc-2.0.8/src/lxc/log.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/log.c 2017-09-06 02:32:37.000000000 +0000 @@ -35,12 +35,16 @@ #include #include +#include +#include + #include #include #include "log.h" #include "caps.h" #include "utils.h" +#include "lxccontainer.h" /* We're logging in seconds and nanoseconds. Assuming that the underlying * datatype is currently at maximum a 64bit integer, we have a date string that @@ -49,23 +53,87 @@ #define LXC_LOG_TIME_SIZE ((LXC_NUMSTRLEN64)*2) int lxc_log_fd = -1; +static int syslog_enable = 0; int lxc_quiet_specified; int lxc_log_use_global_fd; static int lxc_loglevel_specified; static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc"; static char *log_fname = NULL; +static char *log_vmname = NULL; lxc_log_define(lxc_log, lxc); +static int lxc_log_priority_to_syslog(int priority) +{ + switch (priority) { + case LXC_LOG_LEVEL_FATAL: + return LOG_EMERG; + case LXC_LOG_LEVEL_ALERT: + return LOG_ALERT; + case LXC_LOG_LEVEL_CRIT: + return LOG_CRIT; + case LXC_LOG_LEVEL_ERROR: + return LOG_ERR; + case LXC_LOG_LEVEL_WARN: + return LOG_WARNING; + case LXC_LOG_LEVEL_NOTICE: + case LXC_LOG_LEVEL_NOTSET: + return LOG_NOTICE; + case LXC_LOG_LEVEL_INFO: + return LOG_INFO; + case LXC_LOG_LEVEL_TRACE: + case LXC_LOG_LEVEL_DEBUG: + return LOG_DEBUG; + } + + /* Not reached */ + return LOG_NOTICE; +} + +/*---------------------------------------------------------------------------*/ +static int log_append_syslog(const struct lxc_log_appender *appender, + struct lxc_log_event *event) +{ + char *msg; + int rc, len; + va_list args; + + if (!syslog_enable) + return 0; + + va_copy(args, *event->vap); + len = vsnprintf(NULL, 0, event->fmt, args) + 1; + va_end(args); + msg = malloc(len * sizeof(char)); + if (msg == NULL) + return 0; + rc = vsnprintf(msg, len, event->fmt, *event->vap); + if (rc == -1 || rc >= len) { + free(msg); + return 0; + } + + syslog(lxc_log_priority_to_syslog(event->priority), + "%s%s %s - %s:%s:%d - %s" , + log_vmname ? log_vmname : "", + log_vmname ? ":" : "", + event->category, + event->locinfo->file, event->locinfo->func, + event->locinfo->line, + msg); + free(msg); + return 0; +} + /*---------------------------------------------------------------------------*/ static int log_append_stderr(const struct lxc_log_appender *appender, struct lxc_log_event *event) { - if (event->priority < LXC_LOG_PRIORITY_ERROR) + if (event->priority < LXC_LOG_LEVEL_ERROR) return 0; - fprintf(stderr, "%s: ", log_prefix); + fprintf(stderr, "%s: %s%s", log_prefix, log_vmname ? log_vmname : "", log_vmname ? ": " : ""); fprintf(stderr, "%s: %s: %d ", event->locinfo->file, event->locinfo->func, event->locinfo->line); vfprintf(stderr, event->fmt, *event->vap); fprintf(stderr, "\n"); @@ -214,8 +282,10 @@ return 0; n = snprintf(buffer, sizeof(buffer), - "%15s %s %-8s %s - %s:%s:%d - ", + "%15s%s%s %s %-8s %s - %s:%s:%d - ", log_prefix, + log_vmname ? " " : "", + log_vmname ? log_vmname : "", date_time, lxc_log_priority_to_string(event->priority), event->category, @@ -235,6 +305,12 @@ return write(fd_to_use, buffer, n + 1); } +static struct lxc_log_appender log_appender_syslog = { + .name = "syslog", + .append = log_append_syslog, + .next = NULL, +}; + static struct lxc_log_appender log_appender_stderr = { .name = "stderr", .append = log_append_stderr, @@ -249,14 +325,14 @@ static struct lxc_log_category log_root = { .name = "root", - .priority = LXC_LOG_PRIORITY_ERROR, + .priority = LXC_LOG_LEVEL_ERROR, .appender = NULL, .parent = NULL, }; struct lxc_log_category lxc_log_category_lxc = { .name = "lxc", - .priority = LXC_LOG_PRIORITY_ERROR, + .priority = LXC_LOG_LEVEL_ERROR, .appender = &log_appender_logfile, .parent = &log_root }; @@ -264,10 +340,11 @@ /*---------------------------------------------------------------------------*/ static int build_dir(const char *name) { - char *n = strdup(name); // because we'll be modifying it - char *p, *e; int ret; + char *e, *n, *p; + /* Make copy of string since we'll be modifying it. */ + n = strdup(name); if (!n) { ERROR("Out of memory while creating directory '%s'.", name); return -1; @@ -373,6 +450,9 @@ extern void lxc_log_close(void) { + closelog(); + free(log_vmname); + log_vmname = NULL; if (lxc_log_fd == -1) return; close(lxc_log_fd); @@ -384,17 +464,16 @@ /* * This can be called: * 1. when a program calls lxc_log_init with no logfile parameter (in which - * case the default is used). In this case lxc.logfile can override this. + * case the default is used). In this case lxc.loge can override this. * 2. when a program calls lxc_log_init with a logfile parameter. In this - * case we don't want lxc.logfile to override this. - * 3. When a lxc.logfile entry is found in config file. + * case we don't want lxc.log to override this. + * 3. When a lxc.log entry is found in config file. */ static int __lxc_log_set_file(const char *fname, int create_dirs) { - if (lxc_log_fd != -1) { - // we are overriding the default. + /* we are overriding the default. */ + if (lxc_log_fd != -1) lxc_log_close(); - } if (!fname) return -1; @@ -405,8 +484,9 @@ } #if USE_CONFIGPATH_LOGS - // we don't build_dir for the default if the default is - // i.e. /var/lib/lxc/$container/$container.log + /* We don't build_dir for the default if the default is i.e. + * /var/lib/lxc/$container/$container.log. + */ if (create_dirs) #endif if (build_dir(fname)) { @@ -438,16 +518,36 @@ return ret; } +extern int lxc_log_syslog(int facility) +{ + struct lxc_log_appender *appender; + + openlog(log_prefix, LOG_PID, facility); + if (!lxc_log_category_lxc.appender) { + lxc_log_category_lxc.appender = &log_appender_syslog; + return 0; + } + appender = lxc_log_category_lxc.appender; + while (appender->next != NULL) + appender = appender->next; + appender->next = &log_appender_syslog; + + return 0; +} + +extern void lxc_log_enable_syslog(void) +{ + syslog_enable = 1; +} + /* * lxc_log_init: * Called from lxc front-end programs (like lxc-create, lxc-start) to * initalize the log defaults. */ -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_init(struct lxc_log *log) { - int lxc_priority = LXC_LOG_PRIORITY_ERROR; + int lxc_priority = LXC_LOG_LEVEL_ERROR; int ret; if (lxc_log_fd != -1) { @@ -455,8 +555,8 @@ return 0; } - if (priority) - lxc_priority = lxc_log_priority_to_int(priority); + if (log->level) + lxc_priority = lxc_log_priority_to_int(log->level); if (!lxc_loglevel_specified) { lxc_log_category_lxc.priority = lxc_priority; @@ -464,46 +564,49 @@ } if (!lxc_quiet_specified) { - if (!quiet) + if (!log->quiet) lxc_log_category_lxc.appender->next = &log_appender_stderr; } - if (prefix) - lxc_log_set_prefix(prefix); + if (log->prefix) + lxc_log_set_prefix(log->prefix); + + if (log->name) + log_vmname = strdup(log->name); - if (file) { - if (strcmp(file, "none") == 0) + if (log->file) { + if (strcmp(log->file, "none") == 0) return 0; - ret = __lxc_log_set_file(file, 1); + ret = __lxc_log_set_file(log->file, 1); lxc_log_use_global_fd = 1; } else { /* if no name was specified, there nothing to do */ - if (!name) + if (!log->name) return 0; ret = -1; - if (!lxcpath) - lxcpath = LOGPATH; + if (!log->lxcpath) + log->lxcpath = LOGPATH; /* try LOGPATH if lxcpath is the default for the privileged containers */ - if (!geteuid() && strcmp(LXCPATH, lxcpath) == 0) - ret = _lxc_log_set_file(name, NULL, 0); + if (!geteuid() && strcmp(LXCPATH, log->lxcpath) == 0) + ret = _lxc_log_set_file(log->name, NULL, 0); /* try in lxcpath */ if (ret < 0) - ret = _lxc_log_set_file(name, lxcpath, 1); + ret = _lxc_log_set_file(log->name, log->lxcpath, 1); /* try LOGPATH in case its writable by the caller */ if (ret < 0) - ret = _lxc_log_set_file(name, NULL, 0); + ret = _lxc_log_set_file(log->name, NULL, 0); } /* * If !file, that is, if the user did not request this logpath, then * ignore failures and continue logging to console */ - if (!file && ret != 0) { + if (!log->file && ret != 0) { INFO("Ignoring failure to open default logfile."); ret = 0; } @@ -512,13 +615,13 @@ } /* - * This is called when we read a lxc.loglevel entry in a lxc.conf file. This + * This is called when we read a lxc.log.level entry in a lxc.conf file. This * happens after processing command line arguments, which override the .conf * settings. So only set the level if previously unset. */ extern int lxc_log_set_level(int *dest, int level) { - if (level < 0 || level >= LXC_LOG_PRIORITY_NOTSET) { + if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET) { ERROR("invalid log priority %d", level); return -1; } @@ -534,7 +637,7 @@ extern bool lxc_log_has_valid_level(void) { int log_level = lxc_log_get_level(); - if (log_level < 0 || log_level >= LXC_LOG_PRIORITY_NOTSET) + if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET) return false; return true; } diff -Nru lxc-2.0.8/src/lxc/log.h lxc-2.1.0/src/lxc/log.h --- lxc-2.0.8/src/lxc/log.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/log.h 2017-09-06 02:32:37.000000000 +0000 @@ -26,12 +26,14 @@ #include "config.h" +#include #include #include #include #include #include #include +#include #include #include "conf.h" @@ -54,18 +56,18 @@ #define ATTR_UNUSED #endif -/* predefined priorities. */ +/* predefined lxc log 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, + LXC_LOG_LEVEL_TRACE, + LXC_LOG_LEVEL_DEBUG, + LXC_LOG_LEVEL_INFO, + LXC_LOG_LEVEL_NOTICE, + LXC_LOG_LEVEL_WARN, + LXC_LOG_LEVEL_ERROR, + LXC_LOG_LEVEL_CRIT, + LXC_LOG_LEVEL_ALERT, + LXC_LOG_LEVEL_FATAL, + LXC_LOG_LEVEL_NOTSET, }; /* location information of the logging event */ @@ -119,14 +121,14 @@ lxc_log_priority_is_enabled(const struct lxc_log_category* category, int priority) { - while (category->priority == LXC_LOG_PRIORITY_NOTSET && + while (category->priority == LXC_LOG_LEVEL_NOTSET && category->parent) category = category->parent; int cmp_prio = category->priority; #ifndef NO_LXC_CONF if (!lxc_log_use_global_fd && current_config && - current_config->loglevel != LXC_LOG_PRIORITY_NOTSET) + current_config->loglevel != LXC_LOG_LEVEL_NOTSET) cmp_prio = current_config->loglevel; #endif @@ -139,35 +141,68 @@ 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"; + case LXC_LOG_LEVEL_TRACE: return "TRACE"; + case LXC_LOG_LEVEL_DEBUG: return "DEBUG"; + case LXC_LOG_LEVEL_INFO: return "INFO"; + case LXC_LOG_LEVEL_NOTICE: return "NOTICE"; + case LXC_LOG_LEVEL_WARN: return "WARN"; + case LXC_LOG_LEVEL_ERROR: return "ERROR"; + case LXC_LOG_LEVEL_CRIT: return "CRIT"; + case LXC_LOG_LEVEL_ALERT: return "ALERT"; + case LXC_LOG_LEVEL_FATAL: return "FATAL"; default: return "NOTSET"; } } + +static inline const char* lxc_syslog_priority_to_string(int priority) +{ + switch (priority) { + case LOG_DAEMON: return "daemon"; + case LOG_LOCAL0: return "local0"; + case LOG_LOCAL1: return "local1"; + case LOG_LOCAL2: return "local2"; + case LOG_LOCAL3: return "local3"; + case LOG_LOCAL4: return "local4"; + case LOG_LOCAL5: return "local5"; + case LOG_LOCAL6: return "local6"; + case LOG_LOCAL7: return "local7"; + 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; + if (!strcasecmp("TRACE", name)) return LXC_LOG_LEVEL_TRACE; + if (!strcasecmp("DEBUG", name)) return LXC_LOG_LEVEL_DEBUG; + if (!strcasecmp("INFO", name)) return LXC_LOG_LEVEL_INFO; + if (!strcasecmp("NOTICE", name)) return LXC_LOG_LEVEL_NOTICE; + if (!strcasecmp("WARN", name)) return LXC_LOG_LEVEL_WARN; + if (!strcasecmp("ERROR", name)) return LXC_LOG_LEVEL_ERROR; + if (!strcasecmp("CRIT", name)) return LXC_LOG_LEVEL_CRIT; + if (!strcasecmp("ALERT", name)) return LXC_LOG_LEVEL_ALERT; + if (!strcasecmp("FATAL", name)) return LXC_LOG_LEVEL_FATAL; + + return LXC_LOG_LEVEL_NOTSET; +} + +static inline int lxc_syslog_priority_to_int(const char* name) +{ + if (!strcasecmp("daemon", name)) return LOG_DAEMON; + if (!strcasecmp("local0", name)) return LOG_LOCAL0; + if (!strcasecmp("local1", name)) return LOG_LOCAL1; + if (!strcasecmp("local2", name)) return LOG_LOCAL2; + if (!strcasecmp("local3", name)) return LOG_LOCAL3; + if (!strcasecmp("local4", name)) return LOG_LOCAL4; + if (!strcasecmp("local5", name)) return LOG_LOCAL5; + if (!strcasecmp("local6", name)) return LOG_LOCAL6; + if (!strcasecmp("local7", name)) return LOG_LOCAL7; - return LXC_LOG_PRIORITY_NOTSET; + return -EINVAL; } static inline void @@ -199,19 +234,19 @@ /* * Helper macro to define log functions. */ -#define lxc_log_priority_define(acategory, PRIORITY) \ +#define lxc_log_priority_define(acategory, LEVEL) \ \ -ATTR_UNUSED static inline void LXC_##PRIORITY(struct lxc_log_locinfo *, \ +ATTR_UNUSED static inline void LXC_##LEVEL(struct lxc_log_locinfo *, \ const char *, ...) __attribute__ ((format (printf, 2, 3))); \ \ -ATTR_UNUSED static inline void LXC_##PRIORITY(struct lxc_log_locinfo* locinfo, \ +ATTR_UNUSED static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \ const char* format, ...) \ { \ if (lxc_log_priority_is_enabled(acategory, \ - LXC_LOG_PRIORITY_##PRIORITY)) { \ + LXC_LOG_LEVEL_##LEVEL)) { \ struct lxc_log_event evt = { \ .category = (acategory)->name, \ - .priority = LXC_LOG_PRIORITY_##PRIORITY, \ + .priority = LXC_LOG_LEVEL_##LEVEL, \ .fmt = format, \ .locinfo = locinfo \ }; \ @@ -236,7 +271,7 @@ extern struct lxc_log_category lxc_log_category_##parent; \ struct lxc_log_category lxc_log_category_##name = { \ #name, \ - LXC_LOG_PRIORITY_NOTSET, \ + LXC_LOG_LEVEL_NOTSET, \ NULL, \ &lxc_log_category_##parent \ }; @@ -313,11 +348,9 @@ extern int lxc_log_fd; -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(int *fd, const char *fname); +extern int lxc_log_syslog(int facility); +extern void lxc_log_enable_syslog(void); extern int lxc_log_set_level(int *dest, int level); extern void lxc_log_set_prefix(const char *prefix); extern const char *lxc_log_get_file(void); diff -Nru lxc-2.0.8/src/lxc/lsm/apparmor.c lxc-2.1.0/src/lxc/lsm/apparmor.c --- lxc-2.0.8/src/lxc/lsm/apparmor.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lsm/apparmor.c 2017-09-06 02:32:37.000000000 +0000 @@ -189,10 +189,10 @@ curlabel = apparmor_process_label_get(getpid()); if (!aa_stacking_supported() && aa_needs_transition(curlabel)) { - // we're already confined, and stacking isn't supported + /* we're already confined, and stacking isn't supported */ if (!label || strcmp(curlabel, label) == 0) { - // no change requested + /* no change requested */ free(curlabel); return 0; } @@ -218,7 +218,7 @@ WARN("Incomplete AppArmor support in your kernel"); if (!conf->lsm_aa_allow_incomplete) { ERROR("If you really want to start this container, set"); - ERROR("lxc.aa_allow_incomplete = 1"); + ERROR("lxc.apparmor.allow_incomplete = 1"); ERROR("in your container configuration file"); return -1; } diff -Nru lxc-2.0.8/src/lxc/lsm/lsm.h lxc-2.1.0/src/lxc/lsm/lsm.h --- lxc-2.0.8/src/lxc/lsm/lsm.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lsm/lsm.h 2017-09-06 02:32:37.000000000 +0000 @@ -21,8 +21,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __lxc_lsm_h -#define __lxc_lsm_h +#ifndef __LXC_LSM_H +#define __LXC_LSM_H struct lxc_conf; diff -Nru lxc-2.0.8/src/lxc/lxccontainer.c lxc-2.1.0/src/lxc/lxccontainer.c --- lxc-2.0.8/src/lxc/lxccontainer.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxccontainer.c 2017-09-06 02:32:37.000000000 +0000 @@ -38,15 +38,16 @@ #include #include +#include "af_unix.h" #include "attach.h" -#include "bdev.h" -#include "lxcoverlay.h" -#include "lxcbtrfs.h" #include "cgroup.h" #include "conf.h" #include "config.h" #include "commands.h" +#include "commands_utils.h" #include "confile.h" +#include "confile_legacy.h" +#include "confile_utils.h" #include "console.h" #include "criu.h" #include "log.h" @@ -56,8 +57,12 @@ #include "monitor.h" #include "namespace.h" #include "network.h" -#include "sync.h" +#include "start.h" #include "state.h" +#include "storage.h" +#include "storage/btrfs.h" +#include "storage/overlay.h" +#include "sync.h" #include "utils.h" #include "version.h" @@ -101,7 +106,8 @@ static const char *lxcapi_get_config_path(struct lxc_container *c); #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c) static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); -static bool container_destroy(struct lxc_container *c); +static bool container_destroy(struct lxc_container *c, + struct lxc_storage *storage); static bool get_snappath_dir(struct lxc_container *c, char *snappath); static bool lxcapi_snapshot_destroy_all(struct lxc_container *c); static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file); @@ -145,7 +151,7 @@ return 0; fd = open(path, O_RDWR); if (fd < 0) { - // give benefit of the doubt + /* give benefit of the doubt */ SYSERROR("Error opening partial file"); return 0; } @@ -155,18 +161,18 @@ lk.l_len = 0; lk.l_pid = -1; if (fcntl(fd, F_GETLK, &lk) == 0 && lk.l_pid != -1) { - // create is still ongoing + /* create is still ongoing */ close(fd); return 1; } - // create completed but partial is still there. + /* create completed but partial is still there. */ close(fd); return 2; } static int create_partial(struct lxc_container *c) { - // $lxcpath + '/' + $name + '/partial' + \0 + /* $lxcpath + '/' + $name + '/partial' + \0 */ int len = strlen(c->config_path) + strlen(c->name) + 10; char *path = alloca(len); int fd, ret; @@ -196,7 +202,7 @@ static void remove_partial(struct lxc_container *c, int fd) { - // $lxcpath + '/' + $name + '/partial' + \0 + /* $lxcpath + '/' + $name + '/partial' + \0 */ int len = strlen(c->config_path) + strlen(c->name) + 10; char *path = alloca(len); int ret; @@ -290,18 +296,21 @@ 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 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 + + /* Bail without trying to unlock, bc the privlock is now probably in + * freed memory. + */ + if (c->numthreads < 1) return 0; - } + c->numthreads++; container_mem_unlock(c); return 1; @@ -609,23 +618,6 @@ WRAP_API_2(bool, lxcapi_wait, const char *, int) -static bool do_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 do_lxcapi_wait(c, "RUNNING", timeout); -} - -WRAP_API_1(bool, wait_on_daemonized_start, int) - static bool am_single_threaded(void) { struct dirent *direntp; @@ -712,9 +704,71 @@ free(argv); } +static int lxc_rcv_status(int state_socket) +{ + int ret; + int state = -1; + +again: + /* Receive container state. */ + ret = lxc_abstract_unix_rcv_credential(state_socket, &state, + sizeof(int)); + if (ret <= 0) { + if (errno != EINTR) + return -1; + TRACE("Caught EINTR; retrying"); + goto again; + } + + return state; +} + +static bool wait_on_daemonized_start(struct lxc_handler *handler, int pid) +{ + int state, status; + + /* Close write end of the socket pair. */ + close(handler->state_socket_pair[1]); + handler->state_socket_pair[1] = -1; + + state = lxc_rcv_status(handler->state_socket_pair[0]); + + /* Close read end of the socket pair. */ + close(handler->state_socket_pair[0]); + handler->state_socket_pair[0] = -1; + + /* The first child is going to fork() again and then exits. So we reap + * the first child here. + */ + if (waitpid(pid, &status, 0) < 0) + DEBUG("Failed waiting on first child"); + else if (!WIFEXITED(status)) + DEBUG("Failed to retrieve exit status of first child"); + else if (WEXITSTATUS(status) != 0) + DEBUG("First child exited with: %d", WEXITSTATUS(status)); + + if (state < 0) { + SYSERROR("Failed to receive the container state"); + return false; + } + + /* If we receive anything else then running we know that the container + * failed to start. + */ + if (state != RUNNING) { + ERROR("Received container state \"%s\" instead of \"RUNNING\"", + lxc_state2str(state)); + return false; + } + + TRACE("Container is in \"RUNNING\" state"); + return true; +} + static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) { int ret; + struct lxc_handler *handler; struct lxc_conf *conf; bool daemonize = false; FILE *pid_fp = NULL; @@ -724,31 +778,32 @@ }; char **init_cmd = NULL; - /* container exists */ + /* container does exist */ if (!c) return false; - /* If anything fails before we set error_num, we want an error in there */ + /* If anything fails before we set error_num, we want an error in there. + */ c->error_num = 1; - /* container has been setup */ + /* Container has not 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); - do_lxcapi_destroy(c); + ret = ongoing_create(c); + if (ret < 0) { + ERROR("Failed checking for incomplete container creation"); return false; } else if (ret == 1) { - ERROR("Error: creation of %s is ongoing", c->name); + ERROR("Ongoing container creation detected"); + return false; + } else if (ret == 2) { + ERROR("Failed to create container"); + do_lxcapi_destroy(c); return false; } - /* is this app meant to be run through lxcinit, as in lxc-execute? */ + /* Is this app meant to be run through lxcinit, as in lxc-execute? */ if (useinit && !argv) return false; @@ -756,79 +811,133 @@ return false; conf = c->lxc_conf; daemonize = c->daemonize; + + /* initialize handler */ + handler = lxc_init_handler(c->name, conf, c->config_path, daemonize); container_mem_unlock(c); + if (!handler) + return false; if (useinit) { - ret = lxc_execute(c->name, argv, 1, conf, c->config_path, daemonize); - return ret == 0 ? true : false; + TRACE("calling \"lxc_execute\""); + ret = lxc_execute(c->name, argv, 1, handler, c->config_path, + daemonize); + c->error_num = ret; + + if (ret != 0) + return false; + + return true; } - /* if no argv was passed in, use lxc.init_cmd if provided in - * configuration */ + /* If no argv was passed in, use lxc.init_cmd if provided in the + * configuration + */ if (!argv) argv = init_cmd = split_init_cmd(conf->init_cmd); - /* ... and otherwise use default_args */ + /* ... otherwise use default_args. */ 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... - */ + /* I'm not sure what locks we want here.Any? Is liblxc's locking enough + * here to protect the on disk container? We don't want to exclude + * things like lxc_info while the container is running. + */ if (daemonize) { + bool started; char title[2048]; - lxc_monitord_spawn(c->config_path); + pid_t pid; - pid_t pid = fork(); - if (pid < 0) + pid = fork(); + if (pid < 0) { + free_init_cmd(init_cmd); + lxc_free_handler(handler); return false; + } + /* first parent */ if (pid != 0) { /* Set to NULL because we don't want father unlink * the PID file, child will do the free and unlink. */ c->pidfile = NULL; - return wait_on_daemonized_start(c, pid); + + /* Wait for container to tell us whether it started + * successfully. + */ + started = wait_on_daemonized_start(handler, pid); + + free_init_cmd(init_cmd); + lxc_free_handler(handler); + return started; } + /* first child */ + /* We don't really care if this doesn't print all the - * characters; all that it means is that the proctitle will be - * ugly. Similarly, we also don't care if setproctitle() - * fails. */ + * characters. All that it means is that the proctitle will be + * ugly. Similarly, we also don't care if setproctitle() fails. + * */ snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); INFO("Attempting to set proc title to %s", title); setproctitle(title); - /* second fork to be reparented by init */ + /* We fork() a second time to be reparented to init. Like + * POSIX's daemon() function we change to "/" and redirect + * std{in,out,err} to /dev/null. + */ pid = fork(); if (pid < 0) { - SYSERROR("Error doing dual-fork"); - exit(1); + SYSERROR("Failed to fork first child process"); + exit(EXIT_FAILURE); } - 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); + + /* second parent */ + if (pid != 0) { + free_init_cmd(init_cmd); + lxc_free_handler(handler); + exit(EXIT_SUCCESS); } - lxc_check_inherited(conf, true, -1); - if (null_stdfds() < 0) { - ERROR("failed to close fds"); - exit(1); + + /* second child */ + + /* change to / directory */ + ret = chdir("/"); + if (ret < 0) { + SYSERROR("Failed to change to \"/\" directory"); + exit(EXIT_FAILURE); } - setsid(); + + ret = lxc_check_inherited(conf, true, + (int[]){handler->conf->maincmd_fd, + handler->state_socket_pair[0], + handler->state_socket_pair[1]}, + 3); + if (ret < 0) + exit(EXIT_FAILURE); + + /* redirect std{in,out,err} to /dev/null */ + ret = null_stdfds(); + if (ret < 0) { + ERROR("Failed to redirect std{in,out,err} to /dev/null"); + exit(EXIT_FAILURE); + } + + /* become session leader */ + ret = setsid(); + if (ret < 0) + TRACE("Process %d is already process group leader", getpid()); } else { if (!am_single_threaded()) { ERROR("Cannot start non-daemonized container when threaded"); + free_init_cmd(init_cmd); + lxc_free_handler(handler); return false; } } - /* We need to write PID file after daeminize, so we always + /* We need to write PID file after daemonize, so we always * write the right PID. */ if (c->pidfile) { @@ -836,8 +945,10 @@ if (pid_fp == NULL) { SYSERROR("Failed to create pidfile '%s' for '%s'", c->pidfile, c->name); + free_init_cmd(init_cmd); + lxc_free_handler(handler); if (daemonize) - exit(1); + exit(EXIT_FAILURE); return false; } @@ -845,8 +956,10 @@ SYSERROR("Failed to write '%s'", c->pidfile); fclose(pid_fp); pid_fp = NULL; + free_init_cmd(init_cmd); + lxc_free_handler(handler); if (daemonize) - exit(1); + exit(EXIT_FAILURE); return false; } @@ -858,45 +971,70 @@ /* Unshare the mount namespace if requested */ if (conf->monitor_unshare) { - if (unshare(CLONE_NEWNS)) { + ret = unshare(CLONE_NEWNS); + if (ret < 0) { SYSERROR("failed to unshare mount namespace"); - return false; + lxc_free_handler(handler); + ret = 1; + goto on_error; } - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { + + ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); + if (ret < 0) { SYSERROR("Failed to make / rslave at startup"); - return false; + lxc_free_handler(handler); + ret = 1; + goto on_error; } } reboot: - if (lxc_check_inherited(conf, daemonize, -1)) { - ERROR("Inherited fds found"); + if (conf->reboot == 2) { + /* initialize handler */ + handler = lxc_init_handler(c->name, conf, c->config_path, daemonize); + if (!handler) { + ret = 1; + goto on_error; + } + } + + ret = lxc_check_inherited(conf, daemonize, + (int[]){handler->conf->maincmd_fd, + handler->state_socket_pair[0], + handler->state_socket_pair[1]}, + 3); + if (ret < 0) { + lxc_free_handler(handler); ret = 1; - goto out; + goto on_error; } - ret = lxc_start(c->name, argv, conf, c->config_path, daemonize); + ret = lxc_start(c->name, argv, handler, c->config_path, daemonize); c->error_num = ret; if (conf->reboot == 1) { - INFO("container requested reboot"); + INFO("Container requested reboot"); conf->reboot = 2; goto reboot; } -out: +on_error: if (c->pidfile) { unlink(c->pidfile); free(c->pidfile); c->pidfile = NULL; } - free_init_cmd(init_cmd); - if (daemonize) - exit (ret == 0 ? true : false); - else - return (ret == 0 ? true : false); + if (daemonize && ret != 0) + exit(EXIT_FAILURE); + else if (daemonize) + exit(EXIT_SUCCESS); + + if (ret != 0) + return false; + + return true; } static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) @@ -1008,20 +1146,20 @@ return ret == 0; } -/* - * do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(), - * it returns a mounted bdev on success, NULL on error. +/* do_storage_create: thin wrapper around storage_create(). Like + * storage_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) +static struct lxc_storage *do_storage_create(struct lxc_container *c, + const char *type, + struct bdev_specs *specs) { char *dest; size_t len; - struct bdev *bdev; + struct lxc_storage *bdev; int ret; /* rootfs.path or lxcpath/lxcname/rootfs */ - if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) { + if (c->lxc_conf->rootfs.path && !access(c->lxc_conf->rootfs.path, F_OK)) { const char *rpath = c->lxc_conf->rootfs.path; len = strlen(rpath) + 1; dest = alloca(len); @@ -1035,23 +1173,26 @@ if (ret < 0 || ret >= len) return NULL; - bdev = bdev_create(dest, type, c->name, specs); + bdev = storage_create(dest, type, c->name, specs); if (!bdev) { ERROR("Failed to create backing store type %s", type); return NULL; } - do_lxcapi_set_config_item(c, "lxc.rootfs", bdev->src); - do_lxcapi_set_config_item(c, "lxc.rootfs.backend", bdev->type); - - /* if we are not root, chown the rootfs dir to root in the - * target uidmap */ + if (!c->set_config_item(c, "lxc.rootfs.path", bdev->src)) { + ERROR("Failed to set config item \"lxc.rootfs.path\" to \"%s\"", + bdev->src); + return NULL; + } + /* 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); + storage_put(bdev); return NULL; } } @@ -1081,9 +1222,9 @@ return false; } - if (pid == 0) { // child + if (pid == 0) { /* child */ char *patharg, *namearg, *rootfsarg; - struct bdev *bdev = NULL; + struct lxc_storage *bdev = NULL; int i; int ret, len, nargs = 0; char **newargv; @@ -1093,7 +1234,7 @@ exit(1); } - bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Error opening rootfs"); exit(1); @@ -1114,15 +1255,51 @@ 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); + exit(EXIT_FAILURE); } - if (bdev->ops->mount(bdev) < 0) { - ERROR("Error mounting rootfs"); - exit(1); + + if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) { + /* If we create an overlay container we need to + * rsync the contents into + * //rootfs. + * However, the overlay mount function will + * mount will mount + * //delta0 + * over + * //rootfs + * which means we would rsync the rootfs into + * the delta directory. That doesn't make sense + * since the delta directory only exists to + * record the differences to + * //rootfs. So + * let's simply bind-mount here and then rsync + * directly into + * //rootfs. + */ + char *src; + + src = ovl_get_rootfs(bdev->src, &(size_t){0}); + if (!src) { + ERROR("Failed to get rootfs"); + exit(EXIT_FAILURE); + } + + ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); + if (ret < 0) { + ERROR("Failed to mount rootfs"); + return -1; + } + } else { + if (bdev->ops->mount(bdev) < 0) { + ERROR("Failed to mount rootfs"); + exit(EXIT_FAILURE); + } } - } else { // TODO come up with a better way here! + } else { /* TODO come up with a better way here! */ + char *src; free(bdev->dest); - bdev->dest = strdup(bdev->src); + src = lxc_storage_get_path(bdev->src, bdev->type); + bdev->dest = strdup(src); } /* @@ -1131,7 +1308,7 @@ */ if (argv) for (nargs = 0; argv[nargs]; nargs++) ; - nargs += 4; // template, path, rootfs and name args + nargs += 4; /* template, path, rootfs and name args */ newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) @@ -1220,7 +1397,7 @@ if (!n2) exit(1); if (hostid_mapped < 0) { - hostid_mapped = find_unmapped_nsuid(conf, ID_TYPE_UID); + hostid_mapped = find_unmapped_nsid(conf, ID_TYPE_UID); n2[n2args++] = "-m"; if (hostid_mapped < 0) { ERROR("Could not find free uid to map"); @@ -1244,7 +1421,7 @@ if (!n2) exit(1); if (hostgid_mapped < 0) { - hostgid_mapped = find_unmapped_nsuid(conf, ID_TYPE_GID); + hostgid_mapped = find_unmapped_nsid(conf, ID_TYPE_GID); n2[n2args++] = "-m"; if (hostgid_mapped < 0) { ERROR("Could not find free uid to map"); @@ -1266,15 +1443,16 @@ 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 + /* 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 + /* note n2[n2args-1] is NULL */ n2[n2args-5] = "--mapped-uid"; snprintf(txtuid, 20, "%d", hostid_mapped); n2[n2args-4] = txtuid; @@ -1287,7 +1465,7 @@ } /* execute */ execvp(tpath, newargv); - SYSERROR("failed to execute template %s", tpath); + SYSERROR("Failed to execute template %s", tpath); exit(1); } @@ -1508,24 +1686,25 @@ goto out_unlock; } - if (pid == 0) { // child - struct bdev *bdev = NULL; + if (pid == 0) { /* child */ + struct lxc_storage *bdev = NULL; - if (!(bdev = do_bdev_create(c, bdevtype, specs))) { + bdev = do_storage_create(c, bdevtype, specs); + if (!bdev) { ERROR("Error creating backing store type %s for %s", bdevtype ? bdevtype : "(none)", c->name); - exit(1); + exit(EXIT_FAILURE); } /* save config file again to store the new rootfs location */ if (!do_lxcapi_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 + /* Parent task won't see bdev in config so we delete it. */ bdev->ops->umount(bdev); bdev->ops->destroy(bdev); - exit(1); + exit(EXIT_FAILURE); } - exit(0); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid) != 0) goto out_unlock; @@ -1539,8 +1718,9 @@ 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 + /* Now clear out the lxc_conf we have, reload from the created + * container. + */ do_lxcapi_clear_config(c); if (t) { @@ -1556,7 +1736,7 @@ remove_partial(c, partial_fd); out: if (!ret) - container_destroy(c); + container_destroy(c, NULL); free_tpath: free(tpath); return ret; @@ -1599,9 +1779,11 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) { - bool retv; + int ret, state_client_fd = -1; + bool retv = false; pid_t pid; int haltsignal = SIGPWR; + lxc_state_t states[MAX_STATE] = {0}; if (!c) return false; @@ -1621,10 +1803,33 @@ INFO("Using signal number '%d' as halt signal.", haltsignal); + /* Add a new state client before sending the shutdown signal so that we + * don't miss a state. + */ + states[STOPPED] = 1; + ret = lxc_cmd_add_state_client(c->name, c->config_path, states, + &state_client_fd); + + /* Send shutdown signal to container. */ if (kill(pid, haltsignal) < 0) WARN("Could not send signal %d to pid %d.", haltsignal, pid); - retv = do_lxcapi_wait(c, "STOPPED", timeout); + /* Retrieve the state. */ + if (state_client_fd >= 0) { + int state; + state = lxc_cmd_sock_rcv_state(state_client_fd, timeout); + close(state_client_fd); + if (state != STOPPED) + return false; + retv = true; + } else if (ret == STOPPED) { + TRACE("Container is already stopped"); + retv = true; + } else { + TRACE("Received state \"%s\" instead of expected \"STOPPED\"", + lxc_state2str(ret)); + } + return retv; } @@ -1664,29 +1869,53 @@ static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key) { - if (strcmp(key, "lxc.cgroup") == 0) - clear_unexp_config_line(conf, key, true); - else if (strcmp(key, "lxc.network") == 0) - clear_unexp_config_line(conf, key, true); - else if (strcmp(key, "lxc.hook") == 0) - clear_unexp_config_line(conf, key, true); - else - clear_unexp_config_line(conf, key, false); - if (!do_append_unexp_config_line(conf, key, "")) - WARN("Error clearing configuration for %s", key); + if (!strcmp(key, "lxc.cgroup")) + return clear_unexp_config_line(conf, key, true); + + if (!strcmp(key, "lxc.network")) + return clear_unexp_config_line(conf, key, true); + + if (!strcmp(key, "lxc.net")) + return clear_unexp_config_line(conf, key, true); + + /* Clear a network with a specific index. */ + if (!strncmp(key, "lxc.net.", 8)) { + int ret; + const char *idx; + + idx = key + 8; + ret = lxc_safe_uint(idx, &(unsigned int){0}); + if (!ret) + return clear_unexp_config_line(conf, key, true); + } + + if (!strcmp(key, "lxc.hook")) + return clear_unexp_config_line(conf, key, true); + + return clear_unexp_config_line(conf, key, false); } -static bool do_lxcapi_clear_config_item(struct lxc_container *c, const char *key) +static bool do_lxcapi_clear_config_item(struct lxc_container *c, + const char *key) { - int ret; + int ret = 1; + struct lxc_config_t *config; if (!c || !c->lxc_conf) return false; + if (container_mem_lock(c)) return false; - ret = lxc_clear_config_item(c->lxc_conf, key); + + config = lxc_get_config(key); + /* Verify that the config key exists and that it has a callback + * implemented. + */ + if (config && config->clr) + ret = config->clr(key, c->lxc_conf, NULL); if (!ret) do_clear_unexp_config_line(c->lxc_conf, key); + container_mem_unlock(c); return ret == 0; } @@ -1704,14 +1933,15 @@ return switch_to_ns(pid, "net"); } -// used by qsort and bsearch functions for comparing names +/* 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) +/* 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); } @@ -1729,15 +1959,17 @@ 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); + /* 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) +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 *)); + struct lxc_container **newlist = realloc(*list, (pos + 1) * sizeof(struct lxc_container *)); if (!newlist) { ERROR("Out of memory"); return false; @@ -1746,9 +1978,10 @@ *list = newlist; newlist[pos] = c; - // sort the arrray as we will use binary search on it + /* 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); + qsort(newlist, pos + 1, sizeof(struct lxc_container *), + (int (*)(const void *, const void *))container_cmp); return true; } @@ -1795,7 +2028,7 @@ return NULL; } - if (pid == 0) { // child + if (pid == 0) { /* child */ int ret = 1, nbytes; struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; @@ -1884,7 +2117,7 @@ return NULL; } - if (pid == 0) { // child + if (pid == 0) { /* child */ int ret = 1, nbytes; struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; char addressOutputBuffer[INET6_ADDRSTRLEN]; @@ -1985,13 +2218,22 @@ static int do_lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) { - int ret; + int ret = -1; + struct lxc_config_t *config; if (!c || !c->lxc_conf) return -1; + if (container_mem_lock(c)) return -1; - ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); + + config = lxc_get_config(key); + /* Verify that the config key exists and that it has a callback + * implemented. + */ + if (config && config->get) + ret = config->get(key, retv, inlen, c->lxc_conf, NULL); + container_mem_unlock(c); return ret; } @@ -2015,20 +2257,29 @@ static int do_lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen) { + int ret = -1; + + /* List all config items. */ 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 - */ + return lxc_list_config_items(retv, inlen); + 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); + + /* Support 'lxc.net.', i.e. 'lxc.net.0' + * This is an intelligent result to show which keys are valid given the + * type of nic it is. + */ + if (!strncmp(key, "lxc.net.", 8)) + ret = lxc_list_net(c->lxc_conf, key, retv, inlen); + else if (strncmp(key, "lxc.network.", 12) == 0) + ret = lxc_list_nicconfigs_legacy(c->lxc_conf, key, retv, inlen); + else + ret = lxc_list_subkeys(c->lxc_conf, key, retv, inlen); + container_mem_unlock(c); return ret; } @@ -2044,9 +2295,9 @@ if (!alt_file) alt_file = c->configfile; if (!alt_file) - return false; // should we write to stdout if no file is specified? + return false; - // If we haven't yet loaded a config, load the stock config + /* If we haven't yet loaded a config, load the stock config. */ if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s while saving %s", lxc_global_config_value("lxc.default_config"), c->name); @@ -2057,10 +2308,9 @@ 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 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; @@ -2280,7 +2530,7 @@ goto out; ret = fscanf(f, "%d", &v); fclose(f); - // TODO: Figure out what to do with the return value of fscanf. + /* TODO: Figure out what to do with the return value of fscanf. */ if (ret != 1) INFO("Container uses new lxc-snapshots format %s", path); } @@ -2320,11 +2570,12 @@ static bool do_destroy_container(struct lxc_conf *conf) { if (am_unpriv()) { - if (userns_exec_1(conf, bdev_destroy_wrapper, conf) < 0) + if (userns_exec_1(conf, storage_destroy_wrapper, conf, + "storage_destroy_wrapper") < 0) return false; return true; } - return bdev_destroy(conf); + return storage_destroy(conf); } static int lxc_rmdir_onedev_wrapper(void *data) @@ -2333,11 +2584,21 @@ return lxc_rmdir_onedev(arg, "snaps"); } -static bool container_destroy(struct lxc_container *c) +static int lxc_unlink_exec_wrapper(void *data) +{ + char *arg = data; + return unlink(arg); +} + +static bool container_destroy(struct lxc_container *c, + struct lxc_storage *storage) { + const char *p1; + size_t len; + struct lxc_conf *conf; + char *path = NULL; bool bret = false; int ret = 0; - struct lxc_conf *conf; if (!c || !do_lxcapi_is_defined(c)) return false; @@ -2347,35 +2608,34 @@ return false; if (!is_stopped(c)) { - // we should queue some sort of error - in c->error_string? + /* We should queue some sort of error - in c->error_string? */ ERROR("container %s is not stopped", c->name); goto out; } if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) { /* Start of environment variable setup for hooks */ - if (c->name && setenv("LXC_NAME", c->name, 1)) { - SYSERROR("failed to set environment variable for container name"); - } - if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) { - SYSERROR("failed to set environment variable for config path"); - } - if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) { - SYSERROR("failed to set environment variable for rootfs mount"); - } - if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) { - SYSERROR("failed to set environment variable for rootfs mount"); - } - if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) { - SYSERROR("failed to set environment variable for console path"); - } - if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) { - SYSERROR("failed to set environment variable for console log"); - } + if (c->name && setenv("LXC_NAME", c->name, 1)) + SYSERROR("Failed to set environment variable for container name"); + + if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) + SYSERROR("Failed to set environment variable for config path"); + + if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) + SYSERROR("Failed to set environment variable for rootfs mount"); + + if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) + SYSERROR("Failed to set environment variable for rootfs mount"); + + if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) + SYSERROR("Failed to set environment variable for console path"); + + if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) + SYSERROR("Failed to set environment variable for console log"); /* End of environment variable setup for hooks */ if (run_lxc_hooks(c->name, "destroy", conf, c->get_config_path(c), NULL)) { - ERROR("Error executing clone hook for %s", c->name); + ERROR("Failed to execute clone hook for \"%s\"", c->name); goto out; } } @@ -2398,22 +2658,72 @@ mod_all_rdeps(c, false); - const char *p1 = do_lxcapi_get_config_path(c); - char *path = alloca(strlen(p1) + strlen(c->name) + 2); - sprintf(path, "%s/%s", p1, c->name); + p1 = do_lxcapi_get_config_path(c); + /* strlen(p1) + * + + * / + * + + * strlen(c->name) + * + + * / + * + + * strlen("config") = 6 + * + + * \0 + */ + len = strlen(p1) + 1 + strlen(c->name) + 1 + 6 + 1; + path = malloc(len); + if (!path) { + ERROR("Failed to allocate memory"); + goto out; + } + + /* For an overlay container the rootfs is considered immutable and + * cannot be removed when restoring from a snapshot. + */ + if (storage && (!strcmp(storage->type, "overlay") || + !strcmp(storage->type, "overlayfs")) && + (storage->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { + ret = snprintf(path, len, "%s/%s/config", p1, c->name); + if (ret < 0 || (size_t)ret >= len) + goto out; + + if (am_unpriv()) + ret = userns_exec_1(conf, lxc_unlink_exec_wrapper, path, + "lxc_unlink_exec_wrapper"); + else + ret = unlink(path); + if (ret < 0) { + SYSERROR("Failed to destroy config file \"%s\" for \"%s\"", + path, c->name); + goto out; + } + INFO("Destroyed config file \"%s\" for \"%s\"", path, c->name); + + bret = true; + goto out; + } + + ret = snprintf(path, len, "%s/%s", p1, c->name); + if (ret < 0 || (size_t)ret >= len) + goto out; if (am_unpriv()) - ret = userns_exec_1(conf, lxc_rmdir_onedev_wrapper, path); + ret = userns_exec_1(conf, lxc_rmdir_onedev_wrapper, path, + "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(path, "snaps"); if (ret < 0) { - ERROR("Error destroying container directory for %s", c->name); + ERROR("Failed to destroy directory \"%s\" for \"%s\"", path, + c->name); goto out; } - INFO("Destroyed directory for %s", c->name); + INFO("Destroyed directory \"%s\" for \"%s\"", path, c->name); bret = true; out: + if (path) + free(path); container_disk_unlock(c); return bret; } @@ -2432,7 +2742,7 @@ return false; } - return container_destroy(c); + return container_destroy(c, NULL); } WRAP_API(bool, lxcapi_destroy) @@ -2456,13 +2766,22 @@ if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); + if (!c->lxc_conf) return false; - config = lxc_getconfig(key); + + config = lxc_get_config(key); if (!config) return false; - if (config->cb(key, v, c->lxc_conf) != 0) + + if (config->set(key, v, c->lxc_conf, NULL) != 0) return false; + + if (lxc_config_value_empty(v)) { + do_clear_unexp_config_line(c->lxc_conf, key); + return true; + } + return do_append_unexp_config_line(c->lxc_conf, key, v); } @@ -2661,7 +2980,7 @@ if (len == 0) break; ret = write(out, buf, len); - if (ret < len) { // should we retry? + if (ret < len) { /* should we retry? */ SYSERROR("Error: write to new file %s was interrupted", new); goto err; } @@ -2669,7 +2988,7 @@ close(in); close(out); - // we set mode, but not owner/group + /* We set mode, but not owner/group. */ ret = chmod(new, sbuf.st_mode); if (ret) { SYSERROR("Error setting mode on %s", new); @@ -2701,13 +3020,13 @@ char *hookname = it->elem; char *fname = strrchr(hookname, '/'); char tmppath[MAXPATHLEN]; - if (!fname) // relative path - we don't support, but maybe we should + 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 + /* this hook is public - ignore */ continue; } - // copy the script, and change the entry in confile + /* 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) @@ -2743,8 +3062,13 @@ if (!oldpath) return 0; + /* REMOVE IN LXC 3.0 + legacy mount key + */ clear_unexp_config_line(c->lxc_conf, "lxc.mount", false); + clear_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", false); + char *p = strrchr(oldpath, '/'); if (!p) return -1; @@ -2769,7 +3093,7 @@ ERROR("error: allocating pathname"); return -1; } - if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount", newpath)) { + if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", newpath)) { ERROR("error saving new lxctab"); return -1; } @@ -2815,7 +3139,7 @@ if (!f) return false; bret = true; - // if anything goes wrong, just return an error + /* 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) @@ -2853,14 +3177,14 @@ const char *newtype, int flags, const char *bdevdata, uint64_t newsize) { - struct bdev *bdev; - int need_rdep; + struct lxc_storage *bdev; + bool need_rdep; if (should_default_to_snapshot(c0, c)) flags |= LXC_CLONE_SNAPSHOT; - bdev = bdev_copy(c0, c->name, c->config_path, newtype, flags, bdevdata, - newsize, &need_rdep); + bdev = storage_copy(c0, c->name, c->config_path, newtype, flags, + bdevdata, newsize, &need_rdep); if (!bdev) { ERROR("Error copying storage."); return -1; @@ -2869,36 +3193,30 @@ /* Set new rootfs. */ free(c->lxc_conf->rootfs.path); c->lxc_conf->rootfs.path = strdup(bdev->src); - - /* Set new bdev type. */ - free(c->lxc_conf->rootfs.bdev_type); - c->lxc_conf->rootfs.bdev_type = strdup(bdev->type); - bdev_put(bdev); + storage_put(bdev); if (!c->lxc_conf->rootfs.path) { ERROR("Out of memory while setting storage path."); return -1; } - if (!c->lxc_conf->rootfs.bdev_type) { - ERROR("Out of memory while setting rootfs backend."); - return -1; - } - /* Append a new lxc.rootfs entry to the unexpanded config. */ + /* REMOVE IN LXC 3.0 + * legacy rootfs key + */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false); - if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs", + + /* Append a new lxc.rootfs.path entry to the unexpanded config. */ + clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", false); + if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", c->lxc_conf->rootfs.path)) { ERROR("Error saving new rootfs to cloned config."); return -1; } - /* Append a new lxc.rootfs.backend entry to the unexpanded config. */ + /* REMOVE IN LXC 3.0 + * legacy rootfs.backend key + */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.backend", false); - if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs.backend", - c->lxc_conf->rootfs.bdev_type)) { - ERROR("Error saving new rootfs backend to cloned config."); - return -1; - } if (flags & LXC_CLONE_SNAPSHOT) copy_rdepends(c, c0); @@ -2928,7 +3246,7 @@ char **hookargs = data->hookargs; int ret = -1; char path[MAXPATHLEN]; - struct bdev *bdev; + struct lxc_storage *bdev; FILE *fout; struct lxc_conf *conf = c->lxc_conf; @@ -2948,13 +3266,13 @@ if (unshare(CLONE_NEWNS) < 0) return -1; - bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) return -1; if (strcmp(bdev->type, "dir") != 0) { if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); - bdev_put(bdev); + storage_put(bdev); return -1; } if (detect_shared_rootfs()) { @@ -2964,10 +3282,10 @@ } } if (bdev->ops->mount(bdev) < 0) { - bdev_put(bdev); + storage_put(bdev); return -1; } - } else { // TODO come up with a better way + } else { /* TODO come up with a better way */ free(bdev->dest); bdev->dest = strdup(bdev->src); } @@ -2992,14 +3310,14 @@ 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); + storage_put(bdev); return -1; } } if (!(flags & LXC_CLONE_KEEPNAME)) { ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); - bdev_put(bdev); + storage_put(bdev); if (ret < 0 || ret >= MAXPATHLEN) return -1; @@ -3015,9 +3333,9 @@ } if (fclose(fout) < 0) return -1; + } else { + storage_put(bdev); } - else - bdev_put(bdev); return 0; } @@ -3033,8 +3351,8 @@ 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 [ implies overlay] +-s -B overlay -s -B aufs only rootfs gets converted (copied/snapshotted) on clone. @@ -3058,14 +3376,15 @@ const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { - struct lxc_container *c2 = NULL; char newpath[MAXPATHLEN]; - int ret, storage_copied = 0; - char *origroot = NULL, *saved_unexp_conf = NULL; + int ret; struct clone_update_data data; size_t saved_unexp_len; FILE *fout; pid_t pid; + int storage_copied = 0; + char *origroot = NULL, *saved_unexp_conf = NULL; + struct lxc_container *c2 = NULL; if (!c || !do_lxcapi_is_defined(c)) return NULL; @@ -3078,7 +3397,7 @@ goto out; } - // Make sure the container doesn't yet exist. + /* Make sure the container doesn't yet exist. */ if (!newname) newname = c->name; if (!lxcpath) @@ -3088,6 +3407,7 @@ SYSERROR("clone: failed making config pathname"); goto out; } + if (file_exists(newpath)) { ERROR("error: clone: %s exists", newpath); goto out; @@ -3099,7 +3419,7 @@ goto out; } - // copy the configuration, tweak it as needed, + /* 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; @@ -3118,7 +3438,13 @@ fclose(fout); goto out; } + + /* REMOVE IN LXC 3.0 + * legacy rootfs key + */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false); + + clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", false); write_config(fout, c->lxc_conf); fclose(fout); c->lxc_conf->rootfs.path = origroot; @@ -3127,12 +3453,25 @@ saved_unexp_conf = NULL; c->lxc_conf->unexpanded_len = saved_unexp_len; - sprintf(newpath, "%s/%s/rootfs", lxcpath, newname); - if (mkdir(newpath, 0755) < 0) { - SYSERROR("error creating %s", newpath); + ret = snprintf(newpath, MAXPATHLEN, "%s/%s/rootfs", lxcpath, newname); + if (ret < 0 || ret >= MAXPATHLEN) { + SYSERROR("clone: failed making rootfs pathname"); goto out; } + ret = mkdir(newpath, 0755); + if (ret < 0) { + /* For an overlay container the rootfs is considered immutable + * and will not have been removed when restoring from a + * snapshot. + */ + if (errno != ENOENT && + !(flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { + SYSERROR("Failed to create directory \"%s\"", newpath); + goto out; + } + } + if (am_unpriv()) { if (chown_mapped_root(newpath, c->lxc_conf) < 0) { ERROR("Error chowning %s to container root", newpath); @@ -3147,23 +3486,24 @@ goto out; } - // copy/snapshot rootfs's + /* copy/snapshot rootfs's */ ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); if (ret < 0) goto out; - // update utsname + /* update utsname */ if (!(flags & LXC_CLONE_KEEPNAME)) { clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false); + clear_unexp_config_line(c2->lxc_conf, "lxc.uts.name", false); - if (!set_config_item_locked(c2, "lxc.utsname", newname)) { + if (!set_config_item_locked(c2, "lxc.uts.name", newname)) { ERROR("Error setting new hostname"); goto out; } } - // copy hooks + /* copy hooks */ ret = copyhooks(c, c2); if (ret < 0) { ERROR("error copying hooks"); @@ -3175,7 +3515,7 @@ goto out; } - // update macaddrs + /* update macaddrs */ if (!(flags & LXC_CLONE_KEEPMACADDR)) { if (!network_new_hwaddrs(c2->lxc_conf)) { ERROR("Error updating mac addresses"); @@ -3183,12 +3523,13 @@ } } - // update absolute paths for overlay mount directories + /* Update absolute paths for overlay mount directories. */ if (ovl_update_abs_paths(c2->lxc_conf, c->config_path, c->name, lxcpath, newname) < 0) goto out; - // We've now successfully created c2's storage, so clear it out if we - // fail after this + /* 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)) @@ -3211,7 +3552,7 @@ data.hookargs = hookargs; if (am_unpriv()) ret = userns_exec_1(c->lxc_conf, clone_update_rootfs_wrapper, - &data); + &data, "clone_update_rootfs_wrapper"); else ret = clone_update_rootfs(&data); if (ret < 0) @@ -3246,7 +3587,7 @@ static bool do_lxcapi_rename(struct lxc_container *c, const char *newname) { - struct bdev *bdev; + struct lxc_storage *bdev; struct lxc_container *newc; if (!c || !c->name || !c->config_path || !c->lxc_conf) @@ -3256,14 +3597,14 @@ ERROR("Renaming a container with snapshots is not supported"); return false; } - bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } newc = lxcapi_clone(c, newname, c->config_path, LXC_CLONE_KEEPMACADDR, NULL, bdev->type, 0, NULL); - bdev_put(bdev); + storage_put(bdev); if (!newc) { lxc_container_put(newc); return false; @@ -3272,7 +3613,7 @@ if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); - if (!container_destroy(c)) { + if (!container_destroy(c, NULL)) { ERROR("Could not destroy existing container %s", c->name); return false; } @@ -3375,7 +3716,7 @@ if (!c || !lxcapi_is_defined(c)) return -1; - if (!bdev_can_backup(c->lxc_conf)) { + if (!storage_can_backup(c->lxc_conf)) { ERROR("%s's backing store cannot be backed up.", c->name); ERROR("Your container must use another backing store type."); return -1; @@ -3401,10 +3742,10 @@ */ flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME | LXC_CLONE_KEEPBDEVTYPE | LXC_CLONE_MAYBE_SNAPSHOT; - if (bdev_is_dir(c->lxc_conf, c->lxc_conf->rootfs.path)) { + if (storage_is_dir(c->lxc_conf, c->lxc_conf->rootfs.path)) { ERROR("Snapshot of directory-backed container requested."); ERROR("Making a copy-clone. If you do want snapshots, then"); - ERROR("please create an aufs or overlayfs clone first, snapshot that"); + ERROR("please create an aufs or overlay clone first, snapshot that"); ERROR("and keep the original container pristine."); flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; } @@ -3416,7 +3757,7 @@ lxc_container_put(c2); - // Now write down the creation time + /* Now write down the creation time. */ time_t timer; char buffer[25]; struct tm* tm_info; @@ -3446,7 +3787,7 @@ } if (commentfile) { - // $p / $name / comment \0 + /* $p / $name / comment \0 */ int len = strlen(snappath) + strlen(newname) + 10; char *path = alloca(len); sprintf(path, "%s/%s/comment", snappath, newname); @@ -3468,7 +3809,7 @@ static char *get_snapcomment_path(char* snappath, char *name) { - // $snappath/$name/comment + /* $snappath/$name/comment */ int ret, len = strlen(snappath) + strlen(name) + 10; char *s = malloc(len); @@ -3595,7 +3936,7 @@ char clonelxcpath[MAXPATHLEN]; int flags = 0; struct lxc_container *snap, *rest; - struct bdev *bdev; + struct lxc_storage *bdev; bool b = false; if (!c || !c->name || !c->config_path) @@ -3606,47 +3947,61 @@ return false; } - bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, + c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } + /* For an overlay container the rootfs is considered immutable + * and cannot be removed when restoring from a snapshot. We pass this + * internal flag along to communicate this to various parts of the + * codebase. + */ + if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) + bdev->flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; + if (!newname) newname = c->name; if (!get_snappath_dir(c, clonelxcpath)) { - bdev_put(bdev); + storage_put(bdev); return false; } - // how should we lock this? + /* 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); + if (snap) + lxc_container_put(snap); + storage_put(bdev); return false; } - if (strcmp(c->name, newname) == 0) { - if (!container_destroy(c)) { + if (!strcmp(c->name, newname)) { + if (!container_destroy(c, bdev)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); - bdev_put(bdev); + storage_put(bdev); return false; } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0) flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; + + if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) + flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; rest = lxcapi_clone(snap, newname, c->config_path, flags, bdev->type, NULL, 0, NULL); - bdev_put(bdev); + storage_put(bdev); if (rest && lxcapi_is_defined(rest)) b = true; if (rest) lxc_container_put(rest); + lxc_container_put(snap); return b; } @@ -3930,7 +4285,7 @@ return false; } - if (pid == 0) { // child + if (pid == 0) { /* child */ int ret = 0; if (!enter_net_ns(c)) { ERROR("failed to enter namespace"); @@ -4147,13 +4502,13 @@ if (ongoing_create(c) == 2) { ERROR("Error: %s creation was not completed", c->name); - container_destroy(c); + container_destroy(c, NULL); lxcapi_clear_config(c); } c->daemonize = true; c->pidfile = NULL; - // assign the member functions + /* Assign the member functions. */ c->is_defined = lxcapi_is_defined; c->state = lxcapi_state; c->is_running = lxcapi_is_running; @@ -4254,7 +4609,7 @@ if (!direntp) break; - // Ignore '.', '..' and any hidden directory + /* Ignore '.', '..' and any hidden directory. */ if (!strncmp(direntp->d_name, ".", 1)) continue; @@ -4363,7 +4718,7 @@ while (*p == '/') p++; - // Now p is the start of lxc_name + /* Now p is the start of lxc_name. */ p2 = strchr(p, '/'); if (!p2 || strncmp(p2, "/command", 8) != 0) continue; @@ -4525,3 +4880,8 @@ free(ct_name); return ret; } + +bool lxc_config_item_is_supported(const char *key) +{ + return !!lxc_get_config(key); +} diff -Nru lxc-2.0.8/src/lxc/lxccontainer.h lxc-2.1.0/src/lxc/lxccontainer.h --- lxc-2.0.8/src/lxc/lxccontainer.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxccontainer.h 2017-09-06 02:32:37.000000000 +0000 @@ -59,7 +59,7 @@ * changes, whenever possible stick to simply appending new members. */ struct lxc_container { - // private fields + /* private fields */ /*! * \private * Name of container. @@ -105,7 +105,7 @@ */ struct lxc_conf *lxc_conf; - // public fields + /* public fields */ /*! Human-readable string representing last error */ char *error_string; @@ -408,8 +408,8 @@ * \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. + * \note If \p inlen is smaller than required, nothing will be written to \p retv and still return + * the length of config item value. */ int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen); @@ -1017,11 +1017,34 @@ */ int list_all_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); +struct lxc_log { + const char *name; + const char *lxcpath; + const char *file; + const char *level; + const char *prefix; + bool quiet; +}; + +/*! + *\brief Initialize the log + * + *\param log lxc log configuration. + */ +int lxc_log_init(struct lxc_log *log); + /*! * \brief Close log file. */ void lxc_log_close(void); +/*! + * \brief Check if the configuration item is supported by this LXC instance. + * + * \param key Configuration item to check for. + */ +bool lxc_config_item_is_supported(const char *key); + #ifdef __cplusplus } #endif diff -Nru lxc-2.0.8/src/lxc/lxc.h lxc-2.1.0/src/lxc/lxc.h --- lxc-2.0.8/src/lxc/lxc.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxc.h 2017-09-06 02:32:37.000000000 +0000 @@ -36,6 +36,7 @@ struct lxc_msg; struct lxc_conf; struct lxc_arguments; +struct lxc_handler; /** Following code is for liblxc. @@ -51,8 +52,9 @@ * @backgrounded : whether or not the container is daemonized * Returns 0 on success, < 0 otherwise */ -extern int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf, - const char *lxcpath, bool backgrounded); +extern int lxc_start(const char *name, char *const argv[], + struct lxc_handler *handler, const char *lxcpath, + bool backgrounded); /* * Start the specified command inside an application container @@ -64,7 +66,7 @@ * Returns 0 on success, < 0 otherwise */ extern int lxc_execute(const char *name, char *const argv[], int quiet, - struct lxc_conf *conf, const char *lxcpath, + struct lxc_handler *handler, const char *lxcpath, bool backgrounded); /* diff -Nru lxc-2.0.8/src/lxc/lxclock.c lxc-2.1.0/src/lxc/lxclock.c --- lxc-2.0.8/src/lxc/lxclock.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxclock.c 2017-09-06 02:32:37.000000000 +0000 @@ -19,7 +19,6 @@ */ #define _GNU_SOURCE -#include "lxclock.h" #include #include #include @@ -30,6 +29,7 @@ #include +#include "lxclock.h" #include "utils.h" #include "log.h" @@ -39,11 +39,6 @@ #define MAX_STACKDEPTH 25 -#define OFLAG (O_CREAT | O_RDWR) -#define SEMMODE 0660 -#define SEMVALUE 1 -#define SEMVALUE_LOCKED 0 - lxc_log_define(lxc_lock, lxc); #ifdef MUTEX_DEBUGGING @@ -59,13 +54,13 @@ size = backtrace(array, MAX_STACKDEPTH); strings = backtrace_symbols(array, size); - // Using fprintf here as our logging module is not thread safe - fprintf(stderr, "\tObtained %zd stack frames.\n", size); + /* Using fprintf here as our logging module is not thread safe. */ + fprintf(stderr, "\tObtained %zu stack frames.\n", size); for (i = 0; i < size; i++) fprintf(stderr, "\t\t%s\n", strings[i]); - free (strings); + free(strings); } #else static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -80,7 +75,7 @@ if ((ret = pthread_mutex_lock(l)) != 0) { fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret)); dump_stacktrace(); - exit(1); + exit(EXIT_FAILURE); } } @@ -91,7 +86,7 @@ if ((ret = pthread_mutex_unlock(l)) != 0) { fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret)); dump_stacktrace(); - exit(1); + exit(EXIT_FAILURE); } } @@ -216,12 +211,10 @@ ret = -2; if (timeout) { ERROR("Error: timeout not supported with flock"); - ret = -2; goto out; } if (!l->u.f.fname) { ERROR("Error: filename not set for flock"); - ret = -2; goto out; } if (l->u.f.fd == -1) { @@ -229,6 +222,7 @@ S_IWUSR | S_IRUSR); if (l->u.f.fd == -1) { ERROR("Error opening %s", l->u.f.fname); + saved_errno = errno; goto out; } } diff -Nru lxc-2.0.8/src/lxc/lxclock.h lxc-2.1.0/src/lxc/lxclock.h --- lxc-2.0.8/src/lxc/lxclock.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxclock.h 2017-09-06 02:32:37.000000000 +0000 @@ -33,21 +33,21 @@ #define LXC_LOCK_ANON_SEM 1 /*!< Anonymous semaphore lock */ #define LXC_LOCK_FLOCK 2 /*!< flock(2) lock */ -// private +/* private */ /*! * LXC Lock */ struct lxc_lock { - short type; //!< Lock type + short type; /*!< Lock type */ union { - sem_t *sem; //!< Anonymous semaphore (LXC_LOCK_ANON_SEM) + 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 + int fd; /*!< fd on which a lock is held (if not -1) */ + char *fname; /*!< Name of lock */ } f; - } u; //!< Container for lock type elements + } u; /*!< Container for lock type elements */ }; /*! @@ -68,7 +68,8 @@ * 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. + * given) then a lockfile is created as \c /run/lxc/lock/$lxcpath/.$name if root, + * or \c $XDG_RUNTIME_DIR/lxc/lock/$lxcpath/.$name if non-root. * The lock is used to protect the containers on-disk representation. * * \internal This function allocates the pathname for the given lock in memory @@ -87,7 +88,8 @@ * indefinite wait). * * \return \c 0 if lock obtained, \c -2 on failure to set timeout, - * or \c -1 on any other error (\c errno will be set by \c sem_wait(3)). + * or \c -1 on any other error (\c errno will be set by \c sem_wait(3) + * or \c fcntl(2)). * * \note \p timeout is (currently?) only supported for privlock, not * for slock. Since currently there is not a single use of the timeout @@ -102,7 +104,7 @@ * \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. + * otherwise \c -1 with \c errno saved from \c fcntl(2) or sem_post function. */ extern int lxcunlock(struct lxc_lock *lock); @@ -153,6 +155,9 @@ /*! * \brief Unlock the containers disk data. + * + * \param c Container. + * */ extern void container_disk_unlock(struct lxc_container *c); diff -Nru lxc-2.0.8/src/lxc/lxc_monitord.c lxc-2.1.0/src/lxc/lxc_monitord.c --- lxc-2.0.8/src/lxc/lxc_monitord.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxc_monitord.c 2017-09-06 02:32:37.000000000 +0000 @@ -44,6 +44,7 @@ #include "mainloop.h" #include "monitor.h" #include "utils.h" +#include "lxccontainer.h" #define CLIENTFDS_CHUNK 64 @@ -350,6 +351,7 @@ char *lxcpath = argv[1]; bool mainloop_opened = false; bool monitord_created = false; + struct lxc_log log; if (argc != 3) { fprintf(stderr, @@ -364,7 +366,13 @@ if (ret < 0 || ret >= sizeof(logpath)) exit(EXIT_FAILURE); - ret = lxc_log_init(NULL, logpath, "DEBUG", "lxc-monitord", 0, lxcpath); + log.name = NULL; + log.file = logpath; + log.level = "DEBUG"; + log.prefix = "lxc-monitord"; + log.quiet = 0; + log.lxcpath = lxcpath; + ret = lxc_log_init(&log); if (ret) INFO("Failed to open log file %s, log will be lost.", lxcpath); lxc_log_options_no_override(); @@ -422,10 +430,18 @@ getpid(), mon.lxcpath); for (;;) { ret = lxc_mainloop(&mon.descr, 1000 * 30); + if (ret) { + ERROR("mainloop returned an error"); + break; + } if (mon.clientfds_cnt <= 0) { NOTICE("No remaining clients. lxc-monitord is exiting."); break; } + if (quit == 1) { + NOTICE("got quit command. lxc-monitord is exitting."); + break; + } } on_signal: diff -Nru lxc-2.0.8/src/lxc/lxc_user_nic.c lxc-2.1.0/src/lxc/lxc_user_nic.c --- lxc-2.0.8/src/lxc/lxc_user_nic.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxc_user_nic.c 2017-09-06 02:32:37.000000000 +0000 @@ -17,7 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE /* See feature_test_macros(7) */ +#define _GNU_SOURCE #include #include #include @@ -59,21 +59,27 @@ static void usage(char *me, bool fail) { - fprintf(stderr, "Usage: %s lxcpath name pid type bridge nicname\n", me); - fprintf(stderr, " nicname is the name to use inside the container\n"); - exit(fail ? 1 : 0); -} + fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} " + "{bridge} {nicname}\n", me); + fprintf(stderr, "Usage: %s delete {lxcpath} {name} " + "{/proc//ns/net} {type} {bridge} {nicname}\n", me); + fprintf(stderr, "{nicname} is the name to use inside the container\n"); + + if (fail) + exit(EXIT_FAILURE); -static char *lxcpath, *lxcname; + exit(EXIT_SUCCESS); +} static int open_and_lock(char *path) { - int fd; + int fd, ret; struct flock lk; - fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR); + fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); if (fd < 0) { - usernic_error("Failed to open %s: %s.\n", path, strerror(errno)); + usernic_error("Failed to open \"%s\": %s\n", path, + strerror(errno)); return -1; } @@ -81,8 +87,11 @@ lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; - if (fcntl(fd, F_SETLKW, &lk) < 0) { - usernic_error("Failed to lock %s: %s.\n", path, strerror(errno)); + + ret = fcntl(fd, F_SETLKW, &lk); + if (ret < 0) { + usernic_error("Failed to lock \"%s\": %s\n", path, + strerror(errno)); close(fd); return -1; } @@ -90,14 +99,13 @@ return fd; } - static char *get_username(void) { struct passwd *pwd; pwd = getpwuid(getuid()); if (!pwd) { - usernic_error("Failed to call get username: %s.\n", strerror(errno)); + usernic_error("Failed to get username: %s\n", strerror(errno)); return NULL; } @@ -127,9 +135,8 @@ ngroups = getgroups(0, NULL); if (ngroups < 0) { - usernic_error( - "Failed to get number of groups the user belongs to: %s.\n", - strerror(errno)); + usernic_error("Failed to get number of groups the user " + "belongs to: %s\n", strerror(errno)); return NULL; } if (ngroups == 0) @@ -138,7 +145,7 @@ group_ids = malloc(sizeof(gid_t) * ngroups); if (!group_ids) { usernic_error("Failed to allocate memory while getting groups " - "the user belongs to: %s.\n", + "the user belongs to: %s\n", strerror(errno)); return NULL; } @@ -146,7 +153,7 @@ ret = getgroups(ngroups, group_ids); if (ret < 0) { free(group_ids); - usernic_error("Failed to get process groups: %s.\n", + usernic_error("Failed to get process groups: %s\n", strerror(errno)); return NULL; } @@ -155,7 +162,7 @@ if (!groupnames) { free(group_ids); usernic_error("Failed to allocate memory while getting group " - "names: %s.\n", + "names: %s\n", strerror(errno)); return NULL; } @@ -165,7 +172,7 @@ for (i = 0; i < ngroups; i++) { gr = getgrgid(group_ids[i]); if (!gr) { - usernic_error("Failed to get group name: %s.\n", + usernic_error("Failed to get group name: %s\n", strerror(errno)); free(group_ids); free_groupnames(groupnames); @@ -174,7 +181,7 @@ groupnames[i] = strdup(gr->gr_name); if (!groupnames[i]) { - usernic_error("Failed to copy group name \"%s\".", + usernic_error("Failed to copy group name \"%s\"", gr->gr_name); free(group_ids); free_groupnames(groupnames); @@ -203,19 +210,21 @@ struct alloted_s *next; }; -static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n) +static struct alloted_s *append_alloted(struct alloted_s **head, char *name, + int n) { struct alloted_s *cur, *al; if (!head || !name) { - // sanity check. parameters should not be null - usernic_error("%s\n", "Unexpected NULL argument."); + /* Sanity check. Parameters should not be null. */ + usernic_error("%s\n", "Unexpected NULL argument"); return NULL; } al = malloc(sizeof(struct alloted_s)); if (!al) { - usernic_error("Failed to allocate memory: %s.\n", strerror(errno)); + usernic_error("Failed to allocate memory: %s\n", + strerror(errno)); return NULL; } @@ -266,7 +275,8 @@ * Return the count entry for the calling user if there is one. Else * return -1. */ -static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted) +static int get_alloted(char *me, char *intype, char *link, + struct alloted_s **alloted) { int n, ret; char name[100], type[100], br[100]; @@ -279,13 +289,15 @@ fin = fopen(LXC_USERNIC_CONF, "r"); if (!fin) { - usernic_error("Failed to open \"%s\": %s.\n", LXC_USERNIC_CONF, strerror(errno)); + usernic_error("Failed to open \"%s\": %s\n", LXC_USERNIC_CONF, + strerror(errno)); return -1; } groups = get_groupnames(); while ((getline(&line, &len, fin)) != -1) { - ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n); + ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, + type, br, &n); if (ret != 4) continue; @@ -345,92 +357,119 @@ return s; } -static char *find_line(char *p, char *e, char *u, char *t, char *l) -{ - char *p1, *p2, *ret; +static char *find_line(char *buf_start, char *buf_end, char *name, + char *net_type, char *net_link, char *net_dev, + bool *owner, bool *found, bool *keep) +{ + char *end_of_line, *end_of_word, *line; + + while (buf_start < buf_end) { + size_t len; + char netdev_name[IFNAMSIZ]; + + *found = false; + *keep = true; + *owner = false; + + end_of_line = get_eol(buf_start, buf_end); + if (end_of_line >= buf_end) + return NULL; - while ((p < e) && (p1 = get_eol(p, e)) < e) { - ret = p; - if (*p == '#') + line = buf_start; + if (*buf_start == '#') goto next; - while ((p < e) && isblank(*p)) - p++; + while ((buf_start < buf_end) && isblank(*buf_start)) + buf_start++; - p2 = get_eow(p, e); - if (!p2 || ((size_t)(p2 - p)) != strlen(u) || strncmp(p, u, strlen(u))) - goto next; + /* Check whether the line contains the caller's name. */ + end_of_word = get_eow(buf_start, buf_end); + /* corrupt db */ + if (!end_of_word) + return NULL; - p = p2 + 1; - while ((p < e) && isblank(*p)) - p++; + if (strncmp(buf_start, name, strlen(name))) + *found = false; - p2 = get_eow(p, e); - if (!p2 || ((size_t)(p2 - p)) != strlen(t) || strncmp(p, t, strlen(t))) - goto next; + *owner = true; - p = p2 + 1; - while ((p < e) && isblank(*p)) - p++; + buf_start = end_of_word + 1; + while ((buf_start < buf_end) && isblank(*buf_start)) + buf_start++; + + /* Check whether line is of the right network type. */ + end_of_word = get_eow(buf_start, buf_end); + /* corrupt db */ + if (!end_of_word) + return NULL; - p2 = get_eow(p, e); - if (!p2 || ((size_t)(p2 - p)) != strlen(l) || strncmp(p, l, strlen(l))) - goto next; + if (strncmp(buf_start, net_type, strlen(net_type))) + *found = false; - return ret; -next: - p = p1 + 1; - } + buf_start = end_of_word + 1; + while ((buf_start < buf_end) && isblank(*buf_start)) + buf_start++; + + /* Check whether line is contains the right link. */ + end_of_word = get_eow(buf_start, buf_end); + /* corrupt db */ + if (!end_of_word) + return NULL; - return NULL; -} + if (strncmp(buf_start, net_link, strlen(net_link))) + *found = false; -static bool nic_exists(char *nic) -{ - char path[MAXPATHLEN]; - int ret; - struct stat sb; + buf_start = end_of_word + 1; + while ((buf_start < buf_end) && isblank(*buf_start)) + buf_start++; + + /* Check whether line contains the right network device. */ + end_of_word = get_eow(buf_start, buf_end); + /* corrupt db */ + if (!end_of_word) + return NULL; - if (!strcmp(nic, "none")) - return true; + len = end_of_word - buf_start; + /* corrupt db */ + if (len >= IFNAMSIZ) + return NULL; - ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic); - if (ret < 0 || ret >= MAXPATHLEN) - return false; + memcpy(netdev_name, buf_start, len); + netdev_name[len] = '\0'; + *keep = lxc_nic_exists(netdev_name); - ret = stat(path, &sb); - if (ret < 0) - return false; + if (net_dev && !strcmp(netdev_name, net_dev)) + *found = true; - return true; + return line; + + next: + buf_start = end_of_line + 1; + } + + return NULL; } -static int instantiate_veth(char *n1, char **n2) +static int instantiate_veth(char *veth1, char *veth2) { - int err; - - err = snprintf(*n2, IFNAMSIZ, "%sp", n1); - if (err < 0 || err >= IFNAMSIZ) { - usernic_error("%s\n", "Could not create nic name."); - return -1; - } + int ret; - err = lxc_veth_create(n1, *n2); - if (err) { - usernic_error("Failed to create %s-%s : %s.\n", n1, *n2, strerror(-err)); + ret = lxc_veth_create(veth1, veth2); + if (ret < 0) { + usernic_error("Failed to create %s-%s : %s.\n", veth1, veth2, + strerror(-ret)); return -1; } /* Changing the high byte of the mac address to 0xfe, the bridge * interface will always keep the host's mac address and not take the * mac address of a container. */ - err = setup_private_host_hw_addr(n1); - if (err) + ret = setup_private_host_hw_addr(veth1); + if (ret < 0) usernic_error("Failed to change mac address of host interface " - "%s : %s.\n", - n1, strerror(-err)); + "%s : %s\n", veth1, strerror(-ret)); - return netdev_set_flag(n1, IFF_UP); + return netdev_set_flag(veth1, IFF_UP); } static int get_mtu(char *name) @@ -438,31 +477,32 @@ int idx; idx = if_nametoindex(name); + if (idx < 0) + return -1; return netdev_get_mtu(idx); } -static bool create_nic(char *nic, char *br, int pid, char **cnic) +static int create_nic(char *nic, char *br, int pid, char **cnic) { - char *veth1buf, *veth2buf; + char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ]; int mtu, ret; - veth1buf = alloca(IFNAMSIZ); - veth2buf = alloca(IFNAMSIZ); - if (!veth1buf || !veth2buf) { - usernic_error("Failed allocate memory: %s.\n", strerror(errno)); - return false; - } - ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic); if (ret < 0 || ret >= IFNAMSIZ) { - usernic_error("%s", "Could not create nic name.\n"); - return false; + usernic_error("%s", "Could not create nic name\n"); + return -1; } + ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf); + if (ret < 0 || ret >= IFNAMSIZ) { + usernic_error("%s\n", "Could not create nic name"); + return -1; + } /* create the nics */ - if (instantiate_veth(veth1buf, &veth2buf) < 0) { - usernic_error("%s", "Error creating veth tunnel.\n"); - return false; + ret = instantiate_veth(veth1buf, veth2buf); + if (ret < 0) { + usernic_error("%s", "Error creating veth tunnel\n"); + return -1; } if (strcmp(br, "none")) { @@ -471,21 +511,23 @@ if (mtu > 0) { ret = lxc_netdev_set_mtu(veth1buf, mtu); if (ret < 0) { - usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth1buf); + usernic_error("Failed to set mtu to %d on %s\n", + mtu, veth1buf); goto out_del; } ret = lxc_netdev_set_mtu(veth2buf, mtu); if (ret < 0) { - usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth2buf); + usernic_error("Failed to set mtu to %d on %s\n", + mtu, veth2buf); goto out_del; } } /* attach veth1 to bridge */ - ret = lxc_bridge_attach(lxcpath, lxcname, br, veth1buf); + ret = lxc_bridge_attach(br, veth1buf); if (ret < 0) { - usernic_error("Error attaching %s to %s.\n", veth1buf, br); + usernic_error("Error attaching %s to %s\n", veth1buf, br); goto out_del; } } @@ -493,54 +535,22 @@ /* pass veth2 to target netns */ ret = lxc_netdev_move_by_name(veth2buf, pid, NULL); if (ret < 0) { - usernic_error("Error moving %s to network namespace of %d.\n", veth2buf, pid); + usernic_error("Error moving %s to network namespace of %d\n", + veth2buf, pid); goto out_del; } *cnic = strdup(veth2buf); if (!*cnic) { - usernic_error("Failed to copy string \"%s\".\n", veth2buf); - return false; + usernic_error("Failed to copy string \"%s\"\n", veth2buf); + return -1; } - return true; + return 0; out_del: lxc_netdev_delete_by_name(veth1buf); - return false; -} - -/* - * Get a new nic. - * *dest will contain the name (vethXXXXXX) which is attached - * on the host to the lxc bridge - */ -static bool get_new_nicname(char **dest, char *br, int pid, char **cnic) -{ - int ret; - char template[IFNAMSIZ]; - - ret = snprintf(template, sizeof(template), "vethXXXXXX"); - if (ret < 0 || (size_t)ret >= sizeof(template)) - return false; - - *dest = lxc_mkifname(template); - if (!create_nic(*dest, br, pid, cnic)) - return false; - - return true; -} - -static bool get_nic_from_line(char *p, char **nic) -{ - int ret; - char user[100], type[100], br[100]; - - ret = sscanf(p, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user, type, br, *nic); - if (ret != 4) - return false; - - return true; + return -1; } struct entry_line { @@ -549,186 +559,230 @@ bool keep; }; -static bool cull_entries(int fd, char *me, char *t, char *br) +static bool cull_entries(int fd, char *name, char *net_type, char *net_link, + char *net_dev, bool *found_nicname) { - int i, n = 0; - off_t len; - char *buf, *p, *e, *nic; + int i, ret; + char *buf, *buf_end, *buf_start; struct stat sb; + int n = 0; + bool found, keep; struct entry_line *entry_lines = NULL; - nic = alloca(100); - if (!nic) - return false; - - if (fstat(fd, &sb) < 0) { - usernic_error("Failed to fstat: %s.\n", strerror(errno)); + ret = fstat(fd, &sb); + if (ret < 0) { + usernic_error("Failed to fstat: %s\n", strerror(errno)); return false; } - len = sb.st_size; - if (len == 0) - return true; + if (!sb.st_size) + return false; - buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { - usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno)); + usernic_error("Failed to establish shared memory mapping: %s\n", + strerror(errno)); return false; } - p = buf; - e = buf + len; - while ((p = find_line(p, e, me, t, br))) { + buf_start = buf; + buf_end = buf + sb.st_size; + while ((buf_start = find_line(buf_start, buf_end, name, net_type, + net_link, net_dev, &(bool){true}, &found, + &keep))) { struct entry_line *newe; newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1)); if (!newe) { free(entry_lines); + lxc_strmunmap(buf, sb.st_size); return false; } + if (found) + *found_nicname = true; + entry_lines = newe; - entry_lines[n].start = p; - entry_lines[n].len = get_eol(p, e) - entry_lines[n].start; - entry_lines[n].keep = true; + entry_lines[n].start = buf_start; + entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start; + entry_lines[n].keep = keep; n++; - if (!get_nic_from_line(p, &nic)) - continue; - - if (nic && !nic_exists(nic)) - entry_lines[n - 1].keep = false; - p += entry_lines[n - 1].len + 1; - if (p >= e) + buf_start += entry_lines[n - 1].len + 1; + if (buf_start >= buf_end) break; } - p = buf; + buf_start = buf; for (i = 0; i < n; i++) { if (!entry_lines[i].keep) continue; - memcpy(p, entry_lines[i].start, entry_lines[i].len); - p += entry_lines[i].len; - *p = '\n'; - p++; + memcpy(buf_start, entry_lines[i].start, entry_lines[i].len); + buf_start += entry_lines[i].len; + *buf_start = '\n'; + buf_start++; } free(entry_lines); - munmap(buf, sb.st_size); - if (ftruncate(fd, p - buf)) - usernic_error("Failed to set new file size: %s.\n", strerror(errno)); + lxc_strmunmap(buf, sb.st_size); + ret = ftruncate(fd, buf_start - buf); + if (ret < 0) + usernic_error("Failed to set new file size: %s\n", + strerror(errno)); return true; } -static int count_entries(char *buf, off_t len, char *me, char *t, char *br) +static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link) { - char *e; int count = 0; + bool owner = false;; + char *buf_end = &buf[len]; - e = &buf[len]; - while ((buf = find_line(buf, e, me, t, br))) { - count++; - buf = get_eol(buf, e) + 1; - if (buf >= e) + buf_end = &buf[len]; + while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL, + &owner, &(bool){true}, &(bool){true}))) { + if (owner) + count++; + buf = get_eol(buf, buf_end) + 1; + if (buf >= buf_end) break; } return count; } -/* - * The dbfile has lines of the format: - * user type bridge nicname - */ -static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, - char *intype, char *br, int allowed, - char **nicname, char **cnic) +/* The dbfile has lines of the format: user type bridge nicname. */ +static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid, + char *intype, char *br, int allowed, char **cnic) { int ret; - off_t len, slen; + size_t slen; char *newline, *owner; + char nicname[IFNAMSIZ]; struct stat sb; struct alloted_s *n; int count = 0; char *buf = NULL; for (n = names; n != NULL; n = n->next) - cull_entries(fd, n->name, intype, br); + cull_entries(fd, n->name, intype, br, NULL, NULL); if (allowed == 0) - return false; + return NULL; owner = names->name; - if (fstat(fd, &sb) < 0) { - usernic_error("Failed to fstat: %s.\n", strerror(errno)); - return false; + ret = fstat(fd, &sb); + if (ret < 0) { + usernic_error("Failed to fstat: %s\n", strerror(errno)); + return NULL; } - len = sb.st_size; - if (len > 0) { - buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (sb.st_size > 0) { + buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { - usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno)); - return false; + usernic_error("Failed to establish shared memory " + "mapping: %s\n", strerror(errno)); + return NULL; } owner = NULL; for (n = names; n != NULL; n = n->next) { - count = count_entries(buf, len, n->name, intype, br); - + count = count_entries(buf, sb.st_size, n->name, intype, br); if (count >= n->allowed) continue; owner = n->name; break; } + + lxc_strmunmap(buf, sb.st_size); } if (owner == NULL) - return false; + return NULL; - if (!get_new_nicname(nicname, br, pid, cnic)) - return false; + ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX"); + if (ret < 0 || (size_t)ret >= sizeof(nicname)) + return NULL; + + if (!lxc_mkifname(nicname)) + return NULL; + + ret = create_nic(nicname, br, pid, cnic); + if (ret < 0) { + usernic_error("%s", "Failed to create new nic\n"); + return NULL; + } - /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */ - slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5; - newline = alloca(slen); + /* strlen(owner) + * + + * " " + * + + * strlen(intype) + * + + * " " + * + + * strlen(br) + * + + * " " + * + + * strlen(nicname) + * + + * \n + * + + * \0 + */ + slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4; + newline = malloc(slen + 1); if (!newline) { - usernic_error("Failed allocate memory: %s.\n", strerror(errno)); - return false; + free(newline); + usernic_error("Failed allocate memory: %s\n", strerror(errno)); + return NULL; } - ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, *nicname); - if (ret < 0 || ret >= slen) { - if (lxc_netdev_delete_by_name(*nicname) != 0) - usernic_error("Error unlinking %s.\n", *nicname); - return false; + ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname); + if (ret < 0 || (size_t)ret >= (slen + 1)) { + if (lxc_netdev_delete_by_name(nicname) != 0) + usernic_error("Error unlinking %s\n", nicname); + free(newline); + return NULL; } - if (len) - munmap(buf, len); - if (ftruncate(fd, len + slen)) - usernic_error("Failed to set new file size: %s.\n", strerror(errno)); + /* Note that the file needs to be truncated to the size **without** the + * \0 byte! Files are not \0-terminated! + */ + ret = ftruncate(fd, sb.st_size + slen); + if (ret < 0) + usernic_error("Failed to truncate file: %s\n", strerror(errno)); - buf = mmap(NULL, len + slen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + buf = lxc_strmmap(NULL, sb.st_size + slen, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { - usernic_error("Failed to establish shared memory mapping: %s.\n", strerror(errno)); - if (lxc_netdev_delete_by_name(*nicname) != 0) - usernic_error("Error unlinking %s.\n", *nicname); - return false; + usernic_error("Failed to establish shared memory mapping: %s\n", + strerror(errno)); + if (lxc_netdev_delete_by_name(nicname) != 0) + usernic_error("Error unlinking %s\n", nicname); + free(newline); + return NULL; } - strcpy(buf + len, newline); - munmap(buf, len + slen); + /* Note that the memory needs to be moved in the buffer **without** the + * \0 byte! Files are not \0-terminated! + */ + memmove(buf + sb.st_size, newline, slen); + free(newline); + lxc_strmunmap(buf, sb.st_size + slen); - return true; + return strdup(nicname); } static bool create_db_dir(char *fnam) { + int ret; char *p; p = alloca(strlen(fnam) + 1); @@ -743,8 +797,11 @@ return true; *p = '\0'; - if (mkdir(fnam, 0755) && errno != EEXIST) { - usernic_error("Failed to create %s: %s.\n", fnam, strerror(errno)); + + ret = mkdir(fnam, 0755); + if (ret < 0 && errno != EEXIST) { + usernic_error("Failed to create %s: %s\n", fnam, + strerror(errno)); *p = '/'; return false; } @@ -753,23 +810,24 @@ goto again; } -#define VETH_DEF_NAME "eth%d" -static int rename_in_ns(int pid, char *oldname, char **newnamep) +static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname, + int *container_veth_ifidx) { + int ret; uid_t ruid, suid, euid; - int fret = -1; - int fd = -1, ifindex = -1, ofd = -1, ret; - bool grab_newname = false; + char ifname[IFNAMSIZ]; + char *string_ret = NULL, *name = NULL; + int fd = -1, ifindex = -1, ofd = -1; ofd = lxc_preserve_ns(getpid(), "net"); if (ofd < 0) { - usernic_error("Failed opening network namespace path for '%d'.", getpid()); - return fret; + usernic_error("Failed opening network namespace path for %d", getpid()); + return NULL; } fd = lxc_preserve_ns(pid, "net"); if (fd < 0) { - usernic_error("Failed opening network namespace path for '%d'.", pid); + usernic_error("Failed opening network namespace path for %d", pid); goto do_partial_cleanup; } @@ -786,7 +844,7 @@ fd = -1; if (ret < 0) { usernic_error("Failed to setns() to the network namespace of " - "the container with PID %d: %s.\n", + "the container with PID %d: %s\n", pid, strerror(errno)); goto do_partial_cleanup; } @@ -795,80 +853,83 @@ if (ret < 0) { usernic_error("Failed to drop privilege by setting effective " "user id and real user id to %d, and saved user " - "ID to 0: %s.\n", + "ID to 0: %s\n", ruid, strerror(errno)); - // COMMENT(brauner): It's ok to jump to do_full_cleanup here - // since setresuid() will succeed when trying to set real, - // effective, and saved to values they currently have. + /* It's ok to jump to do_full_cleanup here since setresuid() + * will succeed when trying to set real, effective, and saved to + * values they currently have. + */ goto do_full_cleanup; } - if (!*newnamep) { - grab_newname = true; - *newnamep = VETH_DEF_NAME; - - ifindex = if_nametoindex(oldname); - if (!ifindex) { - usernic_error("Failed to get netdev index: %s.\n", strerror(errno)); - goto do_full_cleanup; - } + /* Check if old interface exists. */ + ifindex = if_nametoindex(oldname); + if (!ifindex) { + usernic_error("Failed to get netdev index: %s\n", strerror(errno)); + goto do_full_cleanup; } - ret = lxc_netdev_rename_by_name(oldname, *newnamep); + /* When the IFLA_IFNAME attribute is passed something like "%d" + * netlink will replace the format specifier with an appropriate index. + * So we pass "eth%d". + */ + if (newname) + name = newname; + else + name = "eth%d"; + + ret = lxc_netdev_rename_by_name(oldname, name); + name = NULL; if (ret < 0) { - usernic_error("Error %d renaming netdev %s to %s in container.\n", ret, oldname, *newnamep); + usernic_error("Error %d renaming netdev %s to %s in container\n", + ret, oldname, newname ? newname : "eth%d"); goto do_full_cleanup; } - if (grab_newname) { - char ifname[IFNAMSIZ]; - char *namep = ifname; - - if (!if_indextoname(ifindex, namep)) { - usernic_error("Failed to get new netdev name: %s.\n", strerror(errno)); - goto do_full_cleanup; - } - - *newnamep = strdup(namep); - if (!*newnamep) - goto do_full_cleanup; + /* Retrieve new name for interface. */ + if (!if_indextoname(ifindex, ifname)) { + usernic_error("Failed to get new netdev name: %s\n", strerror(errno)); + goto do_full_cleanup; } - fret = 0; + /* Allocation failure for strdup() is checked below. */ + name = strdup(ifname); + string_ret = name; + *container_veth_ifidx = ifindex; do_full_cleanup: ret = setresuid(ruid, euid, suid); if (ret < 0) { - usernic_error("Failed to restore privilege by setting effective " - "user id to %d, real user id to %d, and saved user " - "ID to %d: %s.\n", - ruid, euid, suid, strerror(errno)); - fret = -1; - // COMMENT(brauner): setns() should fail if setresuid() doesn't - // succeed but there's no harm in falling through; keeps the - // code cleaner. + usernic_error("Failed to restore privilege by setting " + "effective user id to %d, real user id to %d, " + "and saved user ID to %d: %s\n", ruid, euid, suid, + strerror(errno)); + + string_ret = NULL; } ret = setns(ofd, CLONE_NEWNET); if (ret < 0) { usernic_error("Failed to setns() to original network namespace " - "of PID %d: %s.\n", - ofd, strerror(errno)); - fret = -1; + "of PID %d: %s\n", ofd, strerror(errno)); + + string_ret = NULL; } do_partial_cleanup: if (fd >= 0) close(fd); + + if (!string_ret && name) + free(name); + close(ofd); - return fret; + return string_ret; } -/* - * If the caller (real uid, not effective uid) may read the - * /proc/[pid]/ns/net, then it is either the caller's netns or one - * which it created. +/* If the caller (real uid, not effective uid) may read the /proc/[pid]/ns/net, + * then it is either the caller's netns or one which it created. */ static bool may_access_netns(int pid) { @@ -889,7 +950,7 @@ if (ret < 0) { usernic_error("Failed to drop privilege by setting effective " "user id and real user id to %d, and saved user " - "ID to %d: %s.\n", + "ID to %d: %s\n", ruid, euid, strerror(errno)); return false; } @@ -908,7 +969,7 @@ ret = setresuid(ruid, euid, suid); if (ret < 0) { usernic_error("Failed to restore user id to %d, real user id " - "to %d, and saved user ID to %d: %s.\n", + "to %d, and saved user ID to %d: %s\n", ruid, euid, suid, strerror(errno)); may_access = false; } @@ -916,90 +977,272 @@ return may_access; } +struct user_nic_args { + char *cmd; + char *lxc_path; + char *lxc_name; + char *pid; + char *type; + char *link; + char *veth_name; +}; + +#define LXC_USERNIC_CREATE 0 +#define LXC_USERNIC_DELETE 1 + +static bool is_privileged_over_netns(int netns_fd) +{ + int ret; + uid_t euid, ruid, suid; + bool bret = false; + int ofd = -1; + + ofd = lxc_preserve_ns(getpid(), "net"); + if (ofd < 0) { + usernic_error("Failed opening network namespace path for %d", getpid()); + return false; + } + + ret = getresuid(&ruid, &euid, &suid); + if (ret < 0) { + usernic_error("Failed to retrieve real, effective, and saved " + "user IDs: %s\n", + strerror(errno)); + goto do_partial_cleanup; + } + + ret = setns(netns_fd, CLONE_NEWNET); + if (ret < 0) { + usernic_error("Failed to setns() to network namespace %s\n", + strerror(errno)); + goto do_partial_cleanup; + } + + ret = setresuid(ruid, ruid, 0); + if (ret < 0) { + usernic_error("Failed to drop privilege by setting effective " + "user id and real user id to %d, and saved user " + "ID to 0: %s\n", + ruid, strerror(errno)); + /* It's ok to jump to do_full_cleanup here since setresuid() + * will succeed when trying to set real, effective, and saved to + * values they currently have. + */ + goto do_full_cleanup; + } + + /* Test whether we are privileged over the network namespace. To do this + * we try to delete the loopback interface which is not possible. If we + * are privileged over the network namespace we will get ENOTSUP. If we + * are not privileged over the network namespace we will get EPERM. + */ + ret = lxc_netdev_delete_by_name("lo"); + if (ret == -ENOTSUP) + bret = true; + +do_full_cleanup: + ret = setresuid(ruid, euid, suid); + if (ret < 0) { + usernic_error("Failed to restore privilege by setting " + "effective user id to %d, real user id to %d, " + "and saved user ID to %d: %s\n", ruid, euid, suid, + strerror(errno)); + + bret = false; + } + + ret = setns(ofd, CLONE_NEWNET); + if (ret < 0) { + usernic_error("Failed to setns() to original network namespace " + "of PID %d: %s\n", ofd, strerror(errno)); + + bret = false; + } + +do_partial_cleanup: + + close(ofd); + return bret; +} + int main(int argc, char *argv[]) { - int n, fd; - char *me; - char *nicname; - int pid; - char *cnic = NULL; /* Created nic name in container is returned here. */ - char *vethname = NULL; - bool gotone = false; + int fd, n, pid, request, ret; + char *me, *newname; + struct user_nic_args args; + int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1; + char *cnic = NULL, *nicname = NULL; struct alloted_s *alloted = NULL; - nicname = alloca(40); - if (!nicname) { - usernic_error("Failed allocate memory: %s.\n", strerror(errno)); + if (argc < 7 || argc > 8) { + usage(argv[0], true); exit(EXIT_FAILURE); } - /* set a sane env, because we are setuid-root */ - if (clearenv() < 0) { - usernic_error("%s", "Failed to clear environment.\n"); + memset(&args, 0, sizeof(struct user_nic_args)); + args.cmd = argv[1]; + args.lxc_path = argv[2]; + args.lxc_name = argv[3]; + args.pid = argv[4]; + args.type = argv[5]; + args.link = argv[6]; + if (argc >= 8) + args.veth_name = argv[7]; + + if (!strcmp(args.cmd, "create")) { + request = LXC_USERNIC_CREATE; + } else if (!strcmp(args.cmd, "delete")) { + request = LXC_USERNIC_DELETE; + } else { + usage(argv[0], true); exit(EXIT_FAILURE); } - if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) { - usernic_error("%s", "Failed to set PATH, exiting.\n"); + + /* Set a sane env, because we are setuid-root. */ + ret = clearenv(); + if (ret) { + usernic_error("%s", "Failed to clear environment\n"); exit(EXIT_FAILURE); } - if ((me = get_username()) == NULL) { - usernic_error("%s", "Failed to get username.\n"); + + ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); + if (ret < 0) { + usernic_error("%s", "Failed to set PATH, exiting\n"); exit(EXIT_FAILURE); } - if (argc < 6) - usage(argv[0], true); - - if (argc >= 7) - vethname = argv[6]; - - lxcpath = argv[1]; - lxcname = argv[2]; - - errno = 0; - pid = strtol(argv[3], NULL, 10); - if (errno) { - usernic_error("Could not read pid: %s.\n", argv[1]); + me = get_username(); + if (!me) { + usernic_error("%s", "Failed to get username\n"); exit(EXIT_FAILURE); } + if (request == LXC_USERNIC_CREATE) { + ret = lxc_safe_int(args.pid, &pid); + if (ret < 0) { + usernic_error("Could not read pid: %s\n", args.pid); + exit(EXIT_FAILURE); + } + } else if (request == LXC_USERNIC_DELETE) { + netns_fd = open(args.pid, O_RDONLY); + if (netns_fd < 0) { + usernic_error("Could not open \"%s\": %s\n", args.pid, + strerror(errno)); + exit(EXIT_FAILURE); + } + } + if (!create_db_dir(LXC_USERNIC_DB)) { - usernic_error("%s", "Failed to create directory for db file.\n"); + usernic_error("%s", "Failed to create directory for db file\n"); + if (netns_fd >= 0) + close(netns_fd); exit(EXIT_FAILURE); } - if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) { - usernic_error("Failed to lock %s.\n", LXC_USERNIC_DB); + fd = open_and_lock(LXC_USERNIC_DB); + if (fd < 0) { + usernic_error("Failed to lock %s\n", LXC_USERNIC_DB); + if (netns_fd >= 0) + close(netns_fd); exit(EXIT_FAILURE); } - if (!may_access_netns(pid)) { - usernic_error("User %s may not modify netns for pid %d.\n", me, pid); - exit(EXIT_FAILURE); + if (request == LXC_USERNIC_CREATE) { + if (!may_access_netns(pid)) { + usernic_error("User %s may not modify netns for pid %d\n", me, pid); + exit(EXIT_FAILURE); + } + } else if (request == LXC_USERNIC_DELETE) { + bool has_priv; + has_priv = is_privileged_over_netns(netns_fd); + close(netns_fd); + if (!has_priv) { + usernic_error("%s", "Process is not privileged over " + "network namespace\n"); + exit(EXIT_FAILURE); + } } - n = get_alloted(me, argv[4], argv[5], &alloted); + n = get_alloted(me, args.type, args.link, &alloted); + + if (request == LXC_USERNIC_DELETE) { + int ret; + struct alloted_s *it; + bool found_nicname = false; + + if (!is_ovs_bridge(args.link)) { + usernic_error("%s", "Deletion of non ovs type network " + "devices not implemented\n"); + close(fd); + free_alloted(&alloted); + exit(EXIT_FAILURE); + } + + /* Check whether the network device we are supposed to delete + * exists in the db. If it doesn't we will not delete it as we + * need to assume the network device is not under our control. + * As a side effect we also clear any invalid entries from the + * database. + */ + for (it = alloted; it; it = it->next) + cull_entries(fd, it->name, args.type, args.link, + args.veth_name, &found_nicname); + close(fd); + free_alloted(&alloted); + + if (!found_nicname) { + usernic_error("Caller is not allowed to delete network " + "device \"%s\"\n", args.veth_name); + exit(EXIT_FAILURE); + } + + ret = lxc_ovs_delete_port(args.link, args.veth_name); + if (ret < 0) { + usernic_error("Failed to remove port \"%s\" from " + "openvswitch bridge \"%s\"", + args.veth_name, args.link); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + } if (n > 0) - gotone = get_nic_if_avail(fd, alloted, pid, argv[4], argv[5], n, &nicname, &cnic); + nicname = get_nic_if_avail(fd, alloted, pid, args.type, + args.link, n, &cnic); close(fd); free_alloted(&alloted); - if (!gotone) { - usernic_error("%s", "Quota reached.\n"); + if (!nicname) { + usernic_error("%s", "Quota reached\n"); exit(EXIT_FAILURE); } /* Now rename the link. */ - if (rename_in_ns(pid, cnic, &vethname) < 0) { - usernic_error("%s", "Failed to rename the link.\n"); - if (lxc_netdev_delete_by_name(cnic) < 0) - usernic_error("Failed to delete link \"%s\" the link. Manual cleanup needed.\n", cnic); + newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name, + &container_veth_ifidx); + if (!newname) { + usernic_error("%s", "Failed to rename the link\n"); + ret = lxc_netdev_delete_by_name(cnic); + if (ret < 0) + usernic_error("Failed to delete \"%s\"\n", cnic); + free(nicname); + exit(EXIT_FAILURE); + } + + host_veth_ifidx = if_nametoindex(nicname); + if (!host_veth_ifidx) { + free(newname); + free(nicname); + usernic_error("Failed to get netdev index: %s\n", strerror(errno)); exit(EXIT_FAILURE); } - /* Write the name of the interface pair to the stdout - like - * eth0:veth9MT2L4. + /* Write names of veth pairs and their ifindeces to stout: + * (e.g. eth0:731:veth9MT2L4:730) */ - fprintf(stdout, "%s:%s\n", vethname, nicname); + fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname, + host_veth_ifidx); + free(newname); + free(nicname); exit(EXIT_SUCCESS); } diff -Nru lxc-2.0.8/src/lxc/lxcutmp.c lxc-2.1.0/src/lxc/lxcutmp.c --- lxc-2.0.8/src/lxc/lxcutmp.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxcutmp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,489 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_TIMERFD_H -#include -#else -#include -#ifndef TFD_NONBLOCK -#define TFD_NONBLOCK O_NONBLOCK -#endif - -#ifndef TFD_CLOEXEC -#define TFD_CLOEXEC O_CLOEXEC -#endif -static int timerfd_create (clockid_t __clock_id, int __flags) { - return syscall(__NR_timerfd_create, __clock_id, __flags); -} - -static int timerfd_settime (int __ufd, int __flags, - const struct itimerspec *__utmr, - struct itimerspec *__otmr) { - - return syscall(__NR_timerfd_settime, __ufd, __flags, - __utmr, __otmr); -} - -#endif - -#include "conf.h" -#include "cgroup.h" -#include "start.h" -#include "mainloop.h" -#include "lxc.h" -#include "log.h" - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#ifdef HAVE_UTMPX_H -#include -#ifndef HAVE_UTMPXNAME -#include -#endif - -#else -#include - -#ifndef RUN_LVL -#define RUN_LVL 1 -#endif - -static void setutxent(void) { - return setutent(); -} - -static struct utmp * getutxent (void) { - return (struct utmp *) getutent(); -} - -static void endutxent (void) { -#ifdef IS_BIONIC - /* bionic isn't exporting endutend */ - return; -#else - return endutent(); -#endif -} -#endif - -#ifndef HAVE_UTMPXNAME -static int utmpxname(const char *file) { - int result; - result = utmpname(file); - -#ifdef IS_BIONIC - /* Yeah bionic is that weird */ - result = result - 1; -#endif - - return result; -} -#endif - -#undef __USE_GNU - -/* This file watches the /var/run/utmp file in the container - * (that should probably be configurable) - * We use inotify to put a watch on the /var/run directory for - * create and modify events. These can trigger a read of the - * utmp file looking for runlevel changes. If a runlevel change - * to reboot or halt states is detected, we set up an itimer to - * regularly check for the container shutdown, and reboot or halt - * as appropriate when we get down to 1 task remaining. - */ - -lxc_log_define(lxc_utmp, lxc); - -struct lxc_utmp { - struct lxc_handler *handler; -#define CONTAINER_STARTING 0 -#define CONTAINER_REBOOTING 1 -#define CONTAINER_HALTING 2 -#define CONTAINER_RUNNING 4 - char container_state; - int timer_fd; - int prev_runlevel, curr_runlevel; -}; - -typedef void (*lxc_mainloop_timer_t) (void *data); - -static int utmp_get_runlevel(struct lxc_utmp *utmp_data); -static int utmp_get_ntasks(struct lxc_handler *handler); -static int utmp_shutdown_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr); -static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, - lxc_mainloop_callback_t callback, void *data); -static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, - struct lxc_utmp *utmp_data); - -static int utmp_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr) -{ - struct inotify_event *ie; - int size, ret, length; - - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - - /* - * we're monitoring a directory. ie->name is not included in - * sizeof(struct inotify_event) if we don't read it all at once, - * read gives us EINVAL, so we read and cast to struct ie - */ - char buffer[MAXPATHLEN]; - - if (ioctl(fd, FIONREAD, &size) < 0) { - SYSERROR("cannot determine the size of this notification"); - return -1; - } - - if (read(fd, buffer, size) < size) { - SYSERROR("failed to read notification"); - return -1; - } - - ie = (struct inotify_event *)buffer; - - if (ie->len <= 0) { - - if (ie->mask & IN_UNMOUNT) { - DEBUG("watched directory removed"); - goto out; - } - - SYSERROR("inotify event with no name (mask %d)", ie->mask); - return -1; - } - - ret = 0; - - DEBUG("got inotify event %d for %s", ie->mask, ie->name); - - length = (4 < ie->len) ? 4 : ie->len; - - /* only care about utmp */ - - if (strncmp(ie->name, "utmp", length)) - return 0; - - if (ie->mask & (IN_MODIFY | IN_CREATE)) - ret = utmp_get_runlevel(utmp_data); - - if (ret < 0) - goto out; - - /* container halting, from running or starting state */ - if (utmp_data->curr_runlevel == '0' - && ((utmp_data->container_state == CONTAINER_RUNNING) - || (utmp_data->container_state == CONTAINER_STARTING))) { - utmp_data->container_state = CONTAINER_HALTING; - if (utmp_data->timer_fd == -1) - lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); - DEBUG("Container halting"); - goto out; - } - - /* container rebooting, from running or starting state */ - if (utmp_data->curr_runlevel == '6' - && ((utmp_data->container_state == CONTAINER_RUNNING) - || (utmp_data->container_state == CONTAINER_STARTING))) { - utmp_data->container_state = CONTAINER_REBOOTING; - if (utmp_data->timer_fd == -1) - lxc_utmp_add_timer(descr, utmp_shutdown_handler, data); - DEBUG("Container rebooting"); - goto out; - } - - /* normal operation, running, from starting state. */ - if (utmp_data->curr_runlevel > '0' && utmp_data->curr_runlevel < '6') { - utmp_data->container_state = CONTAINER_RUNNING; - if (utmp_data->timer_fd > 0) - lxc_utmp_del_timer(descr, utmp_data); - DEBUG("Container running"); - goto out; - } - -out: - return 0; -} - -static int utmp_get_runlevel(struct lxc_utmp *utmp_data) -{ - #if HAVE_UTMPX_H - struct utmpx *utmpx; - #else - struct utmp *utmpx; - #endif - char path[MAXPATHLEN]; - struct lxc_handler *handler = utmp_data->handler; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (!access(path, F_OK) && !utmpxname(path)) - goto utmp_ok; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (utmpxname(path)) { - SYSERROR("failed to 'utmpxname'"); - return -1; - } - -utmp_ok: - - setutxent(); - - while ((utmpx = getutxent())) { - - if (utmpx->ut_type == RUN_LVL) { - utmp_data->prev_runlevel = utmpx->ut_pid / 256; - utmp_data->curr_runlevel = utmpx->ut_pid % 256; - DEBUG("utmp handler - run level is %c/%c", - utmp_data->prev_runlevel, - utmp_data->curr_runlevel); - } - } - - endutxent(); - - return 0; -} - -static int utmp_get_ntasks(struct lxc_handler *handler) -{ - int ntasks; - - ntasks = cgroup_nrtasks(handler); - - if (ntasks < 0) { - ERROR("failed to get the number of tasks"); - return -1; - } - - DEBUG("there are %d tasks running", ntasks); - - return ntasks; -} - -int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr, - struct lxc_handler *handler) -{ - char path[MAXPATHLEN]; - char path2[MAXPATHLEN]; - int fd, wd; - struct lxc_utmp *utmp_data; - - /* We set up a watch for the /var/run directory. We're only interested - * in utmp at the moment, but want to watch for delete and create - * events as well. - */ - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/run", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - if (snprintf(path2, MAXPATHLEN, "/proc/%d/root/run/utmp", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - if (!access(path2, F_OK)) - goto run_ok; - - if (snprintf(path, MAXPATHLEN, "/proc/%d/root/var/run", - handler->pid) > MAXPATHLEN) { - ERROR("path is too long"); - return -1; - } - - if (access(path, F_OK)) { - WARN("'%s' not found", path); - return 0; - } - -run_ok: - - utmp_data = (struct lxc_utmp *)malloc(sizeof(struct lxc_utmp)); - - if (NULL == utmp_data) { - SYSERROR("failed to malloc handler utmp_data"); - return -1; - } - - memset(utmp_data, 0, sizeof(struct lxc_utmp)); - - fd = inotify_init(); - if (fd < 0) { - SYSERROR("failed to inotify_init"); - goto out; - } - - if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { - SYSERROR("failed to set inotify fd to close-on-exec"); - goto out_close; - - } - - wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE); - if (wd < 0) { - SYSERROR("failed to add watch for '%s'", path); - goto out_close; - } - - utmp_data->handler = handler; - utmp_data->container_state = CONTAINER_STARTING; - utmp_data->timer_fd = -1; - utmp_data->prev_runlevel = 'N'; - utmp_data->curr_runlevel = 'N'; - - if (lxc_mainloop_add_handler - (descr, fd, utmp_handler, (void *)utmp_data)) { - SYSERROR("failed to add mainloop"); - goto out_close; - } - - DEBUG("Added '%s' to inotifywatch", path); - - return 0; -out_close: - close(fd); -out: - free(utmp_data); - return -1; -} - -static int utmp_shutdown_handler(int fd, uint32_t events, void *data, - struct lxc_epoll_descr *descr) -{ - int ntasks; - ssize_t nread; - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - struct lxc_handler *handler = utmp_data->handler; - struct lxc_conf *conf = handler->conf; - uint64_t expirations; - - /* read and clear notifications */ - nread = read(fd, &expirations, sizeof(expirations)); - if (nread < 0) - SYSERROR("Failed to read timer notification"); - - ntasks = utmp_get_ntasks(handler); - - if (ntasks == 1 && (utmp_data->container_state == CONTAINER_HALTING)) { - INFO("container has shutdown"); - /* shutdown timer */ - lxc_utmp_del_timer(descr, utmp_data); - - kill(handler->pid, SIGKILL); - } - - if (ntasks == 1 && (utmp_data->container_state == CONTAINER_REBOOTING)) { - INFO("container has rebooted"); - conf->reboot = 1; - /* shutdown timer */ - lxc_utmp_del_timer(descr, utmp_data); - /* this seems a bit rough. */ - kill(handler->pid, SIGKILL); - } - return 0; - -} - -int lxc_utmp_add_timer(struct lxc_epoll_descr *descr, - lxc_mainloop_callback_t callback, void *data) -{ - int fd, result; - struct itimerspec timeout; - struct lxc_utmp *utmp_data = (struct lxc_utmp *)data; - - fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - if (fd < 0) { - SYSERROR("failed to create timer"); - return -1; - } - - DEBUG("Setting up utmp shutdown timer"); - - /* set a one second timeout. Repeated. */ - timeout.it_value.tv_sec = 1; - timeout.it_value.tv_nsec = 0; - - timeout.it_interval.tv_sec = 1; - timeout.it_interval.tv_nsec = 0; - - result = timerfd_settime(fd, 0, &timeout, NULL); - - if (result < 0) { - SYSERROR("timerfd_settime:"); - return -1; - } - - if (lxc_mainloop_add_handler(descr, fd, callback, utmp_data)) { - SYSERROR("failed to add utmp timer to mainloop"); - close(fd); - return -1; - } - - utmp_data->timer_fd = fd; - - return 0; -} - -int lxc_utmp_del_timer(struct lxc_epoll_descr *descr, - struct lxc_utmp *utmp_data) -{ - int result; - - DEBUG("Clearing utmp shutdown timer"); - - result = lxc_mainloop_del_handler(descr, utmp_data->timer_fd); - if (result < 0) - SYSERROR("failed to del utmp timer from mainloop"); - - /* shutdown timer_fd */ - close(utmp_data->timer_fd); - utmp_data->timer_fd = -1; - - if (result < 0) - return -1; - else - return 0; -} diff -Nru lxc-2.0.8/src/lxc/lxcutmp.h lxc-2.1.0/src/lxc/lxcutmp.h --- lxc-2.0.8/src/lxc/lxcutmp.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/lxcutmp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/* - * 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 diff -Nru lxc-2.0.8/src/lxc/Makefile.am lxc-2.1.0/src/lxc/Makefile.am --- lxc-2.0.8/src/lxc/Makefile.am 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/Makefile.am 2017-09-06 02:32:37.000000000 +0000 @@ -6,20 +6,25 @@ noinst_HEADERS = \ arguments.h \ attach.h \ - bdev/bdev.h \ - bdev/lxcaufs.h \ - bdev/lxcbtrfs.h \ - bdev/lxcdir.h \ - bdev/lxcloop.h \ - bdev/lxclvm.h \ - bdev/lxcnbd.h \ - bdev/lxcoverlay.h \ - bdev/lxcrbd.h \ - bdev/lxcrsync.h \ - bdev/lxczfs.h \ + storage/storage.h \ + storage/aufs.h \ + storage/btrfs.h \ + storage/dir.h \ + storage/loop.h \ + storage/lvm.h \ + storage/nbd.h \ + storage/overlay.h \ + storage/rbd.h \ + storage/rsync.h \ + storage/zfs.h \ + storage/storage_utils.h \ cgroups/cgroup.h \ + cgroups/cgroup_utils.h \ caps.h \ conf.h \ + confile.h \ + confile_legacy.h \ + confile_utils.h \ console.h \ error.h \ initutils.h \ @@ -40,6 +45,9 @@ ../include/ifaddrs.h \ ../include/openpty.h \ ../include/lxcmntent.h +if !HAVE_PRLIMIT +noinst_HEADERS += ../include/prlimit.h +endif endif if !HAVE_GETLINE @@ -69,21 +77,24 @@ lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = \ arguments.c arguments.h \ - bdev/bdev.c bdev/bdev.h \ - bdev/lxcaufs.c bdev/lxcaufs.h \ - bdev/lxcbtrfs.c bdev/lxcbtrfs.h \ - bdev/lxcdir.c bdev/lxcdir.h \ - bdev/lxcloop.c bdev/lxcloop.h \ - bdev/lxclvm.c bdev/lxclvm.h \ - bdev/lxcnbd.c bdev/lxcnbd.h \ - bdev/lxcoverlay.c bdev/lxcoverlay.h \ - bdev/lxcrbd.c bdev/lxcrbd.h \ - bdev/lxcrsync.c bdev/lxcrsync.h \ - bdev/lxczfs.c bdev/lxczfs.h \ + storage/storage.c storage/storage.h \ + storage/aufs.c storage/aufs.h \ + storage/btrfs.c storage/btrfs.h \ + storage/dir.c storage/dir.h \ + storage/loop.c storage/loop.h \ + storage/lvm.c storage/lvm.h \ + storage/nbd.c storage/nbd.h \ + storage/overlay.c storage/overlay.h \ + storage/rbd.c storage/rbd.h \ + storage/rsync.c storage/rsync.h \ + storage/zfs.c storage/zfs.h \ + storage/storage_utils.c storage/storage_utils.h \ cgroups/cgfs.c \ cgroups/cgfsng.c \ + cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ cgroups/cgroup.c cgroups/cgroup.h \ commands.c commands.h \ + commands_utils.c commands_utils.h \ start.c start.h \ execute.c \ monitor.c monitor.h \ @@ -98,6 +109,8 @@ namespace.h namespace.c \ conf.c conf.h \ confile.c confile.h \ + confile_legacy.c confile_legacy.h \ + confile_utils.c confile_utils.h \ list.h \ state.c state.h \ log.c log.h \ @@ -114,7 +127,6 @@ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ - lxcutmp.c lxcutmp.h \ lxclock.h lxclock.c \ lxccontainer.c lxccontainer.h \ version.h \ @@ -130,6 +142,9 @@ ../include/ifaddrs.c ../include/ifaddrs.h \ ../include/openpty.c ../include/openpty.h \ ../include/lxcmntent.c ../include/lxcmntent.h +if !HAVE_PRLIMIT +liblxc_la_SOURCES += ../include/prlimit.c ../include/prlimit.h +endif endif if !HAVE_GETLINE @@ -154,7 +169,7 @@ -DSBINDIR=\"$(SBINDIR)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/lxc \ - -I $(top_srcdir)/src/lxc/bdev \ + -I $(top_srcdir)/src/lxc/storage \ -I $(top_srcdir)/src/lxc/cgroups if ENABLE_APPARMOR @@ -193,7 +208,8 @@ liblxc_la_CFLAGS += $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) endif -bin_SCRIPTS = tools/lxc-checkconfig +bin_SCRIPTS = tools/lxc-checkconfig \ + tools/lxc-update-config EXTRA_DIST = \ tools/lxc-top.lua diff -Nru lxc-2.0.8/src/lxc/Makefile.in lxc-2.1.0/src/lxc/Makefile.in --- lxc-2.0.8/src/lxc/Makefile.in 2017-05-11 17:23:10.000000000 +0000 +++ lxc-2.1.0/src/lxc/Makefile.in 2017-09-06 02:32:42.000000000 +0000 @@ -96,26 +96,28 @@ @IS_BIONIC_TRUE@ ../include/openpty.h \ @IS_BIONIC_TRUE@ ../include/lxcmntent.h -@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_2 = ../include/getline.h -@HAVE_GETSUBOPT_FALSE@am__append_3 = ../include/getsubopt.h -@ENABLE_APPARMOR_TRUE@am__append_4 = lsm/apparmor.c -@ENABLE_SELINUX_TRUE@am__append_5 = lsm/selinux.c -@ENABLE_CGMANAGER_TRUE@am__append_6 = cgroups/cgmanager.c -@IS_BIONIC_TRUE@am__append_7 = \ +@HAVE_PRLIMIT_FALSE@@IS_BIONIC_TRUE@am__append_2 = ../include/prlimit.h +@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_3 = ../include/getline.h +@HAVE_GETSUBOPT_FALSE@am__append_4 = ../include/getsubopt.h +@ENABLE_APPARMOR_TRUE@am__append_5 = lsm/apparmor.c +@ENABLE_SELINUX_TRUE@am__append_6 = lsm/selinux.c +@ENABLE_CGMANAGER_TRUE@am__append_7 = cgroups/cgmanager.c +@IS_BIONIC_TRUE@am__append_8 = \ @IS_BIONIC_TRUE@ ../include/ifaddrs.c ../include/ifaddrs.h \ @IS_BIONIC_TRUE@ ../include/openpty.c ../include/openpty.h \ @IS_BIONIC_TRUE@ ../include/lxcmntent.c ../include/lxcmntent.h -@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_8 = ../include/getline.c ../include/getline.h -@ENABLE_APPARMOR_TRUE@am__append_9 = -DHAVE_APPARMOR -@ENABLE_CGMANAGER_TRUE@am__append_10 = -DHAVE_CGMANAGER -@ENABLE_SELINUX_TRUE@am__append_11 = -DHAVE_SELINUX -@USE_CONFIGPATH_LOGS_TRUE@am__append_12 = -DUSE_CONFIGPATH_LOGS -@ENABLE_SECCOMP_TRUE@am__append_13 = -DHAVE_SECCOMP $(SECCOMP_CFLAGS) -@ENABLE_SECCOMP_TRUE@am__append_14 = seccomp.c -@ENABLE_CGMANAGER_TRUE@am__append_15 = $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) -@ENABLE_CGMANAGER_TRUE@am__append_16 = $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) -@ENABLE_DEPRECATED_TRUE@@ENABLE_PYTHON_TRUE@am__append_17 = tools/lxc-start-ephemeral +@HAVE_PRLIMIT_FALSE@@IS_BIONIC_TRUE@am__append_9 = ../include/prlimit.c ../include/prlimit.h +@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_10 = ../include/getline.c ../include/getline.h +@ENABLE_APPARMOR_TRUE@am__append_11 = -DHAVE_APPARMOR +@ENABLE_CGMANAGER_TRUE@am__append_12 = -DHAVE_CGMANAGER +@ENABLE_SELINUX_TRUE@am__append_13 = -DHAVE_SELINUX +@USE_CONFIGPATH_LOGS_TRUE@am__append_14 = -DUSE_CONFIGPATH_LOGS +@ENABLE_SECCOMP_TRUE@am__append_15 = -DHAVE_SECCOMP $(SECCOMP_CFLAGS) +@ENABLE_SECCOMP_TRUE@am__append_16 = seccomp.c +@ENABLE_CGMANAGER_TRUE@am__append_17 = $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) +@ENABLE_CGMANAGER_TRUE@am__append_18 = $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) +@ENABLE_DEPRECATED_TRUE@@ENABLE_PYTHON_TRUE@am__append_19 = tools/lxc-start-ephemeral bin_PROGRAMS = lxc-attach$(EXEEXT) lxc-autostart$(EXEEXT) \ lxc-cgroup$(EXEEXT) lxc-checkpoint$(EXEEXT) lxc-copy$(EXEEXT) \ lxc-config$(EXEEXT) lxc-console$(EXEEXT) lxc-create$(EXEEXT) \ @@ -125,13 +127,13 @@ lxc-stop$(EXEEXT) lxc-top$(EXEEXT) lxc-unfreeze$(EXEEXT) \ lxc-unshare$(EXEEXT) lxc-usernsexec$(EXEEXT) lxc-wait$(EXEEXT) \ $(am__EXEEXT_1) -@ENABLE_DEPRECATED_TRUE@am__append_18 = lxc-clone +@ENABLE_DEPRECATED_TRUE@am__append_20 = lxc-clone sbin_PROGRAMS = init.lxc$(EXEEXT) $(am__EXEEXT_2) pkglibexec_PROGRAMS = lxc-monitord$(EXEEXT) lxc-user-nic$(EXEEXT) -@ENABLE_RPATH_TRUE@am__append_19 = -Wl,-rpath -Wl,$(libdir) -@HAVE_GETSUBOPT_FALSE@am__append_20 = ../include/getsubopt.c ../include/getsubopt.h -@HAVE_STATIC_LIBCAP_TRUE@am__append_21 = init.lxc.static -@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__append_22 = ../include/getline.c +@ENABLE_RPATH_TRUE@am__append_21 = -Wl,-rpath -Wl,$(libdir) +@HAVE_GETSUBOPT_FALSE@am__append_22 = ../include/getsubopt.c ../include/getsubopt.h +@HAVE_STATIC_LIBCAP_TRUE@am__append_23 = init.lxc.static +@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__append_24 = ../include/getline.c subdir = src/lxc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ @@ -188,28 +190,32 @@ liblxc_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) -am__liblxc_la_SOURCES_DIST = arguments.c arguments.h bdev/bdev.c \ - bdev/bdev.h bdev/lxcaufs.c bdev/lxcaufs.h bdev/lxcbtrfs.c \ - bdev/lxcbtrfs.h bdev/lxcdir.c bdev/lxcdir.h bdev/lxcloop.c \ - bdev/lxcloop.h bdev/lxclvm.c bdev/lxclvm.h bdev/lxcnbd.c \ - bdev/lxcnbd.h bdev/lxcoverlay.c bdev/lxcoverlay.h \ - bdev/lxcrbd.c bdev/lxcrbd.h bdev/lxcrsync.c bdev/lxcrsync.h \ - bdev/lxczfs.c bdev/lxczfs.h cgroups/cgfs.c cgroups/cgfsng.c \ +am__liblxc_la_SOURCES_DIST = arguments.c arguments.h storage/storage.c \ + storage/storage.h storage/aufs.c storage/aufs.h \ + storage/btrfs.c storage/btrfs.h storage/dir.c storage/dir.h \ + storage/loop.c storage/loop.h storage/lvm.c storage/lvm.h \ + storage/nbd.c storage/nbd.h storage/overlay.c \ + storage/overlay.h storage/rbd.c storage/rbd.h storage/rsync.c \ + storage/rsync.h storage/zfs.c storage/zfs.h \ + storage/storage_utils.c storage/storage_utils.h cgroups/cgfs.c \ + cgroups/cgfsng.c cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ cgroups/cgroup.c cgroups/cgroup.h commands.c commands.h \ - start.c start.h execute.c monitor.c monitor.h console.c \ - freezer.c error.h error.c parse.c parse.h lxc.h initutils.c \ - initutils.h utils.c utils.h sync.c sync.h namespace.h \ - namespace.c conf.c conf.h confile.c confile.h list.h state.c \ - state.h log.c log.h attach.c attach.h criu.c criu.h network.c \ - network.h nl.c nl.h rtnl.c rtnl.h genl.c genl.h caps.c caps.h \ - lxcseccomp.h mainloop.c mainloop.h af_unix.c af_unix.h \ - lxcutmp.c lxcutmp.h lxclock.h lxclock.c lxccontainer.c \ - lxccontainer.h version.h lsm/nop.c lsm/lsm.h lsm/lsm.c \ - lsm/apparmor.c lsm/selinux.c cgroups/cgmanager.c \ + commands_utils.c commands_utils.h start.c start.h execute.c \ + monitor.c monitor.h console.c freezer.c error.h error.c \ + parse.c parse.h lxc.h initutils.c initutils.h utils.c utils.h \ + sync.c sync.h namespace.h namespace.c conf.c conf.h confile.c \ + confile.h confile_legacy.c confile_legacy.h confile_utils.c \ + confile_utils.h list.h state.c state.h log.c log.h attach.c \ + attach.h criu.c criu.h network.c network.h nl.c nl.h rtnl.c \ + rtnl.h genl.c genl.h caps.c caps.h lxcseccomp.h mainloop.c \ + mainloop.h af_unix.c af_unix.h lxclock.h lxclock.c \ + lxccontainer.c lxccontainer.h version.h lsm/nop.c lsm/lsm.h \ + lsm/lsm.c lsm/apparmor.c lsm/selinux.c cgroups/cgmanager.c \ ../include/ifaddrs.c ../include/ifaddrs.h ../include/openpty.c \ ../include/openpty.h ../include/lxcmntent.c \ - ../include/lxcmntent.h ../include/getline.c \ - ../include/getline.h seccomp.c + ../include/lxcmntent.h ../include/prlimit.c \ + ../include/prlimit.h ../include/getline.c ../include/getline.h \ + seccomp.c am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_APPARMOR_TRUE@am__objects_1 = lsm/liblxc_la-apparmor.lo @ENABLE_SELINUX_TRUE@am__objects_2 = lsm/liblxc_la-selinux.lo @@ -219,27 +225,31 @@ @IS_BIONIC_TRUE@am__objects_5 = ../include/liblxc_la-ifaddrs.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-openpty.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-lxcmntent.lo -@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__objects_6 = ../include/liblxc_la-getline.lo -@ENABLE_SECCOMP_TRUE@am__objects_7 = liblxc_la-seccomp.lo -am_liblxc_la_OBJECTS = liblxc_la-arguments.lo bdev/liblxc_la-bdev.lo \ - bdev/liblxc_la-lxcaufs.lo bdev/liblxc_la-lxcbtrfs.lo \ - bdev/liblxc_la-lxcdir.lo bdev/liblxc_la-lxcloop.lo \ - bdev/liblxc_la-lxclvm.lo bdev/liblxc_la-lxcnbd.lo \ - bdev/liblxc_la-lxcoverlay.lo bdev/liblxc_la-lxcrbd.lo \ - bdev/liblxc_la-lxcrsync.lo bdev/liblxc_la-lxczfs.lo \ +@HAVE_PRLIMIT_FALSE@@IS_BIONIC_TRUE@am__objects_6 = ../include/liblxc_la-prlimit.lo +@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__objects_7 = ../include/liblxc_la-getline.lo +@ENABLE_SECCOMP_TRUE@am__objects_8 = liblxc_la-seccomp.lo +am_liblxc_la_OBJECTS = liblxc_la-arguments.lo \ + storage/liblxc_la-storage.lo storage/liblxc_la-aufs.lo \ + storage/liblxc_la-btrfs.lo storage/liblxc_la-dir.lo \ + storage/liblxc_la-loop.lo storage/liblxc_la-lvm.lo \ + storage/liblxc_la-nbd.lo storage/liblxc_la-overlay.lo \ + storage/liblxc_la-rbd.lo storage/liblxc_la-rsync.lo \ + storage/liblxc_la-zfs.lo storage/liblxc_la-storage_utils.lo \ cgroups/liblxc_la-cgfs.lo cgroups/liblxc_la-cgfsng.lo \ - cgroups/liblxc_la-cgroup.lo liblxc_la-commands.lo \ + cgroups/liblxc_la-cgroup_utils.lo cgroups/liblxc_la-cgroup.lo \ + liblxc_la-commands.lo liblxc_la-commands_utils.lo \ liblxc_la-start.lo liblxc_la-execute.lo liblxc_la-monitor.lo \ liblxc_la-console.lo liblxc_la-freezer.lo liblxc_la-error.lo \ liblxc_la-parse.lo liblxc_la-initutils.lo liblxc_la-utils.lo \ liblxc_la-sync.lo liblxc_la-namespace.lo liblxc_la-conf.lo \ - liblxc_la-confile.lo liblxc_la-state.lo liblxc_la-log.lo \ + liblxc_la-confile.lo liblxc_la-confile_legacy.lo \ + liblxc_la-confile_utils.lo liblxc_la-state.lo liblxc_la-log.lo \ liblxc_la-attach.lo liblxc_la-criu.lo liblxc_la-network.lo \ liblxc_la-nl.lo liblxc_la-rtnl.lo liblxc_la-genl.lo \ liblxc_la-caps.lo liblxc_la-mainloop.lo liblxc_la-af_unix.lo \ - liblxc_la-lxcutmp.lo liblxc_la-lxclock.lo \ - liblxc_la-lxccontainer.lo $(am__objects_3) $(am__objects_4) \ - $(am__objects_5) $(am__objects_6) $(am__objects_7) + liblxc_la-lxclock.lo liblxc_la-lxccontainer.lo \ + $(am__objects_3) $(am__objects_4) $(am__objects_5) \ + $(am__objects_6) $(am__objects_7) $(am__objects_8) liblxc_la_OBJECTS = $(am_liblxc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -257,13 +267,13 @@ init_lxc_DEPENDENCIES = liblxc.la am__init_lxc_static_SOURCES_DIST = tools/lxc_init.c error.c log.c \ initutils.c caps.c ../include/getline.c -@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__objects_8 = ../include/init_lxc_static-getline.$(OBJEXT) +@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__objects_9 = ../include/init_lxc_static-getline.$(OBJEXT) @HAVE_STATIC_LIBCAP_TRUE@am_init_lxc_static_OBJECTS = tools/init_lxc_static-lxc_init.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-error.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-log.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-initutils.$(OBJEXT) \ @HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-caps.$(OBJEXT) \ -@HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_8) +@HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_9) init_lxc_static_OBJECTS = $(am_init_lxc_static_OBJECTS) init_lxc_static_DEPENDENCIES = init_lxc_static_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @@ -302,8 +312,8 @@ lxc_console_DEPENDENCIES = liblxc.la am__lxc_copy_SOURCES_DIST = tools/lxc_copy.c ../include/getsubopt.c \ ../include/getsubopt.h -@HAVE_GETSUBOPT_FALSE@am__objects_9 = ../include/getsubopt.$(OBJEXT) -am_lxc_copy_OBJECTS = tools/lxc_copy.$(OBJEXT) $(am__objects_9) +@HAVE_GETSUBOPT_FALSE@am__objects_10 = ../include/getsubopt.$(OBJEXT) +am_lxc_copy_OBJECTS = tools/lxc_copy.$(OBJEXT) $(am__objects_10) lxc_copy_OBJECTS = $(am_lxc_copy_OBJECTS) lxc_copy_LDADD = $(LDADD) lxc_copy_DEPENDENCIES = liblxc.la @@ -447,15 +457,17 @@ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac -am__noinst_HEADERS_DIST = arguments.h attach.h bdev/bdev.h \ - bdev/lxcaufs.h bdev/lxcbtrfs.h bdev/lxcdir.h bdev/lxcloop.h \ - bdev/lxclvm.h bdev/lxcnbd.h bdev/lxcoverlay.h bdev/lxcrbd.h \ - bdev/lxcrsync.h bdev/lxczfs.h cgroups/cgroup.h caps.h conf.h \ - console.h error.h initutils.h list.h log.h lxc.h lxclock.h \ - monitor.h namespace.h start.h state.h utils.h criu.h \ - ../tests/lxctest.h ../include/ifaddrs.h ../include/openpty.h \ - ../include/lxcmntent.h ../include/getline.h \ - ../include/getsubopt.h +am__noinst_HEADERS_DIST = arguments.h attach.h storage/storage.h \ + storage/aufs.h storage/btrfs.h storage/dir.h storage/loop.h \ + storage/lvm.h storage/nbd.h storage/overlay.h storage/rbd.h \ + storage/rsync.h storage/zfs.h storage/storage_utils.h \ + cgroups/cgroup.h cgroups/cgroup_utils.h caps.h conf.h \ + confile.h confile_legacy.h confile_utils.h console.h error.h \ + initutils.h list.h log.h lxc.h lxclock.h monitor.h namespace.h \ + start.h state.h utils.h criu.h ../tests/lxctest.h \ + ../include/ifaddrs.h ../include/openpty.h \ + ../include/lxcmntent.h ../include/prlimit.h \ + ../include/getline.h ../include/getsubopt.h HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, @@ -676,35 +688,41 @@ lxccontainer.h \ version.h -noinst_HEADERS = arguments.h attach.h bdev/bdev.h bdev/lxcaufs.h \ - bdev/lxcbtrfs.h bdev/lxcdir.h bdev/lxcloop.h bdev/lxclvm.h \ - bdev/lxcnbd.h bdev/lxcoverlay.h bdev/lxcrbd.h bdev/lxcrsync.h \ - bdev/lxczfs.h cgroups/cgroup.h caps.h conf.h console.h error.h \ - initutils.h list.h log.h lxc.h lxclock.h monitor.h namespace.h \ - start.h state.h utils.h criu.h ../tests/lxctest.h \ - $(am__append_1) $(am__append_2) $(am__append_3) +noinst_HEADERS = arguments.h attach.h storage/storage.h storage/aufs.h \ + storage/btrfs.h storage/dir.h storage/loop.h storage/lvm.h \ + storage/nbd.h storage/overlay.h storage/rbd.h storage/rsync.h \ + storage/zfs.h storage/storage_utils.h cgroups/cgroup.h \ + cgroups/cgroup_utils.h caps.h conf.h confile.h \ + confile_legacy.h confile_utils.h console.h error.h initutils.h \ + list.h log.h lxc.h lxclock.h monitor.h namespace.h start.h \ + state.h utils.h criu.h ../tests/lxctest.h $(am__append_1) \ + $(am__append_2) $(am__append_3) $(am__append_4) sodir = $(libdir) -LSM_SOURCES = lsm/nop.c lsm/lsm.h lsm/lsm.c $(am__append_4) \ - $(am__append_5) +LSM_SOURCES = lsm/nop.c lsm/lsm.h lsm/lsm.c $(am__append_5) \ + $(am__append_6) lib_LTLIBRARIES = liblxc.la -liblxc_la_SOURCES = arguments.c arguments.h bdev/bdev.c bdev/bdev.h \ - bdev/lxcaufs.c bdev/lxcaufs.h bdev/lxcbtrfs.c bdev/lxcbtrfs.h \ - bdev/lxcdir.c bdev/lxcdir.h bdev/lxcloop.c bdev/lxcloop.h \ - bdev/lxclvm.c bdev/lxclvm.h bdev/lxcnbd.c bdev/lxcnbd.h \ - bdev/lxcoverlay.c bdev/lxcoverlay.h bdev/lxcrbd.c \ - bdev/lxcrbd.h bdev/lxcrsync.c bdev/lxcrsync.h bdev/lxczfs.c \ - bdev/lxczfs.h cgroups/cgfs.c cgroups/cgfsng.c cgroups/cgroup.c \ - cgroups/cgroup.h commands.c commands.h start.c start.h \ - execute.c monitor.c monitor.h console.c freezer.c error.h \ - error.c parse.c parse.h lxc.h initutils.c initutils.h utils.c \ - utils.h sync.c sync.h namespace.h namespace.c conf.c conf.h \ - confile.c confile.h list.h state.c state.h log.c log.h \ - attach.c attach.h criu.c criu.h network.c network.h nl.c nl.h \ - rtnl.c rtnl.h genl.c genl.h caps.c caps.h lxcseccomp.h \ - mainloop.c mainloop.h af_unix.c af_unix.h lxcutmp.c lxcutmp.h \ - lxclock.h lxclock.c lxccontainer.c lxccontainer.h version.h \ - $(LSM_SOURCES) $(am__append_6) $(am__append_7) $(am__append_8) \ - $(am__append_14) +liblxc_la_SOURCES = arguments.c arguments.h storage/storage.c \ + storage/storage.h storage/aufs.c storage/aufs.h \ + storage/btrfs.c storage/btrfs.h storage/dir.c storage/dir.h \ + storage/loop.c storage/loop.h storage/lvm.c storage/lvm.h \ + storage/nbd.c storage/nbd.h storage/overlay.c \ + storage/overlay.h storage/rbd.c storage/rbd.h storage/rsync.c \ + storage/rsync.h storage/zfs.c storage/zfs.h \ + storage/storage_utils.c storage/storage_utils.h cgroups/cgfs.c \ + cgroups/cgfsng.c cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ + cgroups/cgroup.c cgroups/cgroup.h commands.c commands.h \ + commands_utils.c commands_utils.h start.c start.h execute.c \ + monitor.c monitor.h console.c freezer.c error.h error.c \ + parse.c parse.h lxc.h initutils.c initutils.h utils.c utils.h \ + sync.c sync.h namespace.h namespace.c conf.c conf.h confile.c \ + confile.h confile_legacy.c confile_legacy.h confile_utils.c \ + confile_utils.h list.h state.c state.h log.c log.h attach.c \ + attach.h criu.c criu.h network.c network.h nl.c nl.h rtnl.c \ + rtnl.h genl.c genl.h caps.c caps.h lxcseccomp.h mainloop.c \ + mainloop.h af_unix.c af_unix.h lxclock.h lxclock.c \ + lxccontainer.c lxccontainer.h version.h $(LSM_SOURCES) \ + $(am__append_7) $(am__append_8) $(am__append_9) \ + $(am__append_10) $(am__append_16) AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ @@ -718,10 +736,10 @@ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" -DSBINDIR=\"$(SBINDIR)\" -I \ $(top_srcdir)/src -I $(top_srcdir)/src/lxc -I \ - $(top_srcdir)/src/lxc/bdev -I $(top_srcdir)/src/lxc/cgroups \ - $(am__append_9) $(am__append_10) $(am__append_11) \ - $(am__append_12) $(am__append_13) -liblxc_la_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) -pthread $(am__append_16) + $(top_srcdir)/src/lxc/storage -I $(top_srcdir)/src/lxc/cgroups \ + $(am__append_11) $(am__append_12) $(am__append_13) \ + $(am__append_14) $(am__append_15) +liblxc_la_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) -pthread $(am__append_18) liblxc_la_LDFLAGS = \ -pthread \ -shared \ @@ -729,12 +747,13 @@ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) $(APPARMOR_LIBS) $(SELINUX_LIBS) \ - $(SECCOMP_LIBS) $(am__append_15) -bin_SCRIPTS = tools/lxc-checkconfig $(am__append_17) + $(SECCOMP_LIBS) $(am__append_17) +bin_SCRIPTS = tools/lxc-checkconfig tools/lxc-update-config \ + $(am__append_19) EXTRA_DIST = \ tools/lxc-top.lua -AM_LDFLAGS = -Wl,-E $(am__append_19) +AM_LDFLAGS = -Wl,-E $(am__append_21) LDADD = liblxc.la @CAP_LIBS@ @APPARMOR_LIBS@ @SELINUX_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = tools/lxc_attach.c lxc_autostart_SOURCES = tools/lxc_autostart.c @@ -749,7 +768,7 @@ init_lxc_SOURCES = tools/lxc_init.c lxc_monitor_SOURCES = tools/lxc_monitor.c lxc_ls_SOURCES = tools/lxc_ls.c -lxc_copy_SOURCES = tools/lxc_copy.c $(am__append_20) +lxc_copy_SOURCES = tools/lxc_copy.c $(am__append_22) lxc_start_SOURCES = tools/lxc_start.c lxc_stop_SOURCES = tools/lxc_stop.c lxc_top_SOURCES = tools/lxc_top.c @@ -765,7 +784,7 @@ @ENABLE_DEPRECATED_TRUE@lxc_clone_SOURCES = tools/lxc_clone.c @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_SOURCES = tools/lxc_init.c \ @HAVE_STATIC_LIBCAP_TRUE@ error.c log.c initutils.c caps.c \ -@HAVE_STATIC_LIBCAP_TRUE@ $(am__append_22) +@HAVE_STATIC_LIBCAP_TRUE@ $(am__append_24) @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDFLAGS = -all-static @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDADD = @CAP_LIBS@ @HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF @@ -841,34 +860,36 @@ echo rm -f $${locs}; \ rm -f $${locs}; \ } -bdev/$(am__dirstamp): - @$(MKDIR_P) bdev - @: > bdev/$(am__dirstamp) -bdev/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) bdev/$(DEPDIR) - @: > bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-bdev.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcaufs.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcbtrfs.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcdir.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcloop.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxclvm.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcnbd.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcoverlay.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcrbd.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxcrsync.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) -bdev/liblxc_la-lxczfs.lo: bdev/$(am__dirstamp) \ - bdev/$(DEPDIR)/$(am__dirstamp) +storage/$(am__dirstamp): + @$(MKDIR_P) storage + @: > storage/$(am__dirstamp) +storage/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) storage/$(DEPDIR) + @: > storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-storage.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-aufs.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-btrfs.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-dir.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-loop.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-lvm.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-nbd.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-overlay.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-rbd.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-rsync.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-zfs.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) +storage/liblxc_la-storage_utils.lo: storage/$(am__dirstamp) \ + storage/$(DEPDIR)/$(am__dirstamp) cgroups/$(am__dirstamp): @$(MKDIR_P) cgroups @: > cgroups/$(am__dirstamp) @@ -879,6 +900,8 @@ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgfsng.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) +cgroups/liblxc_la-cgroup_utils.lo: cgroups/$(am__dirstamp) \ + cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgroup.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) lsm/$(am__dirstamp): @@ -909,6 +932,8 @@ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-lxcmntent.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) +../include/liblxc_la-prlimit.lo: ../include/$(am__dirstamp) \ + ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-getline.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) @@ -1276,12 +1301,12 @@ -rm -f *.$(OBJEXT) -rm -f ../include/*.$(OBJEXT) -rm -f ../include/*.lo - -rm -f bdev/*.$(OBJEXT) - -rm -f bdev/*.lo -rm -f cgroups/*.$(OBJEXT) -rm -f cgroups/*.lo -rm -f lsm/*.$(OBJEXT) -rm -f lsm/*.lo + -rm -f storage/*.$(OBJEXT) + -rm -f storage/*.lo -rm -f tools/*.$(OBJEXT) distclean-compile: @@ -1293,6 +1318,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-ifaddrs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-openpty.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-prlimit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-caps.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-error.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-initutils.Po@am__quote@ @@ -1302,8 +1328,11 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-attach.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-caps.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-conf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile_legacy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-console.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-criu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-error.Plo@am__quote@ @@ -1314,7 +1343,6 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxccontainer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxclock.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxcutmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-mainloop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-monitor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-namespace.Plo@am__quote@ @@ -1330,25 +1358,27 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc_monitord.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc_user_nic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-bdev.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcaufs.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcbtrfs.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcdir.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcloop.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxclvm.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcnbd.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcoverlay.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcrbd.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxcrsync.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bdev/$(DEPDIR)/liblxc_la-lxczfs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgfs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgmanager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-apparmor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-lsm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-nop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-selinux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-aufs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-btrfs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-dir.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-loop.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-lvm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-nbd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-overlay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rbd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rsync.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage_utils.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-zfs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/init_lxc_static-lxc_init.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_attach.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_autostart.Po@am__quote@ @@ -1407,82 +1437,89 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-arguments.lo `test -f 'arguments.c' || echo '$(srcdir)/'`arguments.c -bdev/liblxc_la-bdev.lo: bdev/bdev.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-bdev.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-bdev.Tpo -c -o bdev/liblxc_la-bdev.lo `test -f 'bdev/bdev.c' || echo '$(srcdir)/'`bdev/bdev.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-bdev.Tpo bdev/$(DEPDIR)/liblxc_la-bdev.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/bdev.c' object='bdev/liblxc_la-bdev.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-storage.lo: storage/storage.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-storage.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-storage.Tpo -c -o storage/liblxc_la-storage.lo `test -f 'storage/storage.c' || echo '$(srcdir)/'`storage/storage.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-storage.Tpo storage/$(DEPDIR)/liblxc_la-storage.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/storage.c' object='storage/liblxc_la-storage.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-storage.lo `test -f 'storage/storage.c' || echo '$(srcdir)/'`storage/storage.c + +storage/liblxc_la-aufs.lo: storage/aufs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-aufs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-aufs.Tpo -c -o storage/liblxc_la-aufs.lo `test -f 'storage/aufs.c' || echo '$(srcdir)/'`storage/aufs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-aufs.Tpo storage/$(DEPDIR)/liblxc_la-aufs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/aufs.c' object='storage/liblxc_la-aufs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-bdev.lo `test -f 'bdev/bdev.c' || echo '$(srcdir)/'`bdev/bdev.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-aufs.lo `test -f 'storage/aufs.c' || echo '$(srcdir)/'`storage/aufs.c -bdev/liblxc_la-lxcaufs.lo: bdev/lxcaufs.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcaufs.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcaufs.Tpo -c -o bdev/liblxc_la-lxcaufs.lo `test -f 'bdev/lxcaufs.c' || echo '$(srcdir)/'`bdev/lxcaufs.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcaufs.Tpo bdev/$(DEPDIR)/liblxc_la-lxcaufs.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcaufs.c' object='bdev/liblxc_la-lxcaufs.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-btrfs.lo: storage/btrfs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-btrfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-btrfs.Tpo -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-btrfs.Tpo storage/$(DEPDIR)/liblxc_la-btrfs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/btrfs.c' object='storage/liblxc_la-btrfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcaufs.lo `test -f 'bdev/lxcaufs.c' || echo '$(srcdir)/'`bdev/lxcaufs.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c -bdev/liblxc_la-lxcbtrfs.lo: bdev/lxcbtrfs.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcbtrfs.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcbtrfs.Tpo -c -o bdev/liblxc_la-lxcbtrfs.lo `test -f 'bdev/lxcbtrfs.c' || echo '$(srcdir)/'`bdev/lxcbtrfs.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcbtrfs.Tpo bdev/$(DEPDIR)/liblxc_la-lxcbtrfs.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcbtrfs.c' object='bdev/liblxc_la-lxcbtrfs.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-dir.lo: storage/dir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-dir.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-dir.Tpo -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-dir.Tpo storage/$(DEPDIR)/liblxc_la-dir.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/dir.c' object='storage/liblxc_la-dir.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcbtrfs.lo `test -f 'bdev/lxcbtrfs.c' || echo '$(srcdir)/'`bdev/lxcbtrfs.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c -bdev/liblxc_la-lxcdir.lo: bdev/lxcdir.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcdir.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcdir.Tpo -c -o bdev/liblxc_la-lxcdir.lo `test -f 'bdev/lxcdir.c' || echo '$(srcdir)/'`bdev/lxcdir.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcdir.Tpo bdev/$(DEPDIR)/liblxc_la-lxcdir.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcdir.c' object='bdev/liblxc_la-lxcdir.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-loop.lo: storage/loop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-loop.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-loop.Tpo -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-loop.Tpo storage/$(DEPDIR)/liblxc_la-loop.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/loop.c' object='storage/liblxc_la-loop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcdir.lo `test -f 'bdev/lxcdir.c' || echo '$(srcdir)/'`bdev/lxcdir.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c -bdev/liblxc_la-lxcloop.lo: bdev/lxcloop.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcloop.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcloop.Tpo -c -o bdev/liblxc_la-lxcloop.lo `test -f 'bdev/lxcloop.c' || echo '$(srcdir)/'`bdev/lxcloop.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcloop.Tpo bdev/$(DEPDIR)/liblxc_la-lxcloop.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcloop.c' object='bdev/liblxc_la-lxcloop.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-lvm.lo: storage/lvm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-lvm.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-lvm.Tpo -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-lvm.Tpo storage/$(DEPDIR)/liblxc_la-lvm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/lvm.c' object='storage/liblxc_la-lvm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcloop.lo `test -f 'bdev/lxcloop.c' || echo '$(srcdir)/'`bdev/lxcloop.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c -bdev/liblxc_la-lxclvm.lo: bdev/lxclvm.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxclvm.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxclvm.Tpo -c -o bdev/liblxc_la-lxclvm.lo `test -f 'bdev/lxclvm.c' || echo '$(srcdir)/'`bdev/lxclvm.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxclvm.Tpo bdev/$(DEPDIR)/liblxc_la-lxclvm.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxclvm.c' object='bdev/liblxc_la-lxclvm.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-nbd.lo: storage/nbd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-nbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-nbd.Tpo -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-nbd.Tpo storage/$(DEPDIR)/liblxc_la-nbd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/nbd.c' object='storage/liblxc_la-nbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxclvm.lo `test -f 'bdev/lxclvm.c' || echo '$(srcdir)/'`bdev/lxclvm.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c -bdev/liblxc_la-lxcnbd.lo: bdev/lxcnbd.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcnbd.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcnbd.Tpo -c -o bdev/liblxc_la-lxcnbd.lo `test -f 'bdev/lxcnbd.c' || echo '$(srcdir)/'`bdev/lxcnbd.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcnbd.Tpo bdev/$(DEPDIR)/liblxc_la-lxcnbd.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcnbd.c' object='bdev/liblxc_la-lxcnbd.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-overlay.lo: storage/overlay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-overlay.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-overlay.Tpo -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-overlay.Tpo storage/$(DEPDIR)/liblxc_la-overlay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/overlay.c' object='storage/liblxc_la-overlay.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcnbd.lo `test -f 'bdev/lxcnbd.c' || echo '$(srcdir)/'`bdev/lxcnbd.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c -bdev/liblxc_la-lxcoverlay.lo: bdev/lxcoverlay.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcoverlay.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcoverlay.Tpo -c -o bdev/liblxc_la-lxcoverlay.lo `test -f 'bdev/lxcoverlay.c' || echo '$(srcdir)/'`bdev/lxcoverlay.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcoverlay.Tpo bdev/$(DEPDIR)/liblxc_la-lxcoverlay.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcoverlay.c' object='bdev/liblxc_la-lxcoverlay.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-rbd.lo: storage/rbd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rbd.Tpo -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rbd.Tpo storage/$(DEPDIR)/liblxc_la-rbd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rbd.c' object='storage/liblxc_la-rbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcoverlay.lo `test -f 'bdev/lxcoverlay.c' || echo '$(srcdir)/'`bdev/lxcoverlay.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c -bdev/liblxc_la-lxcrbd.lo: bdev/lxcrbd.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcrbd.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcrbd.Tpo -c -o bdev/liblxc_la-lxcrbd.lo `test -f 'bdev/lxcrbd.c' || echo '$(srcdir)/'`bdev/lxcrbd.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcrbd.Tpo bdev/$(DEPDIR)/liblxc_la-lxcrbd.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcrbd.c' object='bdev/liblxc_la-lxcrbd.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-rsync.lo: storage/rsync.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rsync.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rsync.Tpo -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rsync.Tpo storage/$(DEPDIR)/liblxc_la-rsync.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rsync.c' object='storage/liblxc_la-rsync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcrbd.lo `test -f 'bdev/lxcrbd.c' || echo '$(srcdir)/'`bdev/lxcrbd.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c -bdev/liblxc_la-lxcrsync.lo: bdev/lxcrsync.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxcrsync.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxcrsync.Tpo -c -o bdev/liblxc_la-lxcrsync.lo `test -f 'bdev/lxcrsync.c' || echo '$(srcdir)/'`bdev/lxcrsync.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxcrsync.Tpo bdev/$(DEPDIR)/liblxc_la-lxcrsync.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxcrsync.c' object='bdev/liblxc_la-lxcrsync.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-zfs.lo: storage/zfs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-zfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-zfs.Tpo -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-zfs.Tpo storage/$(DEPDIR)/liblxc_la-zfs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/zfs.c' object='storage/liblxc_la-zfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxcrsync.lo `test -f 'bdev/lxcrsync.c' || echo '$(srcdir)/'`bdev/lxcrsync.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c -bdev/liblxc_la-lxczfs.lo: bdev/lxczfs.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT bdev/liblxc_la-lxczfs.lo -MD -MP -MF bdev/$(DEPDIR)/liblxc_la-lxczfs.Tpo -c -o bdev/liblxc_la-lxczfs.lo `test -f 'bdev/lxczfs.c' || echo '$(srcdir)/'`bdev/lxczfs.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) bdev/$(DEPDIR)/liblxc_la-lxczfs.Tpo bdev/$(DEPDIR)/liblxc_la-lxczfs.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bdev/lxczfs.c' object='bdev/liblxc_la-lxczfs.lo' libtool=yes @AMDEPBACKSLASH@ +storage/liblxc_la-storage_utils.lo: storage/storage_utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-storage_utils.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo storage/$(DEPDIR)/liblxc_la-storage_utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/storage_utils.c' object='storage/liblxc_la-storage_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o bdev/liblxc_la-lxczfs.lo `test -f 'bdev/lxczfs.c' || echo '$(srcdir)/'`bdev/lxczfs.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c cgroups/liblxc_la-cgfs.lo: cgroups/cgfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgfs.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgfs.Tpo -c -o cgroups/liblxc_la-cgfs.lo `test -f 'cgroups/cgfs.c' || echo '$(srcdir)/'`cgroups/cgfs.c @@ -1498,6 +1535,13 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgfsng.lo `test -f 'cgroups/cgfsng.c' || echo '$(srcdir)/'`cgroups/cgfsng.c +cgroups/liblxc_la-cgroup_utils.lo: cgroups/cgroup_utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgroup_utils.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Tpo -c -o cgroups/liblxc_la-cgroup_utils.lo `test -f 'cgroups/cgroup_utils.c' || echo '$(srcdir)/'`cgroups/cgroup_utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Tpo cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgroup_utils.c' object='cgroups/liblxc_la-cgroup_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgroup_utils.lo `test -f 'cgroups/cgroup_utils.c' || echo '$(srcdir)/'`cgroups/cgroup_utils.c + cgroups/liblxc_la-cgroup.lo: cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgroup.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo -c -o cgroups/liblxc_la-cgroup.lo `test -f 'cgroups/cgroup.c' || echo '$(srcdir)/'`cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo @@ -1512,6 +1556,13 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands.lo `test -f 'commands.c' || echo '$(srcdir)/'`commands.c +liblxc_la-commands_utils.lo: commands_utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-commands_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-commands_utils.Tpo -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-commands_utils.Tpo $(DEPDIR)/liblxc_la-commands_utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands_utils.c' object='liblxc_la-commands_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c + liblxc_la-start.lo: start.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-start.lo -MD -MP -MF $(DEPDIR)/liblxc_la-start.Tpo -c -o liblxc_la-start.lo `test -f 'start.c' || echo '$(srcdir)/'`start.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-start.Tpo $(DEPDIR)/liblxc_la-start.Plo @@ -1603,6 +1654,20 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile.lo `test -f 'confile.c' || echo '$(srcdir)/'`confile.c +liblxc_la-confile_legacy.lo: confile_legacy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile_legacy.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile_legacy.Tpo -c -o liblxc_la-confile_legacy.lo `test -f 'confile_legacy.c' || echo '$(srcdir)/'`confile_legacy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile_legacy.Tpo $(DEPDIR)/liblxc_la-confile_legacy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile_legacy.c' object='liblxc_la-confile_legacy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile_legacy.lo `test -f 'confile_legacy.c' || echo '$(srcdir)/'`confile_legacy.c + +liblxc_la-confile_utils.lo: confile_utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile_utils.Tpo -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile_utils.Tpo $(DEPDIR)/liblxc_la-confile_utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile_utils.c' object='liblxc_la-confile_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c + liblxc_la-state.lo: state.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-state.lo -MD -MP -MF $(DEPDIR)/liblxc_la-state.Tpo -c -o liblxc_la-state.lo `test -f 'state.c' || echo '$(srcdir)/'`state.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-state.Tpo $(DEPDIR)/liblxc_la-state.Plo @@ -1680,13 +1745,6 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-af_unix.lo `test -f 'af_unix.c' || echo '$(srcdir)/'`af_unix.c -liblxc_la-lxcutmp.lo: lxcutmp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxcutmp.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxcutmp.Tpo -c -o liblxc_la-lxcutmp.lo `test -f 'lxcutmp.c' || echo '$(srcdir)/'`lxcutmp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxcutmp.Tpo $(DEPDIR)/liblxc_la-lxcutmp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxcutmp.c' object='liblxc_la-lxcutmp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxcutmp.lo `test -f 'lxcutmp.c' || echo '$(srcdir)/'`lxcutmp.c - liblxc_la-lxclock.lo: lxclock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxclock.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxclock.Tpo -c -o liblxc_la-lxclock.lo `test -f 'lxclock.c' || echo '$(srcdir)/'`lxclock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxclock.Tpo $(DEPDIR)/liblxc_la-lxclock.Plo @@ -1757,6 +1815,13 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-lxcmntent.lo `test -f '../include/lxcmntent.c' || echo '$(srcdir)/'`../include/lxcmntent.c +../include/liblxc_la-prlimit.lo: ../include/prlimit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-prlimit.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-prlimit.Tpo -c -o ../include/liblxc_la-prlimit.lo `test -f '../include/prlimit.c' || echo '$(srcdir)/'`../include/prlimit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-prlimit.Tpo ../include/$(DEPDIR)/liblxc_la-prlimit.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/prlimit.c' object='../include/liblxc_la-prlimit.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-prlimit.lo `test -f '../include/prlimit.c' || echo '$(srcdir)/'`../include/prlimit.c + ../include/liblxc_la-getline.lo: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-getline.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-getline.Tpo -c -o ../include/liblxc_la-getline.lo `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-getline.Tpo ../include/$(DEPDIR)/liblxc_la-getline.Plo @@ -1861,9 +1926,9 @@ clean-libtool: -rm -rf .libs _libs -rm -rf ../include/.libs ../include/_libs - -rm -rf bdev/.libs bdev/_libs -rm -rf cgroups/.libs cgroups/_libs -rm -rf lsm/.libs lsm/_libs + -rm -rf storage/.libs storage/_libs install-pkgincludeHEADERS: $(pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ @@ -2005,12 +2070,12 @@ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../include/$(DEPDIR)/$(am__dirstamp) -rm -f ../include/$(am__dirstamp) - -rm -f bdev/$(DEPDIR)/$(am__dirstamp) - -rm -f bdev/$(am__dirstamp) -rm -f cgroups/$(DEPDIR)/$(am__dirstamp) -rm -f cgroups/$(am__dirstamp) -rm -f lsm/$(DEPDIR)/$(am__dirstamp) -rm -f lsm/$(am__dirstamp) + -rm -f storage/$(DEPDIR)/$(am__dirstamp) + -rm -f storage/$(am__dirstamp) -rm -f tools/$(DEPDIR)/$(am__dirstamp) -rm -f tools/$(am__dirstamp) @@ -2024,7 +2089,7 @@ mostlyclean-am distclean: distclean-am - -rm -rf ../include/$(DEPDIR) ./$(DEPDIR) bdev/$(DEPDIR) cgroups/$(DEPDIR) lsm/$(DEPDIR) tools/$(DEPDIR) + -rm -rf ../include/$(DEPDIR) ./$(DEPDIR) cgroups/$(DEPDIR) lsm/$(DEPDIR) storage/$(DEPDIR) tools/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -2073,7 +2138,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ../include/$(DEPDIR) ./$(DEPDIR) bdev/$(DEPDIR) cgroups/$(DEPDIR) lsm/$(DEPDIR) tools/$(DEPDIR) + -rm -rf ../include/$(DEPDIR) ./$(DEPDIR) cgroups/$(DEPDIR) lsm/$(DEPDIR) storage/$(DEPDIR) tools/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff -Nru lxc-2.0.8/src/lxc/monitor.c lxc-2.1.0/src/lxc/monitor.c --- lxc-2.0.8/src/lxc/monitor.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/monitor.c 2017-09-06 02:32:37.000000000 +0000 @@ -262,7 +262,7 @@ if (ret == -1) return -1; else if (ret == 0) - return -2; // timed out + return -2; /* timed out */ /* Only read from the first ready fd, the others will remain ready for * when this routine is called again. @@ -370,7 +370,7 @@ exit(EXIT_FAILURE); } - lxc_check_inherited(NULL, true, pipefd[1]); + lxc_check_inherited(NULL, true, &pipefd[1], 1); if (null_stdfds() < 0) { SYSERROR("Failed to dup2() standard file descriptors to /dev/null."); exit(EXIT_FAILURE); diff -Nru lxc-2.0.8/src/lxc/monitor.h lxc-2.1.0/src/lxc/monitor.h --- lxc-2.0.8/src/lxc/monitor.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/monitor.h 2017-09-06 02:32:37.000000000 +0000 @@ -28,8 +28,6 @@ #include #include -#include "conf.h" - typedef enum { lxc_msg_state, lxc_msg_priority, diff -Nru lxc-2.0.8/src/lxc/network.c lxc-2.1.0/src/lxc/network.c --- lxc-2.0.8/src/lxc/network.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/network.c 2017-09-06 02:32:37.000000000 +0000 @@ -20,35 +20,37 @@ * 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 -#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 #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "nl.h" -#include "network.h" +#include "af_unix.h" #include "conf.h" +#include "config.h" +#include "log.h" +#include "network.h" +#include "nl.h" #include "utils.h" #if HAVE_IFADDRS_H @@ -58,44 +60,415 @@ #endif #ifndef IFLA_LINKMODE -# define IFLA_LINKMODE 17 +#define IFLA_LINKMODE 17 #endif #ifndef IFLA_LINKINFO -# define IFLA_LINKINFO 18 +#define IFLA_LINKINFO 18 #endif #ifndef IFLA_NET_NS_PID -# define IFLA_NET_NS_PID 19 +#define IFLA_NET_NS_PID 19 #endif #ifndef IFLA_INFO_KIND -# define IFLA_INFO_KIND 1 +#define IFLA_INFO_KIND 1 #endif #ifndef IFLA_VLAN_ID -# define IFLA_VLAN_ID 1 +#define IFLA_VLAN_ID 1 #endif #ifndef IFLA_INFO_DATA -# define IFLA_INFO_DATA 2 +#define IFLA_INFO_DATA 2 #endif #ifndef VETH_INFO_PEER -# define VETH_INFO_PEER 1 +#define VETH_INFO_PEER 1 #endif #ifndef IFLA_MACVLAN_MODE -# define IFLA_MACVLAN_MODE 1 +#define IFLA_MACVLAN_MODE 1 #endif +lxc_log_define(lxc_network, lxc); + +typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *); + +static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int bridge_index, err; + char *veth1, *veth2; + char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ]; + unsigned int mtu = 0; + + if (netdev->priv.veth_attr.pair[0] != '\0') { + veth1 = netdev->priv.veth_attr.pair; + if (handler->conf->reboot) + lxc_netdev_delete_by_name(veth1); + } else { + err = snprintf(veth1buf, sizeof(veth1buf), "vethXXXXXX"); + if (err < 0 || (size_t)err >= sizeof(veth1buf)) + return -1; + + veth1 = lxc_mkifname(veth1buf); + if (!veth1) + return -1; + + /* store away for deconf */ + memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ); + } + + snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX"); + veth2 = lxc_mkifname(veth2buf); + if (!veth2) + goto out_delete; + + err = lxc_veth_create(veth1, veth2); + if (err) { + ERROR("Failed to create veth pair \"%s\" and \"%s\": %s", veth1, + veth2, strerror(-err)); + goto out_delete; + } + + /* changing the high byte of the mac address to 0xfe, the bridge interface + * will always keep the host's mac address and not take the mac address + * of a container */ + err = setup_private_host_hw_addr(veth1); + if (err) { + ERROR("Failed to change mac address of host interface \"%s\": %s", + veth1, strerror(-err)); + goto out_delete; + } + + /* Retrieve ifindex of the host's veth device. */ + netdev->priv.veth_attr.ifindex = if_nametoindex(veth1); + if (!netdev->priv.veth_attr.ifindex) { + ERROR("Failed to retrieve ifindex for \"%s\"", veth1); + goto out_delete; + } + + /* Note that we're retrieving the container's ifindex in the host's + * network namespace because we need it to move the device from the + * host's network namespace to the container's network namespace later + * on. + */ + netdev->ifindex = if_nametoindex(veth2); + if (!netdev->ifindex) { + ERROR("Failed to retrieve ifindex for \"%s\"", veth2); + goto out_delete; + } + + if (netdev->mtu) { + if (lxc_safe_uint(netdev->mtu, &mtu) < 0) + WARN("Failed to parse mtu"); + else + INFO("Retrieved mtu %d", mtu); + } else if (netdev->link[0] != '\0') { + bridge_index = if_nametoindex(netdev->link); + if (bridge_index) { + mtu = netdev_get_mtu(bridge_index); + INFO("Retrieved mtu %d from %s", mtu, netdev->link); + } else { + mtu = netdev_get_mtu(netdev->ifindex); + INFO("Retrieved mtu %d from %s", mtu, veth2); + } + } + + if (mtu) { + err = lxc_netdev_set_mtu(veth1, mtu); + if (!err) + err = lxc_netdev_set_mtu(veth2, mtu); + if (err) { + ERROR("Failed to set mtu \"%d\" for veth pair \"%s\" " + "and \"%s\": %s", + mtu, veth1, veth2, strerror(-err)); + goto out_delete; + } + } + + if (netdev->link[0] != '\0') { + err = lxc_bridge_attach(netdev->link, veth1); + if (err) { + ERROR("Failed to attach \"%s\" to bridge \"%s\": %s", + veth1, netdev->link, strerror(-err)); + goto out_delete; + } + INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link); + } + + err = lxc_netdev_up(veth1); + if (err) { + ERROR("Failed to set \"%s\" up: %s", veth1, strerror(-err)); + goto out_delete; + } + + if (netdev->upscript) { + err = run_script(handler->name, "net", netdev->upscript, "up", + "veth", veth1, (char*) NULL); + if (err) + goto out_delete; + } + + DEBUG("Instantiated veth \"%s/%s\", index is \"%d\"", veth1, veth2, + netdev->ifindex); + + return 0; + +out_delete: + if (netdev->ifindex != 0) + lxc_netdev_delete_by_name(veth1); + return -1; +} + +static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + char peerbuf[IFNAMSIZ], *peer; + int err; + + if (netdev->link[0] == '\0') { + ERROR("No link for macvlan network device specified"); + return -1; + } + + err = snprintf(peerbuf, sizeof(peerbuf), "mcXXXXXX"); + if (err < 0 || (size_t)err >= sizeof(peerbuf)) + return -1; + + peer = lxc_mkifname(peerbuf); + if (!peer) + return -1; + + err = lxc_macvlan_create(netdev->link, peer, + netdev->priv.macvlan_attr.mode); + if (err) { + ERROR("Failed to create macvlan interface \"%s\" on \"%s\": %s", + peer, netdev->link, strerror(-err)); + goto on_error; + } + + netdev->ifindex = if_nametoindex(peer); + if (!netdev->ifindex) { + ERROR("Failed to retrieve ifindex for \"%s\"", peer); + goto on_error; + } + + if (netdev->upscript) { + err = run_script(handler->name, "net", netdev->upscript, "up", + "macvlan", netdev->link, (char*) NULL); + if (err) + goto on_error; + } + + DEBUG("Instantiated macvlan \"%s\" with ifindex is %d and mode %d", + peer, netdev->ifindex, netdev->priv.macvlan_attr.mode); + + return 0; + +on_error: + lxc_netdev_delete_by_name(peer); + return -1; +} + +static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + char peer[IFNAMSIZ]; + int err; + static uint16_t vlan_cntr = 0; + unsigned int mtu = 0; + + if (netdev->link[0] == '\0') { + ERROR("No link for vlan network device specified"); + return -1; + } + + err = snprintf(peer, sizeof(peer), "vlan%d-%d", netdev->priv.vlan_attr.vid, vlan_cntr++); + if (err < 0 || (size_t)err >= sizeof(peer)) + return -1; + + err = lxc_vlan_create(netdev->link, peer, netdev->priv.vlan_attr.vid); + if (err) { + ERROR("Failed to create vlan interface \"%s\" on \"%s\": %s", + peer, netdev->link, strerror(-err)); + return -1; + } + + netdev->ifindex = if_nametoindex(peer); + if (!netdev->ifindex) { + ERROR("Failed to retrieve ifindex for \"%s\"", peer); + lxc_netdev_delete_by_name(peer); + return -1; + } + + DEBUG("Instantiated vlan \"%s\" with ifindex is \"%d\" (vlan1000)", + peer, netdev->ifindex); + if (netdev->mtu) { + if (lxc_safe_uint(netdev->mtu, &mtu) < 0) { + ERROR("Failed to retrieve mtu from \"%d\"/\"%s\".", + netdev->ifindex, + netdev->name[0] != '\0' ? netdev->name : "(null)"); + return -1; + } + err = lxc_netdev_set_mtu(peer, mtu); + if (err) { + ERROR("Failed to set mtu \"%s\" for \"%s\": %s", + netdev->mtu, peer, strerror(-err)); + lxc_netdev_delete_by_name(peer); + return -1; + } + } + + return 0; +} + +static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + if (netdev->link[0] == '\0') { + ERROR("No link for physical interface specified"); + return -1; + } + + /* Note that we're retrieving the container's ifindex in the host's + * network namespace because we need it to move the device from the + * host's network namespace to the container's network namespace later + * on. + * Note that netdev->link will contain the name of the physical network + * device in the host's namespace. + */ + netdev->ifindex = if_nametoindex(netdev->link); + if (!netdev->ifindex) { + ERROR("Failed to retrieve ifindex for \"%s\"", netdev->link); + return -1; + } + + /* Store the ifindex of the host's network device in the host's + * namespace. + */ + netdev->priv.phys_attr.ifindex = netdev->ifindex; + + if (netdev->upscript) { + int err; + err = run_script(handler->name, "net", netdev->upscript, + "up", "phys", netdev->link, (char*) NULL); + if (err) + return -1; + } + + return 0; +} + +static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + netdev->ifindex = 0; + if (netdev->upscript) { + int err; + err = run_script(handler->name, "net", netdev->upscript, + "up", "empty", (char*) NULL); + if (err) + return -1; + } + return 0; +} + +static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + netdev->ifindex = 0; + return 0; +} + +static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = { + [LXC_NET_VETH] = instantiate_veth, + [LXC_NET_MACVLAN] = instantiate_macvlan, + [LXC_NET_VLAN] = instantiate_vlan, + [LXC_NET_PHYS] = instantiate_phys, + [LXC_NET_EMPTY] = instantiate_empty, + [LXC_NET_NONE] = instantiate_none, +}; + +static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + char *veth1; + int err; + + if (netdev->priv.veth_attr.pair[0] != '\0') + veth1 = netdev->priv.veth_attr.pair; + else + veth1 = netdev->priv.veth_attr.veth1; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "veth", veth1, (char*) NULL); + if (err) + return -1; + } + return 0; +} + +static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "macvlan", netdev->link, + (char*) NULL); + if (err) + return -1; + } + return 0; +} + +static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + return 0; +} + +static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "phys", netdev->link, (char*) NULL); + if (err) + return -1; + } + return 0; +} + +static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "empty", (char*) NULL); + if (err) + return -1; + } + return 0; +} + +static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + return 0; +} + +static instantiate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = { + [LXC_NET_VETH] = shutdown_veth, + [LXC_NET_MACVLAN] = shutdown_macvlan, + [LXC_NET_VLAN] = shutdown_vlan, + [LXC_NET_PHYS] = shutdown_phys, + [LXC_NET_EMPTY] = shutdown_empty, + [LXC_NET_NONE] = shutdown_none, +}; -int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname) +int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname) { + int err; struct nl_handler nlh; - struct nlmsg *nlmsg = NULL; struct ifinfomsg *ifi; - int err; + struct nlmsg *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -106,7 +479,7 @@ if (!nlmsg) goto out; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -130,50 +503,56 @@ return err; } -/* - * If we are asked to move a wireless interface, then - * we must actually move its phyN device. Detect - * that condition and return the physname here. The - * physname will be passed to lxc_netdev_move_wlan() - * which will free it when done +/* If we are asked to move a wireless interface, then we must actually move its + * phyN device. Detect that condition and return the physname here. The physname + * will be passed to lxc_netdev_move_wlan() which will free it when done. */ #define PHYSNAME "/sys/class/net/%s/phy80211/name" -static char * is_wlan(const char *ifname) +static char *is_wlan(const char *ifname) { - char *path, *physname = NULL; - size_t len = strlen(ifname) + strlen(PHYSNAME) - 1; - struct stat sb; + int i, ret; long physlen; + size_t len; + char *path; FILE *f; - int ret, i; + struct stat sb; + char *physname = NULL; - path = alloca(len+1); + len = strlen(ifname) + strlen(PHYSNAME) - 1; + path = alloca(len + 1); ret = snprintf(path, len, PHYSNAME, ifname); - if (ret < 0 || ret >= len) + if (ret < 0 || (size_t)ret >= len) goto bad; + ret = stat(path, &sb); if (ret) goto bad; - if (!(f = fopen(path, "r"))) + + f = fopen(path, "r"); + if (!f) goto bad; - // feh - sb.st_size is always 4096 + + /* Feh - sb.st_size is always 4096. */ fseek(f, 0, SEEK_END); physlen = ftell(f); fseek(f, 0, SEEK_SET); - physname = malloc(physlen+1); + + physname = malloc(physlen + 1); if (!physname) { fclose(f); goto bad; } - memset(physname, 0, physlen+1); + + memset(physname, 0, physlen + 1); ret = fread(physname, 1, physlen, f); fclose(f); if (ret < 0) goto bad; - for (i = 0; i < physlen; i++) { + for (i = 0; i < physlen; i++) { if (physname[i] == '\n') physname[i] = '\0'; + if (physname[i] == '\0') break; } @@ -185,30 +564,34 @@ return NULL; } -static int -lxc_netdev_rename_by_name_in_netns(pid_t pid, const char *old, const char *new) +static int lxc_netdev_rename_by_name_in_netns(pid_t pid, const char *old, + const char *new) { - pid_t fpid = fork(); + pid_t fpid; + fpid = fork(); if (fpid < 0) return -1; + if (fpid != 0) return wait_for_pid(fpid); + if (!switch_to_ns(pid, "net")) return -1; + exit(lxc_netdev_rename_by_name(old, new)); } -static int -lxc_netdev_move_wlan(char *physname, const char *ifname, pid_t pid, const char* newname) +static int lxc_netdev_move_wlan(char *physname, const char *ifname, pid_t pid, + const char *newname) { - int err = -1; - pid_t fpid; char *cmd; + pid_t fpid; + int err = -1; /* Move phyN into the container. TODO - do this using netlink. - * However, IIUC this involves a bit more complicated work to - * talk to the 80211 module, so for now just call out to iw + * However, IIUC this involves a bit more complicated work to talk to + * the 80211 module, so for now just call out to iw. */ cmd = on_path("iw", NULL); if (!cmd) @@ -218,13 +601,15 @@ fpid = fork(); if (fpid < 0) goto out1; + if (fpid == 0) { char pidstr[30]; sprintf(pidstr, "%d", pid); - if (execlp("iw", "iw", "phy", physname, "set", "netns", pidstr, (char *)NULL)) - exit(1); - exit(0); // notreached + execlp("iw", "iw", "phy", physname, "set", "netns", pidstr, + (char *)NULL); + exit(EXIT_FAILURE); } + if (wait_for_pid(fpid)) goto out1; @@ -249,7 +634,8 @@ if (!index) return -EINVAL; - if ((physname = is_wlan(ifname))) + physname = is_wlan(ifname); + if (physname) return lxc_netdev_move_wlan(physname, ifname, pid, newname); return lxc_netdev_move_by_index(index, pid, newname); @@ -257,10 +643,10 @@ int lxc_netdev_delete_by_index(int ifindex) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; - struct ifinfomsg *ifi; int err; + struct ifinfomsg *ifi; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -275,7 +661,7 @@ if (!answer) goto out; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST; nlmsg->nlmsghdr->nlmsg_type = RTM_DELLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -305,10 +691,10 @@ int lxc_netdev_rename_by_index(int ifindex, const char *newname) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, len; struct ifinfomsg *ifi; - int len, err; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -327,7 +713,7 @@ if (!answer) goto out; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -364,10 +750,10 @@ int netdev_set_flag(const char *name, int flag) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, index, len; struct ifinfomsg *ifi; - int index, len, err; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -392,7 +778,7 @@ if (!index) goto out; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -413,12 +799,12 @@ return err; } -int netdev_get_flag(const char* name, int *flag) +int netdev_get_flag(const char *name, int *flag) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, index, len; struct ifinfomsg *ifi; - int index, len, err; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; if (!name) return -EINVAL; @@ -481,30 +867,28 @@ * 1 means interface is up. * Others means error happened, and ret-value is the error number. */ -int lxc_netdev_isup(const char* name) +int lxc_netdev_isup(const char *name) { - int flag; - int err; + int err, flag; err = netdev_get_flag(name, &flag); if (err) - goto out; + return err; + if (flag & IFF_UP) return 1; + return 0; -out: - return err; } int netdev_get_mtu(int ifindex) { + int answer_len, err, res; 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; + int readmore = 0, recv_len = 0; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -521,10 +905,11 @@ /* Save the answer buffer length, since it will be overwritten * on the first receive (and we might need to receive more than - * once. */ + * once. + */ answer_len = answer->nlmsghdr->nlmsg_len; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -540,7 +925,8 @@ do { /* Restore the answer buffer length, it might have been - * overwritten by a previous receive. */ + * overwritten by a previous receive. + */ answer->nlmsghdr->nlmsg_len = answer_len; /* Get the (next) batch of reply messages */ @@ -558,7 +944,8 @@ /* Stop reading if we see an error message */ if (msg->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); + struct nlmsgerr *errmsg = + (struct nlmsgerr *)NLMSG_DATA(msg); err = errmsg->error; goto out; } @@ -572,32 +959,34 @@ 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)); + 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. */ + 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)); + 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 */ + /* 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 */ + /* 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 */ + /* If we end up here, we didn't find any result, so signal an error. */ err = -1; out: @@ -609,10 +998,10 @@ int lxc_netdev_set_mtu(const char *name, int mtu) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, index, len; struct ifinfomsg *ifi; - int index, len, err; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -637,7 +1026,7 @@ if (!index) goto out; - nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); @@ -671,11 +1060,11 @@ int lxc_veth_create(const char *name1, const char *name2) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, len; struct ifinfomsg *ifi; + struct nl_handler nlh; struct rtattr *nest1, *nest2, *nest3; - int len, err; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -700,7 +1089,7 @@ goto out; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; + 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)); @@ -734,9 +1123,7 @@ 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)) @@ -750,14 +1137,14 @@ return err; } -/* XXX: merge with lxc_macvlan_create */ +/* TODO: 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; + int err, len, lindex; struct ifinfomsg *ifi; + struct nl_handler nlh; struct rtattr *nest, *nest2; - int lindex, len, err; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -787,7 +1174,7 @@ goto err1; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; + 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)); @@ -812,7 +1199,6 @@ goto err1; nla_end_nested(nlmsg, nest2); - nla_end_nested(nlmsg, nest); if (nla_put_u32(nlmsg, IFLA_LINK, lindex)) @@ -833,11 +1219,11 @@ int lxc_macvlan_create(const char *master, const char *name, int mode) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int err, index, len; struct ifinfomsg *ifi; + struct nl_handler nlh; struct rtattr *nest, *nest2; - int index, len, err; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -867,7 +1253,7 @@ goto out; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; + 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)); @@ -913,7 +1299,8 @@ static int proc_sys_net_write(const char *path, const char *value) { - int fd, err = 0; + int fd; + int err = 0; fd = open(path, O_WRONLY); if (fd < 0) @@ -928,18 +1315,18 @@ static int ip_forward_set(const char *ifname, int family, int flag) { - char path[MAXPATHLEN]; int rc; + char path[MAXPATHLEN]; 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) + family == AF_INET ? "ipv4" : "ipv6", ifname); + if (rc < 0 || (size_t)rc >= MAXPATHLEN) return -E2BIG; - return proc_sys_net_write(path, flag?"1":"0"); + return proc_sys_net_write(path, flag ? "1" : "0"); } int lxc_ip_forward_on(const char *ifname, int family) @@ -954,19 +1341,19 @@ static int neigh_proxy_set(const char *ifname, int family, int flag) { - char path[MAXPATHLEN]; int ret; + char path[MAXPATHLEN]; if (family != AF_INET && family != AF_INET6) return -EINVAL; ret = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/%s", - family == AF_INET?"ipv4":"ipv6" , ifname, - family == AF_INET?"proxy_arp":"proxy_ndp"); - if (ret < 0 || ret >= MAXPATHLEN) + family == AF_INET ? "ipv4" : "ipv6", ifname, + family == AF_INET ? "proxy_arp" : "proxy_ndp"); + if (ret < 0 || (size_t)ret >= MAXPATHLEN) return -E2BIG; - return proc_sys_net_write(path, flag?"1":"0"); + return proc_sys_net_write(path, flag ? "1" : "0"); } int lxc_neigh_proxy_on(const char *name, int family) @@ -981,62 +1368,60 @@ int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr) { - unsigned char *data; - char c; int i = 0; unsigned val; + char c; + unsigned char *data; 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++; + 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) +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; + int addrlen, err; struct ifaddrmsg *ifa; - int addrlen; - int err; + struct nl_handler nlh; + struct nlmsg *answer = NULL, *nlmsg = NULL; - addrlen = family == AF_INET ? sizeof(struct in_addr) : - sizeof(struct in6_addr); + addrlen = family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -1052,7 +1437,7 @@ goto out; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; + 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)); @@ -1073,7 +1458,7 @@ if (nla_put_buffer(nlmsg, IFA_BROADCAST, bcast, addrlen)) goto out; - /* TODO : multicast, anycast with ipv6 */ + /* TODO: multicast, anycast with ipv6 */ err = -EPROTONOSUPPORT; if (family == AF_INET6 && (memcmp(bcast, &in6addr_any, sizeof(in6addr_any)) || @@ -1089,48 +1474,52 @@ } int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, - struct in6_addr *mcast, - struct in6_addr *acast, int prefix) + 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) +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**). +/* 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) { +static int ifa_get_local_ip(int family, struct nlmsghdr *msg, void **res) +{ + int addrlen; 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); + 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)) { + 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 */ + * 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. */ + /* We might have found an IFA_ADDRESS before, which we + * now overwrite with an IFA_LOCAL. + */ if (!*res) { *res = malloc(addrlen); if (!*res) @@ -1138,7 +1527,6 @@ } memcpy(*res, RTA_DATA(rta), addrlen); - if (rta->rta_type == IFA_LOCAL) break; } @@ -1149,13 +1537,12 @@ static int ip_addr_get(int family, int ifindex, void **res) { - struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; + int answer_len, err; struct ifaddrmsg *ifa; + struct nl_handler nlh; struct nlmsghdr *msg; - int err; - int recv_len = 0, answer_len; - int readmore = 0; + int readmore = 0, recv_len = 0; + struct nlmsg *answer = NULL, *nlmsg = NULL; err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -1170,12 +1557,12 @@ 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. */ + /* 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_flags = NLM_F_REQUEST | NLM_F_ROOT; nlmsg->nlmsghdr->nlmsg_type = RTM_GETADDR; ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); @@ -1183,18 +1570,20 @@ goto out; ifa->ifa_family = family; - /* Send the request for addresses, which returns all addresses - * on all interfaces. */ + /* 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. */ + * overwritten by a previous receive. + */ answer->nlmsghdr->nlmsg_len = answer_len; - /* Get the (next) batch of reply messages */ + /* Get the (next) batch of reply messages. */ err = netlink_rcv(&nlh, answer); if (err < 0) goto out; @@ -1202,18 +1591,19 @@ recv_len = err; err = 0; - /* Satisfy the typing for the netlink macros */ + /* Satisfy the typing for the netlink macros. */ msg = answer->nlmsghdr; while (NLMSG_OK(msg, recv_len)) { - /* Stop reading if we see an error message */ + /* Stop reading if we see an error message. */ if (msg->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); + struct nlmsgerr *errmsg = + (struct nlmsgerr *)NLMSG_DATA(msg); err = errmsg->error; goto out; } - /* Stop reading if we see a NLMSG_DONE message */ + /* Stop reading if we see a NLMSG_DONE message. */ if (msg->nlmsg_type == NLMSG_DONE) { readmore = 0; break; @@ -1231,22 +1621,24 @@ goto out; } - /* Found a result, stop searching */ + /* 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 */ + /* 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 */ + /* 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 */ + * error. + */ err = -1; out: @@ -1258,24 +1650,23 @@ int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res) { - return ip_addr_get(AF_INET6, ifindex, (void**)res); + return ip_addr_get(AF_INET6, ifindex, (void **)res); } -int lxc_ipv4_addr_get(int ifindex, struct in_addr** res) +int lxc_ipv4_addr_get(int ifindex, struct in_addr **res) { - return ip_addr_get(AF_INET, ifindex, (void**)res); + return ip_addr_get(AF_INET, ifindex, (void **)res); } static int ip_gateway_add(int family, int ifindex, void *gw) { + int addrlen, err; struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; struct rtmsg *rt; - int addrlen; - int err; + struct nlmsg *answer = NULL, *nlmsg = NULL; - addrlen = family == AF_INET ? sizeof(struct in_addr) : - sizeof(struct in6_addr); + addrlen = family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -1291,7 +1682,7 @@ goto out; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; + 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)); @@ -1310,7 +1701,8 @@ goto out; /* Adding the interface index enables the use of link-local - * addresses for the gateway */ + * addresses for the gateway. + */ if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) goto out; @@ -1334,14 +1726,13 @@ static int ip_route_dest_add(int family, int ifindex, void *dest) { + int addrlen, err; struct nl_handler nlh; - struct nlmsg *nlmsg = NULL, *answer = NULL; struct rtmsg *rt; - int addrlen; - int err; + struct nlmsg *answer = NULL, *nlmsg = NULL; - addrlen = family == AF_INET ? sizeof(struct in_addr) : - sizeof(struct in6_addr); + addrlen = family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); err = netlink_open(&nlh, NETLINK_ROUTE); if (err) @@ -1357,7 +1748,7 @@ goto out; nlmsg->nlmsghdr->nlmsg_flags = - NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; + 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)); @@ -1368,7 +1759,7 @@ rt->rtm_scope = RT_SCOPE_LINK; rt->rtm_protocol = RTPROT_BOOT; rt->rtm_type = RTN_UNICAST; - rt->rtm_dst_len = addrlen*8; + rt->rtm_dst_len = addrlen * 8; err = -EINVAL; if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen)) @@ -1393,71 +1784,91 @@ return ip_route_dest_add(AF_INET6, ifindex, dest); } -static bool is_ovs_bridge(const char *bridge) +bool is_ovs_bridge(const char *bridge) { - char brdirname[22 + IFNAMSIZ + 1] = {0}; + int ret; struct stat sb; + char brdirname[22 + IFNAMSIZ + 1] = {0}; + + ret = snprintf(brdirname, 22 + IFNAMSIZ + 1, "/sys/class/net/%s/bridge", + bridge); + if (ret < 0 || (size_t)ret >= 22 + IFNAMSIZ + 1) + return false; - snprintf(brdirname, 22 +IFNAMSIZ + 1, "/sys/class/net/%s/bridge", bridge); - if (stat(brdirname, &sb) == -1 && errno == ENOENT) + ret = stat(brdirname, &sb); + if (ret < 0 && errno == ENOENT) return true; + return false; } -/* - * Called from a background thread - when nic goes away, remove - * it from the bridge +struct ovs_veth_args { + const char *bridge; + const char *nic; +}; + +/* Called from a background thread - when nic goes away, remove it from the + * bridge. */ -static void ovs_cleanup_nic(const char *lxcpath, const char *name, const char *bridge, const char *nic) +static int lxc_ovs_delete_port_exec(void *data) { - if (lxc_check_inherited(NULL, true, -1) < 0) - return; - if (lxc_wait(name, "STOPPED", -1, lxcpath) < 0) - return; - execlp("ovs-vsctl", "ovs-vsctl", "del-port", bridge, nic, (char *)NULL); - exit(1); /* not reached */ + struct ovs_veth_args *args = data; + + execlp("ovs-vsctl", "ovs-vsctl", "del-port", args->bridge, args->nic, + (char *)NULL); + return -1; } -static int attach_to_ovs_bridge(const char *lxcpath, const char *name, const char *bridge, const char *nic) +int lxc_ovs_delete_port(const char *bridge, const char *nic) { - pid_t pid; - char *cmd; int ret; + char cmd_output[MAXPATHLEN]; + struct ovs_veth_args args; - cmd = on_path("ovs-vsctl", NULL); - if (!cmd) + args.bridge = bridge; + args.nic = nic; + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_ovs_delete_port_exec, (void *)&args); + if (ret < 0) { + ERROR("Failed to delete \"%s\" from openvswitch bridge \"%s\": " + "%s", bridge, nic, cmd_output); return -1; - free(cmd); + } + + return 0; +} - pid = fork(); - if (pid < 0) +static int lxc_ovs_attach_bridge_exec(void *data) +{ + struct ovs_veth_args *args = data; + + execlp("ovs-vsctl", "ovs-vsctl", "add-port", args->bridge, args->nic, + (char *)NULL); + return -1; +} + +static int lxc_ovs_attach_bridge(const char *bridge, const char *nic) +{ + int ret; + char cmd_output[MAXPATHLEN]; + struct ovs_veth_args args; + + args.bridge = bridge; + args.nic = nic; + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_ovs_attach_bridge_exec, (void *)&args); + if (ret < 0) { + ERROR("Failed to attach \"%s\" to openvswitch bridge \"%s\": %s", + bridge, nic, cmd_output); return -1; - if (pid > 0) { - ret = wait_for_pid(pid); - if (ret < 0) - return ret; - pid = fork(); - if (pid < 0) - return -1; // how to properly recover? - if (pid > 0) - return 0; - ovs_cleanup_nic(lxcpath, name, bridge, nic); - exit(0); } - if (execlp("ovs-vsctl", "ovs-vsctl", "add-port", bridge, nic, (char *)NULL)) - exit(1); - // not reached - exit(1); + return 0; } -/* - * 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 *lxcpath, const char *name, const char *bridge, const char *ifname) +int lxc_bridge_attach(const char *bridge, const char *ifname) { - int fd, index, err; + int err, fd, index; struct ifreq ifr; if (strlen(ifname) >= IFNAMSIZ) @@ -1468,14 +1879,14 @@ return -EINVAL; if (is_ovs_bridge(bridge)) - return attach_to_ovs_bridge(lxcpath, name, bridge, ifname); + return lxc_ovs_attach_bridge(bridge, ifname); 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'; + strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; ifr.ifr_ifindex = index; err = ioctl(fd, SIOCBRADDIF, &ifr); close(fd); @@ -1485,7 +1896,7 @@ return err; } -static const char* const lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { +static const char *const lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { [LXC_NET_EMPTY] = "empty", [LXC_NET_VETH] = "veth", [LXC_NET_MACVLAN] = "macvlan", @@ -1498,46 +1909,47 @@ { if (type < 0 || type > LXC_NET_MAXCONFTYPE) return NULL; + return lxc_network_types[type]; } -static const char padchar[] = -"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char *lxc_mkifname(char *template) { - char *name = NULL; - size_t i = 0; - FILE *urandom; unsigned int seed; - struct ifaddrs *ifaddr, *ifa; - int ifexists = 0; + FILE *urandom; + struct ifaddrs *ifa, *ifaddr; + char name[IFNAMSIZ]; + bool exists = false; + size_t i = 0; + + if (strlen(template) >= IFNAMSIZ) + return NULL; - /* Get all the network interfaces */ + /* Get all the network interfaces. */ getifaddrs(&ifaddr); - /* Initialize the random number generator */ - urandom = fopen ("/dev/urandom", "r"); + /* Initialize the random number generator. */ + urandom = fopen("/dev/urandom", "r"); if (urandom != NULL) { - if (fread (&seed, sizeof(seed), 1, urandom) <= 0) + if (fread(&seed, sizeof(seed), 1, urandom) <= 0) seed = time(0); fclose(urandom); - } - else + } 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; + /* Generate random names until we find one that doesn't exist. */ + while (true) { + name[0] = '\0'; + strcpy(name, template); + exists = false; for (i = 0; i < strlen(name); i++) { if (name[i] == 'X') { #ifdef HAVE_RAND_R @@ -1549,33 +1961,33 @@ } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (strcmp(ifa->ifa_name, name) == 0) { - ifexists = 1; + if (!strcmp(ifa->ifa_name, name)) { + exists = true; break; } } - if (ifexists == 0) + if (!exists) break; - - free(name); } freeifaddrs(ifaddr); - return name; + return strcpy(template, name); } int setup_private_host_hw_addr(char *veth1) { + int err, sockfd; 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 = snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1); + if (err < 0 || (size_t)err >= IFNAMSIZ) + return -E2BIG; + err = ioctl(sockfd, SIOCGIFHWADDR, &ifr); if (err < 0) { close(sockfd); @@ -1590,3 +2002,1097 @@ return 0; } + +int lxc_find_gateway_addresses(struct lxc_handler *handler) +{ + struct lxc_list *network = &handler->conf->network; + struct lxc_list *iterator; + struct lxc_netdev *netdev; + int link_index; + + lxc_list_for_each(iterator, network) { + netdev = iterator->elem; + + if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto) + continue; + + if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) { + ERROR("Automatic gateway detection is only supported " + "for veth and macvlan"); + return -1; + } + + if (netdev->link[0] == '\0') { + ERROR("Automatic gateway detection needs a link interface"); + return -1; + } + + link_index = if_nametoindex(netdev->link); + if (!link_index) + return -EINVAL; + + if (netdev->ipv4_gateway_auto) { + if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) { + ERROR("Failed to automatically find ipv4 gateway " + "address from link interface \"%s\"", netdev->link); + return -1; + } + } + + if (netdev->ipv6_gateway_auto) { + if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) { + ERROR("Failed to automatically find ipv6 gateway " + "address from link interface \"%s\"", netdev->link); + return -1; + } + } + } + + return 0; +} + +#define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic" +static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname, + struct lxc_netdev *netdev, pid_t pid) +{ + int ret; + pid_t child; + int bytes, pipefd[2]; + char *token, *saveptr = NULL; + char netdev_link[IFNAMSIZ + 1]; + char buffer[MAXPATHLEN] = {0}; + + if (netdev->type != LXC_NET_VETH) { + ERROR("Network type %d not support for unprivileged use", netdev->type); + return -1; + } + + ret = pipe(pipefd); + if (ret < 0) { + SYSERROR("Failed to create pipe"); + return -1; + } + + child = fork(); + if (child < 0) { + SYSERROR("Failed to create new process"); + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + if (child == 0) { + int ret; + char pidstr[LXC_NUMSTRLEN64]; + + close(pipefd[0]); + + ret = dup2(pipefd[1], STDOUT_FILENO); + if (ret >= 0) + ret = dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); + if (ret < 0) { + SYSERROR("Failed to duplicate std{err,out} file descriptor"); + exit(EXIT_FAILURE); + } + + if (netdev->link[0] != '\0') + strncpy(netdev_link, netdev->link, IFNAMSIZ); + else + strncpy(netdev_link, "none", IFNAMSIZ); + + ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + pidstr[LXC_NUMSTRLEN64 - 1] = '\0'; + + INFO("Execing lxc-user-nic create %s %s %s veth %s %s", lxcpath, + lxcname, pidstr, netdev_link, + netdev->name[0] != '\0' ? netdev->name : "(null)"); + if (netdev->name[0] != '\0') + execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create", + lxcpath, lxcname, pidstr, "veth", netdev_link, + netdev->name, (char *)NULL); + else + execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create", + lxcpath, lxcname, pidstr, "veth", netdev_link, + (char *)NULL); + SYSERROR("Failed to execute lxc-user-nic"); + exit(EXIT_FAILURE); + } + + /* close the write-end of the pipe */ + close(pipefd[1]); + + bytes = read(pipefd[0], &buffer, MAXPATHLEN); + if (bytes < 0) { + SYSERROR("Failed to read from pipe file descriptor"); + close(pipefd[0]); + return -1; + } + buffer[bytes - 1] = '\0'; + + ret = wait_for_pid(child); + close(pipefd[0]); + if (ret != 0) { + ERROR("lxc-user-nic failed to configure requested network: %s", + buffer[0] != '\0' ? buffer : "(null)"); + return -1; + } + TRACE("Received output \"%s\" from lxc-user-nic", buffer); + + /* netdev->name */ + token = strtok_r(buffer, ":", &saveptr); + if (!token) { + ERROR("Failed to parse lxc-user-nic output"); + return -1; + } + + memset(netdev->name, 0, IFNAMSIZ + 1); + strncpy(netdev->name, token, IFNAMSIZ); + + /* netdev->ifindex */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) { + ERROR("Failed to parse lxc-user-nic output"); + return -1; + } + + ret = lxc_safe_int(token, &netdev->ifindex); + if (ret < 0) { + ERROR("%s - Failed to convert string \"%s\" to integer", + strerror(-ret), token); + return -1; + } + + /* netdev->priv.veth_attr.veth1 */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) { + ERROR("Failed to parse lxc-user-nic output"); + return -1; + } + + if (strlen(token) >= IFNAMSIZ) { + ERROR("Host side veth device name returned by lxc-user-nic is " + "too long"); + return -E2BIG; + } + strcpy(netdev->priv.veth_attr.veth1, token); + + /* netdev->priv.veth_attr.ifindex */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) { + ERROR("Failed to parse lxc-user-nic output"); + return -1; + } + + ret = lxc_safe_int(token, &netdev->priv.veth_attr.ifindex); + if (ret < 0) { + ERROR("%s - Failed to convert string \"%s\" to integer", + strerror(-ret), token); + return -1; + } + + return 0; +} + +static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname, + struct lxc_netdev *netdev, + const char *netns_path) +{ + int bytes, ret; + pid_t child; + int pipefd[2]; + char buffer[MAXPATHLEN] = {0}; + + if (netdev->type != LXC_NET_VETH) { + ERROR("Network type %d not support for unprivileged use", netdev->type); + return -1; + } + + ret = pipe(pipefd); + if (ret < 0) { + SYSERROR("Failed to create pipe"); + return -1; + } + + child = fork(); + if (child < 0) { + SYSERROR("Failed to create new process"); + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + if (child == 0) { + char *hostveth; + int ret; + + close(pipefd[0]); + + ret = dup2(pipefd[1], STDOUT_FILENO); + if (ret >= 0) + ret = dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); + if (ret < 0) { + SYSERROR("Failed to duplicate std{err,out} file descriptor"); + exit(EXIT_FAILURE); + } + + if (netdev->priv.veth_attr.pair[0] != '\0') + hostveth = netdev->priv.veth_attr.pair; + else + hostveth = netdev->priv.veth_attr.veth1; + if (hostveth[0] == '\0') { + SYSERROR("Host side veth device name is missing"); + exit(EXIT_FAILURE); + } + + if (netdev->link[0] == '\0') { + SYSERROR("Network link for network device \"%s\" is " + "missing", netdev->priv.veth_attr.veth1); + exit(EXIT_FAILURE); + } + + INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath, + lxcname, netns_path, netdev->link, hostveth); + execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath, + lxcname, netns_path, "veth", netdev->link, hostveth, + (char *)NULL); + SYSERROR("Failed to exec lxc-user-nic."); + exit(EXIT_FAILURE); + } + + close(pipefd[1]); + + bytes = read(pipefd[0], &buffer, MAXPATHLEN); + if (bytes < 0) { + SYSERROR("Failed to read from pipe file descriptor."); + close(pipefd[0]); + return -1; + } + buffer[bytes - 1] = '\0'; + + if (wait_for_pid(child) != 0) { + ERROR("lxc-user-nic failed to delete requested network: %s", + buffer[0] != '\0' ? buffer : "(null)"); + close(pipefd[0]); + return -1; + } + + close(pipefd[0]); + + return 0; +} + +bool lxc_delete_network_unpriv(struct lxc_handler *handler) +{ + int ret; + struct lxc_list *iterator; + struct lxc_list *network = &handler->conf->network; + /* strlen("/proc/") = 6 + * + + * LXC_NUMSTRLEN64 + * + + * strlen("/fd/") = 4 + * + + * LXC_NUMSTRLEN64 + * + + * \0 + */ + char netns_path[6 + LXC_NUMSTRLEN64 + 4 + LXC_NUMSTRLEN64 + 1]; + bool deleted_all = true; + + if (handler->am_root) + return true; + + *netns_path = '\0'; + + if (handler->netnsfd < 0) { + DEBUG("Cannot not guarantee safe deletion of network devices. " + "Manual cleanup maybe needed"); + return false; + } + + ret = snprintf(netns_path, sizeof(netns_path), "/proc/%d/fd/%d", + getpid(), handler->netnsfd); + if (ret < 0 || ret >= sizeof(netns_path)) + return false; + + lxc_list_for_each(iterator, network) { + char *hostveth = NULL; + struct lxc_netdev *netdev = iterator->elem; + + /* We can only delete devices whose ifindex we have. If we don't + * have the index it means that we didn't create it. + */ + if (!netdev->ifindex) + continue; + + if (netdev->type == LXC_NET_PHYS) { + ret = lxc_netdev_rename_by_index(netdev->ifindex, + netdev->link); + if (ret < 0) + WARN("Failed to rename interface with index %d " + "to its initial name \"%s\"", + netdev->ifindex, netdev->link); + else + TRACE("Renamed interface with index %d to its " + "initial name \"%s\"", + netdev->ifindex, netdev->link); + continue; + } + + ret = netdev_deconf[netdev->type](handler, netdev); + if (ret < 0) + WARN("Failed to deconfigure network device"); + + if (netdev->type != LXC_NET_VETH) + continue; + + if (!is_ovs_bridge(netdev->link)) + continue; + + if (netdev->priv.veth_attr.pair[0] != '\0') + hostveth = netdev->priv.veth_attr.pair; + else + hostveth = netdev->priv.veth_attr.veth1; + if (hostveth[0] == '\0') + continue; + + ret = lxc_delete_network_unpriv_exec(handler->lxcpath, + handler->name, netdev, + netns_path); + if (ret < 0) { + deleted_all = false; + WARN("Failed to remove port \"%s\" from openvswitch " + "bridge \"%s\"", hostveth, netdev->link); + continue; + } + INFO("Removed interface \"%s\" from \"%s\"", hostveth, + netdev->link); + } + + return deleted_all; +} + +int lxc_create_network_priv(struct lxc_handler *handler) +{ + struct lxc_list *iterator; + struct lxc_list *network = &handler->conf->network; + + if (!handler->am_root) + return 0; + + lxc_list_for_each(iterator, network) { + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type < 0 || netdev->type > LXC_NET_MAXCONFTYPE) { + ERROR("Invalid network configuration type %d", netdev->type); + return -1; + } + + if (netdev_conf[netdev->type](handler, netdev)) { + ERROR("Failed to create network device"); + return -1; + } + + } + + return 0; +} + +int lxc_network_move_created_netdev_priv(const char *lxcpath, char *lxcname, + struct lxc_list *network, pid_t pid) +{ + int ret; + char ifname[IFNAMSIZ]; + struct lxc_list *iterator; + + if (am_unpriv()) + return 0; + + lxc_list_for_each(iterator, network) { + struct lxc_netdev *netdev = iterator->elem; + + if (!netdev->ifindex) + continue; + + /* retrieve the name of the interface */ + if (!if_indextoname(netdev->ifindex, ifname)) { + ERROR("No interface corresponding to ifindex \"%d\"", + netdev->ifindex); + return -1; + } + + ret = lxc_netdev_move_by_name(ifname, pid, NULL); + if (ret) { + ERROR("Failed to move network device \"%s\" to " + "network namespace %d: %s", ifname, pid, + strerror(-ret)); + return -1; + } + + DEBUG("Moved network device \"%s\"/\"%s\" to network namespace " + "of %d", + ifname, netdev->name[0] != '\0' ? netdev->name : "(null)", + pid); + } + + return 0; +} + +int lxc_create_network_unpriv(const char *lxcpath, char *lxcname, + struct lxc_list *network, pid_t pid) +{ + struct lxc_list *iterator; + + if (!am_unpriv()) + return 0; + + lxc_list_for_each(iterator, network) { + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type == LXC_NET_EMPTY) + continue; + + if (netdev->type == LXC_NET_NONE) + continue; + + if (netdev->type != LXC_NET_VETH) { + ERROR("Networks of type %s are not supported by " + "unprivileged containers", + lxc_net_type_to_str(netdev->type)); + return -1; + } + + if (netdev->mtu) + INFO("mtu ignored due to insufficient privilege"); + + if (lxc_create_network_unpriv_exec(lxcpath, lxcname, netdev, pid)) + return -1; + } + + return 0; +} + +bool lxc_delete_network_priv(struct lxc_handler *handler) +{ + int ret; + struct lxc_list *iterator; + struct lxc_list *network = &handler->conf->network; + bool deleted_all = true; + + if (!handler->am_root) + return true; + + lxc_list_for_each(iterator, network) { + char *hostveth = NULL; + struct lxc_netdev *netdev = iterator->elem; + + /* We can only delete devices whose ifindex we have. If we don't + * have the index it means that we didn't create it. + */ + if (!netdev->ifindex) + continue; + + if (netdev->type == LXC_NET_PHYS) { + ret = lxc_netdev_rename_by_index(netdev->ifindex, netdev->link); + if (ret < 0) + WARN("Failed to rename interface with index %d " + "from \"%s\" to its initial name \"%s\"", + netdev->ifindex, netdev->name, netdev->link); + else + TRACE("Renamed interface with index %d from " + "\"%s\" to its initial name \"%s\"", + netdev->ifindex, netdev->name, + netdev->link); + continue; + } + + ret = netdev_deconf[netdev->type](handler, netdev); + if (ret < 0) + WARN("Failed to deconfigure network device"); + + /* Recent kernels remove the virtual interfaces when the network + * namespace is destroyed but in case we did not move the + * interface to the network namespace, we have to destroy it. + */ + ret = lxc_netdev_delete_by_index(netdev->ifindex); + if (-ret == ENODEV) { + INFO("Interface \"%s\" with index %d already " + "deleted or existing in different network " + "namespace", + netdev->name[0] != '\0' ? netdev->name : "(null)", + netdev->ifindex); + } else if (ret < 0) { + deleted_all = false; + WARN("Failed to remove interface \"%s\" with " + "index %d: %s", + netdev->name[0] != '\0' ? netdev->name : "(null)", + netdev->ifindex, strerror(-ret)); + continue; + } + INFO("Removed interface \"%s\" with index %d", + netdev->name[0] != '\0' ? netdev->name : "(null)", + netdev->ifindex); + + if (netdev->type != LXC_NET_VETH) + continue; + + /* Explicitly delete host veth device to prevent lingering + * devices. We had issues in LXD around this. + */ + if (netdev->priv.veth_attr.pair[0] != '\0') + hostveth = netdev->priv.veth_attr.pair; + else + hostveth = netdev->priv.veth_attr.veth1; + if (hostveth[0] == '\0') + continue; + + ret = lxc_netdev_delete_by_name(hostveth); + if (ret < 0) { + deleted_all = false; + WARN("Failed to remove interface \"%s\" from \"%s\": %s", + hostveth, netdev->link, strerror(-ret)); + continue; + } + INFO("Removed interface \"%s\" from \"%s\"", hostveth, netdev->link); + + if (!is_ovs_bridge(netdev->link)) { + netdev->priv.veth_attr.veth1[0] = '\0'; + continue; + } + + /* Delete the openvswitch port. */ + ret = lxc_ovs_delete_port(netdev->link, hostveth); + if (ret < 0) + WARN("Failed to remove port \"%s\" from openvswitch " + "bridge \"%s\"", hostveth, netdev->link); + else + INFO("Removed port \"%s\" from openvswitch bridge \"%s\"", + hostveth, netdev->link); + + netdev->priv.veth_attr.veth1[0] = '\0'; + } + + return deleted_all; +} + +int lxc_requests_empty_network(struct lxc_handler *handler) +{ + struct lxc_list *network = &handler->conf->network; + struct lxc_list *iterator; + bool found_none = false, found_nic = false; + + if (lxc_list_empty(network)) + return 0; + + lxc_list_for_each(iterator, network) { + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type == LXC_NET_NONE) + found_none = true; + else + found_nic = true; + } + if (found_none && !found_nic) + return 1; + return 0; +} + +/* try to move physical nics to the init netns */ +int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler) +{ + int ret; + int oldfd; + char ifname[IFNAMSIZ]; + struct lxc_list *iterator; + int netnsfd = handler->netnsfd; + struct lxc_conf *conf = handler->conf; + + /* We need CAP_NET_ADMIN in the parent namespace in order to setns() to + * the parent network namespace. We won't have this capability if we are + * unprivileged. + */ + if (!handler->am_root) + return 0; + + TRACE("Moving physical network devices back to parent network namespace"); + + oldfd = lxc_preserve_ns(getpid(), "net"); + if (oldfd < 0) { + SYSERROR("Failed to preserve network namespace"); + return -1; + } + + ret = setns(netnsfd, CLONE_NEWNET); + if (ret < 0) { + SYSERROR("Failed to enter network namespace"); + close(oldfd); + return -1; + } + + lxc_list_for_each(iterator, &conf->network) { + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type != LXC_NET_PHYS) + continue; + + /* Retrieve the name of the interface in the container's network + * namespace. + */ + if (!if_indextoname(netdev->ifindex, ifname)) { + WARN("No interface corresponding to ifindex %d", + netdev->ifindex); + continue; + } + + ret = lxc_netdev_move_by_name(ifname, 1, netdev->link); + if (ret < 0) + WARN("Error moving network device \"%s\" back to " + "network namespace", ifname); + else + TRACE("Moved network device \"%s\" back to network " + "namespace", ifname); + } + + ret = setns(oldfd, CLONE_NEWNET); + close(oldfd); + if (ret < 0) { + SYSERROR("Failed to enter network namespace"); + return -1; + } + + return 0; +} + +static int setup_hw_addr(char *hwaddr, const char *ifname) +{ + struct sockaddr sockaddr; + struct ifreq ifr; + int ret, fd, saved_errno; + + ret = lxc_convert_mac(hwaddr, &sockaddr); + if (ret) { + ERROR("Mac address \"%s\" conversion failed: %s", hwaddr, + strerror(-ret)); + return -1; + } + + memcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr)); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + ret = ioctl(fd, SIOCSIFHWADDR, &ifr); + saved_errno = errno; + close(fd); + if (ret) + ERROR("Failed to perform ioctl: %s", strerror(saved_errno)); + + DEBUG("Mac address \"%s\" on \"%s\" has been setup", hwaddr, + ifr.ifr_name); + + return ret; +} + +static int setup_ipv4_addr(struct lxc_list *ip, int ifindex) +{ + struct lxc_list *iterator; + int err; + + lxc_list_for_each(iterator, ip) { + struct lxc_inetdev *inetdev = iterator->elem; + + err = lxc_ipv4_addr_add(ifindex, &inetdev->addr, + &inetdev->bcast, inetdev->prefix); + if (err) { + ERROR("Failed to setup ipv4 address for network device " + "with eifindex %d: %s", ifindex, strerror(-err)); + return -1; + } + } + + return 0; +} + +static int setup_ipv6_addr(struct lxc_list *ip, int ifindex) +{ + struct lxc_list *iterator; + int err; + + lxc_list_for_each(iterator, ip) { + struct lxc_inet6dev *inet6dev = iterator->elem; + + err = lxc_ipv6_addr_add(ifindex, &inet6dev->addr, + &inet6dev->mcast, &inet6dev->acast, + inet6dev->prefix); + if (err) { + ERROR("Failed to setup ipv6 address for network device " + "with eifindex %d: %s", ifindex, strerror(-err)); + return -1; + } + } + + return 0; +} + +static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) +{ + char ifname[IFNAMSIZ]; + int err; + const char *net_type_name; + char *current_ifname = ifname; + + /* empty network namespace */ + if (!netdev->ifindex) { + if (netdev->flags & IFF_UP) { + err = lxc_netdev_up("lo"); + if (err) { + ERROR("Failed to set the loopback network " + "device up: %s", + strerror(-err)); + return -1; + } + } + + if (netdev->type == LXC_NET_EMPTY) + return 0; + + if (netdev->type == LXC_NET_NONE) + return 0; + + if (netdev->type != LXC_NET_VETH) { + net_type_name = lxc_net_type_to_str(netdev->type); + ERROR("%s networks are not supported for containers " + "not setup up by privileged users", net_type_name); + return -1; + } + + netdev->ifindex = if_nametoindex(netdev->name); + } + + /* get the new ifindex in case of physical netdev */ + if (netdev->type == LXC_NET_PHYS) { + netdev->ifindex = if_nametoindex(netdev->link); + if (!netdev->ifindex) { + ERROR("Failed to get ifindex for network device \"%s\"", + netdev->link); + return -1; + } + } + + /* retrieve the name of the interface */ + if (!if_indextoname(netdev->ifindex, current_ifname)) { + ERROR("Failed get name for network device with ifindex %d", + netdev->ifindex); + return -1; + } + + /* Default: let the system to choose one interface name. + * When the IFLA_IFNAME attribute is passed something like "%d" + * netlink will replace the format specifier with an appropriate index. + */ + if (netdev->name[0] == '\0') { + if (netdev->type == LXC_NET_PHYS) + strcpy(netdev->name, netdev->link); + else + strcpy(netdev->name, "eth%d"); + } + + /* rename the interface name */ + if (strcmp(ifname, netdev->name) != 0) { + err = lxc_netdev_rename_by_name(ifname, netdev->name); + if (err) { + ERROR("Failed to rename network device \"%s\" to " + "\"%s\": %s", ifname, netdev->name, strerror(-err)); + return -1; + } + } + + /* Re-read the name of the interface because its name has changed + * and would be automatically allocated by the system + */ + if (!if_indextoname(netdev->ifindex, current_ifname)) { + ERROR("Failed get name for network device with ifindex %d", + netdev->ifindex); + return -1; + } + + /* Now update the recorded name of the network device to reflect the + * name of the network device in the child's network namespace. We will + * later on send this information back to the parent. + */ + strcpy(netdev->name, current_ifname); + + /* set a mac address */ + if (netdev->hwaddr) { + if (setup_hw_addr(netdev->hwaddr, current_ifname)) { + ERROR("Failed to setup hw address for network device \"%s\"", + current_ifname); + return -1; + } + } + + /* setup ipv4 addresses on the interface */ + if (setup_ipv4_addr(&netdev->ipv4, netdev->ifindex)) { + ERROR("Failed to setup ip addresses for network device \"%s\"", + ifname); + return -1; + } + + /* setup ipv6 addresses on the interface */ + if (setup_ipv6_addr(&netdev->ipv6, netdev->ifindex)) { + ERROR("Failed to setup ipv6 addresses for network device \"%s\"", + ifname); + return -1; + } + + /* set the network device up */ + if (netdev->flags & IFF_UP) { + int err; + + err = lxc_netdev_up(current_ifname); + if (err) { + ERROR("Failed to set network device \"%s\" up: %s", + current_ifname, strerror(-err)); + return -1; + } + + /* the network is up, make the loopback up too */ + err = lxc_netdev_up("lo"); + if (err) { + ERROR("Failed to set the loopback network device up: %s", + strerror(-err)); + return -1; + } + } + + /* We can only set up the default routes after bringing + * up the interface, sine bringing up the interface adds + * the link-local routes and we can't add a default + * route if the gateway is not reachable. */ + + /* setup ipv4 gateway on the interface */ + if (netdev->ipv4_gateway) { + if (!(netdev->flags & IFF_UP)) { + ERROR("Cannot add ipv4 gateway for network device " + "\"%s\" when not bringing up the interface", ifname); + return -1; + } + + if (lxc_list_empty(&netdev->ipv4)) { + ERROR("Cannot add ipv4 gateway for network device " + "\"%s\" when not assigning an address", ifname); + return -1; + } + + err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); + if (err) { + err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway); + if (err) { + ERROR("Failed to add ipv4 dest for network " + "device \"%s\": %s", ifname, strerror(-err)); + } + + err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); + if (err) { + ERROR("Failed to setup ipv4 gateway for " + "network device \"%s\": %s", + ifname, strerror(-err)); + if (netdev->ipv4_gateway_auto) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); + ERROR("Fried to set autodetected ipv4 gateway \"%s\"", buf); + } + return -1; + } + } + } + + /* setup ipv6 gateway on the interface */ + if (netdev->ipv6_gateway) { + if (!(netdev->flags & IFF_UP)) { + ERROR("Cannot add ipv6 gateway for network device " + "\"%s\" when not bringing up the interface", ifname); + return -1; + } + + if (lxc_list_empty(&netdev->ipv6) && !IN6_IS_ADDR_LINKLOCAL(netdev->ipv6_gateway)) { + ERROR("Cannot add ipv6 gateway for network device " + "\"%s\" when not assigning an address", ifname); + return -1; + } + + err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); + if (err) { + err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway); + if (err) { + ERROR("Failed to add ipv6 dest for network " + "device \"%s\": %s", ifname, strerror(-err)); + } + + err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); + if (err) { + ERROR("Failed to setup ipv6 gateway for " + "network device \"%s\": %s", ifname, + strerror(-err)); + if (netdev->ipv6_gateway_auto) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); + ERROR("Tried to set autodetected ipv6 " + "gateway for network device " + "\"%s\"", buf); + } + return -1; + } + } + } + + DEBUG("Network device \"%s\" has been setup", current_ifname); + + return 0; +} + +int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, + struct lxc_list *network) +{ + struct lxc_list *iterator; + struct lxc_netdev *netdev; + + lxc_list_for_each(iterator, network) { + netdev = iterator->elem; + + /* REMOVE in LXC 3.0 */ + if (netdev->idx < 0) { + ERROR("WARNING: using \"lxc.network.*\" keys to define " + "networks is DEPRECATED, please switch to using " + "\"lxc.net.[i].* keys\""); + } + + if (lxc_setup_netdev_in_child_namespaces(netdev)) { + ERROR("failed to setup netdev"); + return -1; + } + } + + if (!lxc_list_empty(network)) + INFO("network has been setup"); + + return 0; +} + +int lxc_network_send_veth_names_to_child(struct lxc_handler *handler) +{ + struct lxc_list *iterator; + struct lxc_list *network = &handler->conf->network; + int data_sock = handler->data_sock[0]; + + if (handler->am_root) + return 0; + + lxc_list_for_each(iterator, network) { + int ret; + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type != LXC_NET_VETH) + continue; + + ret = send(data_sock, netdev->name, IFNAMSIZ, 0); + if (ret < 0) + return -1; + TRACE("Sent network device name \"%s\" to child", netdev->name); + } + + return 0; +} + +int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler) +{ + struct lxc_list *iterator; + struct lxc_list *network = &handler->conf->network; + int data_sock = handler->data_sock[1]; + + if (handler->am_root) + return 0; + + lxc_list_for_each(iterator, network) { + int ret; + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type != LXC_NET_VETH) + continue; + + ret = recv(data_sock, netdev->name, IFNAMSIZ, 0); + if (ret < 0) + return -1; + TRACE("Received network device name \"%s\" from parent", netdev->name); + } + + return 0; +} + +int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler) +{ + struct lxc_list *iterator, *network; + int data_sock = handler->data_sock[0]; + + if (!handler->am_root) + return 0; + + network = &handler->conf->network; + lxc_list_for_each(iterator, network) { + int ret; + struct lxc_netdev *netdev = iterator->elem; + + /* Send network device name in the child's namespace to parent. */ + ret = send(data_sock, netdev->name, IFNAMSIZ, 0); + if (ret < 0) + return -1; + + /* Send network device ifindex in the child's namespace to + * parent. + */ + ret = send(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0); + if (ret < 0) + return -1; + } + + TRACE("Sent network device names and ifindeces to parent"); + return 0; +} + +int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler) +{ + struct lxc_list *iterator, *network; + int data_sock = handler->data_sock[1]; + + if (!handler->am_root) + return 0; + + network = &handler->conf->network; + lxc_list_for_each(iterator, network) { + int ret; + struct lxc_netdev *netdev = iterator->elem; + + /* Receive network device name in the child's namespace to + * parent. + */ + ret = recv(data_sock, netdev->name, IFNAMSIZ, 0); + if (ret < 0) + return -1; + + /* Receive network device ifindex in the child's namespace to + * parent. + */ + ret = recv(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0); + if (ret < 0) + return -1; + } + + return 0; +} diff -Nru lxc-2.0.8/src/lxc/network.h lxc-2.1.0/src/lxc/network.h --- lxc-2.0.8/src/lxc/network.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/network.h 2017-09-06 02:32:37.000000000 +0000 @@ -23,64 +23,197 @@ #ifndef __LXC_NETWORK_H #define __LXC_NETWORK_H -/* - * Convert a string mac address to a socket structure - */ +#include +#include +#include +#include +#include + +#include "list.h" + +struct lxc_conf; +struct lxc_handler; +struct lxc_netdev; + +enum { + LXC_NET_EMPTY, + LXC_NET_VETH, + LXC_NET_MACVLAN, + LXC_NET_PHYS, + LXC_NET_VLAN, + LXC_NET_NONE, + LXC_NET_MAXCONFTYPE, +}; + +/* + * Defines the structure to configure an ipv4 address + * @address : ipv4 address + * @broadcast : ipv4 broadcast address + * @mask : network mask + */ +struct lxc_inetdev { + struct in_addr addr; + struct in_addr bcast; + unsigned int prefix; +}; + +struct lxc_route { + struct in_addr addr; +}; + +/* + * Defines the structure to configure an ipv6 address + * @flags : set the address up + * @address : ipv6 address + * @broadcast : ipv6 broadcast address + * @mask : network mask + */ +struct lxc_inet6dev { + struct in6_addr addr; + struct in6_addr mcast; + struct in6_addr acast; + unsigned int prefix; +}; + +struct lxc_route6 { + struct in6_addr addr; +}; + +/* Contains information about the host side veth device. + * @pair : Name of the host side veth device. + * If the user requested that the host veth device be created with a + * specific names this field will be set. If this field is set @veth1 + * is not set. + * @veth1 : Name of the host side veth device. + * If the user did not request that the host veth device be created + * with a specific name this field will be set. If this field is set + * @pair is not set. + * @ifindex : Ifindex of the network device. + */ +struct ifla_veth { + char pair[IFNAMSIZ]; + char veth1[IFNAMSIZ]; + int ifindex; +}; + +struct ifla_vlan { + unsigned int flags; + unsigned int fmask; + unsigned short vid; + unsigned short pad; +}; + +struct ifla_macvlan { + int mode; /* private, vepa, bridge, passthru */ +}; + +/* Contains information about the physical network device as seen from the host. + * @ifindex : The ifindex of the physical network device in the host's network + * namespace. + */ +struct ifla_phys { + int ifindex; +}; + +union netdev_p { + struct ifla_macvlan macvlan_attr; + struct ifla_phys phys_attr; + struct ifla_veth veth_attr; + struct ifla_vlan vlan_attr; +}; + +/* + * Defines a structure to configure a network device + * @idx : network counter + * @ifindex : ifindex of the network device + * Note that this is the ifindex of the network device in + * the container's network namespace. If the network device + * consists of a pair of network devices (e.g. veth pairs + * attached to a network bridge) then this index cannot be + * used to identify or modify the host veth device. See + * struct ifla_veth for the host side information. + * @type : network type (veth, macvlan, vlan, ...) + * @flags : flag of the network device (IFF_UP, ... ) + * @link : lxc.net.[i].link, name of bridge or host iface to attach + * if any + * @name : lxc.net.[i].name, name of iface on the container side + * @hwaddr : mac address + * @mtu : maximum transmission unit + * @priv : information specific to the specificed network type + * Note that this is a union so whether accessing a struct + * is possible is dependent on the network type. + * @ipv4 : a list of ipv4 addresses to be set on the network device + * @ipv6 : a list of ipv6 addresses to be set on the network device + * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered + * from the associated @link + * @ipv4_gateway : ipv4 gateway + * @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered + * from the associated @link + * @ipv6_gateway : ipv6 gateway + * @upscript : a script filename to be executed during interface + * configuration + * @downscript : a script filename to be executed during interface + * destruction + */ +struct lxc_netdev { + ssize_t idx; + int ifindex; + int type; + int flags; + char link[IFNAMSIZ]; + char name[IFNAMSIZ]; + char *hwaddr; + char *mtu; + union netdev_p priv; + struct lxc_list ipv4; + struct lxc_list ipv6; + bool ipv4_gateway_auto; + struct in_addr *ipv4_gateway; + bool ipv6_gateway_auto; + struct in6_addr *ipv6_gateway; + char *upscript; + char *downscript; +}; + +/* Convert a string mac address to a socket structure. */ extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr); -/* - * Move a device between namespaces - */ -extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname); -extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char* newname); +/* Move a device between namespaces. */ +extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname); +extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid, + const char *newname); -/* - * Delete a network device - */ +/* Delete a network device. */ extern int lxc_netdev_delete_by_name(const char *name); extern int lxc_netdev_delete_by_index(int ifindex); -/* - * Change the device name - */ +/* Change the device name. */ extern int lxc_netdev_rename_by_name(const char *oldname, const char *newname); extern int lxc_netdev_rename_by_index(int ifindex, const char *newname); extern int netdev_set_flag(const char *name, int flag); -/* - * Set the device network up or down - */ - +/* Set the device network up or down. */ extern int lxc_netdev_isup(const char *name); extern int lxc_netdev_up(const char *name); extern int lxc_netdev_down(const char *name); -/* - * Change the mtu size for the specified device - */ +/* Change the mtu size for the specified device. */ extern int lxc_netdev_set_mtu(const char *name, int mtu); -/* - * Create a virtual network devices - */ +/* Create a virtual network devices. */ extern int lxc_veth_create(const char *name1, const char *name2); extern int lxc_macvlan_create(const char *master, const char *name, int mode); -extern int lxc_vlan_create(const char *master, const char *name, unsigned short vid); +extern int lxc_vlan_create(const char *master, const char *name, + unsigned short vid); -/* - * Activate forwarding - */ +/* Activate forwarding.*/ extern int lxc_ip_forward_on(const char *name, int family); -/* - * Disable forwarding - */ +/* Disable forwarding. */ extern int lxc_ip_forward_off(const char *name, int family); -/* - * Set ip address - */ +/* Set ip address. */ extern int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, struct in6_addr *mcast, struct in6_addr *acast, int prefix); @@ -88,57 +221,63 @@ extern int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, struct in_addr *bcast, int prefix); -/* - * Get ip address - */ +/* Get ip address. */ extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res); extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res); -/* - * Set a destination route to an interface - */ +/* Set a destination route to an interface. */ extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest); extern int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest); -/* - * Set default route. - */ +/* Set default route. */ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); -/* - * Attach an interface to the bridge - */ -extern int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge, const char *ifname); +/* Attach an interface to the bridge. */ +extern int lxc_bridge_attach(const char *bridge, const char *ifname); +extern int lxc_ovs_delete_port(const char *bridge, const char *nic); -/* - * Create default gateway - */ +extern bool is_ovs_bridge(const char *bridge); + +/* Create default gateway. */ extern int lxc_route_create_default(const char *addr, const char *ifname, int gateway); -/* - * Delete default gateway - */ +/* Delete default gateway. */ extern int lxc_route_delete_default(const char *addr, const char *ifname, int gateway); -/* - * Activate neighbor proxying - */ +/* Activate neighbor proxying. */ extern int lxc_neigh_proxy_on(const char *name, int family); -/* - * Disable neighbor proxying - */ +/* Disable neighbor proxying. */ extern int lxc_neigh_proxy_off(const char *name, int family); -/* - * Generate a new unique network interface name +/* Generate a new unique network interface name. + * Allocated memory must be freed by caller. */ extern char *lxc_mkifname(char *template); extern const char *lxc_net_type_to_str(int type); extern int setup_private_host_hw_addr(char *veth1); extern int netdev_get_mtu(int ifindex); -#endif +extern int lxc_create_network_priv(struct lxc_handler *handler); +extern int lxc_network_move_created_netdev_priv(const char *lxcpath, + char *lxcname, + struct lxc_list *network, + pid_t pid); +extern bool lxc_delete_network_priv(struct lxc_handler *handler); +extern bool lxc_delete_network_unpriv(struct lxc_handler *handler); +extern int lxc_find_gateway_addresses(struct lxc_handler *handler); +extern int lxc_create_network_unpriv(const char *lxcpath, char *lxcname, + struct lxc_list *network, pid_t pid); +extern int lxc_requests_empty_network(struct lxc_handler *handler); +extern int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler); +extern int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, + struct lxc_list *network); +extern int lxc_network_send_veth_names_to_child(struct lxc_handler *handler); +extern int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler); +extern int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler); +extern int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler); + +#endif /* __LXC_NETWORK_H */ diff -Nru lxc-2.0.8/src/lxc/nl.c lxc-2.1.0/src/lxc/nl.c --- lxc-2.0.8/src/lxc/nl.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/nl.c 2017-09-06 02:32:37.000000000 +0000 @@ -156,7 +156,7 @@ if (!nlmsg) return NULL; - // just set message length to cap directly + /* Just set message length to cap directly. */ nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap; return nlmsg; } @@ -178,14 +178,14 @@ .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; @@ -223,7 +223,7 @@ .msg_iovlen = 1, }; int ret; - + memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; diff -Nru lxc-2.0.8/src/lxc/parse.c lxc-2.1.0/src/lxc/parse.c --- lxc-2.0.8/src/lxc/parse.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/parse.c 2017-09-06 02:32:37.000000000 +0000 @@ -51,8 +51,9 @@ while (getline(&line, &len, f) != -1) { err = callback(line, data); if (err) { - // callback rv > 0 means stop here - // callback rv < 0 means error + /* Callback rv > 0 means stop here callback rv < 0 means + * error. + */ if (err < 0) ERROR("Failed to parse config: %s", line); break; diff -Nru lxc-2.0.8/src/lxc/rtnl.c lxc-2.1.0/src/lxc/rtnl.c --- lxc-2.0.8/src/lxc/rtnl.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/rtnl.c 2017-09-06 02:32:37.000000000 +0000 @@ -54,20 +54,24 @@ } extern int rtnetlink_transaction(struct rtnl_handler *handler, - struct rtnlmsg *request, struct rtnlmsg *answer) + struct rtnlmsg *request, + struct rtnlmsg *answer) { - return netlink_transaction(&handler->nlh, (struct nlmsg *)&request->nlmsghdr, + return netlink_transaction(&handler->nlh, + (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&answer->nlmsghdr); } extern struct rtnlmsg *rtnlmsg_alloc(size_t size) { -/* size_t len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtnlmsghdr))) + size; */ -/* return (struct rtnlmsg *)nlmsg_alloc(len); */ + /* + size_t len; + + len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtnlmsghdr))) + size; + return (struct rtnlmsg *)nlmsg_alloc(len); + */ + return NULL; } -extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg) -{ - free(rtnlmsg); -} +extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg) { free(rtnlmsg); } diff -Nru lxc-2.0.8/src/lxc/rtnl.h lxc-2.1.0/src/lxc/rtnl.h --- lxc-2.0.8/src/lxc/rtnl.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/rtnl.h 2017-09-06 02:32:37.000000000 +0000 @@ -20,6 +20,7 @@ * 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 @@ -35,8 +36,7 @@ * * @nlh: the netlink socket handler */ -struct rtnl_handler -{ +struct rtnl_handler { struct nl_handler nlh; }; @@ -59,7 +59,7 @@ * * Returns 0 on success, < 0 otherwise */ -int rtnetlink_open(struct rtnl_handler *handler); +extern int rtnetlink_open(struct rtnl_handler *handler); /* * genetlink_close : close a route netlink socket @@ -68,7 +68,7 @@ * * Returns 0 on success, < 0 otherwise */ -int rtnetlink_close(struct rtnl_handler *handler); +extern int rtnetlink_close(struct rtnl_handler *handler); /* * rtnetlink_rcv : receive a route netlink socket, it is up @@ -79,7 +79,7 @@ * * Returns 0 on success, < 0 otherwise */ -int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); +extern int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); /* * rtnetlink_send : send a route netlink socket, it is up @@ -90,11 +90,12 @@ * * Returns 0 on success, < 0 otherwise */ -int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); +extern int rtnetlink_send(struct rtnl_handler *handler, + struct rtnlmsg *rtnlmsg); struct genlmsg *genlmsg_alloc(size_t size); -void rtnlmsg_free(struct rtnlmsg *rtnlmsg); +extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg); /* * rtnetlink_transaction : send and receive a route netlink message in one shot @@ -105,6 +106,8 @@ * * Returns 0 on success, < 0 otherwise */ -int rtnetlink_transaction(struct rtnl_handler *handler, - struct rtnlmsg *request, struct rtnlmsg *answer); -#endif +extern int rtnetlink_transaction(struct rtnl_handler *handler, + struct rtnlmsg *request, + struct rtnlmsg *answer); + +#endif /* __LXC_RTNL_H */ diff -Nru lxc-2.0.8/src/lxc/seccomp.c lxc-2.1.0/src/lxc/seccomp.c --- lxc-2.0.8/src/lxc/seccomp.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/seccomp.c 2017-09-06 02:32:37.000000000 +0000 @@ -75,7 +75,7 @@ while (*line == ' ') line++; - // after 'whitelist' or 'blacklist' comes default behavior + /* 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) { @@ -92,6 +92,23 @@ return ret_action; } +static const char *get_action_name(uint32_t action) +{ + /* The upper 16 bits indicate the type of the seccomp action. */ + switch(action & 0xffff0000){ + case SCMP_ACT_KILL: + return "kill"; + case SCMP_ACT_ALLOW: + return "allow"; + case SCMP_ACT_TRAP: + return "trap"; + case SCMP_ACT_ERRNO(0): + return "errno"; + default: + return "invalid action"; + } +} + static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action) { char *p = strchr(line, ' '); @@ -153,7 +170,7 @@ } if (strcmp(uts.machine, "i686") == 0) return lxc_seccomp_arch_i386; - // no x32 kernels + /* no x32 kernels */ else if (strcmp(uts.machine, "x86_64") == 0) return lxc_seccomp_arch_amd64; else if (strncmp(uts.machine, "armv7", 5) == 0) @@ -217,7 +234,7 @@ return NULL; } if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) { - ERROR("Failed to turn off n-new-privs."); + ERROR("Failed to turn off no-new-privs."); seccomp_release(ctx); return NULL; } @@ -281,8 +298,8 @@ } 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)); + ERROR("Failed (%d) loading rule for %s (nr %d action %d(%s)): %s.", + ret, line, nr, action, get_action_name(action), strerror(-ret)); return false; } return true; @@ -398,7 +415,7 @@ return -1; } if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) { - ERROR("Failed to turn off n-new-privs."); + ERROR("Failed to turn off no-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP @@ -417,7 +434,7 @@ remove_trailing_newlines(line); INFO("processing: .%s.", line); if (line[0] == '[') { - // read the architecture for next set of rules + /* Read the architecture for next set of rules. */ if (strcmp(line, "[x86]") == 0 || strcmp(line, "[X86]") == 0) { if (native_arch != lxc_seccomp_arch_i386 && @@ -573,7 +590,8 @@ 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); + INFO("Adding native rule for %s action %d(%s).", line, action, + get_action_name(action)); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; } @@ -582,15 +600,18 @@ 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); + INFO("Adding compat-only rule for %s action %d(%s).", line, action, + get_action_name(action)); if (!do_resolve_add_rule(compat_arch[arch_index], line, compat_ctx[arch_index], action)) goto bad_rule; } else { - INFO("Adding native rule for %s action %d.", line, action); + INFO("Adding native rule for %s action %d(%s).", line, action, + get_action_name(action)); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; - INFO("Adding compat rule for %s action %d.", line, action); + INFO("Adding compat rule for %s action %d(%s).", line, action, + get_action_name(action)); if (!do_resolve_add_rule(compat_arch[0], line, compat_ctx[0], action)) goto bad_rule; if (compat_arch[1] != SCMP_ARCH_NATIVE && @@ -631,9 +652,9 @@ * 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). + * right now version must be '1' or '2' + * the directives must include 'whitelist'(version == 1 or 2) or 'blacklist' + * (version == 2) and can include 'debug' (though debug is not yet supported). */ static int parse_config(FILE *f, struct lxc_conf *conf) { @@ -735,7 +756,7 @@ 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."); + ERROR("Failed to turn off no-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP @@ -767,9 +788,21 @@ #endif ); if (ret < 0) { - ERROR("Error loading the seccomp policy."); + ERROR("Error loading the seccomp policy: %s.", strerror(-ret)); return -1; } + +/* After load seccomp filter into the kernel successfully, export the current seccomp + * filter to log file */ +#if HAVE_SCMP_FILTER_CTX + if ((lxc_log_get_level() <= LXC_LOG_LEVEL_TRACE || conf->loglevel <= LXC_LOG_LEVEL_TRACE) && + lxc_log_fd >= 0) { + ret = seccomp_export_pfc(conf->seccomp_ctx, lxc_log_fd); + /* Just give an warning when export error */ + if (ret < 0) + WARN("Failed to export seccomp filter to log file: %s.", strerror(-ret)); + } +#endif return 0; } diff -Nru lxc-2.0.8/src/lxc/start.c lxc-2.1.0/src/lxc/start.c --- lxc-2.0.8/src/lxc/start.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/start.c 2017-09-06 02:32:37.000000000 +0000 @@ -5,6 +5,8 @@ * * Authors: * Daniel Lezcano + * Serge Hallyn + * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,26 +52,38 @@ #include #endif -#if !HAVE_DECL_PR_CAPBSET_DROP +#ifndef HAVE_DECL_PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif +#ifndef HAVE_DECL_PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + +#ifndef HAVE_DECL_PR_GET_NO_NEW_PRIVS +#define PR_GET_NO_NEW_PRIVS 39 +#endif + #include "af_unix.h" -#include "bdev.h" #include "caps.h" #include "cgroup.h" #include "commands.h" +#include "commands_utils.h" #include "conf.h" +#include "confile_utils.h" #include "console.h" #include "error.h" #include "log.h" +#include "lxccontainer.h" #include "lxclock.h" #include "lxcseccomp.h" -#include "lxcutmp.h" #include "mainloop.h" #include "monitor.h" #include "namespace.h" +#include "network.h" #include "start.h" +#include "storage.h" +#include "storage_utils.h" #include "sync.h" #include "utils.h" #include "lsm/lsm.h" @@ -77,7 +91,7 @@ lxc_log_define(lxc_start, lxc); extern void mod_all_rdeps(struct lxc_container *c, bool inc); -static bool do_destroy_container(struct lxc_conf *conf); +static bool do_destroy_container(struct lxc_handler *handler); static int lxc_rmdir_onedev_wrapper(void *data); static void lxc_destroy_container_on_signal(struct lxc_handler *handler, const char *name); @@ -179,18 +193,12 @@ return (fd == 0 || fd == 1 || fd == 2); } -/* Check for any fds we need to close. - * - If fd_to_ignore != -1, then if we find that fd open we will ignore it. - * - By default we warn about open fds we find. - * - If closeall is true, we will close open fds. - * - If lxc-start was passed "-C", then conf->close_all_fds will be true, in - * which case we also close all open fds. - * - A daemonized container will always pass closeall=true. - */ -int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore) +int lxc_check_inherited(struct lxc_conf *conf, bool closeall, + int *fds_to_ignore, size_t len_fds) { struct dirent *direntp; int fd, fddir; + size_t i; DIR *dir; if (conf && conf->close_all_fds) @@ -199,7 +207,7 @@ restart: dir = opendir("/proc/self/fd"); if (!dir) { - WARN("Failed to open directory: %m."); + WARN("Failed to open directory: %s.", strerror(errno)); return -1; } @@ -220,7 +228,12 @@ continue; } - if (fd == fddir || fd == lxc_log_fd || fd == fd_to_ignore) + for (i = 0; i < len_fds; i++) + if (fds_to_ignore[i] == fd) + break; + + if (fd == fddir || fd == lxc_log_fd || + (i < len_fds && fd == fds_to_ignore[i])) continue; if (current_config && fd == current_config->logfd) @@ -238,6 +251,12 @@ WARN("Inherited fd: %d.", fd); } + /* Only enable syslog at this point to avoid the above logging function + * to open a new fd and make the check_inherited function enter an + * infinite loop. + */ + lxc_log_enable_syslog(); + closedir(dir); /* cannot fail */ return 0; } @@ -327,10 +346,123 @@ return 1; } -int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state) -{ +static int lxc_serve_state_clients(const char *name, + struct lxc_handler *handler, + lxc_state_t state) +{ + ssize_t ret; + struct lxc_list *cur, *next; + struct state_client *client; + struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; + + process_lock(); + + /* Only set state under process lock held so that we don't cause + * lxc_cmd_add_state_client() to miss a state. + */ handler->state = state; + TRACE("set container state to %s", lxc_state2str(state)); + + if (lxc_list_empty(&handler->state_clients)) { + TRACE("no state clients registered"); + process_unlock(); + lxc_monitor_send_state(name, state, handler->lxcpath); + return 0; + } + + strncpy(msg.name, name, sizeof(msg.name)); + msg.name[sizeof(msg.name) - 1] = 0; + + lxc_list_for_each_safe(cur, &handler->state_clients, next) { + client = cur->elem; + + if (!client->states[state]) { + TRACE("state %s not registered for state client %d", + lxc_state2str(state), client->clientfd); + continue; + } + + TRACE("sending state %s to state client %d", + lxc_state2str(state), client->clientfd); + + again: + ret = send(client->clientfd, &msg, sizeof(msg), 0); + if (ret <= 0) { + if (errno == EINTR) { + TRACE("Caught EINTR; retrying"); + goto again; + } + + ERROR("failed to send message to client"); + } + + /* kick client from list */ + close(client->clientfd); + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + process_unlock(); + + return 0; +} + +static int lxc_serve_state_socket_pair(const char *name, + struct lxc_handler *handler, + lxc_state_t state) +{ + ssize_t ret; + + if (!handler->backgrounded || + handler->state_socket_pair[1] < 0 || + state == STARTING) + return 0; + + /* Close read end of the socket pair. */ + close(handler->state_socket_pair[0]); + handler->state_socket_pair[0] = -1; + +again: + ret = lxc_abstract_unix_send_credential(handler->state_socket_pair[1], + &(int){state}, sizeof(int)); + if (ret != sizeof(int)) { + if (errno == EINTR) + goto again; + SYSERROR("Failed to send state to %d", + handler->state_socket_pair[1]); + return -1; + } + + TRACE("Sent container state \"%s\" to %d", lxc_state2str(state), + handler->state_socket_pair[1]); + + /* Close write end of the socket pair. */ + close(handler->state_socket_pair[1]); + handler->state_socket_pair[1] = -1; + + return 0; +} + +int lxc_set_state(const char *name, struct lxc_handler *handler, + lxc_state_t state) +{ + int ret; + + ret = lxc_serve_state_socket_pair(name, handler, state); + if (ret < 0) { + ERROR("Failed to synchronize via anonymous pair of unix sockets"); + return -1; + } + + ret = lxc_serve_state_clients(name, handler, state); + if (ret < 0) + return -1; + + /* This function will try to connect to the legacy lxc-monitord state + * server and only exists for backwards compatibility. + */ lxc_monitor_send_state(name, state, handler->lxcpath); + return 0; } @@ -360,16 +492,7 @@ goto out_mainloop_open; } - if (handler->conf->need_utmp_watch) { - #if HAVE_LIBCAP - if (lxc_utmp_mainloop_add(&descr, handler)) { - ERROR("Failed to add utmp handler to LXC mainloop."); - goto out_mainloop_open; - } - #else - DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support."); - #endif - } + TRACE("lxc mainloop is ready"); return lxc_mainloop(&descr, -1); @@ -382,46 +505,110 @@ return -1; } -struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char *lxcpath) +void lxc_free_handler(struct lxc_handler *handler) { - int i; + if (handler->conf && handler->conf->maincmd_fd) + close(handler->conf->maincmd_fd); + + if (handler->state_socket_pair[0] >= 0) + close(handler->state_socket_pair[0]); + + if (handler->state_socket_pair[1] >= 0) + close(handler->state_socket_pair[1]); + + if (handler->name) + free(handler->name); + + handler->conf = NULL; + free(handler); +} + +struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, + const char *lxcpath, bool daemonize) +{ + int i, ret; struct lxc_handler *handler; handler = malloc(sizeof(*handler)); - if (!handler) + if (!handler) { + ERROR("failed to allocate memory"); return NULL; + } memset(handler, 0, sizeof(*handler)); - handler->ttysock[0] = handler->ttysock[1] = -1; + /* Note that am_unpriv() checks the effective uid. We probably don't + * care if we are real root only if we are running as root so this + * should be fine. + */ + handler->am_root = !am_unpriv(); + handler->data_sock[0] = handler->data_sock[1] = -1; handler->conf = conf; handler->lxcpath = lxcpath; handler->pinfd = -1; + handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1; + lxc_list_init(&handler->state_clients); for (i = 0; i < LXC_NS_MAX; i++) handler->nsfd[i] = -1; - lsm_init(); - handler->name = strdup(name); if (!handler->name) { - ERROR("Failed to allocate memory."); - goto out_free; + ERROR("failed to allocate memory"); + goto on_error; + } + + if (daemonize && !handler->conf->reboot) { + /* Create socketpair() to synchronize on daemonized startup. + * When the container reboots we don't need to synchronize again + * currently so don't open another socketpair(). + */ + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, + handler->state_socket_pair); + if (ret < 0) { + ERROR("Failed to create anonymous pair of unix sockets"); + goto on_error; + } + TRACE("Created anonymous pair {%d,%d} of unix sockets", + handler->state_socket_pair[0], + handler->state_socket_pair[1]); + } + + if (lxc_cmd_init(name, handler, lxcpath)) { + ERROR("failed to set up command socket"); + goto on_error; } - if (lxc_cmd_init(name, handler, lxcpath)) - goto out_free_name; + TRACE("unix domain socket %d for command server is ready", + handler->conf->maincmd_fd); + + return handler; + +on_error: + lxc_free_handler(handler); + + return NULL; +} + +int lxc_init(const char *name, struct lxc_handler *handler) +{ + struct lxc_conf *conf = handler->conf; + + lsm_init(); + TRACE("initialized LSM"); if (lxc_read_seccomp_config(conf) != 0) { ERROR("Failed loading seccomp policy."); goto out_close_maincmd_fd; } + TRACE("read seccomp policy"); /* Begin by setting the state to STARTING. */ if (lxc_set_state(name, handler, STARTING)) { ERROR("Failed to set state for container \"%s\" to \"%s\".", name, lxc_state2str(STARTING)); goto out_close_maincmd_fd; } + TRACE("set container state to \"STARTING\""); /* Start of environment variable setup for hooks. */ if (name && setenv("LXC_NAME", name, 1)) @@ -446,10 +633,13 @@ SYSERROR("Failed to set environment variable LXC_CGNS_AWARE=1."); /* End of environment variable setup for hooks. */ + TRACE("set environment variables"); + if (run_lxc_hooks(name, "pre-start", conf, handler->lxcpath, NULL)) { ERROR("Failed to run lxc.hook.pre-start for container \"%s\".", name); goto out_aborting; } + TRACE("ran pre-start hooks"); /* The signal fd has to be created before forking otherwise if the child * process exits before we setup the signal fd, the event will be lost @@ -460,20 +650,23 @@ ERROR("Failed to setup SIGCHLD fd handler."); goto out_delete_tty; } + TRACE("set up signal fd"); /* Do this after setting up signals since it might unblock SIGWINCH. */ if (lxc_console_create(conf)) { ERROR("Failed to create console for container \"%s\".", name); goto out_restore_sigmask; } + TRACE("created console"); - if (ttys_shift_ids(conf) < 0) { + if (lxc_ttys_shift_ids(conf) < 0) { ERROR("Failed to shift tty into container."); goto out_restore_sigmask; } + TRACE("shifted tty ids"); - INFO("Container \"%s\" is initialized.", name); - return handler; + INFO("container \"%s\" is initialized", name); + return 0; out_restore_sigmask: sigprocmask(SIG_SETMASK, &handler->oldmask, NULL); @@ -484,19 +677,15 @@ out_close_maincmd_fd: close(conf->maincmd_fd); conf->maincmd_fd = -1; -out_free_name: - free(handler->name); - handler->name = NULL; -out_free: - free(handler); - return NULL; + return -1; } void lxc_fini(const char *name, struct lxc_handler *handler) { int i, rc; + struct lxc_list *cur, *next; pid_t self = getpid(); - char *namespaces[LXC_NS_MAX+1]; + char *namespaces[LXC_NS_MAX + 1]; size_t namespace_count = 0; /* The STOPPING state is there for future cleanup code which can take @@ -540,8 +729,14 @@ handler->netnsfd = -1; } + cgroup_destroy(handler); + lxc_set_state(name, handler, STOPPED); + /* close command socket */ + close(handler->conf->maincmd_fd); + handler->conf->maincmd_fd = -1; + if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) { ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name); if (handler->conf->reboot) { @@ -558,18 +753,28 @@ lxc_console_delete(&handler->conf->console); lxc_delete_tty(&handler->conf->tty_info); - close(handler->conf->maincmd_fd); - handler->conf->maincmd_fd = -1; - free(handler->name); - if (handler->ttysock[0] != -1) { - close(handler->ttysock[0]); - close(handler->ttysock[1]); + + /* The command socket is now closed, no more state clients can register + * themselves from now on. So free the list of state clients. + */ + lxc_list_for_each_safe(cur, &handler->state_clients, next) { + struct state_client *client = cur->elem; + /* close state client socket */ + close(client->clientfd); + lxc_list_del(cur); + free(cur->elem); + free(cur); + } + + if (handler->data_sock[0] != -1) { + close(handler->data_sock[0]); + close(handler->data_sock[1]); } if (handler->conf->ephemeral == 1 && handler->conf->reboot != 1) lxc_destroy_container_on_signal(handler, name); - cgroup_destroy(handler); + free(handler->name); free(handler); } @@ -585,120 +790,6 @@ } } -#include -#include - -/* reboot(LINUX_REBOOT_CMD_CAD_ON) will return -EINVAL in a child pid namespace - * if container reboot support exists. Otherwise, it will either succeed or - * return -EPERM. - */ -static int container_reboot_supported(void *arg) -{ - int *cmd = arg; - int ret; - - ret = reboot(*cmd); - if (ret == -1 && errno == EINVAL) - return 1; - return 0; -} - -static int must_drop_cap_sys_boot(struct lxc_conf *conf) -{ - FILE *f; - int ret, cmd, v, flags; - long stack_size = 4096; - void *stack = alloca(stack_size); - int status; - pid_t pid; - - f = fopen("/proc/sys/kernel/ctrl-alt-del", "r"); - if (!f) { - DEBUG("failed to open /proc/sys/kernel/ctrl-alt-del"); - return 1; - } - - ret = fscanf(f, "%d", &v); - fclose(f); - if (ret != 1) { - DEBUG("Failed to read /proc/sys/kernel/ctrl-alt-del."); - return 1; - } - cmd = v ? LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF; - - flags = CLONE_NEWPID | SIGCHLD; - if (!lxc_list_empty(&conf->id_map)) - flags |= CLONE_NEWUSER; - -#ifdef __ia64__ - pid = __clone2(container_reboot_supported, stack, stack_size, flags, &cmd); -#else - stack += stack_size; - pid = clone(container_reboot_supported, stack, flags, &cmd); -#endif - if (pid < 0) { - if (flags & CLONE_NEWUSER) - ERROR("Failed to clone (%#x): %s (includes CLONE_NEWUSER).", flags, strerror(errno)); - else - ERROR("Failed to clone (%#x): %s.", flags, strerror(errno)); - return -1; - } - if (wait(&status) < 0) { - SYSERROR("Unexpected wait error: %m."); - return -1; - } - - if (WEXITSTATUS(status) != 1) - return 1; - - return 0; -} - -/* netpipe is used in the unprivileged case to transfer the ifindexes from - * parent to child - */ -static int netpipe = -1; - -static inline int count_veths(struct lxc_list *network) -{ - struct lxc_list *iterator; - struct lxc_netdev *netdev; - int count = 0; - - lxc_list_for_each(iterator, network) { - netdev = iterator->elem; - if (netdev->type != LXC_NET_VETH) - continue; - count++; - } - return count; -} - -static int read_unpriv_netifindex(struct lxc_list *network) -{ - struct lxc_list *iterator; - struct lxc_netdev *netdev; - - if (netpipe == -1) - return 0; - lxc_list_for_each(iterator, network) { - netdev = iterator->elem; - if (netdev->type != LXC_NET_VETH) - continue; - if (!(netdev->name = malloc(IFNAMSIZ))) { - ERROR("Out of memory."); - close(netpipe); - return -1; - } - if (read(netpipe, netdev->name, IFNAMSIZ) != IFNAMSIZ) { - close(netpipe); - return -1; - } - } - close(netpipe); - return 0; -} - static int do_start(void *data) { struct lxc_list *iterator; @@ -751,8 +842,10 @@ if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE)) return -1; - if (read_unpriv_netifindex(&handler->conf->network) < 0) + if (lxc_network_recv_veth_names_from_parent(handler) < 0) { + ERROR("Failed to receive veth names from parent"); goto out_warn_father; + } /* If we are in a new user namespace, become root there to have * privilege over our namespace. @@ -773,16 +866,6 @@ goto out_warn_father; } - #if HAVE_LIBCAP - if (handler->conf->need_utmp_watch) { - if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) { - SYSERROR("Failed to remove the CAP_SYS_BOOT capability."); - goto out_warn_father; - } - DEBUG("Dropped the CAP_SYS_BOOT capability."); - } - #endif - ret = snprintf(path, sizeof(path), "%s/dev/null", handler->conf->rootfs.mount); if (ret < 0 || ret >= sizeof(path)) goto out_warn_father; @@ -806,12 +889,6 @@ "standard file descriptors. Migration will not work."); } - /* Setup the container, ip, names, utsname, ... */ - if (lxc_setup(handler)) { - ERROR("Failed to setup container \"%s\".", handler->name); - goto out_warn_father; - } - /* Ask father to setup cgroups and wait for him to finish. */ if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP)) goto out_error; @@ -836,18 +913,43 @@ INFO("Unshared CLONE_NEWCGROUP."); } + /* Setup the container, ip, names, utsname, ... */ + ret = lxc_setup(handler); + close(handler->data_sock[0]); + close(handler->data_sock[1]); + if (ret < 0) { + ERROR("Failed to setup container \"%s\".", handler->name); + goto out_warn_father; + } + /* Set the label to change to when we exec(2) the container's init. */ if (lsm_process_label_set(NULL, handler->conf, 1, 1) < 0) goto out_warn_father; + /* Set PR_SET_NO_NEW_PRIVS after we changed the lsm label. If we do it + * before we aren't allowed anymore. + */ + if (handler->conf->no_new_privs) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + SYSERROR("Could not set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges."); + goto out_warn_father; + } + DEBUG("Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges."); + } + /* Some init's such as busybox will set sane tty settings on stdin, * stdout, stderr which it thinks is the console. We already set them * the way we wanted on the real terminal, and we want init to do its * setup on its console ie. the pty allocated in lxc_console_create() so * make sure that that pty is stdin,stdout,stderr. */ - if (lxc_console_set_stdfds(handler->conf->console.slave) < 0) - goto out_warn_father; + if (handler->conf->console.slave >= 0) + if (set_stdfds(handler->conf->console.slave) < 0) { + ERROR("Failed to redirect std{in,out,err} to pty file " + "descriptor %d", + handler->conf->console.slave); + goto out_warn_father; + } /* If we mounted a temporary proc, then unmount it now. */ tmp_proc_unmount(handler->conf); @@ -925,8 +1027,12 @@ goto out_warn_father; } - if (handler->backgrounded && set_stdfds(devnull_fd)) - goto out_warn_father; + if (handler->conf->console.slave < 0 && handler->backgrounded) + if (set_stdfds(devnull_fd) < 0) { + ERROR("Failed to redirect std{in,out,err} to " + "\"/dev/null\""); + goto out_warn_father; + } if (devnull_fd >= 0) { close(devnull_fd); @@ -935,6 +1041,9 @@ setsid(); + if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP_LIMITS)) + goto out_warn_father; + /* After this call, we are in error because this ops should not return * as it execs. */ @@ -953,51 +1062,13 @@ return -1; } -static int save_phys_nics(struct lxc_conf *conf) -{ - struct lxc_list *iterator; - int am_root = (getuid() == 0); - - if (!am_root) - return 0; - - lxc_list_for_each(iterator, &conf->network) { - struct lxc_netdev *netdev = iterator->elem; - - if (netdev->type != LXC_NET_PHYS) - continue; - conf->saved_nics = realloc(conf->saved_nics, - (conf->num_savednics+1)*sizeof(struct saved_nic)); - if (!conf->saved_nics) - return -1; - conf->saved_nics[conf->num_savednics].ifindex = netdev->ifindex; - conf->saved_nics[conf->num_savednics].orig_name = strdup(netdev->link); - if (!conf->saved_nics[conf->num_savednics].orig_name) - return -1; - INFO("Stored saved_nic #%d idx %d name %s.", conf->num_savednics, - conf->saved_nics[conf->num_savednics].ifindex, - conf->saved_nics[conf->num_savednics].orig_name); - conf->num_savednics++; - } - - return 0; -} - -static int recv_fd(int sock, int *fd) -{ - if (lxc_abstract_unix_recv_fd(sock, fd, NULL, 0) < 0) { - SYSERROR("Error receiving tty file descriptor from child process."); - return -1; - } - if (*fd == -1) - return -1; - return 0; -} - -static int recv_ttys_from_child(struct lxc_handler *handler) +static int lxc_recv_ttys_from_child(struct lxc_handler *handler) { + int i; + struct lxc_pty_info *pty_info; + int ret = -1; + int sock = handler->data_sock[1]; struct lxc_conf *conf = handler->conf; - int i, sock = handler->ttysock[1]; struct lxc_tty_info *tty_info = &conf->tty_info; if (!conf->tty) @@ -1008,17 +1079,28 @@ return -1; for (i = 0; i < conf->tty; i++) { - struct lxc_pty_info *pty_info = &tty_info->pty_info[i]; + int ttyfds[2]; + + ret = lxc_abstract_unix_recv_fds(sock, ttyfds, 2, NULL, 0); + if (ret < 0) + break; + + pty_info = &tty_info->pty_info[i]; pty_info->busy = 0; - if (recv_fd(sock, &pty_info->slave) < 0 || - recv_fd(sock, &pty_info->master) < 0) { - ERROR("Error receiving tty info from child process."); - return -1; - } - } + pty_info->master = ttyfds[0]; + pty_info->slave = ttyfds[1]; + TRACE("Received pty with master fd %d and slave fd %d from " + "parent", pty_info->master, pty_info->slave); + } + if (ret < 0) + ERROR("Failed to receive %d ttys from child: %s", conf->tty, + strerror(errno)); + else + TRACE("Received %d ttys from child", conf->tty); + tty_info->nbtty = conf->tty; - return 0; + return ret; } void resolve_clone_flags(struct lxc_handler *handler) @@ -1055,14 +1137,16 @@ */ static int lxc_spawn(struct lxc_handler *handler) { - int failed_before_rename = 0; + int i, flags, ret; const char *name = handler->name; - bool cgroups_connected = false; + bool wants_to_map_ids; int saved_ns_fd[LXC_NS_MAX]; - int preserve_mask = 0, i, flags; - int netpipepair[2], nveths; + struct lxc_list *id_map; + int failed_before_rename = 0, preserve_mask = 0; + bool cgroups_connected = false; - netpipe = -1; + id_map = &handler->conf->id_map; + wants_to_map_ids = !lxc_list_empty(id_map); for (i = 0; i < LXC_NS_MAX; i++) if (handler->conf->inherit_ns_fd[i] != -1) @@ -1071,7 +1155,9 @@ if (lxc_sync_init(handler)) return -1; - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, handler->ttysock) < 0) { + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, + handler->data_sock); + if (ret < 0) { lxc_sync_fini(handler); return -1; } @@ -1095,17 +1181,12 @@ /* That should be done before the clone because we will * fill the netdev index and use them in the child. */ - if (lxc_create_network(handler)) { + if (lxc_create_network_priv(handler)) { ERROR("Failed to create the network."); lxc_sync_fini(handler); return -1; } } - - if (save_phys_nics(handler->conf)) { - ERROR("Failed to save physical nic info."); - goto out_abort; - } } if (!cgroup_init(handler)) { @@ -1124,7 +1205,7 @@ * it readonly. * If the container is unprivileged then skip rootfs pinning. */ - if (lxc_list_empty(&handler->conf->id_map)) { + if (!wants_to_map_ids) { handler->pinfd = pin_rootfs(handler->conf->rootfs.path); if (handler->pinfd == -1) INFO("Failed to pin the rootfs for container \"%s\".", handler->name); @@ -1136,15 +1217,6 @@ if (attach_ns(handler->conf->inherit_ns_fd) < 0) goto out_delete_net; - if (am_unpriv() && (nveths = count_veths(&handler->conf->network))) { - if (pipe(netpipepair) < 0) { - SYSERROR("Failed to create pipe."); - goto out_delete_net; - } - /* Store netpipe in the global var for do_start's use. */ - netpipe = netpipepair[0]; - } - /* Create a process in a new set of namespaces. */ flags = handler->clone_flags; if (handler->clone_flags & CLONE_NEWUSER) { @@ -1160,6 +1232,7 @@ SYSERROR("Failed to clone a new set of namespaces."); goto out_delete_net; } + for (i = 0; i < LXC_NS_MAX; i++) if (flags & ns_info[i].clone_flag) INFO("Cloned %s.", ns_info[i].flag_name); @@ -1178,7 +1251,7 @@ * mapped to something else on the host.) later to become a valid uid * again. */ - if (lxc_map_ids(&handler->conf->id_map, handler->pid)) { + if (wants_to_map_ids && lxc_map_ids(id_map, handler->pid)) { ERROR("Failed to set up id mapping."); goto out_delete_net; } @@ -1211,30 +1284,33 @@ if (failed_before_rename) goto out_delete_net; + handler->netnsfd = lxc_preserve_ns(handler->pid, "net"); + if (handler->netnsfd < 0) { + ERROR("Failed to preserve network namespace"); + goto out_delete_net; + } + /* Create the network configuration. */ if (handler->clone_flags & CLONE_NEWNET) { - if (lxc_assign_network(handler->lxcpath, handler->name, - &handler->conf->network, handler->pid)) { + if (lxc_network_move_created_netdev_priv(handler->lxcpath, + handler->name, + &handler->conf->network, + handler->pid)) { ERROR("Failed to create the configured network."); goto out_delete_net; } - } - if (netpipe != -1) { - struct lxc_list *iterator; - struct lxc_netdev *netdev; - - close(netpipe); - lxc_list_for_each(iterator, &handler->conf->network) { - netdev = iterator->elem; - if (netdev->type != LXC_NET_VETH) - continue; - if (write(netpipepair[1], netdev->name, IFNAMSIZ) != IFNAMSIZ) { - ERROR("Error writing veth name to container."); - goto out_delete_net; - } + if (lxc_create_network_unpriv(handler->lxcpath, handler->name, + &handler->conf->network, + handler->pid)) { + ERROR("Failed to create the configured network."); + goto out_delete_net; } - close(netpipepair[1]); + } + + if (lxc_network_send_veth_names_to_child(handler) < 0) { + ERROR("Failed to send veth names to child"); + goto out_delete_net; } /* Tell the child to continue its initialization. We'll get @@ -1243,20 +1319,23 @@ if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE)) goto out_delete_net; + if (!lxc_list_empty(&handler->conf->limits) && setup_resource_limits(&handler->conf->limits, handler->pid)) { + ERROR("failed to setup resource limits for '%s'", name); + goto out_delete_net; + } + + if (lxc_sync_barrier_child(handler, LXC_SYNC_CGROUP_UNSHARE)) + goto out_delete_net; + if (!cgroup_setup_limits(handler, true)) { ERROR("Failed to setup the devices cgroup for container \"%s\".", name); goto out_delete_net; } + TRACE("Set up cgroup device limits"); cgroup_disconnect(); cgroups_connected = false; - /* Read tty fds allocated by child. */ - if (recv_ttys_from_child(handler) < 0) { - ERROR("Failed to receive tty info from child process."); - goto out_delete_net; - } - /* Tell the child to complete its initialization and wait for it to exec * or return an error. (The child will never return * LXC_SYNC_POST_CGROUP+1. It will either close the sync pipe, causing @@ -1266,6 +1345,25 @@ if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP)) return -1; + if (lxc_network_recv_name_and_ifindex_from_child(handler) < 0) { + ERROR("Failed to receive names and ifindices for network " + "devices from child"); + goto out_delete_net; + } + + /* Now all networks are created, network devices are moved into place, + * and the correct names and ifindeces in the respective namespaces have + * been recorded. The corresponding structs have now all been filled. So + * log them for debugging purposes. + */ + lxc_log_configured_netdevs(handler->conf); + + /* Read tty fds allocated by child. */ + if (lxc_recv_ttys_from_child(handler) < 0) { + ERROR("Failed to receive tty info from child process."); + goto out_delete_net; + } + if (handler->ops->post_start(handler, handler->data)) goto out_abort; @@ -1276,15 +1374,22 @@ } lxc_sync_fini(handler); - handler->netnsfd = lxc_preserve_ns(handler->pid, "net"); return 0; out_delete_net: if (cgroups_connected) cgroup_disconnect(); - if (handler->clone_flags & CLONE_NEWNET) - lxc_delete_network(handler); + + if (handler->clone_flags & CLONE_NEWNET) { + DEBUG("Tearing down network devices"); + if (!lxc_delete_network_priv(handler)) + DEBUG("Failed tearing down network devices"); + + if (!lxc_delete_network_unpriv(handler)) + DEBUG("Failed tearing down network devices"); + } + out_abort: lxc_abort(name, handler); lxc_sync_fini(handler); @@ -1293,20 +1398,23 @@ handler->pinfd = -1; } + if (handler->netnsfd >= 0) { + close(handler->netnsfd); + handler->netnsfd = -1; + } + return -1; } -int __lxc_start(const char *name, struct lxc_conf *conf, +int __lxc_start(const char *name, struct lxc_handler *handler, struct lxc_operations* ops, void *data, const char *lxcpath, bool backgrounded) { - struct lxc_handler *handler; - int err = -1; int status; - bool removed_all_netdevs = true; + int err = -1; + struct lxc_conf *conf = handler->conf; - handler = lxc_init(name, conf, lxcpath); - if (!handler) { + if (lxc_init(name, handler) < 0) { ERROR("Failed to initialize container \"%s\".", name); return -1; } @@ -1315,17 +1423,6 @@ handler->backgrounded = backgrounded; handler->netnsfd = -1; - if (must_drop_cap_sys_boot(handler->conf)) { - #if HAVE_LIBCAP - DEBUG("Dropping CAP_SYS_BOOT capability."); - #else - DEBUG("Not dropping CAP_SYS_BOOT capability as capabilities aren't supported."); - #endif - } else { - DEBUG("Not dropping CAP_SYS_BOOT or watching utmp."); - handler->conf->need_utmp_watch = 0; - } - if (!attach_block_device(handler->conf)) { ERROR("Failed to attach block device."); goto out_fini_nonet; @@ -1360,10 +1457,6 @@ err = lxc_poll(name, handler); if (err) { ERROR("LXC mainloop exited with error: %d.", err); - if (handler->netnsfd >= 0) { - close(handler->netnsfd); - handler->netnsfd = -1; - } goto out_abort; } @@ -1392,11 +1485,10 @@ } } - DEBUG("Pushing physical nics back to host namespace"); - lxc_restore_phys_nics_to_netns(handler->netnsfd, handler->conf); - - DEBUG("Tearing down virtual network devices used by container \"%s\".", name); - removed_all_netdevs = lxc_delete_network(handler); + err = lxc_restore_phys_nics_to_netns(handler); + if (err < 0) + ERROR("Failed to move physical network devices back to parent " + "network namespace"); if (handler->pinfd >= 0) { close(handler->pinfd); @@ -1405,12 +1497,18 @@ lxc_monitor_send_exit_code(name, status, handler->lxcpath); err = lxc_error_set_and_log(handler->pid, status); + out_fini: - if (!removed_all_netdevs) { - DEBUG("Failed tearing down network devices used by container. Trying again!"); - removed_all_netdevs = lxc_delete_network(handler); - if (!removed_all_netdevs) - DEBUG("Failed tearing down network devices used by container. Not trying again!"); + DEBUG("Tearing down network devices"); + if (!lxc_delete_network_priv(handler)) + DEBUG("Failed tearing down network devices"); + + if (!lxc_delete_network_unpriv(handler)) + DEBUG("Failed tearing down network devices"); + + if (handler->netnsfd >= 0) { + close(handler->netnsfd); + handler->netnsfd = -1; } out_detach_blockdev: @@ -1453,15 +1551,14 @@ .post_start = post_start }; -int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf, +int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler, const char *lxcpath, bool backgrounded) { struct start_args start_arg = { .argv = argv, }; - conf->need_utmp_watch = 1; - return __lxc_start(name, conf, &start_ops, &start_arg, lxcpath, backgrounded); + return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded); } static void lxc_destroy_container_on_signal(struct lxc_handler *handler, @@ -1472,7 +1569,7 @@ int ret = 0; struct lxc_container *c; if (handler->conf->rootfs.path && handler->conf->rootfs.mount) { - bret = do_destroy_container(handler->conf); + bret = do_destroy_container(handler); if (!bret) { ERROR("Error destroying rootfs for container \"%s\".", name); return; @@ -1498,8 +1595,9 @@ } } - if (am_unpriv()) - ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper, destroy); + if (!handler->am_root) + ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper, + destroy, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(destroy, NULL); @@ -1516,11 +1614,14 @@ return lxc_rmdir_onedev(arg, NULL); } -static bool do_destroy_container(struct lxc_conf *conf) { - if (am_unpriv()) { - if (userns_exec_1(conf, bdev_destroy_wrapper, conf) < 0) +static bool do_destroy_container(struct lxc_handler *handler) { + if (!handler->am_root) { + if (userns_exec_1(handler->conf, storage_destroy_wrapper, + handler->conf, "storage_destroy_wrapper") < 0) return false; + return true; } - return bdev_destroy(conf); + + return storage_destroy(handler->conf); } diff -Nru lxc-2.0.8/src/lxc/start.h lxc-2.1.0/src/lxc/start.h --- lxc-2.0.8/src/lxc/start.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/start.h 2017-09-06 02:32:37.000000000 +0000 @@ -5,6 +5,8 @@ * * Authors: * Daniel Lezcano + * Serge Hallyn + * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,54 +26,120 @@ #define __LXC_START_H #include -#include #include +#include +#include +#include +#include "conf.h" #include "config.h" -#include "state.h" #include "namespace.h" +#include "state.h" -struct lxc_conf; +struct lxc_handler { + /* The clone flags that were requested. */ + int clone_flags; -struct lxc_handler; + /* File descriptors referring to the network namespace of the container. */ + int netnsfd; -struct lxc_operations { - int (*start)(struct lxc_handler *, void *); - int (*post_start)(struct lxc_handler *, void *); -}; + /* File descriptor to pin the rootfs for privileged containers. */ + int pinfd; -struct cgroup_desc; + /* Signal file descriptor. */ + int sigfd; -struct lxc_handler { - pid_t pid; + /* List of file descriptors referring to the namespaces of the + * container. Note that these are not necessarily identical to + * the "clone_flags" handler field in case namespace inheritance is + * requested. + */ + int nsfd[LXC_NS_MAX]; + + /* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data + * between child and parent. + */ + int data_sock[2]; + + /* The socketpair() fds used to wait on successful daemonized startup. */ + int state_socket_pair[2]; + + /* Socketpair to synchronize processes during container creation. */ + int sync_sock[2]; + + /* The name of the container. */ char *name; - lxc_state_t state; - int clone_flags; - int sigfd; + + /* The path the container is running in. */ + const char *lxcpath; + + /* Whether the container's startup process euid is 0. */ + bool am_root; + + /* Indicates whether should we close std{in,out,err} on start. */ + bool backgrounded; + + /* The child's pid. */ + pid_t pid; + + /* The signal mask prior to setting up the signal file descriptor. */ sigset_t oldmask; + + /* The container's in-memory configuration. */ struct lxc_conf *conf; + + /* A list of clients registered to be informed about a container state. */ + struct lxc_list state_clients; + + /* A set of operations to be performed at various stages of the + * container's life. + */ struct lxc_operations *ops; - void *data; - int sv[2]; - int pinfd; - const char *lxcpath; + + /* This holds the cgroup information. Note that the data here is + * specific to the cgroup driver used. + */ void *cgroup_data; - int ttysock[2]; // socketpair for child->parent tty fd passing - bool backgrounded; // indicates whether should we close std{in,out,err} on start - int nsfd[LXC_NS_MAX]; - int netnsfd; + + /* Data to be passed to handler ops. */ + void *data; + + /* Current state of the container. */ + lxc_state_t state; +}; + +struct lxc_operations { + int (*start)(struct lxc_handler *, void *); + int (*post_start)(struct lxc_handler *, void *); }; +struct state_client { + int clientfd; + lxc_state_t states[MAX_STATE]; +}; extern int lxc_poll(const char *name, struct lxc_handler *handler); extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state); extern void lxc_abort(const char *name, struct lxc_handler *handler); -extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const char *); +extern struct lxc_handler *lxc_init_handler(const char *name, + struct lxc_conf *conf, + const char *lxcpath, + bool daemonize); +extern void lxc_free_handler(struct lxc_handler *handler); +extern int lxc_init(const char *name, struct lxc_handler *handler); extern void lxc_fini(const char *name, struct lxc_handler *handler); -extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore); -int __lxc_start(const char *, struct lxc_conf *, struct lxc_operations *, - void *, const char *, bool); +/* lxc_check_inherited: Check for any open file descriptors and close them if + * requested. + * @param[in] conf The container's configuration. + * @param[in] closeall Whether we should close all open file descriptors. + * @param[in] fds_to_ignore Array of file descriptors to ignore. + * @param[in] len_fds Length of fds_to_ignore array. + */ +extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, + int *fds_to_ignore, size_t len_fds); +extern int __lxc_start(const char *, struct lxc_handler *, + struct lxc_operations *, void *, const char *, bool); extern void resolve_clone_flags(struct lxc_handler *handler); #endif diff -Nru lxc-2.0.8/src/lxc/state.c lxc-2.1.0/src/lxc/state.c --- lxc-2.0.8/src/lxc/state.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/state.c 2017-09-06 02:32:37.000000000 +0000 @@ -36,6 +36,7 @@ #include "cgroup.h" #include "commands.h" +#include "commands_utils.h" #include "config.h" #include "log.h" #include "lxc.h" @@ -79,7 +80,7 @@ return state; } -static int fillwaitedstates(const char *strstates, int *states) +static int fillwaitedstates(const char *strstates, lxc_state_t *states) { char *token, *saveptr = NULL; char *strstates_dup = strdup(strstates); @@ -108,90 +109,21 @@ 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] = {0}, fd; + int state; + lxc_state_t s[MAX_STATE] = {0}; 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); + state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout); if (state < 0) { - goto out_close; - } else if ((state >= 0) && (s[state])) { - ret = 0; - goto out_close; + SYSERROR("failed to receive state from monitor"); + return -1; } - for (;;) { - int64_t elapsed_time, curtime = 0; - struct timespec tspec; - int stop = 0; - int retval; - - if (timeout != -1) { - retval = clock_gettime(CLOCK_REALTIME, &tspec); - if (retval) - goto out_close; - curtime = tspec.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 = clock_gettime(CLOCK_REALTIME, &tspec); - if (retval) - goto out_close; - elapsed_time = tspec.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) - 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; - } - } + TRACE("retrieved state of container %s", lxc_state2str(state)); + if (!s[state]) + return -1; -out_close: - lxc_monitor_close(fd); - return ret; + return 0; } diff -Nru lxc-2.0.8/src/lxc/state.h lxc-2.1.0/src/lxc/state.h --- lxc-2.0.8/src/lxc/state.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/state.h 2017-09-06 02:32:37.000000000 +0000 @@ -24,11 +24,17 @@ #define __LXC_STATE_H typedef enum { - STOPPED, STARTING, RUNNING, STOPPING, - ABORTING, FREEZING, FROZEN, THAWED, MAX_STATE, + STOPPED, + STARTING, + RUNNING, + STOPPING, + ABORTING, + FREEZING, + FROZEN, + THAWED, + MAX_STATE, } lxc_state_t; -extern int lxc_rmstate(const char *name); extern lxc_state_t lxc_getstate(const char *name, const char *lxcpath); extern lxc_state_t lxc_str2state(const char *state); diff -Nru lxc-2.0.8/src/lxc/storage/aufs.c lxc-2.1.0/src/lxc/storage/aufs.c --- lxc-2.0.8/src/lxc/storage/aufs.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/aufs.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,455 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "aufs.h" +#include "log.h" +#include "rsync.h" +#include "storage.h" +#include "utils.h" + +lxc_log_define(aufs, lxc); + +/* the bulk of this needs to become a common helper */ +extern char *dir_new_path(char *src, const char *oldname, const char *name, + const char *oldpath, const char *lxcpath); + +int lxc_rsync_delta(struct rsync_data_char *data) +{ + int ret; + + ret = lxc_switch_uid_gid(0, 0); + if (ret < 0) + return -1; + + ret = lxc_setgroups(0, NULL); + if (ret < 0) + return -1; + + ret = lxc_rsync_exec(data->src, data->dest); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\"", data->src, + data->dest); + return -1; + } + + return 0; +} + +int lxc_rsync_delta_wrapper(void *data) +{ + struct rsync_data_char *arg = data; + return lxc_rsync_delta(arg); +} + +int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + char cmd_output[MAXPATHLEN]; + + 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, lxc_rsync_delta_wrapper, + &rdata, "lxc_rsync_delta_wrapper"); + else + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_rsync_delta_wrapper, + (void *)&rdata); + if (ret) { + free(osrc); + free(ndelta); + ERROR("copying aufs delta"); + return -1; + } + len = strlen(nsrc) + strlen(ndelta) + 12; + new->src = malloc(len); + if (!new->src) { + free(osrc); + free(ndelta); + return -ENOMEM; + } + ret = snprintf(new->src, len, "aufs:%s:%s", nsrc, ndelta); + free(osrc); + free(ndelta); + if (ret < 0 || ret >= len) + return -ENOMEM; + } else { + ERROR("aufs clone of %s container is not yet supported", + orig->type); + // Note, supporting this will require aufs_mount supporting + // mounting of the underlay. No big deal, just needs to be done. + return -1; + } + + return 0; +} + +/* + * to say 'lxc-create -t ubuntu -n o1 -B aufs' means you want + * $lxcpath/$lxcname/rootfs to have the created container, while all + * changes after starting the container are written to + * $lxcpath/$lxcname/delta0 + */ +int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + char *delta; + int ret, len = strlen(dest), newlen; + + if (len < 8 || strcmp(dest+len-7, "/rootfs") != 0) + return -1; + + if (!(bdev->dest = strdup(dest))) { + ERROR("Out of memory"); + return -1; + } + + delta = alloca(strlen(dest)+1); + strcpy(delta, dest); + strcpy(delta+len-6, "delta0"); + + if (mkdir_p(delta, 0755) < 0) { + ERROR("Error creating %s", delta); + return -1; + } + + /* aufs:lower:upper */ + newlen = (2 * len) + strlen("aufs:") + 2; + bdev->src = malloc(newlen); + if (!bdev->src) { + ERROR("Out of memory"); + return -1; + } + ret = snprintf(bdev->src, newlen, "aufs:%s:%s", dest, delta); + if (ret < 0 || ret >= newlen) + return -1; + + if (mkdir_p(bdev->dest, 0755) < 0) { + ERROR("Error creating %s", bdev->dest); + return -1; + } + + return 0; +} + +int aufs_destroy(struct lxc_storage *orig) +{ + char *upper; + + if (strncmp(orig->src, "aufs:", 5) != 0) + return -22; + upper = strchr(orig->src + 5, ':'); + if (!upper) + return -22; + upper++; + return lxc_rmdir_onedev(upper, NULL); +} + +bool aufs_detect(const char *path) +{ + if (!strncmp(path, "aufs:", 5)) + return true; + + return false; +} + +int aufs_mount(struct lxc_storage *bdev) +{ + char *tmp, *options, *dup, *lower, *upper; + int len; + unsigned long mntflags; + char *mntdata; + int ret; + const char *xinopath = "/dev/shm/aufs.xino"; + + if (strcmp(bdev->type, "aufs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + + // separately mount it first + // mount -t aufs -obr=${upper}=rw:${lower}=ro lower dest + dup = alloca(strlen(bdev->src)+1); + strcpy(dup, bdev->src); + /* support multiple lower layers */ + if (!(lower = strstr(dup, ":/"))) + return -22; + lower++; + upper = lower; + while ((tmp = strstr(++upper, ":/"))) { + upper = tmp; + } + if (--upper == lower) + return -22; + *upper = '\0'; + upper++; + + if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { + free(mntdata); + return -22; + } + + // TODO We should check whether bdev->src is a blockdev, and if so + // but for now, only support aufs of a basic directory + + // AUFS does not work on top of certain filesystems like (XFS or Btrfs) + // so add xino=/dev/shm/aufs.xino parameter to mount options. + // The same xino option can be specified to multiple aufs mounts, and + // a xino file is not shared among multiple aufs mounts. + // + // see http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg02587.html + // http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05126.html + if (mntdata) { + len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,,xino=") + strlen(mntdata) + 1; + options = alloca(len); + ret = snprintf(options, len, "br=%s=rw:%s=ro,%s,xino=%s", upper, lower, mntdata, xinopath); + } + else { + len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,xino=") + 1; + options = alloca(len); + ret = snprintf(options, len, "br=%s=rw:%s=ro,xino=%s", upper, lower, xinopath); + } + + if (ret < 0 || ret >= len) { + free(mntdata); + return -1; + } + + ret = mount(lower, bdev->dest, "aufs", MS_MGC_VAL | mntflags, options); + if (ret < 0) + SYSERROR("aufs: error mounting %s onto %s options %s", + lower, bdev->dest, options); + else + INFO("aufs: mounted %s onto %s options %s", + lower, bdev->dest, options); + return ret; +} + +int aufs_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "aufs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen) +{ + char *rootfsdir = NULL; + char *s1 = NULL; + char *s2 = NULL; + char *s3 = NULL; + + if (!rootfs_path || !rootfslen) + return NULL; + + s1 = strdup(rootfs_path); + if (!s1) + return NULL; + + if ((s2 = strstr(s1, ":/"))) { + s2 = s2 + 1; + if ((s3 = strstr(s2, ":/"))) + *s3 = '\0'; + rootfsdir = strdup(s2); + if (!rootfsdir) { + free(s1); + return NULL; + } + } + + if (!rootfsdir) + rootfsdir = s1; + else + free(s1); + + *rootfslen = strlen(rootfsdir); + + return rootfsdir; +} + +int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, + const char *lxc_name, const char *lxc_path) +{ + char lxcpath[MAXPATHLEN]; + char *rootfs_path = NULL; + char *rootfsdir = NULL; + char *scratch = NULL; + char *tmp = NULL; + char *upperdir = NULL; + char **opts = NULL; + int fret = -1; + int ret = 0; + size_t arrlen = 0; + size_t i; + size_t len = 0; + size_t rootfslen = 0; + + /* When rootfs == NULL we have a container without a rootfs. */ + if (rootfs && rootfs->path) + rootfs_path = rootfs->path; + + opts = lxc_string_split(mntent->mnt_opts, ','); + if (opts) + arrlen = lxc_array_len((void **)opts); + else + goto err; + + for (i = 0; i < arrlen; i++) { + if (strstr(opts[i], "br=") && (strlen(opts[i]) > (len = strlen("br=")))) + tmp = opts[i] + len; + } + if (!tmp) + goto err; + + upperdir = strtok_r(tmp, ":=", &scratch); + if (!upperdir) + goto err; + + if (rootfs_path) { + ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + rootfsdir = aufs_get_rootfs(rootfs->path, &rootfslen); + if (!rootfsdir) + goto err; + } + + /* + * We neither allow users to create upperdirs and workdirs outside the + * containerdir nor inside the rootfs. The latter might be debatable. + * When we have a container without a rootfs we skip the checks. + */ + ret = 0; + if (!rootfs_path) + ret = mkdir_p(upperdir, 0755); + else if ((strncmp(upperdir, lxcpath, strlen(lxcpath)) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) + ret = mkdir_p(upperdir, 0755); + if (ret < 0) + WARN("Failed to create upperdir"); + + fret = 0; + +err: + free(rootfsdir); + lxc_free_array((void **)opts, free); + return fret; +} + diff -Nru lxc-2.0.8/src/lxc/storage/aufs.h lxc-2.1.0/src/lxc/storage/aufs.h --- lxc-2.0.8/src/lxc/storage/aufs.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/aufs.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_AUFS_H +#define __LXC_AUFS_H + +#define _GNU_SOURCE +#include +#include +#include + +#include "storage.h" + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +struct lxc_rootfs; + +int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf); +int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +int aufs_destroy(struct lxc_storage *orig); +bool aufs_detect(const char *path); +int aufs_mount(struct lxc_storage *bdev); +int aufs_umount(struct lxc_storage *bdev); + +/* Get rootfs path for aufs backed containers. Allocated memory must be freed by + * caller. + */ +char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen); + +/* + * Create directories for aufs mounts. + */ +int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, + const char *lxc_name, const char *lxc_path); + +#endif /* __LXC_AUFS_H */ diff -Nru lxc-2.0.8/src/lxc/storage/btrfs.c lxc-2.1.0/src/lxc/storage/btrfs.c --- lxc-2.0.8/src/lxc/storage/btrfs.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/btrfs.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,864 @@ +/* + * 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 "log.h" +#include "btrfs.h" +#include "rsync.h" +#include "storage.h" +#include "utils.h" + +lxc_log_define(btrfs, lxc); + +/* + * 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("Failed to lookup path for %llu %llu %s - %s\n", + (unsigned long long) dir_id, + (unsigned long long) objid, + name, strerror(e)); + return NULL; + } else + INFO("Got path for %llu %llu - %s\n", + (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; +} + +int btrfs_list_get_path_rootid(int fd, u64 *treeid) +{ + int ret; + struct btrfs_ioctl_ino_lookup_args args; + + memset(&args, 0, sizeof(args)); + args.objectid = BTRFS_FIRST_FREE_OBJECTID; + + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret < 0) { + WARN("Warning: can't perform the search -%s\n", + strerror(errno)); + return ret; + } + *treeid = args.treeid; + return 0; +} + +bool is_btrfs_fs(const char *path) +{ + int fd, ret; + struct btrfs_ioctl_space_args sargs; + + /* Make sure this is a btrfs filesystem. */ + fd = open(path, O_RDONLY); + if (fd < 0) + return false; + sargs.space_slots = 0; + sargs.total_spaces = 0; + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, &sargs); + close(fd); + if (ret < 0) + return false; + + return true; +} + +/* + * Taken from btrfs toolsuite. Test if path is a subvolume. + * return 0; path exists but it is not a subvolume + * return 1; path exists and it is a subvolume + * return < 0; error + */ +int is_btrfs_subvol(const char *path) +{ + struct stat st; + struct statfs stfs; + int ret; + + ret = stat(path, &st); + if (ret < 0) + return -errno; + + if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) + return 0; + + ret = statfs(path, &stfs); + if (ret < 0) + return -errno; + + return stfs.f_type == BTRFS_SUPER_MAGIC; +} + +bool btrfs_detect(const char *path) +{ + struct stat st; + int ret; + + if (!strncmp(path, "btrfs:", 6)) + return true; + + if (!is_btrfs_fs(path)) + return false; + + /* make sure it's a subvolume */ + ret = stat(path, &st); + if (ret < 0) + return false; + + if (st.st_ino == 256 && S_ISDIR(st.st_mode)) + return true; + + return false; +} + +int btrfs_mount(struct lxc_storage *bdev) +{ + unsigned long mntflags; + char *mntdata, *src; + 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; + } + + src = lxc_storage_get_path(bdev->src, "btrfs"); + + ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata); + free(mntdata); + return ret; +} + +int btrfs_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "btrfs")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + return umount(bdev->dest); +} + +static int btrfs_subvolume_create(const char *path) +{ + int ret, saved_errno; + struct btrfs_ioctl_vol_args args; + char *p, *newfull; + int fd = -1; + + newfull = strdup(path); + if (!newfull) { + errno = ENOMEM; + return -ENOMEM; + } + + p = strrchr(newfull, '/'); + if (!p) { + free(newfull); + errno = EINVAL; + return -EINVAL; + } + *p = '\0'; + + fd = open(newfull, O_RDONLY); + if (fd < 0) { + free(newfull); + return -errno; + } + + memset(&args, 0, sizeof(args)); + 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); + saved_errno = errno; + + close(fd); + free(newfull); + errno = saved_errno; + return ret; +} + +int btrfs_same_fs(const char *orig, const char *new) +{ + int fd_orig = -1, fd_new = -1, ret = -1; + struct btrfs_ioctl_fs_info_args orig_args, new_args; + + fd_orig = open(orig, O_RDONLY); + if (fd_orig < 0) { + SYSERROR("Error opening original rootfs %s", orig); + goto out; + } + ret = ioctl(fd_orig, BTRFS_IOC_FS_INFO, &orig_args); + if (ret < 0) { + SYSERROR("BTRFS_IOC_FS_INFO %s", orig); + goto out; + } + + fd_new = open(new, O_RDONLY); + if (fd_new < 0) { + SYSERROR("Error opening new container dir %s", new); + ret = -1; + goto out; + } + ret = ioctl(fd_new, BTRFS_IOC_FS_INFO, &new_args); + if (ret < 0) { + SYSERROR("BTRFS_IOC_FS_INFO %s", new); + goto out; + } + + if (strncmp(orig_args.fsid, new_args.fsid, BTRFS_FSID_SIZE) != 0) { + ret = -1; + goto out; + } + ret = 0; +out: + if (fd_new != -1) + close(fd_new); + if (fd_orig != -1) + close(fd_orig); + return ret; +} + +int btrfs_snapshot(const char *orig, const char *new) +{ + struct btrfs_ioctl_vol_args_v2 args; + char *newdir, *newname; + char *newfull = NULL; + int saved_errno = -1; + int fd = -1, fddst = -1, ret = -1; + + newfull = strdup(new); + if (!newfull) + goto out; + + ret = rmdir(newfull); + if (ret < 0 && errno != ENOENT) + goto out; + + newname = basename(newfull); + fd = open(orig, O_RDONLY); + if (fd < 0) + goto out; + + newdir = dirname(newfull); + fddst = open(newdir, O_RDONLY); + if (fddst < 0) + goto out; + + memset(&args, 0, sizeof(args)); + args.fd = fd; + 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); + saved_errno = errno; + +out: + if (fddst != -1) + close(fddst); + if (fd != -1) + close(fd); + free(newfull); + + if (saved_errno >= 0) + errno = saved_errno; + return ret; +} + +int btrfs_snapshot_wrapper(void *data) +{ + char *src; + 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; + } + + src = lxc_storage_get_path(arg->src, "btrfs"); + return btrfs_snapshot(src, arg->dest); +} + +int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf) +{ + char *src; + + if (!orig->dest || !orig->src) + return -1; + + if (strcmp(orig->type, "btrfs") && snap) { + ERROR("btrfs snapshot from %s backing store is not supported", + orig->type); + return -1; + } + + new->src = lxc_string_join( + "/", + (const char *[]){"btrfs:", *lxcpath != '/' ? lxcpath : ++lxcpath, + cname, "rootfs", NULL}, + false); + if (!new->src) { + ERROR("Failed to create new rootfs path"); + return -1; + } + TRACE("Constructed new rootfs path \"%s\"", new->src); + + src = lxc_storage_get_path(new->src, "btrfs"); + new->dest = strdup(src); + if (!new->dest) { + ERROR("Failed to duplicate string \"%s\"", src); + return -1; + } + + if (orig->mntopts) { + new->mntopts = strdup(orig->mntopts); + if (!new->mntopts) { + ERROR("Failed to duplicate string \"%s\"", + orig->mntopts); + return -1; + } + } + + return 0; +} + +bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + int ret; + struct rsync_data data = {0, 0}; + char cmd_output[MAXPATHLEN] = {0}; + + ret = rmdir(new->dest); + if (ret < 0 && errno != ENOENT) + return false; + + ret = btrfs_subvolume_create(new->dest); + if (ret < 0) { + SYSERROR("Failed to create btrfs subvolume \"%s\"", new->dest); + return false; + } + + /* rsync the contents from source to target */ + data.orig = orig; + data.new = new; + if (am_unpriv()) { + ret = userns_exec_1(conf, lxc_storage_rsync_exec_wrapper, &data, + "lxc_storage_rsync_exec_wrapper"); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\"", + orig->dest, new->dest); + return false; + } + + return true; + } + + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_storage_rsync_exec_wrapper, (void *)&data); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest, + new->dest, cmd_output); + return false; + } + + return true; +} + +bool btrfs_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + int ret; + + ret = rmdir(new->dest); + if (ret < 0 && errno != ENOENT) + return false; + + if (am_unpriv()) { + struct rsync_data_char args; + + args.src = orig->dest; + args.dest = new->dest; + + ret = userns_exec_1(conf, btrfs_snapshot_wrapper, &args, + "btrfs_snapshot_wrapper"); + if (ret < 0) { + ERROR("Failed to run \"btrfs_snapshot_wrapper\""); + return false; + } + + TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest, + orig->dest); + return true; + } + + ret = btrfs_snapshot(orig->dest, new->dest); + if (ret < 0) { + SYSERROR("Failed to create btrfs snapshot \"%s\" from \"%s\"", + new->dest, orig->dest); + return false; + } + + TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest, orig->dest); + return true; +} + +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; +} + +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; + u64 dir_id; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ERROR("Failed to open %s\n", path); + return -1; + } + + if (btrfs_list_get_path_rootid(fd, &root_id)) { + e = errno; + close(fd); + if (e == EPERM || e == EACCES) { + WARN("Will simply try removing"); + goto ignore_search; + } + + return -1; + } + + tree = create_my_btrfs_tree(root_id, path, strlen(path)); + if (!tree) { + ERROR("Out of memory\n"); + close(fd); + return -1; + } + /* Walk all subvols looking for any under this id */ + memset(&args, 0, sizeof(args)); + + /* search in the tree of tree roots */ + sk->tree_id = 1; + + sk->max_type = BTRFS_ROOT_REF_KEY; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->min_objectid = 0; + sk->max_objectid = (u64)-1; + sk->max_offset = (u64)-1; + sk->min_offset = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 4096; + + while(1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + e = errno; + if (ret < 0) { + close(fd); + free_btrfs_tree(tree); + if (e == EPERM || e == EACCES) { + WARN("Warn: can't perform the search under %s. Will simply try removing", path); + goto ignore_search; + } + + ERROR("Error: can't perform the search under %s\n", path); + return -1; + } + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + memcpy(&sh, args.buf + off, sizeof(sh)); + off += sizeof(sh); + /* + * A backref key with the name and dirid of the parent + * comes followed by the reoot ref key which has the + * name of the child subvol in question. + */ + if (sh.objectid != root_id && sh.type == BTRFS_ROOT_BACKREF_KEY) { + ref = (struct btrfs_root_ref *)(args.buf + off); + name_len = btrfs_stack_root_ref_name_len(ref); + name = (char *)(ref + 1); + dir_id = btrfs_stack_root_ref_dirid(ref); + tmppath = get_btrfs_subvol_path(fd, sh.offset, + dir_id, name, name_len); + if (!add_btrfs_tree_node(tree, sh.objectid, + sh.offset, name, + name_len, tmppath)) { + ERROR("Out of memory"); + free_btrfs_tree(tree); + free(tmppath); + close(fd); + return -1; + } + free(tmppath); + } + off += sh.len; + + /* + * record the mins in sk so we can make sure the + * next search doesn't repeat this root + */ + sk->min_objectid = sh.objectid; + sk->min_type = sh.type; + sk->min_offset = sh.offset; + } + sk->nr_items = 4096; + sk->min_offset++; + if (!sk->min_offset) + sk->min_type++; + else + continue; + + if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) { + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->min_objectid++; + } else + continue; + + if (sk->min_objectid >= sk->max_objectid) + break; + } + close(fd); + + /* now actually remove them */ + + if (!do_remove_btrfs_children(tree, root_id, path)) { + free_btrfs_tree(tree); + ERROR("failed pruning\n"); + return -1; + } + + free_btrfs_tree(tree); + /* All child subvols have been removed, now remove this one */ +ignore_search: + return btrfs_do_destroy_subvol(path); +} + +bool btrfs_try_remove_subvol(const char *path) +{ + if (!btrfs_detect(path)) + return false; + + return btrfs_recursive_destroy(path) == 0; +} + +int btrfs_destroy(struct lxc_storage *orig) +{ + char *src; + + src = lxc_storage_get_path(orig->src, "btrfs"); + + return btrfs_recursive_destroy(src); +} + +int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + int ret; + size_t len; + + len = strlen(dest) + 1; + /* strlen("btrfs:") */ + len += 6; + bdev->src = malloc(len); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len, "btrfs:%s", dest); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + ret = btrfs_subvolume_create(bdev->dest); + if (ret < 0) { + SYSERROR("Failed to create btrfs subvolume \"%s\"", bdev->dest); + } + + return ret; +} diff -Nru lxc-2.0.8/src/lxc/storage/btrfs.h lxc-2.1.0/src/lxc/storage/btrfs.h --- lxc-2.0.8/src/lxc/storage/btrfs.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/btrfs.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,413 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_BTRFS_H +#define __LXC_BTRFS_H + +#define _GNU_SOURCE +#include /* __le64, __l32 ... */ +#include +#include +#include + +#ifndef BTRFS_SUPER_MAGIC +# define BTRFS_SUPER_MAGIC 0x9123683E +#endif + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +struct btrfs_ioctl_space_info { + unsigned long long flags; + unsigned long long total_bytes; + unsigned long long used_bytes; +}; + +struct btrfs_ioctl_space_args { + unsigned long long space_slots; + unsigned long long total_spaces; + struct btrfs_ioctl_space_info spaces[]; +}; + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long) +#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ + struct btrfs_ioctl_space_args) + +#define BTRFS_FSID_SIZE 16 +struct btrfs_ioctl_fs_info_args { + unsigned long long max_id; + unsigned long long num_devices; + char fsid[BTRFS_FSID_SIZE]; + unsigned long long reserved[124]; +}; + +#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ + struct btrfs_ioctl_fs_info_args) + + +#define BTRFS_SUBVOL_NAME_MAX 4039 +#define BTRFS_PATH_NAME_MAX 4087 + +struct btrfs_ioctl_vol_args { + signed long long fd; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \ + struct btrfs_ioctl_vol_args_v2) +#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ + struct btrfs_ioctl_vol_args_v2) +#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ + struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ + struct btrfs_ioctl_vol_args) + +#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) + +struct btrfs_ioctl_vol_args_v2 { + signed long long fd; + unsigned long long transid; + unsigned long long flags; + union { + struct { + unsigned long long size; + /*struct btrfs_qgroup_inherit *qgroup_inherit; */ + void *qgroup_inherit; + }; + unsigned long long unused[4]; + }; + char name[BTRFS_SUBVOL_NAME_MAX + 1]; +}; + +/* + * root backrefs tie subvols and snapshots to the directory entries that + * reference them + */ +#define BTRFS_ROOT_BACKREF_KEY 144 + +/* + * root items point to tree roots. There are typically in the root + * tree used by the super block to find all the other trees + */ +#define BTRFS_ROOT_ITEM_KEY 132 + +/* + * root refs make a fast index for listing all of the snapshots and + * subvolumes referenced by a given root. They point directly to the + * directory item in the root that references the subvol + */ +#define BTRFS_ROOT_REF_KEY 156 + +#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL +#define BTRFS_DIR_ITEM_KEY 84 + +/* + * * this is used for both forward and backward root refs + * */ +struct btrfs_root_ref { + __le64 dirid; + __le64 sequence; + __le16 name_len; +} __attribute__ ((__packed__)); + +struct btrfs_disk_key { + __le64 objectid; + u8 type; + __le64 offset; +} __attribute__ ((__packed__)); + +struct btrfs_dir_item { + struct btrfs_disk_key location; + __le64 transid; + __le16 data_len; + __le16 name_len; + u8 type; +} __attribute__ ((__packed__)); + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_VOL_NAME_MAX 255 +#define BTRFS_PATH_NAME_MAX 4087 + +struct btrfs_ioctl_search_key { + /* which root are we searching. 0 is the tree of tree roots */ + __u64 tree_id; + + /* keys returned will be >= min and <= max */ + __u64 min_objectid; + __u64 max_objectid; + + /* keys returned will be >= min and <= max */ + __u64 min_offset; + __u64 max_offset; + + /* max and min transids to search for */ + __u64 min_transid; + __u64 max_transid; + + /* keys returned will be >= min and <= max */ + __u32 min_type; + __u32 max_type; + + /* + * how many items did userland ask for, and how many are we + * returning + */ + __u32 nr_items; + + /* align to 64 bits */ + __u32 unused; + + /* some extra for later */ + __u64 unused1; + __u64 unused2; + __u64 unused3; + __u64 unused4; +}; + +struct btrfs_ioctl_search_header { + __u64 transid; + __u64 objectid; + __u64 offset; + __u32 type; + __u32 len; +} __attribute__((may_alias)); + +#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) +/* + * the buf is an array of search headers where + * each header is followed by the actual item + * the type field is expanded to 32 bits for alignment + */ +struct btrfs_ioctl_search_args { + struct btrfs_ioctl_search_key key; + char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; +}; + +#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ + struct btrfs_ioctl_search_args) +#define BTRFS_UUID_SIZE 16 + +struct btrfs_timespec { + __le64 sec; + __le32 nsec; +} __attribute__ ((__packed__)); + +struct btrfs_inode_item { + /* nfs style generation number */ + __le64 generation; + /* transid that last touched this inode */ + __le64 transid; + __le64 size; + __le64 nbytes; + __le64 block_group; + __le32 nlink; + __le32 uid; + __le32 gid; + __le32 mode; + __le64 rdev; + __le64 flags; + + /* modification sequence number for NFS */ + __le64 sequence; + + /* + * a little future expansion, for more than this we can + * just grow the inode item and version it + */ + __le64 reserved[4]; + struct btrfs_timespec atime; + struct btrfs_timespec ctime; + struct btrfs_timespec mtime; + struct btrfs_timespec otime; +} __attribute__ ((__packed__)); + +struct btrfs_root_item_v0 { + struct btrfs_inode_item inode; + __le64 generation; + __le64 root_dirid; + __le64 bytenr; + __le64 byte_limit; + __le64 bytes_used; + __le64 last_snapshot; + __le64 flags; + __le32 refs; + struct btrfs_disk_key drop_progress; + u8 drop_level; + u8 level; +} __attribute__ ((__packed__)); + +struct btrfs_root_item { + struct btrfs_inode_item inode; + __le64 generation; + __le64 root_dirid; + __le64 bytenr; + __le64 byte_limit; + __le64 bytes_used; + __le64 last_snapshot; + __le64 flags; + __le32 refs; + struct btrfs_disk_key drop_progress; + u8 drop_level; + u8 level; + + /* + * The following fields appear after subvol_uuids+subvol_times + * were introduced. + */ + + /* + * This generation number is used to test if the new fields are valid + * and up to date while reading the root item. Every time the root item + * is written out, the "generation" field is copied into this field. If + * anyone ever mounted the fs with an older kernel, we will have + * mismatching generation values here and thus must invalidate the + * new fields. See btrfs_update_root and btrfs_find_last_root for + * details. + * the offset of generation_v2 is also used as the start for the memset + * when invalidating the fields. + */ + __le64 generation_v2; + u8 uuid[BTRFS_UUID_SIZE]; + u8 parent_uuid[BTRFS_UUID_SIZE]; + u8 received_uuid[BTRFS_UUID_SIZE]; + __le64 ctransid; /* updated when an inode changes */ + __le64 otransid; /* trans when created */ + __le64 stransid; /* trans when sent. non-zero for received subvol */ + __le64 rtransid; /* trans when received. non-zero for received subvol */ + struct btrfs_timespec ctime; + struct btrfs_timespec otime; + struct btrfs_timespec stime; + struct btrfs_timespec rtime; + __le64 reserved[8]; /* for future */ +} __attribute__ ((__packed__)); + +#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ + struct btrfs_ioctl_ino_lookup_args) + +#define BTRFS_INO_LOOKUP_PATH_MAX 4080 +struct btrfs_ioctl_ino_lookup_args { + __u64 treeid; + __u64 objectid; + char name[BTRFS_INO_LOOKUP_PATH_MAX]; +}; + +/* + * All files have objectids in this range. + */ +#define BTRFS_FIRST_FREE_OBJECTID 256ULL +#define BTRFS_LAST_FREE_OBJECTID -256ULL +#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL + +/* + * The followings are macro for correctly getting member of + * structures in both low and big endian platforms as per + * btrfs-progs + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#else +#define __force +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le64(x) ((__force __le64)(u64)(bswap_64(x))) +#define le64_to_cpu(x) ((__force u64)(__le64)(bswap_64(x))) +#define cpu_to_le32(x) ((__force __le32)(u32)(bswap_32(x))) +#define le32_to_cpu(x) ((__force u32)(__le32)(bswap_32(x))) +#define cpu_to_le16(x) ((__force __le16)(u16)(bswap_16(x))) +#define le16_to_cpu(x) ((__force u16)(__le16)(bswap_16(x))) +#else +#define cpu_to_le64(x) ((__force __le64)(u64)(x)) +#define le64_to_cpu(x) ((__force u64)(__le64)(x)) +#define cpu_to_le32(x) ((__force __le32)(u32)(x)) +#define le32_to_cpu(x) ((__force u32)(__le32)(x)) +#define cpu_to_le16(x) ((__force __le16)(u16)(x)) +#define le16_to_cpu(x) ((__force u16)(__le16)(x)) +#endif + +#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \ +static inline u##bits btrfs_##name(type *s) \ +{ \ + return le##bits##_to_cpu(s->member); \ +} \ +static inline void btrfs_set_##name(type *s, u##bits val) \ +{ \ + s->member = cpu_to_le##bits(val); \ +} + +/* defined as btrfs_stack_root_ref_dirid */ +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_dirid, struct btrfs_root_ref, dirid, 64); +/* defined as btrfs_stack_root_ref_sequence */ +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_sequence, struct btrfs_root_ref, sequence, 64); +/* defined as btrfs_stack_root_ref_name_len */ +BTRFS_SETGET_STACK_FUNCS(stack_root_ref_name_len, struct btrfs_root_ref, name_len, 16); + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +struct mytree_node { + u64 objid; + u64 parentid; + char *name; + char *dirname; +}; + +struct my_btrfs_tree { + struct mytree_node *nodes; + int num; +}; + +extern int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int btrfs_create(struct lxc_storage *bdev, const char *dest, + const char *n, struct bdev_specs *specs); +extern int btrfs_destroy(struct lxc_storage *orig); +extern bool btrfs_detect(const char *path); +extern int btrfs_mount(struct lxc_storage *bdev); +extern int btrfs_umount(struct lxc_storage *bdev); + +extern char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name, + int name_len); +extern int btrfs_list_get_path_rootid(int fd, u64 *treeid); +extern bool is_btrfs_fs(const char *path); +extern int is_btrfs_subvol(const char *path); +extern bool btrfs_try_remove_subvol(const char *path); +extern int btrfs_same_fs(const char *orig, const char *new); +extern int btrfs_snapshot(const char *orig, const char *new); +extern int btrfs_snapshot_wrapper(void *data); +extern bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); +extern bool btrfs_create_snapshot(struct lxc_conf *conf, + struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); + +#endif /* __LXC_BTRFS_H */ diff -Nru lxc-2.0.8/src/lxc/storage/dir.c lxc-2.1.0/src/lxc/storage/dir.c --- lxc-2.0.8/src/lxc/storage/dir.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/dir.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include + +#include "log.h" +#include "storage.h" +#include "utils.h" + +lxc_log_define(dir, lxc); + +/* For a simple directory bind mount, we substitute the old container name and + * paths for the new. + */ +int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + char *src_no_prefix; + int ret; + size_t len; + + if (snap) { + ERROR("Directories cannot be snapshotted"); + return -1; + } + + if (!orig->dest || !orig->src) + return -1; + + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 4 + 3; + new->src = malloc(len); + if (!new->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(new->src, len, "dir:%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + src_no_prefix = lxc_storage_get_path(new->src, new->type); + new->dest = strdup(src_no_prefix); + if (!new->dest) { + ERROR("Failed to duplicate string \"%s\"", new->src); + return -1; + } + + TRACE("Created new path \"%s\" for dir storage driver", new->dest); + return 0; +} + +int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + int ret; + const char *src; + size_t len; + + /* strlen("dir:") */ + len = 4; + if (specs && specs->dir) + src = specs->dir; + else + src = dest; + + len += strlen(src) + 1; + bdev->src = malloc(len); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len, "dir:%s", src); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + ret = mkdir_p(dest, 0755); + if (ret < 0) { + ERROR("Failed to create directory \"%s\"", dest); + return -1; + } + TRACE("Created directory \"%s\"", dest); + + return 0; +} + +int dir_destroy(struct lxc_storage *orig) +{ + int ret; + char *src; + + src = lxc_storage_get_path(orig->src, orig->src); + + ret = lxc_rmdir_onedev(src, NULL); + if (ret < 0) { + ERROR("Failed to delete \"%s\"", src); + return -1; + } + + return 0; +} + +bool dir_detect(const char *path) +{ + if (!strncmp(path, "dir:", 4)) + return true; + + if (is_dir(path)) + return true; + + return false; +} + +int dir_mount(struct lxc_storage *bdev) +{ + int ret; + unsigned long mflags, mntflags; + char *src, *mntdata; + + if (strcmp(bdev->type, "dir")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata); + if (ret < 0) { + ERROR("Failed to parse mount options \"%s\"", bdev->mntopts); + free(mntdata); + return -22; + } + + src = lxc_storage_get_path(bdev->src, bdev->type); + + ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, + mntdata); + if ((0 == ret) && (mntflags & MS_RDONLY)) { + DEBUG("Remounting \"%s\" on \"%s\" readonly", + src ? src : "(none)", bdev->dest ? bdev->dest : "(none)"); + mflags = add_required_remount_flags(src, bdev->dest, MS_BIND | MS_REC | mntflags | MS_REMOUNT); + ret = mount(src, bdev->dest, "bind", mflags, mntdata); + } + + if (ret < 0) { + SYSERROR("Failed to mount \"%s\" on \"%s\"", src, bdev->dest); + free(mntdata); + return -1; + } + + TRACE("Mounted \"%s\" on \"%s\"", src, bdev->dest); + free(mntdata); + return ret; +} + +int dir_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "dir")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + return umount(bdev->dest); +} diff -Nru lxc-2.0.8/src/lxc/storage/dir.h lxc-2.1.0/src/lxc/storage/dir.h --- lxc-2.0.8/src/lxc/storage/dir.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/dir.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_DIR_H +#define __LXC_DIR_H + +#define _GNU_SOURCE +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern int dir_destroy(struct lxc_storage *orig); +extern bool dir_detect(const char *path); +extern int dir_mount(struct lxc_storage *bdev); +extern int dir_umount(struct lxc_storage *bdev); + +#endif /* __LXC_DIR_H */ diff -Nru lxc-2.0.8/src/lxc/storage/loop.c lxc-2.1.0/src/lxc/storage/loop.c --- lxc-2.0.8/src/lxc/storage/loop.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/loop.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,335 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "loop.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +lxc_log_define(loop, lxc); + +static int do_loop_create(const char *path, uint64_t size, const char *fstype); + +/* + * No idea what the original blockdev will be called, but the copy will be + * called $lxcpath/$lxcname/rootdev + */ +int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + uint64_t size = newsize; + int len, ret; + char *srcdev; + char fstype[100] = "ext4"; + + if (snap) { + ERROR("The loop storage driver does not support snapshots"); + 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) { + ERROR("Failed to create string"); + return -1; + } + + new->src = malloc(len + 5); + if (!new->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(new->src, (len + 5), "loop:%s", srcdev); + if (ret < 0 || ret >= (len + 5)) { + ERROR("Failed to create string"); + return -1; + } + + new->dest = malloc(len); + if (!new->dest) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + 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)) { + /* detect size */ + if (!newsize && blk_getsize(orig, &size) < 0) { + ERROR("Failed to detect size of loop file \"%s\"", + orig->src); + return -1; + } + + /* detect filesystem */ + if (detect_fs(orig, fstype, 100) < 0) { + INFO("Failed to detect filesystem type for \"%s\"", orig->src); + return -1; + } + } else if (!newsize) { + size = DEFAULT_FS_SIZE; + } + + ret = do_loop_create(srcdev, size, fstype); + if (ret < 0) { + ERROR("Failed to create loop storage volume \"%s\" with " + "filesystem \"%s\" and size \"%" PRIu64 "\"", + srcdev, fstype, size); + return -1; + } + + return 0; +} + +int loop_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *fstype; + uint64_t sz; + int ret, len; + char *srcdev; + + if (!specs) + return -1; + + /* is passed in as //rootfs, will + * be //rootdev, and will be "loop:". + */ + len = strlen(dest) + 2; + srcdev = alloca(len); + + ret = snprintf(srcdev, len, "%s", dest); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + ret = sprintf(srcdev + len - 4, "dev"); + if (ret < 0) { + ERROR("Failed to create string"); + return -1; + } + + bdev->src = malloc(len + 5); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len + 5, "loop:%s", srcdev); + if (ret < 0 || ret >= len + 5) { + ERROR("Failed to create string"); + return -1; + } + + sz = specs->fssize; + if (!sz) + sz = DEFAULT_FS_SIZE; + + fstype = specs->fstype; + if (!fstype) + fstype = DEFAULT_FSTYPE; + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + ret = mkdir_p(bdev->dest, 0755); + if (ret < 0) { + ERROR("Failed creating directory \"%s\"", bdev->dest); + return -1; + } + + + ret = do_loop_create(srcdev, sz, fstype); + if (ret < 0) { + ERROR("Failed to create loop storage volume \"%s\" with " + "filesystem \"%s\" and size \"%" PRIu64 "\"", + srcdev, fstype, sz); + return -1; + } + + return 0; +} + +int loop_destroy(struct lxc_storage *orig) { + return unlink(orig->src + 5); +} + +bool loop_detect(const char *path) +{ + int ret; + struct stat s; + + if (!strncmp(path, "loop:", 5)) + return true; + + ret = stat(path, &s); + if (ret < 0) + return false; + + if (__S_ISTYPE(s.st_mode, S_IFREG)) + return true; + + return false; +} + +int loop_mount(struct lxc_storage *bdev) +{ + int ret, loopfd; + char loname[MAXPATHLEN]; + char *src; + + if (strcmp(bdev->type, "loop")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + /* skip prefix */ + src = lxc_storage_get_path(bdev->src, bdev->type); + + loopfd = lxc_prepare_loop_dev(src, loname, LO_FLAGS_AUTOCLEAR); + if (loopfd < 0) { + ERROR("Failed to prepare loop device for loop file \"%s\"", src); + return -1; + } + DEBUG("Prepared loop device \"%s\"", loname); + + ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); + if (ret < 0) { + ERROR("Failed to mount rootfs \"%s\" on \"%s\" via loop device \"%s\"", + bdev->src, bdev->dest, loname); + close(loopfd); + return -1; + } + + bdev->lofd = loopfd; + DEBUG("Mounted rootfs \"%s\" on \"%s\" via loop device \"%s\"", + bdev->src, bdev->dest, loname); + + return 0; +} + +int loop_umount(struct lxc_storage *bdev) +{ + int ret, saved_errno; + + if (strcmp(bdev->type, "loop")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + ret = umount(bdev->dest); + saved_errno = errno; + if (bdev->lofd >= 0) { + close(bdev->lofd); + bdev->lofd = -1; + } + errno = saved_errno; + + if (ret < 0) { + SYSERROR("Failed to umount \"%s\"", bdev->dest); + return -1; + } + + return 0; +} + +static int do_loop_create(const char *path, uint64_t size, const char *fstype) +{ + int fd, ret; + char cmd_output[MAXPATHLEN]; + const char *cmd_args[2] = {fstype, path}; + + /* create the new loopback file */ + fd = creat(path, S_IRUSR | S_IWUSR); + if (fd < 0) { + SYSERROR("Failed to create new loop file \"%s\"", path); + return -1; + } + + ret = lseek(fd, size, SEEK_SET); + if (ret < 0) { + SYSERROR("Failed to seek to set new loop file size for loop " + "file \"%s\"", path); + close(fd); + return -1; + } + + ret = write(fd, "1", 1); + if (ret != 1) { + SYSERROR("Failed creating new loop file \"%s\"", path); + close(fd); + return -1; + } + + ret = close(fd); + if (ret < 0) { + SYSERROR("Failed to create new loop file \"%s\"", path); + return -1; + } + + /* Create an fs in the loopback file. */ + ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, + (void *)cmd_args); + if (ret < 0) { + ERROR("Failed to create new filesystem \"%s\" for loop file " + "\"%s\": %s", fstype, path, cmd_output); + return -1; + } + + return 0; +} diff -Nru lxc-2.0.8/src/lxc/storage/loop.h lxc-2.1.0/src/lxc/storage/loop.h --- lxc-2.0.8/src/lxc/storage/loop.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/loop.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_LOOP_H +#define __LXC_LOOP_H + +#define _GNU_SOURCE +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int loop_create(struct lxc_storage *bdev, const char *dest, + const char *n, struct bdev_specs *specs); +extern int loop_destroy(struct lxc_storage *orig); +extern bool loop_detect(const char *path); +extern int loop_mount(struct lxc_storage *bdev); +extern int loop_umount(struct lxc_storage *bdev); + +#endif /* __LXC_LOOP_H */ diff -Nru lxc-2.0.8/src/lxc/storage/lvm.c lxc-2.1.0/src/lxc/storage/lvm.c --- lxc-2.0.8/src/lxc/storage/lvm.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/lvm.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,648 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "log.h" +#include "lvm.h" +#include "rsync.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +#ifdef MAJOR_IN_MKDEV +#include +#endif + +lxc_log_define(lvm, lxc); + +struct lvcreate_args { + const char *size; + const char *vg; + const char *lv; + const char *thinpool; + + /* snapshot specific arguments */ + const char *source_lv; +}; + +static int lvm_destroy_exec_wrapper(void *data) +{ + struct lvcreate_args *args = data; + + (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); + execlp("lvremove", "lvremove", "-f", args->lv, (char *)NULL); + + return -1; +} + +static int lvm_create_exec_wrapper(void *data) +{ + struct lvcreate_args *args = data; + + (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); + if (args->thinpool) + execlp("lvcreate", "lvcreate", "--thinpool", args->thinpool, + "-V", args->size, args->vg, "-n", args->lv, + (char *)NULL); + else + execlp("lvcreate", "lvcreate", "-L", args->size, args->vg, "-n", + args->lv, (char *)NULL); + + return -1; +} + +static int lvm_snapshot_exec_wrapper(void *data) +{ + struct lvcreate_args *args = data; + + (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1); + if (args->thinpool) + execlp("lvcreate", "lvcreate", "-s", "-n", args->lv, + args->source_lv, (char *)NULL); + else + execlp("lvcreate", "lvcreate", "-s", "-L", args->size, "-n", + args->lv, args->source_lv, (char *)NULL); + + return -1; +} + +/* The path must be "/dev//". The volume group must be an existing + * volume group, and the logical volume must not yet exist. + * This function will attempt to create "/dev// of 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 logical volume from that + * thin pool. + */ +static int do_lvm_create(const char *path, uint64_t size, const char *thinpool) +{ + int len, ret; + char *pathdup, *vg, *lv; + char cmd_output[MAXPATHLEN]; + char sz[24]; + char *tp = NULL; + struct lvcreate_args cmd_args = {0}; + + ret = snprintf(sz, 24, "%" PRIu64 "b", size); + if (ret < 0 || ret >= 24) { + ERROR("Failed to create string: %d", ret); + return -1; + } + + pathdup = strdup(path); + if (!pathdup) { + ERROR("Failed to duplicate string \"%s\"", path); + return -1; + } + + lv = strrchr(pathdup, '/'); + if (!lv) { + ERROR("Failed to detect \"/\" in string \"%s\"", pathdup); + free(pathdup); + return -1; + } + *lv = '\0'; + lv++; + TRACE("Parsed logical volume \"%s\"", lv); + + vg = strrchr(pathdup, '/'); + if (!vg) { + ERROR("Failed to detect \"/\" in string \"%s\"", pathdup); + free(pathdup); + return -1; + } + vg++; + TRACE("Parsed volume group \"%s\"", 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) { + ERROR("Failed to create string: %d", ret); + free(pathdup); + return -1; + } + + ret = lvm_is_thin_pool(tp); + TRACE("got %d for thin pool at path: %s", ret, tp); + if (ret < 0) { + ERROR("Failed to detect whether \"%s\" is a thinpool", tp); + free(pathdup); + return -1; + } else if (!ret) { + TRACE("Detected that \"%s\" is not a thinpool", tp); + tp = NULL; + } else { + TRACE("Detected \"%s\" is a thinpool", tp); + } + } + + cmd_args.thinpool = tp; + cmd_args.vg = vg; + cmd_args.lv = lv; + cmd_args.size = sz; + TRACE("Creating new lvm storage volume \"%s\" on volume group \"%s\" " + "of size \"%s\"", lv, vg, sz); + ret = run_command(cmd_output, sizeof(cmd_output), + lvm_create_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to create logical volume \"%s\": %s", lv, + cmd_output); + free(pathdup); + return -1; + } + TRACE("Created new lvm storage volume \"%s\" on volume group \"%s\" " + "of size \"%s\"", lv, vg, sz); + + free(pathdup); + return ret; +} + +/* Look at "/sys/dev/block/maj:min/dm/uuid". If it contains the hardcoded LVM + * prefix "LVM-" then this is an lvm2 LV. + */ +bool lvm_detect(const char *path) +{ + int fd; + ssize_t ret; + struct stat statbuf; + char devp[MAXPATHLEN], buf[4]; + + if (!strncmp(path, "lvm:", 4)) + return true; + + ret = stat(path, &statbuf); + if (ret < 0) + return false; + + if (!S_ISBLK(statbuf.st_mode)) + return false; + + 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("Failed to create string"); + return false; + } + + fd = open(devp, O_RDONLY); + if (fd < 0) + return false; + + ret = read(fd, buf, sizeof(buf)); + close(fd); + if (ret != sizeof(buf)) + return false; + + if (strncmp(buf, "LVM-", 4)) + return false; + + return true; +} + +int lvm_mount(struct lxc_storage *bdev) +{ + char *src; + + if (strcmp(bdev->type, "lvm")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + src = lxc_storage_get_path(bdev->src, bdev->type); + + /* If we might pass in data sometime, then we'll have to enrich + * mount_unknown_fs(). + */ + return mount_unknown_fs(src, bdev->dest, bdev->mntopts); +} + +int lvm_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "lvm")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + return umount(bdev->dest); +} + +int lvm_compare_lv_attr(const char *path, int pos, const char expected) +{ + struct lxc_popen_FILE *f; + int ret, len, status; + char *cmd; + char output[12]; + int start=0; + 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) { + SYSERROR("popen failed"); + return -1; + } + + if (!fgets(output, 12, f->f)) + ret = 1; + + status = lxc_pclose(f); + /* Assume either vg or lvs do not exist, default comparison to false. */ + if (ret || WEXITSTATUS(status)) + return 0; + + len = strlen(output); + while (start < len && output[start] == ' ') + start++; + + if (start + pos < len && output[start + pos] == expected) + return 1; + + return 0; +} + +int lvm_is_thin_volume(const char *path) +{ + return lvm_compare_lv_attr(path, 6, 't'); +} + +int lvm_is_thin_pool(const char *path) +{ + return lvm_compare_lv_attr(path, 0, 't'); +} + +int lvm_snapshot(const char *orig, const char *path, uint64_t size) +{ + int ret; + char *pathdup, *lv; + char sz[24]; + char cmd_output[MAXPATHLEN]; + struct lvcreate_args cmd_args = {0}; + + ret = snprintf(sz, 24, "%" PRIu64 "b", size); + if (ret < 0 || ret >= 24) { + ERROR("Failed to create string"); + return -1; + } + + pathdup = strdup(path); + if (!pathdup) { + ERROR("Failed to duplicate string \"%s\"", path); + return -1; + } + + lv = strrchr(pathdup, '/'); + if (!lv) { + ERROR("Failed to detect \"/\" in string \"%s\"", pathdup); + free(pathdup); + return -1; + } + *lv = '\0'; + lv++; + TRACE("Parsed logical volume \"%s\"", lv); + + /* Check if the original logical volume is backed by a thinpool, in + * which case we cannot specify a size that's different from the + * original size. + */ + ret = lvm_is_thin_volume(orig); + if (ret < 0) { + free(pathdup); + return -1; + } else if (ret) { + cmd_args.thinpool = orig; + } + + cmd_args.lv = lv; + cmd_args.source_lv = orig; + cmd_args.size = sz; + TRACE("Creating new lvm snapshot \"%s\" of \"%s\" with size \"%s\"", lv, + orig, sz); + ret = run_command(cmd_output, sizeof(cmd_output), + lvm_snapshot_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to create logical volume \"%s\": %s", orig, + cmd_output); + free(pathdup); + return -1; + } + + free(pathdup); + return 0; +} + +int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + int len, ret; + const char *vg; + + if (!orig->src || !orig->dest) + return -1; + + if (strcmp(orig->type, "lvm") && snap) { + ERROR("LVM snapshot from \"%s\" storage driver is not supported", + orig->type); + return -1; + } + + if (strcmp(orig->type, "lvm")) { + vg = lxc_global_config_value("lxc.bdev.lvm.vg"); + new->src = lxc_string_join( + "/", + (const char *[]){"lvm:", "dev", vg, cname, NULL}, + false); + } else { + char *dup, *slider, *src; + + src = lxc_storage_get_path(orig->src, orig->type); + + dup = strdup(src); + if (!dup) { + ERROR("Failed to duplicate string \"%s\"", src); + return -1; + } + + slider = strrchr(dup, '/'); + if (!slider) { + ERROR("Failed to detect \"/\" in string \"%s\"", dup); + free(dup); + return -1; + } + *slider = '\0'; + slider = dup; + + new->src = lxc_string_join( + "/", + (const char *[]){"lvm:", *slider == '/' ? ++slider : slider, + cname, NULL}, + false); + free(dup); + } + if (!new->src) { + ERROR("Failed to create string"); + return -1; + } + + if (orig->mntopts) { + new->mntopts = strdup(orig->mntopts); + if (!new->mntopts) { + ERROR("Failed to duplicate string \"%s\"", orig->mntopts); + return -1; + } + } + + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->dest = malloc(len); + if (!new->dest) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + ret = mkdir_p(new->dest, 0755); + if (ret < 0) { + SYSERROR("Failed to create directory \"%s\"", new->dest); + return -1; + } + + return 0; +} + +bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + char *src; + const char *thinpool; + int ret; + struct rsync_data data; + char *cmd_args[2]; + char cmd_output[MAXPATHLEN] = {0}; + char fstype[100] = "ext4"; + uint64_t size = newsize; + + if (is_blktype(orig)) { + /* detect size */ + if (!newsize && blk_getsize(orig, &size) < 0) { + ERROR("Failed to detect size of logical volume \"%s\"", + orig->src); + return -1; + } + + /* detect filesystem */ + if (detect_fs(orig, fstype, 100) < 0) { + INFO("Failed to detect filesystem type for \"%s\"", orig->src); + return -1; + } + } else if (!newsize) { + size = DEFAULT_FS_SIZE; + } + + src = lxc_storage_get_path(new->src, "lvm"); + thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool"); + + ret = do_lvm_create(src, size, thinpool); + if (ret < 0) { + ERROR("Failed to create lvm storage volume \"%s\"", src); + return -1; + } + + cmd_args[0] = fstype; + cmd_args[1] = src; + ret = run_command(cmd_output, sizeof(cmd_output), + do_mkfs_exec_wrapper, (void *)cmd_args); + if (ret < 0) { + ERROR("Failed to create new filesystem \"%s\" for lvm storage " + "volume \"%s\": %s", fstype, src, cmd_output); + return -1; + } + + data.orig = orig; + data.new = new; + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_storage_rsync_exec_wrapper, (void *)&data); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" to \"%s\"", orig->dest, + new->dest); + return false; + } + + TRACE("Created lvm storage volume \"%s\"", new->dest); + return true; +} + +bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + int ret; + char *newsrc, *origsrc; + uint64_t size = newsize; + + if (is_blktype(orig)) { + if (!newsize && blk_getsize(orig, &size) < 0) { + ERROR("Failed to detect size of logical volume \"%s\"", + orig->src); + return -1; + } + } else if (!newsize) { + size = DEFAULT_FS_SIZE; + } + + origsrc = lxc_storage_get_path(orig->src, "lvm"); + newsrc = lxc_storage_get_path(new->src, "lvm"); + + ret = lvm_snapshot(origsrc, newsrc, size); + if (ret < 0) { + ERROR("Failed to create lvm \"%s\" snapshot of \"%s\"", + new->src, orig->src); + return false; + } + + TRACE("Created lvm snapshot \"%s\" from \"%s\"", new->dest, orig->dest); + return true; +} + +int lvm_destroy(struct lxc_storage *orig) +{ + int ret; + char cmd_output[MAXPATHLEN]; + struct lvcreate_args cmd_args = {0}; + + cmd_args.lv = lxc_storage_get_path(orig->src, "lvm"); + ret = run_command(cmd_output, sizeof(cmd_output), + lvm_destroy_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to destroy logical volume \"%s\": %s", orig->src, + cmd_output); + return -1; + } + + TRACE("Destroyed logical volume \"%s\"", orig->src); + return 0; +} + +int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *vg, *thinpool, *fstype, *lv = n; + uint64_t sz; + int ret, len; + const char *cmd_args[2]; + char cmd_output[MAXPATHLEN]; + + if (!specs) + return -1; + + vg = specs->lvm.vg; + if (!vg) + vg = lxc_global_config_value("lxc.bdev.lvm.vg"); + + thinpool = specs->lvm.thinpool; + if (!thinpool) + thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool"); + + /* /dev/$vg/$lv */ + if (specs->lvm.lv) + lv = specs->lvm.lv; + + len = strlen(vg) + strlen(lv) + 4 + 7; + bdev->src = malloc(len); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len, "lvm:/dev/%s/%s", vg, lv); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + /* size is in bytes */ + sz = specs->fssize; + if (!sz) + sz = DEFAULT_FS_SIZE; + + ret = do_lvm_create(bdev->src + 4, sz, thinpool); + if (ret < 0) { + ERROR("Error creating new logical volume \"%s\" of size " + "\"%" PRIu64 " bytes\"", bdev->src, sz); + return -1; + } + + fstype = specs->fstype; + if (!fstype) + fstype = DEFAULT_FSTYPE; + + cmd_args[0] = fstype; + cmd_args[1] = lxc_storage_get_path(bdev->src, bdev->type); + ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, + (void *)cmd_args); + if (ret < 0) { + ERROR("Failed to create new logical volume \"%s\": %s", + bdev->src, cmd_output); + return -1; + } + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + ret = mkdir_p(bdev->dest, 0755); + if (ret < 0) { + SYSERROR("Failed to create directory \"%s\"", bdev->dest); + return -1; + } + + TRACE("Created new logical volume \"%s\"", bdev->dest); + return 0; +} diff -Nru lxc-2.0.8/src/lxc/storage/lvm.h lxc-2.1.0/src/lxc/storage/lvm.h --- lxc-2.0.8/src/lxc/storage/lvm.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/lvm.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_LVM_H +#define __LXC_LVM_H + +#define _GNU_SOURCE +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern bool lvm_detect(const char *path); +extern int lvm_mount(struct lxc_storage *bdev); +extern int lvm_umount(struct lxc_storage *bdev); +extern int lvm_compare_lv_attr(const char *path, int pos, const char expected); +extern int lvm_is_thin_volume(const char *path); +extern int lvm_is_thin_pool(const char *path); +extern int lvm_snapshot(const char *orig, const char *path, uint64_t size); +extern int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int lvm_destroy(struct lxc_storage *orig); +extern int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); +extern bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); + +#endif /* __LXC_LVM_H */ diff -Nru lxc-2.0.8/src/lxc/storage/nbd.c lxc-2.1.0/src/lxc/storage/nbd.c --- lxc-2.0.8/src/lxc/storage/nbd.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/nbd.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,317 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "nbd.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +lxc_log_define(nbd, lxc); + +struct nbd_attach_data { + const char *nbd; + const char *path; +}; + +static bool clone_attach_nbd(const char *nbd, const char *path); +static int do_attach_nbd(void *d); +static bool nbd_busy(int idx); +static void nbd_detach(const char *path); +static int nbd_get_partition(const char *src); +static bool wait_for_partition(const char *path); + +bool attach_nbd(char *src, struct lxc_conf *conf) +{ + char *orig = alloca(strlen(src)+1), *p, path[50]; + int i = 0; + + strcpy(orig, src); + /* if path is followed by a partition, drop that for now */ + p = strchr(orig, ':'); + if (p) + *p = '\0'; + while (1) { + sprintf(path, "/dev/nbd%d", i); + if (!file_exists(path)) + return false; + if (nbd_busy(i)) { + i++; + continue; + } + if (!clone_attach_nbd(path, orig)) + return false; + conf->nbd_idx = i; + return true; + } +} + +void detach_nbd_idx(int idx) +{ + int ret; + char path[50]; + + ret = snprintf(path, 50, "/dev/nbd%d", idx); + if (ret < 0 || ret >= 50) + return; + + nbd_detach(path); +} + +int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + return -ENOSYS; +} + +int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + return -ENOSYS; +} + +int nbd_destroy(struct lxc_storage *orig) +{ + return -ENOSYS; +} + +bool nbd_detect(const char *path) +{ + if (!strncmp(path, "nbd:", 4)) + return true; + + return false; +} + +int nbd_mount(struct lxc_storage *bdev) +{ + int ret = -1, partition; + char *src; + char path[50]; + + if (strcmp(bdev->type, "nbd")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + /* nbd_idx should have been copied by bdev_init from the lxc_conf */ + if (bdev->nbd_idx < 0) + return -22; + + src = lxc_storage_get_path(bdev->src, bdev->type); + partition = nbd_get_partition(src); + if (partition) + ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx, + partition); + else + ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx); + if (ret < 0 || ret >= 50) { + ERROR("Error setting up nbd device path"); + return ret; + } + + /* It might take awhile for the partition files to show up */ + if (partition) { + if (!wait_for_partition(path)) + return -2; + } + ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts); + if (ret < 0) + ERROR("Error mounting %s", bdev->src); + + return ret; +} + +int nbd_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "nbd")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + return umount(bdev->dest); +} + +bool requires_nbd(const char *path) +{ + if (strncmp(path, "nbd:", 4) == 0) + return true; + return false; +} + +static int do_attach_nbd(void *d) +{ + struct nbd_attach_data *data = d; + const char *nbd, *path; + pid_t pid; + sigset_t mask; + int sfd; + ssize_t s; + struct signalfd_siginfo fdsi; + + sigemptyset(&mask); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGCHLD); + + nbd = data->nbd; + path = data->path; + + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { + SYSERROR("Error blocking signals for nbd watcher"); + exit(1); + } + + sfd = signalfd(-1, &mask, 0); + if (sfd == -1) { + SYSERROR("Error opening signalfd for nbd task"); + exit(1); + } + + if (prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) < 0) + SYSERROR("Error setting parent death signal for nbd watcher"); + + pid = fork(); + if (pid) { + for (;;) { + s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); + if (s != sizeof(struct signalfd_siginfo)) + SYSERROR("Error reading from signalfd"); + + if (fdsi.ssi_signo == SIGHUP) { + /* container has exited */ + nbd_detach(nbd); + exit(0); + } else if (fdsi.ssi_signo == SIGCHLD) { + int status; + /* If qemu-nbd fails, or is killed by a signal, + * then exit */ + while (waitpid(-1, &status, WNOHANG) > 0) { + if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) || + WIFSIGNALED(status)) { + nbd_detach(nbd); + exit(1); + } + } + } + } + } + + close(sfd); + if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) + WARN("Warning: unblocking signals for nbd watcher"); + + execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL); + SYSERROR("Error executing qemu-nbd"); + exit(1); +} + +static bool clone_attach_nbd(const char *nbd, const char *path) +{ + pid_t pid; + struct nbd_attach_data data; + + data.nbd = nbd; + data.path = path; + + pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID); + if (pid < 0) + return false; + return true; +} + +static bool nbd_busy(int idx) +{ + char path[100]; + int ret; + + ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx); + if (ret < 0 || ret >= 100) + return true; + return file_exists(path); +} + +static void nbd_detach(const char *path) +{ + int ret; + pid_t pid = fork(); + + if (pid < 0) { + SYSERROR("Error forking to detach nbd"); + return; + } + if (pid) { + ret = wait_for_pid(pid); + if (ret < 0) + ERROR("nbd disconnect returned an error"); + return; + } + execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL); + SYSERROR("Error executing qemu-nbd"); + exit(1); +} + +/* + * Pick the partition # off the end of a nbd:file:p + * description. Return 1-9 for the partition id, or 0 + * for no partition. + */ +static int nbd_get_partition(const char *src) +{ + char *p = strchr(src, ':'); + if (!p) + return 0; + p = strchr(p+1, ':'); + if (!p) + return 0; + p++; + if (*p < '1' || *p > '9') + return 0; + return *p - '0'; +} + +static bool wait_for_partition(const char *path) +{ + int count = 0; + while (count < 5) { + if (file_exists(path)) + return true; + sleep(1); + count++; + } + ERROR("Device %s did not show up after 5 seconds", path); + return false; +} diff -Nru lxc-2.0.8/src/lxc/storage/nbd.h lxc-2.1.0/src/lxc/storage/nbd.h --- lxc-2.0.8/src/lxc/storage/nbd.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/nbd.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_NBD_H +#define __LXC_NBD_H + +#define _GNU_SOURCE +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern int nbd_destroy(struct lxc_storage *orig); +extern bool nbd_detect(const char *path); +extern int nbd_mount(struct lxc_storage *bdev); +extern int nbd_umount(struct lxc_storage *bdev); + +extern bool attach_nbd(char *src, struct lxc_conf *conf); +extern void detach_nbd_idx(int idx); +extern bool requires_nbd(const char *path); + +#endif /* __LXC_NBD_H */ diff -Nru lxc-2.0.8/src/lxc/storage/overlay.c lxc-2.1.0/src/lxc/storage/overlay.c --- lxc-2.0.8/src/lxc/storage/overlay.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/overlay.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,985 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "conf.h" +#include "confile.h" +#include "log.h" +#include "lxccontainer.h" +#include "overlay.h" +#include "rsync.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +lxc_log_define(overlay, lxc); + +static char *ovl_name; +static char *ovl_version[] = {"overlay", "overlayfs"}; + +static char *ovl_detect_name(void); +static int ovl_do_rsync(const char *src, const char *dest, + struct lxc_conf *conf); +static int ovl_remount_on_enodev(const char *lower, const char *target, + const char *name, unsigned long mountflags, + const void *options); + +int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, + int snap, uint64_t newsize, struct lxc_conf *conf) +{ + int ret; + char *src; + + if (!snap) { + ERROR("The overlay storage driver can only be used for " + "snapshots"); + return -22; + } + + if (!orig->src || !orig->dest) + return -1; + + new->dest = must_make_path(lxcpath, cname, "rootfs", NULL); + + ret = mkdir_p(new->dest, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", new->dest); + return -1; + } + + if (am_unpriv()) { + ret = chown_mapped_root(new->dest, conf); + if (ret < 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) { + ERROR("Failed to detect \"/\" in string \"%s\"", + new->dest); + return -22; + } + + if (strlen(lastslash) < (sizeof("/rootfs") - 1)) { + ERROR("Failed to detect \"/rootfs\" in string \"%s\"", + new->dest); + return -22; + } + + lastslash++; + lastslashidx = lastslash - new->dest; + + delta = malloc(lastslashidx + 7); + if (!delta) { + ERROR("Failed to allocate memory"); + return -1; + } + + strncpy(delta, new->dest, lastslashidx + 1); + strncpy(delta + lastslashidx, "delta0", sizeof("delta0") - 1); + delta[lastslashidx + sizeof("delta0")] = '\0'; + + ret = mkdir(delta, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", delta); + free(delta); + return -1; + } + + if (am_unpriv()) { + ret = chown_mapped_root(delta, conf); + if (ret < 0) + WARN("Failed to update ownership of %s", delta); + } + + /* Make workdir for overlayfs.v22 or higher: + * The workdir will be + * /var/lib/lxc/c2/olwork + * and is used to prepare files before they are atomically + * switched to the overlay destination. Workdirs need to be on + * the same filesystem as the upperdir so it's OK for it to be + * empty. + */ + work = malloc(lastslashidx + 7); + if (!work) { + ERROR("Failed to allocate memory"); + free(delta); + return -1; + } + + strncpy(work, new->dest, lastslashidx + 1); + strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); + work[lastslashidx + sizeof("olwork")] = '\0'; + + if (mkdir(work, 0755) < 0) { + SYSERROR("error: mkdir %s", work); + free(delta); + free(work); + return -1; + } + + if (am_unpriv()) { + ret = chown_mapped_root(work, conf); + if (ret < 0) + WARN("Failed to update ownership of %s", work); + } + free(work); + + /* strlen("overlay:") = 8 + * + + * strlen(delta) + * + + * : + * + + * strlen(src) + * + + * \0 + */ + src = lxc_storage_get_path(orig->src, orig->type); + len = 8 + strlen(delta) + 1 + strlen(src) + 1; + new->src = malloc(len); + if (!new->src) { + ERROR("Failed to allocate memory"); + free(delta); + return -ENOMEM; + } + + ret = snprintf(new->src, len, "overlay:%s:%s", src, delta); + free(delta); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + return -1; + } + } else if (!strcmp(orig->type, "overlayfs") || + !strcmp(orig->type, "overlay")) { + char *clean_old_path, *clean_new_path; + char *lastslash, *ndelta, *nsrc, *odelta, *osrc, *s1, *s2, *s3, + *work; + int ret, lastslashidx; + size_t len, name_len; + + osrc = strdup(orig->src); + if (!osrc) { + ERROR("Failed to duplicate string \"%s\"", orig->src); + return -22; + } + + nsrc = strchr(osrc, ':') + 1; + if ((nsrc != osrc + 8) && (nsrc != osrc + 10)) { + free(osrc); + ERROR("Detected \":\" in \"%s\" at wrong position", + osrc); + return -22; + } + + odelta = strchr(nsrc, ':'); + if (!odelta) { + free(osrc); + ERROR("Failed to find \":\" in \"%s\"", nsrc); + return -22; + } + + *odelta = '\0'; + odelta++; + ndelta = must_make_path(lxcpath, cname, "delta0", NULL); + + ret = mkdir(ndelta, 0755); + if (ret < 0 && errno != EEXIST) { + free(osrc); + free(ndelta); + SYSERROR("Failed to create directory \"%s\"", ndelta); + return -1; + } + + if (am_unpriv()) { + ret = chown_mapped_root(ndelta, conf); + if (ret < 0) + WARN("Failed to update ownership of %s", + ndelta); + } + + /* Make workdir for overlayfs.v22 or higher (See the comment + * further up.). + */ + lastslash = strrchr(ndelta, '/'); + if (!lastslash) { + free(osrc); + free(ndelta); + ERROR("Failed to detect \"/\" in \"%s\"", ndelta); + return -1; + } + lastslash++; + lastslashidx = lastslash - ndelta; + + work = malloc(lastslashidx + 7); + if (!work) { + free(osrc); + free(ndelta); + ERROR("Failed to allocate memory"); + return -1; + } + + strncpy(work, ndelta, lastslashidx + 1); + strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); + work[lastslashidx + sizeof("olwork")] = '\0'; + + ret = mkdir(work, 0755); + if (ret < 0 && errno != EEXIST) { + free(osrc); + free(ndelta); + free(work); + SYSERROR("Failed to create directory \"%s\"", ndelta); + return -1; + } + + if (am_unpriv()) { + ret = chown_mapped_root(work, conf); + if (ret < 0) + WARN("Failed to update ownership of %s", work); + } + free(work); + + /* strlen("overlay:") = 8 + * + + * strlen(delta) + * + + * : + * + + * strlen(src) + * + + * \0 + */ + len = 8 + strlen(ndelta) + 1 + strlen(nsrc) + 1; + new->src = malloc(len); + if (!new->src) { + free(osrc); + free(ndelta); + ERROR("Failed to allocate memory"); + return -ENOMEM; + } + ret = snprintf(new->src, len, "overlay:%s:%s", nsrc, ndelta); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + free(osrc); + free(ndelta); + return -1; + } + + ret = ovl_do_rsync(odelta, ndelta, conf); + free(osrc); + free(ndelta); + if (ret < 0) + return -1; + + /* When we create an overlay snapshot of an overlay container in + * the snapshot directory under "//snaps/" we + * don't need to record a dependency. If we would restore would + * also fail. + */ + clean_old_path = lxc_deslashify(oldpath); + if (!clean_old_path) + return -1; + + clean_new_path = lxc_deslashify(lxcpath); + if (!clean_new_path) { + free(clean_old_path); + return -1; + } + + s1 = strrchr(clean_old_path, '/'); + if (!s1) { + ERROR("Failed to detect \"/\" in string \"%s\"", s1); + free(clean_old_path); + free(clean_new_path); + return -1; + } + + s2 = strrchr(clean_new_path, '/'); + if (!s2) { + ERROR("Failed to detect \"/\" in string \"%s\"", s2); + free(clean_old_path); + free(clean_new_path); + return -1; + } + + if (!strncmp(s1, "/snaps", sizeof("/snaps") - 1)) { + s1 = clean_new_path; + s2 = clean_old_path; + s3 = (char *)cname; + } else if (!strncmp(s2, "/snaps", sizeof("/snaps") - 1)) { + s1 = clean_old_path; + s2 = clean_new_path; + s3 = (char *)oldname; + } else { + free(clean_old_path); + free(clean_new_path); + return 0; + } + + len = strlen(s1); + if (!strncmp(s1, s2, len)) { + char *tmp; + + tmp = (char *)(s2 + len + 1); + if (*tmp == '\0') { + free(clean_old_path); + free(clean_new_path); + return 0; + } + + name_len = strlen(s3); + if (strncmp(s3, tmp, name_len)) { + free(clean_old_path); + free(clean_new_path); + return 0; + } + + free(clean_old_path); + free(clean_new_path); + return LXC_CLONE_SNAPSHOT; + } + + free(clean_old_path); + free(clean_new_path); + return 0; + } else { + ERROR("overlay clone of %s container is not yet supported", + orig->type); + /* Note, supporting this will require ovl_mount supporting + * mounting of the underlay. No big deal, just needs to be done. + */ + return -1; + } + + return 0; +} + +/* To say "lxc-create -t ubuntu -n o1 -B overlay" means you want + * "//rootfs" to have the created container, while all changes + * after starting the container are written to "//delta0". + */ +int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + char *delta; + int ret; + size_t len, newlen; + + len = strlen(dest); + if (len < 8 || strcmp(dest + len - 7, "/rootfs")) { + ERROR("Failed to detect \"/rootfs\" in \"%s\"", dest); + return -1; + } + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + delta = malloc(len + 1); + if (!delta) { + ERROR("Failed to allocate memory"); + return -1; + } + + strncpy(delta, dest, len); + strncpy(delta + len - 6, "delta0", sizeof("delta0") - 1); + delta[len + sizeof("delta0")] = '\0'; + + ret = mkdir_p(delta, 0755); + if (ret < 0) { + SYSERROR("Failed to create directory \"%s\"", delta); + free(delta); + return -1; + } + + /* overlay:lower:upper */ + newlen = (2 * len) + strlen("overlay:") + 2; + bdev->src = malloc(newlen); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + free(delta); + return -1; + } + + ret = snprintf(bdev->src, newlen, "overlay:%s:%s", dest, delta); + if (ret < 0 || (size_t)ret >= newlen) { + ERROR("Failed to create string"); + free(delta); + return -1; + } + + ret = mkdir_p(bdev->dest, 0755); + if (ret < 0) { + SYSERROR("Failed to create directory \"%s\"", bdev->dest); + free(delta); + return -1; + } + + free(delta); + return 0; +} + +int ovl_destroy(struct lxc_storage *orig) +{ + bool ovl; + char *upper = orig->src; + + ovl = !strncmp(upper, "overlay:", 8); + if (!ovl && strncmp(upper, "overlayfs:", 10)) + return -22; + + /* For an overlay container the rootfs is considered immutable + * and cannot be removed when restoring from a snapshot. + */ + if (orig->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE) + return 0; + + if (ovl) + upper += 8; + else + upper += 10; + + upper = strchr(upper, ':'); + if (!upper) + return -22; + upper++; + + return lxc_rmdir_onedev(upper, NULL); +} + +bool ovl_detect(const char *path) +{ + if (!strncmp(path, "overlayfs:", 10)) + return true; + + if (!strncmp(path, "overlay:", 8)) + return true; + + return false; +} + +int ovl_mount(struct lxc_storage *bdev) +{ + char *tmp, *options, *dup, *lower, *upper; + char *options_work, *work, *lastslash; + int lastslashidx; + size_t len, len2; + unsigned long mntflags; + char *mntdata; + int ret, ret2; + + if (strcmp(bdev->type, "overlay") && strcmp(bdev->type, "overlayfs")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + if (!ovl_name) + ovl_name = ovl_detect_name(); + + /* Separately mount it first: + * mount -t overlay * -o upperdir=${upper},lowerdir=${lower} lower dest + */ + dup = strdup(bdev->src); + if (!dup) { + ERROR("Failed to allocate memory"); + return -1; + } + + /* support multiple lower layers */ + lower = strstr(dup, ":/"); + if (!lower) { + ERROR("Failed to detect \":/\" in string \"%s\"", dup); + free(dup); + return -22; + } + + lower++; + upper = lower; + while ((tmp = strstr(++upper, ":/"))) { + upper = tmp; + } + + upper--; + if (upper == lower) { + free(dup); + return -22; + } + *upper = '\0'; + upper++; + + /* if delta doesn't yet exist, create it */ + ret = mkdir_p(upper, 0755) < 0; + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", upper); + free(dup); + 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) { + ERROR("Failed to detect \"/\" in string \"%s\"", upper); + free(dup); + return -22; + } + + lastslash++; + lastslashidx = lastslash - upper; + + work = malloc(lastslashidx + 7); + if (!work) { + ERROR("Failed to allocate memory"); + free(dup); + return -22; + } + + strncpy(work, upper, lastslashidx + 1); + strncpy(work + lastslashidx, "olwork", sizeof("olwork") - 1); + work[lastslashidx + sizeof("olwork")] = '\0'; + + ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata); + if (ret < 0) { + ERROR("Failed to parse mount options"); + free(mntdata); + free(dup); + free(work); + return -22; + } + + ret = mkdir_p(work, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", work); + free(mntdata); + free(dup); + free(work); + return -22; + } + + /* + * TODO: + * We should check whether bdev->src is a blockdev but for now only + * support overlays of a basic directory + */ + + if (mntdata) { + len = strlen(lower) + strlen(upper) + + strlen("upperdir=,lowerdir=,") + strlen(mntdata) + 1; + options = alloca(len); + ret = snprintf(options, len, "upperdir=%s,lowerdir=%s,%s", + upper, lower, mntdata); + + len2 = strlen(lower) + strlen(upper) + strlen(work) + + strlen("upperdir=,lowerdir=,workdir=") + + strlen(mntdata) + 1; + options_work = alloca(len2); + ret2 = snprintf(options, len2, + "upperdir=%s,lowerdir=%s,workdir=%s,%s", upper, + lower, work, mntdata); + } else { + len = strlen(lower) + strlen(upper) + + strlen("upperdir=,lowerdir=") + 1; + options = alloca(len); + ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, + lower); + + len2 = strlen(lower) + strlen(upper) + strlen(work) + + strlen("upperdir=,lowerdir=,workdir=") + 1; + options_work = alloca(len2); + ret2 = snprintf(options_work, len2, + "upperdir=%s,lowerdir=%s,workdir=%s", upper, + lower, work); + } + + if (ret < 0 || ret >= len || ret2 < 0 || ret2 >= len2) { + ERROR("Failed to create string"); + free(mntdata); + free(dup); + free(work); + return -1; + } + + /* Assume we need a workdir as we are on a overlay version >= v22. */ + ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, + MS_MGC_VAL | mntflags, options_work); + if (ret < 0) { + INFO("Failed to mount \"%s\" on \"%s\" with options \"%s\". " + "Retrying without workdir: %s", + lower, bdev->dest, options_work, strerror(errno)); + + /* Assume we cannot use a workdir as we are on a version <= v21. + */ + ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, + MS_MGC_VAL | mntflags, options); + if (ret < 0) + SYSERROR("Failed to mount \"%s\" on \"%s\" with " + "options \"%s\": %s", + lower, bdev->dest, options, strerror(errno)); + else + INFO("Mounted \"%s\" on \"%s\" with options \"%s\"", + lower, bdev->dest, options); + } else { + INFO("Mounted \"%s\" on \"%s\" with options \"%s\"", lower, + bdev->dest, options_work); + } + + free(dup); + free(work); + return ret; +} + +int ovl_umount(struct lxc_storage *bdev) +{ + int ret; + + if (strcmp(bdev->type, "overlay") && strcmp(bdev->type, "overlayfs")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + ret = umount(bdev->dest); + if (ret < 0) + SYSERROR("Failed to unmount \"%s\"", bdev->dest); + else + TRACE("Unmounted \"%s\"", bdev->dest); + + return ret; +} + +char *ovl_get_lower(const char *rootfs_path) +{ + char *s1; + + s1 = strstr(rootfs_path, ":/"); + if (!s1) + return NULL; + s1++; + + s1 = strstr(s1, ":/"); + if (!s1) + return NULL; + s1++; + + return s1; +} + +char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen) +{ + char *rootfsdir = NULL; + char *s1 = NULL; + char *s2 = NULL; + char *s3 = NULL; + + if (!rootfs_path || !rootfslen) + return NULL; + + s1 = strdup(rootfs_path); + if (!s1) + return NULL; + + s2 = strstr(s1, ":/"); + if (s2) { + s2 = s2 + 1; + if ((s3 = strstr(s2, ":/"))) + *s3 = '\0'; + rootfsdir = strdup(s2); + if (!rootfsdir) { + free(s1); + return NULL; + } + } + + if (!rootfsdir) + rootfsdir = s1; + else + free(s1); + + *rootfslen = strlen(rootfsdir); + + return rootfsdir; +} + +int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, + const char *lxc_name, const char *lxc_path) +{ + char lxcpath[MAXPATHLEN]; + char **opts; + int ret; + size_t arrlen, dirlen, i, len, rootfslen; + int fret = -1; + char *rootfs_dir = NULL, *rootfs_path = NULL, *upperdir = NULL, + *workdir = NULL; + + /* When rootfs == NULL we have a container without a rootfs. */ + if (rootfs && rootfs->path) + rootfs_path = rootfs->path; + + opts = lxc_string_split(mntent->mnt_opts, ','); + if (opts) + arrlen = lxc_array_len((void **)opts); + else + goto err; + + for (i = 0; i < arrlen; i++) { + if (strstr(opts[i], "upperdir=") && + (strlen(opts[i]) > (len = strlen("upperdir=")))) + upperdir = opts[i] + len; + else if (strstr(opts[i], "workdir=") && + (strlen(opts[i]) > (len = strlen("workdir=")))) + workdir = opts[i] + len; + } + + if (rootfs_path) { + ret = + snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + rootfs_dir = ovl_get_rootfs(rootfs_path, &rootfslen); + if (!rootfs_dir) + goto err; + + dirlen = strlen(lxcpath); + } + + /* + * We neither allow users to create upperdirs and workdirs outside the + * containerdir nor inside the rootfs. The latter might be debatable. + * When we have a container without a rootfs we skip the checks. + */ + ret = 0; + if (upperdir) { + if (!rootfs_path) + ret = mkdir_p(upperdir, 0755); + else if (!strncmp(upperdir, lxcpath, dirlen) && + strncmp(upperdir, rootfs_dir, rootfslen)) + ret = mkdir_p(upperdir, 0755); + if (ret < 0) + WARN("Failed to create directory \"%s\": %s", upperdir, + strerror(errno)); + } + + ret = 0; + if (workdir) { + if (!rootfs_path) + ret = mkdir_p(workdir, 0755); + else if (!strncmp(workdir, lxcpath, dirlen) && + strncmp(workdir, rootfs_dir, rootfslen)) + ret = mkdir_p(workdir, 0755); + if (ret < 0) + WARN("Failed to create directory \"%s\": %s", workdir, + strerror(errno)); + } + + fret = 0; + +err: + free(rootfs_dir); + lxc_free_array((void **)opts, free); + return fret; +} + +/* To be called from lxcapi_clone() in lxccontainer.c: When we clone a container + * with overlay lxc.mount.entry entries we need to update absolute paths for + * upper- and workdir. This update is done in two locations: + * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done + * independent of each other since lxc_conf->mountlist may contain more mount + * entries (e.g. from other included files) than lxc_conf->unexpanded_config. + */ +int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, + const char *lxc_name, const char *newpath, + const char *newname) +{ + char new_upper[MAXPATHLEN], new_work[MAXPATHLEN], old_upper[MAXPATHLEN], + old_work[MAXPATHLEN]; + size_t i; + struct lxc_list *iterator; + char *cleanpath = NULL; + int fret = -1; + int ret = 0; + const char *ovl_dirs[] = {"br", "upperdir", "workdir"}; + + cleanpath = strdup(newpath); + if (!cleanpath) + goto err; + + remove_trailing_slashes(cleanpath); + + /* + * We have to update lxc_conf->unexpanded_config separately from + * lxc_conf->mount_list. + */ + for (i = 0; i < sizeof(ovl_dirs) / sizeof(ovl_dirs[0]); i++) { + if (!clone_update_unexp_ovl_paths(lxc_conf, lxc_path, newpath, + lxc_name, newname, + ovl_dirs[i])) + goto err; + } + + ret = + snprintf(old_work, MAXPATHLEN, "workdir=%s/%s", lxc_path, lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + ret = + snprintf(new_work, MAXPATHLEN, "workdir=%s/%s", cleanpath, newname); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + lxc_list_for_each(iterator, &lxc_conf->mount_list) { + char *mnt_entry = NULL, *new_mnt_entry = NULL, *tmp = NULL, + *tmp_mnt_entry = NULL; + + mnt_entry = iterator->elem; + + if (strstr(mnt_entry, "overlay")) + tmp = "upperdir"; + else if (strstr(mnt_entry, "aufs")) + tmp = "br"; + + if (!tmp) + continue; + + ret = snprintf(old_upper, MAXPATHLEN, "%s=%s/%s", tmp, lxc_path, + lxc_name); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + ret = snprintf(new_upper, MAXPATHLEN, "%s=%s/%s", tmp, + cleanpath, newname); + if (ret < 0 || ret >= MAXPATHLEN) + goto err; + + if (strstr(mnt_entry, old_upper)) { + tmp_mnt_entry = + lxc_string_replace(old_upper, new_upper, mnt_entry); + } + + if (strstr(mnt_entry, old_work)) { + if (tmp_mnt_entry) + new_mnt_entry = lxc_string_replace( + old_work, new_work, tmp_mnt_entry); + else + new_mnt_entry = lxc_string_replace( + old_work, new_work, mnt_entry); + } + + if (new_mnt_entry) { + free(iterator->elem); + iterator->elem = strdup(new_mnt_entry); + } else if (tmp_mnt_entry) { + free(iterator->elem); + iterator->elem = strdup(tmp_mnt_entry); + } + + free(new_mnt_entry); + free(tmp_mnt_entry); + } + + fret = 0; +err: + free(cleanpath); + return fret; +} + +static int ovl_remount_on_enodev(const char *lower, const char *target, + const char *name, unsigned long mountflags, + const void *options) +{ + int ret; + ret = mount(lower, target, ovl_name, MS_MGC_VAL | mountflags, options); + if (ret < 0 && errno == ENODEV) /* Try other module name. */ + ret = mount(lower, target, + ovl_name == ovl_version[0] ? ovl_version[1] + : ovl_version[0], + MS_MGC_VAL | mountflags, options); + return ret; +} + +static char *ovl_detect_name(void) +{ + FILE *f; + char *v = ovl_version[0]; + char *line = NULL; + size_t len = 0; + + f = fopen("/proc/filesystems", "r"); + if (!f) + return v; + + while (getline(&line, &len, f) != -1) { + if (strcmp(line, "nodev\toverlayfs\n") == 0) { + v = ovl_version[1]; + break; + } + } + + fclose(f); + free(line); + return v; +} + +static int ovl_do_rsync(const char *src, const char *dest, + struct lxc_conf *conf) +{ + int ret = -1; + struct rsync_data_char rdata = {0}; + char cmd_output[MAXPATHLEN] = {0}; + + rdata.src = (char *)src; + rdata.dest = (char *)dest; + if (am_unpriv()) + ret = userns_exec_1(conf, lxc_rsync_exec_wrapper, &rdata, + "lxc_rsync_exec_wrapper"); + else + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_rsync_exec_wrapper, (void *)&rdata); + if (ret < 0) + ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", src, dest, + cmd_output[0] != '\0' ? ": " : "", + cmd_output[0] != '\0' ? cmd_output : ""); + + return ret; +} diff -Nru lxc-2.0.8/src/lxc/storage/overlay.h lxc-2.1.0/src/lxc/storage/overlay.h --- lxc-2.0.8/src/lxc/storage/overlay.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/overlay.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_OVERLAY_H +#define __LXC_OVERLAY_H + +#include +#include +#include +#include +#include +#include + +#include "storage.h" + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +struct lxc_rootfs; + +extern int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern int ovl_destroy(struct lxc_storage *orig); +extern bool ovl_detect(const char *path); +extern int ovl_mount(struct lxc_storage *bdev); +extern int ovl_umount(struct lxc_storage *bdev); + +/* To be called from lxcapi_clone() in lxccontainer.c: When we clone a container + * with overlay lxc.mount.entry entries we need to update absolute paths for + * upper- and workdir. This update is done in two locations: + * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done + * independent of each other since lxc_conf->mountlist may container more mount + * entries (e.g. from other included files) than lxc_conf->unexpanded_config . + */ +extern int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, + const char *lxc_name, const char *newpath, + const char *newname); + +/* To be called from functions in lxccontainer.c: Get lower directory for + * overlay rootfs. + */ +extern char *ovl_get_lower(const char *rootfs_path); + +/* Get rootfs path for overlay backed containers. Allocated memory must be freed + * by caller. + */ +extern char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen); + +/* Create upper- and workdirs for overlay mounts. + */ +extern int ovl_mkdir(const struct mntent *mntent, + const struct lxc_rootfs *rootfs, const char *lxc_name, + const char *lxc_path); + +#endif /* __LXC_OVERLAY_H */ diff -Nru lxc-2.0.8/src/lxc/storage/rbd.c lxc-2.1.0/src/lxc/storage/rbd.c --- lxc-2.0.8/src/lxc/storage/rbd.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/rbd.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,265 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ +#include /* Required for PRIu64 to work. */ +#include +#include +#include +#include + +#include "log.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +lxc_log_define(rbd, lxc); + +struct rbd_args { + const char *osd_pool_name; + const char *rbd_name; + const char *size; +}; + +int rbd_create_wrapper(void *data) +{ + struct rbd_args *args = data; + + execlp("rbd", "rbd", "create", "--pool", args->osd_pool_name, + args->rbd_name, "--size", args->size, (char *)NULL); + + return -1; +} + +int rbd_map_wrapper(void *data) +{ + struct rbd_args *args = data; + + execlp("rbd", "rbd", "map", "--pool", args->osd_pool_name, + args->rbd_name, (char *)NULL); + + return -1; +} + +int rbd_unmap_wrapper(void *data) +{ + struct rbd_args *args = data; + + execlp("rbd", "rbd", "unmap", args->rbd_name, (char *)NULL); + + return -1; +} + +int rbd_delete_wrapper(void *data) +{ + struct rbd_args *args = data; + + execlp("rbd", "rbd", "rm", args->rbd_name, (char *)NULL); + + return -1; +} + +int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + ERROR("rbd clonepaths not implemented"); + return -1; +} + +int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *rbdpool, *fstype; + uint64_t size; + int ret, len; + char sz[24]; + const char *cmd_args[2]; + char cmd_output[MAXPATHLEN]; + const char *rbdname = n; + struct rbd_args args = {0}; + + if (!specs) + return -1; + + rbdpool = specs->rbd.rbdpool; + if (!rbdpool) + rbdpool = lxc_global_config_value("lxc.bdev.rbd.rbdpool"); + + if (specs->rbd.rbdname) + rbdname = specs->rbd.rbdname; + + /* source device /dev/rbd/lxc/ctn */ + len = strlen(rbdpool) + strlen(rbdname) + 4 + 11; + bdev->src = malloc(len); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len, "rbd:/dev/rbd/%s/%s", rbdpool, rbdname); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + /* fssize is in bytes */ + size = specs->fssize; + if (!size) + size = DEFAULT_FS_SIZE; + + /* in megabytes for rbd tool */ + ret = snprintf(sz, 24, "%" PRIu64, size / 1024 / 1024); + if (ret < 0 || ret >= 24) { + ERROR("Failed to create string"); + return -1; + } + + args.osd_pool_name = rbdpool; + args.rbd_name = rbdname; + args.size = sz; + ret = run_command(cmd_output, sizeof(cmd_output), rbd_create_wrapper, + (void *)&args); + if (ret < 0) { + ERROR("Failed to create rbd storage volume \"%s\": %s", rbdname, + cmd_output); + return -1; + } + + ret = run_command(cmd_output, sizeof(cmd_output), rbd_map_wrapper, + (void *)&args); + if (ret < 0) { + ERROR("Failed to map rbd storage volume \"%s\": %s", rbdname, + cmd_output); + return -1; + } + + fstype = specs->fstype; + if (!fstype) + fstype = DEFAULT_FSTYPE; + + cmd_args[0] = fstype; + cmd_args[1] = lxc_storage_get_path(bdev->src, bdev->type); + ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, + (void *)cmd_args); + if (ret < 0) { + ERROR("Failed to map rbd storage volume \"%s\": %s", rbdname, + cmd_output); + return -1; + } + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + ret = mkdir_p(bdev->dest, 0755); + if (ret < 0 && errno != EEXIST) { + ERROR("Failed to create directory \"%s\"", bdev->dest); + return -1; + } + + TRACE("Created rbd storage volume \"%s\"", bdev->dest); + return 0; +} + +int rbd_destroy(struct lxc_storage *orig) +{ + int ret; + char *src; + char *rbdfullname; + char cmd_output[MAXPATHLEN]; + struct rbd_args args = {0}; + + src = lxc_storage_get_path(orig->src, orig->type); + if (file_exists(src)) { + args.rbd_name = src; + ret = run_command(cmd_output, sizeof(cmd_output), + rbd_unmap_wrapper, (void *)&args); + if (ret < 0) { + ERROR("Failed to map rbd storage volume \"%s\": %s", + src, cmd_output); + return -1; + } + } + + rbdfullname = alloca(strlen(src) - 8); + strcpy(rbdfullname, &src[9]); + args.rbd_name = rbdfullname; + ret = run_command(cmd_output, sizeof(cmd_output), + rbd_delete_wrapper, (void *)&args); + if (ret < 0) { + ERROR("Failed to delete rbd storage volume \"%s\": %s", + rbdfullname, cmd_output); + return -1; + } + + return 0; +} + +bool rbd_detect(const char *path) +{ + if (!strncmp(path, "rbd:", 4)) + return true; + + if (!strncmp(path, "/dev/rbd/", 9)) + return true; + + return false; +} + +int rbd_mount(struct lxc_storage *bdev) +{ + char *src; + + if (strcmp(bdev->type, "rbd")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + src = lxc_storage_get_path(bdev->src, bdev->type); + if (!file_exists(src)) { + /* If blkdev does not exist it should be mapped, because it is + * not persistent on reboot. + */ + ERROR("Block device %s is not mapped.", bdev->src); + return -1; + } + + return mount_unknown_fs(src, bdev->dest, bdev->mntopts); +} + +int rbd_umount(struct lxc_storage *bdev) +{ + if (strcmp(bdev->type, "rbd")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + return umount(bdev->dest); +} diff -Nru lxc-2.0.8/src/lxc/storage/rbd.h lxc-2.1.0/src/lxc/storage/rbd.h --- lxc-2.0.8/src/lxc/storage/rbd.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/rbd.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_RDB_H +#define __LXC_RDB_H + +#define _GNU_SOURCE +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern int rbd_destroy(struct lxc_storage *orig); +extern bool rbd_detect(const char *path); +extern int rbd_mount(struct lxc_storage *bdev); +extern int rbd_umount(struct lxc_storage *bdev); + +#endif /* __LXC_RDB_H */ diff -Nru lxc-2.0.8/src/lxc/storage/rsync.c lxc-2.1.0/src/lxc/storage/rsync.c --- lxc-2.0.8/src/lxc/storage/rsync.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/rsync.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,135 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "rsync.h" +#include "storage.h" +#include "utils.h" + +lxc_log_define(rsync, lxc); + +int lxc_storage_rsync_exec_wrapper(void *data) +{ + struct rsync_data *arg = data; + return lxc_rsync(arg); +} + +int lxc_rsync_exec_wrapper(void *data) +{ + int ret; + struct rsync_data_char *args = data; + + ret = lxc_switch_uid_gid(0, 0); + if (ret < 0) + return -1; + + ret = lxc_setgroups(0, NULL); + if (ret < 0) + return -1; + + return lxc_rsync_exec(args->src, args->dest); +} + +int lxc_rsync_exec(const char *src, const char *dest) +{ + int ret; + size_t l; + char *s; + + l = strlen(src) + 2; + s = malloc(l); + if (!s) + return -1; + + ret = snprintf(s, l, "%s", src); + if (ret < 0 || (size_t)ret >= l) + return -1; + + s[l - 2] = '/'; + s[l - 1] = '\0'; + + execlp("rsync", "rsync", "-aHXS", "--delete", s, dest, (char *)NULL); + return -1; +} + +int lxc_rsync(struct rsync_data *data) +{ + int ret; + char *dest, *src; + struct lxc_storage *orig = data->orig, *new = data->new; + + ret = unshare(CLONE_NEWNS); + if (ret < 0) { + SYSERROR("Failed to unshare CLONE_NEWNS"); + return -1; + } + + ret = detect_shared_rootfs(); + if (ret) { + ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); + if (ret < 0) + SYSERROR("Failed to make \"/\" a slave mount"); + } + + ret = orig->ops->mount(orig); + if (ret < 0) { + ERROR("Failed mounting \"%s\" on \"%s\"", orig->src, orig->dest); + return -1; + } + + ret = new->ops->mount(new); + if (ret < 0) { + ERROR("Failed mounting \"%s\" onto \"%s\"", new->src, new->dest); + return -1; + } + + ret = lxc_switch_uid_gid(0, 0); + if (ret < 0) + return -1; + + ret = lxc_setgroups(0, NULL); + if (ret < 0) + return -1; + + src = lxc_storage_get_path(orig->dest, orig->type); + dest = lxc_storage_get_path(new->dest, new->type); + + ret = lxc_rsync_exec(src, dest); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\"", src, dest); + return -1; + } + + return 0; +} diff -Nru lxc-2.0.8/src/lxc/storage/rsync.h lxc-2.1.0/src/lxc/storage/rsync.h --- lxc-2.0.8/src/lxc/storage/rsync.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/rsync.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_RSYNC_H +#define __LXC_RSYNC_H + +#define _GNU_SOURCE +#include + +struct rsync_data { + struct lxc_storage *orig; + struct lxc_storage *new; +}; + +struct rsync_data_char { + char *src; + char *dest; +}; + +/* new helpers */ +extern int lxc_rsync_exec_wrapper(void *data); +extern int lxc_storage_rsync_exec_wrapper(void *data); +extern int lxc_rsync_exec(const char *src, const char *dest); +extern int lxc_rsync(struct rsync_data *data); + +#endif /* __LXC_RSYNC_H */ diff -Nru lxc-2.0.8/src/lxc/storage/storage.c lxc-2.1.0/src/lxc/storage/storage.c --- lxc-2.0.8/src/lxc/storage/storage.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/storage.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,698 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aufs.h" +#include "btrfs.h" +#include "conf.h" +#include "config.h" +#include "dir.h" +#include "error.h" +#include "log.h" +#include "loop.h" +#include "lvm.h" +#include "lxc.h" +#include "lxclock.h" +#include "nbd.h" +#include "namespace.h" +#include "overlay.h" +#include "parse.h" +#include "rbd.h" +#include "rsync.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" +#include "zfs.h" + +#ifndef BLKGETSIZE64 +#define BLKGETSIZE64 _IOR(0x12, 114, size_t) +#endif + +lxc_log_define(storage, lxc); + +/* aufs */ +static const struct lxc_storage_ops aufs_ops = { + .detect = &aufs_detect, + .mount = &aufs_mount, + .umount = &aufs_umount, + .clone_paths = &aufs_clonepaths, + .destroy = &aufs_destroy, + .create = &aufs_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = true, + .can_backup = true, +}; + +/* btrfs */ +static const struct lxc_storage_ops btrfs_ops = { + .detect = &btrfs_detect, + .mount = &btrfs_mount, + .umount = &btrfs_umount, + .clone_paths = &btrfs_clonepaths, + .destroy = &btrfs_destroy, + .create = &btrfs_create, + .copy = &btrfs_create_clone, + .snapshot = &btrfs_create_snapshot, + .can_snapshot = true, + .can_backup = true, +}; + +/* dir */ +static const struct lxc_storage_ops dir_ops = { + .detect = &dir_detect, + .mount = &dir_mount, + .umount = &dir_umount, + .clone_paths = &dir_clonepaths, + .destroy = &dir_destroy, + .create = &dir_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = false, + .can_backup = true, +}; + +/* loop */ +static const struct lxc_storage_ops loop_ops = { + .detect = &loop_detect, + .mount = &loop_mount, + .umount = &loop_umount, + .clone_paths = &loop_clonepaths, + .destroy = &loop_destroy, + .create = &loop_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = false, + .can_backup = true, +}; + +/* lvm */ +static const struct lxc_storage_ops lvm_ops = { + .detect = &lvm_detect, + .mount = &lvm_mount, + .umount = &lvm_umount, + .clone_paths = &lvm_clonepaths, + .destroy = &lvm_destroy, + .create = &lvm_create, + .copy = &lvm_create_clone, + .snapshot = &lvm_create_snapshot, + .can_snapshot = true, + .can_backup = false, +}; + +/* nbd */ +const struct lxc_storage_ops nbd_ops = { + .detect = &nbd_detect, + .mount = &nbd_mount, + .umount = &nbd_umount, + .clone_paths = &nbd_clonepaths, + .destroy = &nbd_destroy, + .create = &nbd_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = true, + .can_backup = false, +}; + +/* overlay */ +static const struct lxc_storage_ops ovl_ops = { + .detect = &ovl_detect, + .mount = &ovl_mount, + .umount = &ovl_umount, + .clone_paths = &ovl_clonepaths, + .destroy = &ovl_destroy, + .create = &ovl_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = true, + .can_backup = true, +}; + +/* rbd */ +static const struct lxc_storage_ops rbd_ops = { + .detect = &rbd_detect, + .mount = &rbd_mount, + .umount = &rbd_umount, + .clone_paths = &rbd_clonepaths, + .destroy = &rbd_destroy, + .create = &rbd_create, + .copy = NULL, + .snapshot = NULL, + .can_snapshot = false, + .can_backup = false, +}; + +/* zfs */ +static const struct lxc_storage_ops zfs_ops = { + .detect = &zfs_detect, + .mount = &zfs_mount, + .umount = &zfs_umount, + .clone_paths = &zfs_clonepaths, + .destroy = &zfs_destroy, + .create = &zfs_create, + .copy = &zfs_copy, + .snapshot = &zfs_snapshot, + .can_snapshot = true, + .can_backup = true, +}; + +struct lxc_storage_type { + const char *name; + const struct lxc_storage_ops *ops; +}; + +static const struct lxc_storage_type bdevs[] = { + { .name = "dir", .ops = &dir_ops, }, + { .name = "zfs", .ops = &zfs_ops, }, + { .name = "lvm", .ops = &lvm_ops, }, + { .name = "rbd", .ops = &rbd_ops, }, + { .name = "btrfs", .ops = &btrfs_ops, }, + { .name = "aufs", .ops = &aufs_ops, }, + { .name = "overlay", .ops = &ovl_ops, }, + { .name = "overlayfs", .ops = &ovl_ops, }, + { .name = "loop", .ops = &loop_ops, }, + { .name = "nbd", .ops = &nbd_ops, }, +}; + +static const size_t numbdevs = sizeof(bdevs) / sizeof(struct lxc_storage_type); + +static const struct lxc_storage_type *get_storage_by_name(const char *name) +{ + size_t i, cmplen; + + cmplen = strcspn(name, ":"); + if (cmplen == 0) + return NULL; + + for (i = 0; i < numbdevs; i++) + if (strncmp(bdevs[i].name, name, cmplen) == 0) + break; + + if (i == numbdevs) + return NULL; + + DEBUG("Detected rootfs type \"%s\"", bdevs[i].name); + return &bdevs[i]; +} + +const struct lxc_storage_type *storage_query(struct lxc_conf *conf, + const char *src) +{ + size_t i; + const struct lxc_storage_type *bdev; + + bdev = get_storage_by_name(src); + if (bdev) + return bdev; + + for (i = 0; i < numbdevs; i++) + if (bdevs[i].ops->detect(src)) + break; + + if (i == numbdevs) + return NULL; + + DEBUG("Detected rootfs type \"%s\"", bdevs[i].name); + return &bdevs[i]; +} + +struct lxc_storage *storage_get(const char *type) +{ + size_t i; + struct lxc_storage *bdev; + + for (i = 0; i < numbdevs; i++) { + if (strcmp(bdevs[i].name, type) == 0) + break; + } + + if (i == numbdevs) + return NULL; + + bdev = malloc(sizeof(struct lxc_storage)); + if (!bdev) + return NULL; + + memset(bdev, 0, sizeof(struct lxc_storage)); + bdev->ops = bdevs[i].ops; + bdev->type = bdevs[i].name; + + if (!strcmp(bdev->type, "aufs")) + WARN("The \"aufs\" driver will is deprecated and will soon be " + "removed. For similar functionality see the \"overlay\" " + "storage driver"); + + return bdev; +} + +static struct lxc_storage *do_storage_create(const char *dest, const char *type, + const char *cname, + struct bdev_specs *specs) +{ + + struct lxc_storage *bdev; + + if (!type) + type = "dir"; + + bdev = storage_get(type); + if (!bdev) + return NULL; + + if (bdev->ops->create(bdev, dest, cname, specs) < 0) { + storage_put(bdev); + return NULL; + } + + return bdev; +} + +bool storage_can_backup(struct lxc_conf *conf) +{ + struct lxc_storage *bdev = storage_init(conf, NULL, NULL, NULL); + bool ret; + + if (!bdev) + return false; + + ret = bdev->ops->can_backup; + storage_put(bdev); + return ret; +} + +/* If we're not snaphotting, then storage_copy becomes a simple case of mount + * the original, mount the new, and rsync the contents. + */ +struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname, + const char *lxcpath, const char *bdevtype, + int flags, const char *bdevdata, + uint64_t newsize, bool *needs_rdep) +{ + int ret; + struct lxc_storage *orig, *new; + char *src_no_prefix; + bool snap = flags & LXC_CLONE_SNAPSHOT; + bool maybe_snap = flags & LXC_CLONE_MAYBE_SNAPSHOT; + bool keepbdevtype = flags & LXC_CLONE_KEEPBDEVTYPE; + const char *src = c->lxc_conf->rootfs.path; + const char *oldname = c->name; + const char *oldpath = c->config_path; + struct rsync_data data = {0}; + char cmd_output[MAXPATHLEN] = {0}; + + /* 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)) { + ERROR("Original rootfs path \"%s\" does not include container " + "name \"%s\"", src, oldname); + return NULL; + } + + orig = storage_init(c->lxc_conf, src, NULL, NULL); + if (!orig) { + ERROR("Failed to detect storage driver for \"%s\"", src); + return NULL; + } + + if (!orig->dest) { + int ret; + size_t len; + struct stat sb; + + len = strlen(oldpath) + strlen(oldname) + strlen("/rootfs") + 2; + orig->dest = malloc(len); + if (!orig->dest) { + ERROR("Failed to allocate memory"); + goto on_error_put_orig; + } + + ret = snprintf(orig->dest, len, "%s/%s/rootfs", oldpath, oldname); + if (ret < 0 || (size_t)ret >= len) { + ERROR("Failed to create string"); + goto on_error_put_orig; + } + + ret = stat(orig->dest, &sb); + if (ret < 0 && errno == ENOENT) { + ret = mkdir_p(orig->dest, 0755); + if (ret < 0) + WARN("Failed to create directoy \"%s\"", orig->dest); + } + } + + /* Special case for snapshot. If the caller requested maybe_snapshot and + * keepbdevtype and the backing store is directory, then proceed with a + * 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 overlay. */ + if (!bdevtype && !keepbdevtype && snap && !strcmp(orig->type, "dir")) + bdevtype = "overlay"; + + if (am_unpriv() && !unpriv_snap_allowed(orig, bdevtype, snap, maybe_snap)) { + ERROR("Unsupported snapshot type \"%s\" for unprivileged users", + bdevtype ? bdevtype : "(null)"); + goto on_error_put_orig; + } + + *needs_rdep = false; + if (bdevtype) { + if (snap && !strcmp(orig->type, "lvm") && + !lvm_is_thin_volume(orig->src)) + *needs_rdep = true; + else if (!strcmp(bdevtype, "overlay") || + !strcmp(bdevtype, "overlayfs")) + *needs_rdep = true; + } else { + if (!snap && strcmp(oldpath, lxcpath)) + bdevtype = "dir"; + else + bdevtype = orig->type; + + if (!strcmp(bdevtype, "overlay") || + !strcmp(bdevtype, "overlayfs")) + *needs_rdep = true; + } + + /* get new bdev type */ + new = storage_get(bdevtype); + if (!new) { + ERROR("Failed to initialize \"%s\" storage driver", + bdevtype ? bdevtype : orig->type); + goto on_error_put_orig; + } + TRACE("Initialized \"%s\" storage driver", new->type); + + /* create new paths */ + ret = new->ops->clone_paths(orig, new, oldname, cname, oldpath, lxcpath, + snap, newsize, c->lxc_conf); + if (ret < 0) { + ERROR("Failed creating new paths for clone of \"%s\"", src); + goto on_error_put_new; + } + + /* When we create an overlay snapshot of an overlay container in the + * snapshot directory under "//snaps/" we don't need to + * record a dependency. If we would restore would also fail. + */ + if ((!strcmp(new->type, "overlay") || + !strcmp(new->type, "overlayfs")) && + ret == LXC_CLONE_SNAPSHOT) + *needs_rdep = false; + + /* btrfs */ + if (!strcmp(orig->type, "btrfs") && !strcmp(new->type, "btrfs")) { + bool bret = false; + if (snap || btrfs_same_fs(orig->dest, new->dest) == 0) + bret = new->ops->snapshot(c->lxc_conf, orig, new, 0); + else + bret = new->ops->copy(c->lxc_conf, orig, new, 0); + if (!bret) + goto on_error_put_new; + + goto on_success; + } + + /* lvm */ + if (!strcmp(orig->type, "lvm") && !strcmp(new->type, "lvm")) { + bool bret = false; + if (snap) + bret = new->ops->snapshot(c->lxc_conf, orig, + new, newsize); + else + bret = new->ops->copy(c->lxc_conf, orig, new, newsize); + if (!bret) + goto on_error_put_new; + + goto on_success; + } + + /* zfs */ + if (!strcmp(orig->type, "zfs") && !strcmp(new->type, "zfs")) { + bool bret = false; + + if (snap) + bret = new->ops->snapshot(c->lxc_conf, orig, new, + newsize); + else + bret = new->ops->copy(c->lxc_conf, orig, new, newsize); + if (!bret) + goto on_error_put_new; + + goto on_success; + } + + if (strcmp(bdevtype, "btrfs")) { + if (!strcmp(new->type, "overlay") || !strcmp(new->type, "overlayfs")) + src_no_prefix = ovl_get_lower(new->src); + else + src_no_prefix = lxc_storage_get_path(new->src, new->type); + + if (am_unpriv()) { + ret = chown_mapped_root(src_no_prefix, c->lxc_conf); + if (ret < 0) + WARN("Failed to chown \"%s\"", new->src); + } + } + + if (snap) + goto on_success; + + /* rsync the contents from source to target */ + data.orig = orig; + data.new = new; + if (am_unpriv()) + ret = userns_exec_1(c->lxc_conf, lxc_storage_rsync_exec_wrapper, + &data, "lxc_storage_rsync_exec_wrapper"); + else + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_storage_rsync_exec_wrapper, (void *)&data); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", orig->dest, + new->dest, + cmd_output[0] != '\0' ? ": " : "", + cmd_output[0] != '\0' ? cmd_output : ""); + goto on_error_put_new; + } + +on_success: + storage_put(orig); + + return new; + +on_error_put_new: + storage_put(new); + +on_error_put_orig: + storage_put(orig); + + return NULL; +} + +/* Create a backing store for a container. + * If successful, return a struct bdev *, with the bdev mounted and ready + * for use. Before completing, the caller will need to call the + * umount operation and storage_put(). + * @dest: the mountpoint (i.e. /var/lib/lxc/$name/rootfs) + * @type: the bdevtype (dir, btrfs, zfs, rbd, etc) + * @cname: the container name + * @specs: details about the backing store to create, like fstype + */ +struct lxc_storage *storage_create(const char *dest, const char *type, + const char *cname, struct bdev_specs *specs) +{ + struct lxc_storage *bdev; + char *best_options[] = {"btrfs", "zfs", "lvm", "dir", "rbd", NULL}; + + if (!type) + return do_storage_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++) { + bdev = do_storage_create(dest, best_options[i], cname, + specs); + if (bdev) + return bdev; + } + + return NULL; + } + + /* -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_storage_create(dest, token, cname, specs))) + return bdev; + } + } + + return do_storage_create(dest, type, cname, specs); +} + +bool storage_destroy(struct lxc_conf *conf) +{ + struct lxc_storage *r; + bool ret = false; + + r = storage_init(conf, conf->rootfs.path, conf->rootfs.mount, NULL); + if (!r) + return ret; + + if (r->ops->destroy(r) == 0) + ret = true; + + storage_put(r); + return ret; +} + +struct lxc_storage *storage_init(struct lxc_conf *conf, const char *src, + const char *dst, const char *mntopts) +{ + struct lxc_storage *bdev; + const struct lxc_storage_type *q; + + BUILD_BUG_ON(LXC_STORAGE_INTERNAL_OVERLAY_RESTORE <= LXC_CLONE_MAXFLAGS); + + if (!src) + src = conf->rootfs.path; + + if (!src) + return NULL; + + q = storage_query(conf, src); + if (!q) + return NULL; + + bdev = malloc(sizeof(struct lxc_storage)); + if (!bdev) + return NULL; + + memset(bdev, 0, sizeof(struct lxc_storage)); + bdev->ops = q->ops; + bdev->type = q->name; + if (mntopts) + bdev->mntopts = strdup(mntopts); + if (src) + bdev->src = strdup(src); + if (dst) + bdev->dest = strdup(dst); + if (strcmp(bdev->type, "nbd") == 0) + bdev->nbd_idx = conf->nbd_idx; + + if (!strcmp(bdev->type, "aufs")) + WARN("The \"aufs\" driver will is deprecated and will soon be " + "removed. For similar functionality see the \"overlay\" " + "storage driver"); + + return bdev; +} + +bool storage_is_dir(struct lxc_conf *conf, const char *path) +{ + struct lxc_storage *orig; + bool bret = false; + + orig = storage_init(conf, path, NULL, NULL); + if (!orig) + return bret; + + if (strcmp(orig->type, "dir") == 0) + bret = true; + + storage_put(orig); + return bret; +} + +void storage_put(struct lxc_storage *bdev) +{ + free(bdev->mntopts); + free(bdev->src); + free(bdev->dest); + free(bdev); +} + +bool rootfs_is_blockdev(struct lxc_conf *conf) +{ + const struct lxc_storage_type *q; + struct stat st; + int ret; + + if (!conf->rootfs.path || strcmp(conf->rootfs.path, "/") == 0 || + strlen(conf->rootfs.path) == 0) + return false; + + ret = stat(conf->rootfs.path, &st); + if (ret == 0 && S_ISBLK(st.st_mode)) + return true; + + q = storage_query(conf, conf->rootfs.path); + if (!q) + return false; + + if (strcmp(q->name, "lvm") == 0 || + strcmp(q->name, "loop") == 0 || + strcmp(q->name, "nbd") == 0 || + strcmp(q->name, "rbd") == 0 || + strcmp(q->name, "zfs") == 0) + return true; + + return false; +} + +char *lxc_storage_get_path(char *src, const char *prefix) +{ + size_t prefix_len; + + prefix_len = strlen(prefix); + if (!strncmp(src, prefix, prefix_len) && (*(src + prefix_len) == ':')) + return (src + prefix_len + 1); + + return src; +} diff -Nru lxc-2.0.8/src/lxc/storage/storage.h lxc-2.1.0/src/lxc/storage/storage.h --- lxc-2.0.8/src/lxc/storage/storage.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/storage.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_STORAGE_H +#define __LXC_STORAGE_H + +#include "config.h" +#include +#include + +#include + +#if IS_BIONIC +#include <../include/lxcmntent.h> +#else +#include +#endif + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +#ifndef MS_REC +#define MS_REC 16384 +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 2 +#endif + +#ifndef MS_SLAVE +#define MS_SLAVE (1 << 19) +#endif + +#ifndef MS_RELATIME +#define MS_RELATIME (1 << 21) +#endif + +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1 << 24) +#endif + +#define DEFAULT_FS_SIZE 1073741824 +#define DEFAULT_FSTYPE "ext3" + +#define LXC_STORAGE_INTERNAL_OVERLAY_RESTORE (1 << 6) + +struct lxc_storage; + +struct lxc_storage_ops { + /* detect whether path is of this bdev type */ + bool (*detect)(const char *path); + + /* mount requires src and dest to be set. */ + int (*mount)(struct lxc_storage *bdev); + int (*umount)(struct lxc_storage *bdev); + int (*destroy)(struct lxc_storage *bdev); + int (*create)(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); + /* given original mount, rename the paths for cloned container */ + int (*clone_paths)(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); + bool (*copy)(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); + bool (*snapshot)(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); + bool can_snapshot; + bool can_backup; +}; + +/* When lxc is mounting a rootfs, then src will be the "lxc.rootfs.path" value, + * dest will be the mount dir (i.e. "/lxc") When clone or create is + * doing so, then dest will be "//rootfs", since we may need + * to rsync from one to the other. + */ +struct lxc_storage { + const struct lxc_storage_ops *ops; + const char *type; + char *src; + char *dest; + char *mntopts; + /* Turn the following into a union if need be. */ + /* lofd is the open fd for the mounted loopback file. */ + int lofd; + /* index for the connected nbd device. */ + int nbd_idx; + int flags; +}; + +extern bool storage_is_dir(struct lxc_conf *conf, const char *path); +extern bool storage_can_backup(struct lxc_conf *conf); + +/* Instantiate a lxc_storage object. The src is used to determine which blockdev + * type this should be. The dst and data are optional, and will be used in case + * of mount/umount. + * + * The source will be "dir:/var/lib/lxc/c1" or "lvm:/dev/lxc/c1". For other + * backing stores, this will allow additional options. In particular, + * "overlayfs:/var/lib/lxc/canonical/rootfs:/var/lib/lxc/c1/delta" will mean use + * /var/lib/lxc/canonical/rootfs as lower dir, and /var/lib/lxc/c1/delta as the + * upper, writeable layer. + */ +extern struct lxc_storage *storage_init(struct lxc_conf *conf, const char *src, + const char *dst, const char *data); + +extern struct lxc_storage *storage_copy(struct lxc_container *c0, + const char *cname, const char *lxcpath, + const char *bdevtype, int flags, + const char *bdevdata, uint64_t newsize, + bool *needs_rdep); +extern struct lxc_storage *storage_create(const char *dest, const char *type, + const char *cname, + struct bdev_specs *specs); +extern void storage_put(struct lxc_storage *bdev); +extern bool storage_destroy(struct lxc_conf *conf); + +/* callback function to be used with userns_exec_1() */ +extern int storage_destroy_wrapper(void *data); +extern bool rootfs_is_blockdev(struct lxc_conf *conf); +extern char *lxc_storage_get_path(char *src, const char *prefix); + +#endif /* #define __LXC_STORAGE_H */ diff -Nru lxc-2.0.8/src/lxc/storage/storage_utils.c lxc-2.1.0/src/lxc/storage/storage_utils.c --- lxc-2.0.8/src/lxc/storage/storage_utils.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/storage_utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,485 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Christian Brauner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "nbd.h" +#include "parse.h" +#include "storage.h" +#include "storage_utils.h" +#include "utils.h" + +#ifndef BLKGETSIZE64 +#define BLKGETSIZE64 _IOR(0x12, 114, size_t) +#endif + +lxc_log_define(storage_utils, lxc); + +/* the bulk of this needs to become a common helper */ +char *dir_new_path(char *src, const char *oldname, const char *name, + const char *oldpath, const char *lxcpath) +{ + char *ret, *p, *p2; + int l1, l2, nlen; + + nlen = strlen(src) + 1; + l1 = strlen(oldpath); + p = src; + /* if src starts with oldpath, look for oldname only after + * that path */ + if (strncmp(src, oldpath, l1) == 0) { + p += l1; + nlen += (strlen(lxcpath) - l1); + } + l2 = strlen(oldname); + while ((p = strstr(p, oldname)) != NULL) { + p += l2; + nlen += strlen(name) - l2; + } + + ret = malloc(nlen); + if (!ret) + return NULL; + + p = ret; + if (strncmp(src, oldpath, l1) == 0) { + p += sprintf(p, "%s", lxcpath); + src += l1; + } + + while ((p2 = strstr(src, oldname)) != NULL) { + /* copy text up to oldname */ + strncpy(p, src, p2 - src); + /* move target pointer (p) */ + p += p2 - src; + /* print new name in place of oldname */ + p += sprintf(p, "%s", name); + /* move src to end of oldname */ + src = p2 + l2; + } + /* copy the rest of src */ + sprintf(p, "%s", src); + return ret; +} + +/* + * attach_block_device returns true if all went well, + * meaning either a block device was attached or was not + * needed. It returns false if something went wrong and + * container startup should be stopped. + */ +bool attach_block_device(struct lxc_conf *conf) +{ + char *path; + + if (!conf->rootfs.path) + return true; + + path = conf->rootfs.path; + if (!requires_nbd(path)) + return true; + + path = strchr(path, ':'); + if (!path) + return false; + + path++; + if (!attach_nbd(path, conf)) + return false; + + return true; +} + +/* + * return block size of dev->src in units of bytes + */ +int blk_getsize(struct lxc_storage *bdev, uint64_t *size) +{ + int fd, ret; + char *src; + + src = lxc_storage_get_path(bdev->src, bdev->type); + fd = open(src, O_RDONLY); + if (fd < 0) + return -1; + + /* size of device in bytes */ + ret = ioctl(fd, BLKGETSIZE64, size); + close(fd); + return ret; +} + +void detach_block_device(struct lxc_conf *conf) +{ + if (conf->nbd_idx != -1) + detach_nbd_idx(conf->nbd_idx); +} + +/* + * Given a lxc_storage (presumably blockdev-based), detect the fstype + * by trying mounting (in a private mntns) it. + * @lxc_storage: bdev to investigate + * @type: preallocated char* in which to write the fstype + * @len: length of passed in char* + * Returns length of fstype, of -1 on error + */ +int detect_fs(struct lxc_storage *bdev, char *type, int len) +{ + int p[2], ret; + size_t linelen; + pid_t pid; + FILE *f; + char *sp1, *sp2, *sp3, *srcdev, *line = NULL; + + if (!bdev || !bdev->src || !bdev->dest) + return -1; + + srcdev = lxc_storage_get_path(bdev->src, bdev->type); + + ret = pipe(p); + if (ret < 0) + return -1; + + if ((pid = fork()) < 0) + return -1; + + if (pid > 0) { + int status; + close(p[1]); + memset(type, 0, len); + ret = read(p[0], type, len - 1); + close(p[0]); + if (ret < 0) { + SYSERROR("error reading from pipe"); + wait(&status); + return -1; + } else if (ret == 0) { + ERROR("child exited early - fstype not found"); + wait(&status); + return -1; + } + wait(&status); + type[len - 1] = '\0'; + INFO("detected fstype %s for %s", type, srcdev); + return ret; + } + + if (unshare(CLONE_NEWNS) < 0) + exit(1); + + if (detect_shared_rootfs()) { + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { + SYSERROR("Failed to make / rslave"); + ERROR("Continuing..."); + } + } + + ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts); + if (ret < 0) { + ERROR("failed mounting %s onto %s to detect fstype", srcdev, + bdev->dest); + exit(1); + } + + /* if symlink, get the real dev name */ + char devpath[MAXPATHLEN]; + char *l = linkderef(srcdev, devpath); + if (!l) + exit(1); + f = fopen("/proc/self/mounts", "r"); + if (!f) + exit(1); + + while (getline(&line, &linelen, f) != -1) { + sp1 = strchr(line, ' '); + if (!sp1) + exit(1); + *sp1 = '\0'; + if (strcmp(line, l)) + continue; + sp2 = strchr(sp1 + 1, ' '); + if (!sp2) + exit(1); + *sp2 = '\0'; + sp3 = strchr(sp2 + 1, ' '); + if (!sp3) + exit(1); + *sp3 = '\0'; + sp2++; + if (write(p[1], sp2, strlen(sp2)) != strlen(sp2)) + exit(1); + + exit(0); + } + + exit(1); +} + +int do_mkfs_exec_wrapper(void *args) +{ + int ret; + char *mkfs; + char **data = args; + /* strlen("mkfs.") + * + + * strlen(data[0]) + * + + * \0 + */ + size_t len = 5 + strlen(data[0]) + 1; + + mkfs = malloc(len); + if (!mkfs) + return -1; + + ret = snprintf(mkfs, len, "mkfs.%s", data[0]); + if (ret < 0 || (size_t)ret >= len) { + free(mkfs); + return -1; + } + + TRACE("executing \"%s %s\"", mkfs, data[1]); + execlp(mkfs, mkfs, data[1], (char *)NULL); + SYSERROR("failed to run \"%s %s \"", mkfs, data[1]); + return -1; +} + +/* + * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm + * is a block device. + */ +int is_blktype(struct lxc_storage *b) +{ + if (strcmp(b->type, "lvm") == 0) + return 1; + + return 0; +} + +int mount_unknown_fs(const char *rootfs, const char *target, + const char *options) +{ + size_t i; + int ret; + struct cbarg { + const char *rootfs; + const char *target; + const char *options; + } cbarg = { + .rootfs = rootfs, + .target = target, + .options = options, + }; + + /* + * find the filesystem type with brute force: + * first we check with /etc/filesystems, in case the modules + * are auto-loaded and fall back to the supported kernel fs + */ + char *fsfile[] = { + "/etc/filesystems", + "/proc/filesystems", + }; + + for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) { + if (access(fsfile[i], F_OK)) + continue; + + ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg); + if (ret < 0) { + ERROR("failed to parse '%s'", fsfile[i]); + return -1; + } + + if (ret) + return 0; + } + + ERROR("failed to determine fs type for '%s'", rootfs); + return -1; +} + +/* + * These are copied from conf.c. However as conf.c will be moved to using + * the callback system, they can be pulled from there eventually, so we + * don't need to pollute utils.c with these low level functions + */ +int find_fstype_cb(char *buffer, void *data) +{ + struct cbarg { + const char *rootfs; + const char *target; + const char *options; + } *cbarg = data; + + unsigned long mntflags; + char *mntdata; + char *fstype; + + /* we don't try 'nodev' entries */ + if (strstr(buffer, "nodev")) + return 0; + + fstype = buffer; + fstype += lxc_char_left_gc(fstype, strlen(fstype)); + fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0'; + + DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs, + cbarg->target, fstype); + + if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) { + free(mntdata); + return 0; + } + + if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) { + DEBUG("mount failed with error: %s", strerror(errno)); + free(mntdata); + return 0; + } + + free(mntdata); + + INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs, + cbarg->target, fstype); + + return 1; +} + +char *linkderef(char *path, char *dest) +{ + struct stat sbuf; + ssize_t ret; + + ret = stat(path, &sbuf); + if (ret < 0) + return NULL; + + if (!S_ISLNK(sbuf.st_mode)) + return path; + + ret = readlink(path, dest, MAXPATHLEN); + if (ret < 0) { + SYSERROR("error reading link %s", path); + return NULL; + } else if (ret >= MAXPATHLEN) { + ERROR("link in %s too long", path); + return NULL; + } + dest[ret] = '\0'; + + return dest; +} + +/* + * is an unprivileged user allowed to make this kind of snapshot + */ +bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, + bool maybesnap) +{ + if (!t) { + /* New type will be same as original (unless snap && b->type == + * dir, in which case it will be overlayfs -- which is also + * allowed). + */ + if (strcmp(b->type, "dir") == 0 || + strcmp(b->type, "aufs") == 0 || + strcmp(b->type, "overlay") == 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, "overlay") == 0 || + strcmp(t, "overlayfs") == 0 || + strcmp(t, "btrfs") == 0 || + strcmp(t, "loop") == 0) + return true; + + return false; +} + +bool is_valid_storage_type(const char *type) +{ + if (strcmp(type, "dir") == 0 || + strcmp(type, "btrfs") == 0 || + strcmp(type, "aufs") == 0 || + strcmp(type, "loop") == 0 || + strcmp(type, "lvm") == 0 || + strcmp(type, "nbd") == 0 || + strcmp(type, "overlay") == 0 || + strcmp(type, "overlayfs") == 0 || + strcmp(type, "rbd") == 0 || + strcmp(type, "zfs") == 0) + return true; + + return false; +} + +int storage_destroy_wrapper(void *data) +{ + struct lxc_conf *conf = data; + + if (setgid(0) < 0) { + ERROR("Failed to setgid to 0"); + return -1; + } + + if (setgroups(0, NULL) < 0) + WARN("Failed to clear groups"); + + if (setuid(0) < 0) { + ERROR("Failed to setuid to 0"); + return -1; + } + + if (!storage_destroy(conf)) + return -1; + + return 0; +} diff -Nru lxc-2.0.8/src/lxc/storage/storage_utils.h lxc-2.1.0/src/lxc/storage/storage_utils.h --- lxc-2.0.8/src/lxc/storage/storage_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/storage_utils.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Christian Brauner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_STORAGE_UTILS_H +#define __LXC_STORAGE_UTILS_H + +#include "config.h" +#include +#include +#include + +#include "conf.h" + +struct lxc_storage; +struct lxc_conf; + +extern char *dir_new_path(char *src, const char *oldname, const char *name, + const char *oldpath, const char *lxcpath); +extern bool attach_block_device(struct lxc_conf *conf); +extern void detach_block_device(struct lxc_conf *conf); +extern int blk_getsize(struct lxc_storage *bdev, uint64_t *size); +extern int detect_fs(struct lxc_storage *bdev, char *type, int len); +extern int do_mkfs_exec_wrapper(void *args); +extern int is_blktype(struct lxc_storage *b); +extern int mount_unknown_fs(const char *rootfs, const char *target, + const char *options); +extern int find_fstype_cb(char *buffer, void *data); +extern char *linkderef(char *path, char *dest); +extern bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, + bool maybesnap); +extern bool is_valid_storage_type(const char *type); +extern int storage_destroy_wrapper(void *data); + +#endif /* __LXC_STORAGE_UTILS_H */ diff -Nru lxc-2.0.8/src/lxc/storage/zfs.c lxc-2.1.0/src/lxc/storage/zfs.c --- lxc-2.0.8/src/lxc/storage/zfs.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/zfs.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,771 @@ +/* + * 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 "config.h" +#include "log.h" +#include "parse.h" +#include "rsync.h" +#include "storage.h" +#include "utils.h" +#include "zfs.h" + +lxc_log_define(zfs, lxc); + +struct zfs_args { + const char *dataset; + const char *snapshot; + const char *options; + void *argv; +}; + +int zfs_detect_exec_wrapper(void *data) +{ + struct zfs_args *args = data; + + execlp("zfs", "zfs", "get", "type", "-H", "-o", "name", args->dataset, + (char *)NULL); + + return -1; +} + +int zfs_create_exec_wrapper(void *args) +{ + struct zfs_args *zfs_args = args; + + execvp("zfs", zfs_args->argv); + + return -1; +} + +int zfs_delete_exec_wrapper(void *args) +{ + struct zfs_args *zfs_args = args; + + execlp("zfs", "zfs", "destroy", "-r", zfs_args->dataset, (char *)NULL); + + return -1; +} + +int zfs_snapshot_exec_wrapper(void *args) +{ + struct zfs_args *zfs_args = args; + + execlp("zfs", "zfs", "snapshot", "-r", zfs_args->snapshot, (char *)NULL); + + return -1; +} + +int zfs_clone_exec_wrapper(void *args) +{ + struct zfs_args *zfs_args = args; + + execlp("zfs", "zfs", "clone", "-p", "-o", "canmount=noauto", "-o", + zfs_args->options, zfs_args->snapshot, zfs_args->dataset, + (char *)NULL); + + return -1; +} + +int zfs_get_parent_snapshot_exec_wrapper(void *args) +{ + struct zfs_args *zfs_args = args; + + execlp("zfs", "zfs", "get", "origin", "-o", "value", "-H", + zfs_args->dataset, (char *)NULL); + + return -1; +} + +static bool zfs_list_entry(const char *path, char *output, size_t inlen) +{ + struct lxc_popen_FILE *f; + bool found = false; + + f = lxc_popen("zfs list 2> /dev/null"); + if (f == NULL) { + SYSERROR("popen failed"); + return false; + } + + while (fgets(output, inlen, f->f)) { + if (strstr(output, path)) { + found = true; + break; + } + } + (void)lxc_pclose(f); + + return found; +} + +bool zfs_detect(const char *path) +{ + int ret; + char *dataset; + struct zfs_args cmd_args = {0}; + char cmd_output[MAXPATHLEN] = {0}; + + if (!strncmp(path, "zfs:", 4)) + return true; + + /* This is a legacy zfs setup where the rootfs path + * "//rootfs" is given. + */ + if (*path == '/') { + bool found; + char *output = malloc(LXC_LOG_BUFFER_SIZE); + + if (!output) { + ERROR("out of memory"); + return false; + } + + found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); + free(output); + return found; + } + + cmd_args.dataset = path; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_detect_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to detect zfs dataset \"%s\": %s", path, cmd_output); + return false; + } + + if (cmd_output[0] == '\0') + return false; + + /* remove any possible leading and trailing whitespace */ + dataset = cmd_output; + dataset += lxc_char_left_gc(dataset, strlen(dataset)); + dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0'; + + if (strcmp(dataset, path)) + return false; + + return true; +} + +int zfs_mount(struct lxc_storage *bdev) +{ + int ret; + size_t oldlen, newlen, totallen; + char *mntdata, *src, *tmp; + unsigned long mntflags; + char cmd_output[MAXPATHLEN] = {0}; + + if (strcmp(bdev->type, "zfs")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata); + if (ret < 0) { + ERROR("Failed to parse mount options"); + free(mntdata); + return -22; + } + + /* This is a legacy zfs setup where the rootfs path + * "//rootfs" is given and we do a bind-mount. + */ + src = lxc_storage_get_path(bdev->src, bdev->type); + if (*src == '/') { + bool found; + + found = zfs_list_entry(src, cmd_output, sizeof(cmd_output)); + if (!found) { + ERROR("Failed to find zfs entry \"%s\"", src); + return -1; + } + + tmp = strchr(cmd_output, ' '); + if (!tmp) { + ERROR("Failed to detect zfs dataset associated with " + "\"%s\"", src); + return -1; + } + *tmp = '\0'; + src = cmd_output; + } + + /* ',' + * + + * strlen("zfsutil") + * + + * ',' + * + + * strlen(mntpoint=) + * + + * strlen(src) + * + + * '\0' + */ + newlen = 1 + 7 + 1 + 9 + strlen(src) + 1; + oldlen = mntdata ? strlen(mntdata) : 0; + totallen = (newlen + oldlen); + tmp = realloc(mntdata, totallen); + if (!tmp) { + ERROR("Failed to reallocate memory"); + free(mntdata); + return -1; + } + mntdata = tmp; + + ret = snprintf((mntdata + oldlen), newlen, ",zfsutil,mntpoint=%s", src); + if (ret < 0 || (size_t)ret >= newlen) { + ERROR("Failed to create string"); + free(mntdata); + return -1; + } + + ret = mount(src, bdev->dest, "zfs", mntflags, mntdata); + free(mntdata); + if (ret < 0 && errno != EBUSY) { + SYSERROR("Failed to mount \"%s\" on \"%s\"", src, bdev->dest); + return -1; + } + + TRACE("Mounted \"%s\" on \"%s\"", src, bdev->dest); + return 0; +} + +int zfs_umount(struct lxc_storage *bdev) +{ + int ret; + + if (strcmp(bdev->type, "zfs")) + return -22; + + if (!bdev->src || !bdev->dest) + return -22; + + ret = umount(bdev->dest); + if (ret < 0) + SYSERROR("Failed to unmount \"%s\"", bdev->dest); + else + TRACE("Unmounted \"%s\"", bdev->dest); + + return ret; +} + +bool zfs_copy(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + int ret; + char cmd_output[MAXPATHLEN], option[MAXPATHLEN]; + struct rsync_data data = {0, 0}; + struct zfs_args cmd_args = {0}; + char *argv[] = {"zfs", /* 0 */ + "create", /* 1 */ + "-o", "", /* 2, 3 */ + "-o", "canmount=noauto", /* 4, 5 */ + "-p", /* 6 */ + "", /* 7 */ + NULL}; + + /* mountpoint */ + ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("Failed to create string"); + return false; + } + argv[3] = option; + argv[7] = lxc_storage_get_path(new->src, new->type); + + cmd_args.argv = argv; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_create_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output); + return false; + } else if (cmd_output[0] != '\0') { + INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output); + } else { + TRACE("Created zfs dataset \"%s\"", new->src); + } + + ret = mkdir_p(new->dest, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", new->dest); + return false; + } + + data.orig = orig; + data.new = new; + ret = run_command(cmd_output, sizeof(cmd_output), + lxc_storage_rsync_exec_wrapper, (void *)&data); + if (ret < 0) { + ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest, + new->dest, cmd_output); + return false; + } + TRACE("Rsynced from \"%s\" to \"%s\"", orig->dest, new->dest); + + return true; +} + +/* create read-only snapshot and create a clone from it */ +bool zfs_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize) +{ + int ret; + size_t snapshot_len, len; + char *orig_src, *tmp, *snap_name, *snapshot; + struct zfs_args cmd_args = {0}; + char cmd_output[MAXPATHLEN] = {0}, option[MAXPATHLEN]; + + orig_src = lxc_storage_get_path(orig->src, orig->type); + if (*orig_src == '/') { + bool found; + + found = zfs_list_entry(orig_src, cmd_output, sizeof(cmd_output)); + if (!found) { + ERROR("Failed to find zfs entry \"%s\"", orig_src); + return false; + } + + tmp = strchr(cmd_output, ' '); + if (!tmp) { + ERROR("Failed to detect zfs dataset associated with " + "\"%s\"", orig_src); + return false; + } + *tmp = '\0'; + orig_src = cmd_output; + } + + snapshot = strdup(orig_src); + if (!snapshot) { + ERROR("Failed to duplicate string \"%s\"", orig_src); + return false; + } + + snap_name = strrchr(new->src, '/'); + if (!snap_name) { + ERROR("Failed to detect \"/\" in \"%s\"", new->src); + free(snapshot); + return false; + } + snap_name++; + + /* strlen(snapshot) + * + + * @ + * + + * strlen(cname) + * + + * \0 + */ + snapshot_len = strlen(snapshot); + len = snapshot_len + 1 + strlen(snap_name) + 1; + tmp = realloc(snapshot, len); + if (!tmp) { + ERROR("Failed to reallocate memory"); + free(snapshot); + return false; + } + snapshot = tmp; + + len -= snapshot_len; + ret = snprintf(snapshot + snapshot_len, len, "@%s", snap_name); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + free(snapshot); + return false; + } + + cmd_args.snapshot = snapshot; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_snapshot_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to create zfs snapshot \"%s\": %s", snapshot, cmd_output); + free(snapshot); + return false; + } else if (cmd_output[0] != '\0') { + INFO("Created zfs snapshot \"%s\": %s", snapshot, cmd_output); + } else { + TRACE("Created zfs snapshot \"%s\"", snapshot); + } + + ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("Failed to create string"); + free(snapshot); + return -1; + } + + cmd_args.dataset = lxc_storage_get_path(new->src, new->type); + cmd_args.snapshot = snapshot; + cmd_args.options = option; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_clone_exec_wrapper, (void *)&cmd_args); + if (ret < 0) + ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output); + else if (cmd_output[0] != '\0') + INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output); + else + TRACE("Created zfs dataset \"%s\"", new->src); + + free(snapshot); + return true; +} + +int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, const char *oldpath, + const char *lxcpath, int snap, uint64_t newsize, + struct lxc_conf *conf) +{ + char *dataset, *orig_src, *tmp; + int ret; + size_t dataset_len, len; + char cmd_output[MAXPATHLEN] = {0}; + + 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; + } + + orig_src = lxc_storage_get_path(orig->src, orig->type); + if (!strcmp(orig->type, "zfs")) { + size_t len; + if (*orig_src == '/') { + bool found; + + found = zfs_list_entry(orig_src, cmd_output, + sizeof(cmd_output)); + if (!found) { + ERROR("Failed to find zfs entry \"%s\"", orig_src); + return -1; + } + + tmp = strchr(cmd_output, ' '); + if (!tmp) { + ERROR("Failed to detect zfs dataset associated " + "with \"%s\"", orig_src); + return -1; + } + *tmp = '\0'; + orig_src = cmd_output; + } + + tmp = strrchr(orig_src, '/'); + if (!tmp) { + ERROR("Failed to detect \"/\" in \"%s\"", orig_src); + return -1; + } + + len = tmp - orig_src; + dataset = strndup(orig_src, len); + if (!dataset) { + ERROR("Failed to duplicate string \"%zu\" " + "bytes of string \"%s\"", len, orig_src); + return -1; + } + } else { + tmp = (char *)lxc_global_config_value("lxc.bdev.zfs.root"); + if (!tmp) { + ERROR("The \"lxc.bdev.zfs.root\" property is not set"); + return -1; + } + + dataset = strdup(tmp); + if (!dataset) { + ERROR("Failed to duplicate string \"%s\"", tmp); + return -1; + } + } + + /* strlen("zfs:") = 4 + * + + * strlen(dataset) + * + + * / = 1 + * + + * strlen(cname) + * + + * \0 + */ + dataset_len = strlen(dataset); + len = 4 + dataset_len + 1 + strlen(cname) + 1; + new->src = realloc(dataset, len); + if (!new->src) { + ERROR("Failed to reallocate memory"); + free(dataset); + return -1; + } + memmove(new->src + 4, new->src, dataset_len); + memmove(new->src, "zfs:", 4); + + len -= dataset_len - 4; + ret = snprintf(new->src + dataset_len + 4, len, "/%s", cname); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + + /* strlen(lxcpath) + * + + * / + * + + * strlen(cname) + * + + * / + * + + * strlen("rootfs") + * + + * \0 + */ + len = strlen(lxcpath) + 1 + strlen(cname) + 1 + strlen("rootfs") + 1; + new->dest = malloc(len); + if (!new->dest) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string \"%s/%s/rootfs\"", lxcpath, cname); + return -1; + } + + ret = mkdir_p(new->dest, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", new->dest); + return -1; + } + + return 0; +} + +int zfs_destroy(struct lxc_storage *orig) +{ + int ret; + char *dataset, *src, *tmp; + bool found; + char *parent_snapshot = NULL; + struct zfs_args cmd_args = {0}; + char cmd_output[MAXPATHLEN] = {0}; + + src = lxc_storage_get_path(orig->src, orig->type); + + /* This is a legacy zfs setup where the rootfs path + * "//rootfs" is given. + */ + if (*src == '/') { + char *tmp; + + found = zfs_list_entry(src, cmd_output, sizeof(cmd_output)); + if (!found) { + ERROR("Failed to find zfs entry \"%s\"", orig->src); + return -1; + } + + tmp = strchr(cmd_output, ' '); + if (!tmp) { + ERROR("Failed to detect zfs dataset associated with " + "\"%s\"", cmd_output); + return -1; + } + *tmp = '\0'; + dataset = cmd_output; + } else { + cmd_args.dataset = src; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_detect_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to detect zfs dataset \"%s\": %s", src, + cmd_output); + return -1; + } + + if (cmd_output[0] == '\0') { + ERROR("Failed to detect zfs dataset \"%s\"", src); + return -1; + } + + /* remove any possible leading and trailing whitespace */ + dataset = cmd_output; + dataset += lxc_char_left_gc(dataset, strlen(dataset)); + dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0'; + + if (strcmp(dataset, src)) { + ERROR("Detected dataset \"%s\" does not match expected " + "dataset \"%s\"", dataset, src); + return -1; + } + } + + cmd_args.dataset = strdup(dataset); + if (!cmd_args.dataset) { + ERROR("Failed to duplicate string \"%s\"", dataset); + return -1; + } + + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_get_parent_snapshot_exec_wrapper, + (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to retrieve parent snapshot of zfs dataset " + "\"%s\": %s", dataset, cmd_output); + free((void *)cmd_args.dataset); + return -1; + } else { + INFO("Retrieved parent snapshot of zfs dataset \"%s\": %s", src, + cmd_output); + } + + /* remove any possible leading and trailing whitespace */ + tmp = cmd_output; + tmp += lxc_char_left_gc(tmp, strlen(tmp)); + tmp[lxc_char_right_gc(tmp, strlen(tmp))] = '\0'; + + /* check whether the dataset has a parent snapshot */ + if (*tmp != '-' && *(tmp + 1) != '\0') { + parent_snapshot = strdup(tmp); + if (!parent_snapshot) { + ERROR("Failed to duplicate string \"%s\"", tmp); + free((void *)cmd_args.dataset); + return -1; + } + } + + /* delete dataset */ + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_delete_exec_wrapper, (void *)&cmd_args); + if (ret < 0) { + ERROR("Failed to delete zfs dataset \"%s\": %s", dataset, + cmd_output); + free((void *)cmd_args.dataset); + free(parent_snapshot); + return -1; + } else if (cmd_output[0] != '\0') { + INFO("Deleted zfs dataset \"%s\": %s", src, cmd_output); + } else { + INFO("Deleted zfs dataset \"%s\"", src); + } + + free((void *)cmd_args.dataset); + + /* Not a clone so nothing more to do. */ + if (!parent_snapshot) + return 0; + + /* delete parent snapshot */ + cmd_args.dataset = parent_snapshot; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_delete_exec_wrapper, (void *)&cmd_args); + if (ret < 0) + ERROR("Failed to delete zfs snapshot \"%s\": %s", dataset, cmd_output); + else if (cmd_output[0] != '\0') + INFO("Deleted zfs snapshot \"%s\": %s", src, cmd_output); + else + INFO("Deleted zfs snapshot \"%s\"", src); + + free((void *)cmd_args.dataset); + return ret; +} + +int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *zfsroot; + int ret; + size_t len; + struct zfs_args cmd_args = {0}; + char cmd_output[MAXPATHLEN], option[MAXPATHLEN]; + char *argv[] = {"zfs", /* 0 */ + "create", /* 1 */ + "-o", "", /* 2, 3 */ + "-o", "canmount=noauto", /* 4, 5 */ + "-p", /* 6 */ + "", /* 7 */ + NULL}; + + if (!specs || !specs->zfs.zfsroot) + zfsroot = lxc_global_config_value("lxc.bdev.zfs.root"); + else + zfsroot = specs->zfs.zfsroot; + + bdev->dest = strdup(dest); + if (!bdev->dest) { + ERROR("Failed to duplicate string \"%s\"", dest); + return -1; + } + + len = strlen(zfsroot) + 1 + strlen(n) + 1; + /* strlen("zfs:") */ + len += 4; + bdev->src = malloc(len); + if (!bdev->src) { + ERROR("Failed to allocate memory"); + return -1; + } + + ret = snprintf(bdev->src, len, "zfs:%s/%s", zfsroot, n); + if (ret < 0 || ret >= len) { + ERROR("Failed to create string"); + return -1; + } + argv[7] = lxc_storage_get_path(bdev->src, bdev->type); + + ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", bdev->dest); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("Failed to create string"); + return -1; + } + argv[3] = option; + + cmd_args.argv = argv; + ret = run_command(cmd_output, sizeof(cmd_output), + zfs_create_exec_wrapper, (void *)&cmd_args); + if (ret < 0) + ERROR("Failed to create zfs dataset \"%s\": %s", bdev->src, cmd_output); + else if (cmd_output[0] != '\0') + INFO("Created zfs dataset \"%s\": %s", bdev->src, cmd_output); + else + TRACE("Created zfs dataset \"%s\"", bdev->src); + + ret = mkdir_p(bdev->dest, 0755); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", bdev->dest); + return -1; + } + + return ret; +} diff -Nru lxc-2.0.8/src/lxc/storage/zfs.h lxc-2.1.0/src/lxc/storage/zfs.h --- lxc-2.0.8/src/lxc/storage/zfs.h 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/storage/zfs.h 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LXC_ZFS_H +#define __LXC_ZFS_H + +#define _GNU_SOURCE +#include +#include +#include + +struct lxc_storage; + +struct bdev_specs; + +struct lxc_conf; + +extern int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, + const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, int snap, + uint64_t newsize, struct lxc_conf *conf); +extern int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, + struct bdev_specs *specs); +extern int zfs_destroy(struct lxc_storage *orig); +extern bool zfs_detect(const char *path); +extern int zfs_mount(struct lxc_storage *bdev); +extern int zfs_umount(struct lxc_storage *bdev); + +extern bool zfs_copy(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); +extern bool zfs_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, + struct lxc_storage *new, uint64_t newsize); + +#endif /* __LXC_ZFS_H */ diff -Nru lxc-2.0.8/src/lxc/sync.c lxc-2.1.0/src/lxc/sync.c --- lxc-2.0.8/src/lxc/sync.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/sync.c 2017-09-06 02:32:37.000000000 +0000 @@ -40,7 +40,7 @@ ret = read(fd, &sync, sizeof(sync)); if (ret < 0) { - ERROR("sync wait failure : %m"); + ERROR("sync wait failure : %s", strerror(errno)); return -1; } @@ -71,7 +71,7 @@ int sync = sequence; if (write(fd, &sync, sizeof(sync)) < 0) { - ERROR("sync wake failure : %m"); + ERROR("sync wake failure : %s", strerror(errno)); return -1; } return 0; @@ -86,63 +86,63 @@ int lxc_sync_barrier_parent(struct lxc_handler *handler, int sequence) { - return __sync_barrier(handler->sv[0], sequence); + return __sync_barrier(handler->sync_sock[0], sequence); } int lxc_sync_barrier_child(struct lxc_handler *handler, int sequence) { - return __sync_barrier(handler->sv[1], sequence); + return __sync_barrier(handler->sync_sock[1], sequence); } int lxc_sync_wake_parent(struct lxc_handler *handler, int sequence) { - return __sync_wake(handler->sv[0], sequence); + return __sync_wake(handler->sync_sock[0], sequence); } int lxc_sync_wait_parent(struct lxc_handler *handler, int sequence) { - return __sync_wait(handler->sv[0], sequence); + return __sync_wait(handler->sync_sock[0], sequence); } int lxc_sync_wait_child(struct lxc_handler *handler, int sequence) { - return __sync_wait(handler->sv[1], sequence); + return __sync_wait(handler->sync_sock[1], sequence); } int lxc_sync_wake_child(struct lxc_handler *handler, int sequence) { - return __sync_wake(handler->sv[1], sequence); + return __sync_wake(handler->sync_sock[1], sequence); } int lxc_sync_init(struct lxc_handler *handler) { int ret; - ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, handler->sv); + ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, handler->sync_sock); if (ret) { SYSERROR("failed to create synchronization socketpair"); return -1; } /* Be sure we don't inherit this after the exec */ - fcntl(handler->sv[0], F_SETFD, FD_CLOEXEC); + fcntl(handler->sync_sock[0], F_SETFD, FD_CLOEXEC); return 0; } void lxc_sync_fini_child(struct lxc_handler *handler) { - if (handler->sv[0] != -1) { - close(handler->sv[0]); - handler->sv[0] = -1; + if (handler->sync_sock[0] != -1) { + close(handler->sync_sock[0]); + handler->sync_sock[0] = -1; } } void lxc_sync_fini_parent(struct lxc_handler *handler) { - if (handler->sv[1] != -1) { - close(handler->sv[1]); - handler->sv[1] = -1; + if (handler->sync_sock[1] != -1) { + close(handler->sync_sock[1]); + handler->sync_sock[1] = -1; } } diff -Nru lxc-2.0.8/src/lxc/sync.h lxc-2.1.0/src/lxc/sync.h --- lxc-2.0.8/src/lxc/sync.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/sync.h 2017-09-06 02:32:37.000000000 +0000 @@ -30,6 +30,8 @@ LXC_SYNC_CONFIGURE, LXC_SYNC_POST_CONFIGURE, LXC_SYNC_CGROUP, + LXC_SYNC_CGROUP_UNSHARE, + LXC_SYNC_CGROUP_LIMITS, LXC_SYNC_POST_CGROUP, LXC_SYNC_RESTART, LXC_SYNC_POST_RESTART, diff -Nru lxc-2.0.8/src/lxc/tools/lxc_attach.c lxc-2.1.0/src/lxc/tools/lxc_attach.c --- lxc-2.0.8/src/lxc/tools/lxc_attach.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_attach.c 2017-09-06 02:32:37.000000000 +0000 @@ -301,15 +301,12 @@ /* In the case of lxc-attach our peer pty will always be the current * controlling terminal. We clear whatever was set by the user for - * lxc.console.path here and set it to "/dev/tty". Doing this will (a) - * prevent segfaults when the container has been setup with - * lxc.console = none and (b) provide an easy way to ensure that we - * always do the correct thing. strdup() must be used since console.path - * is free()ed when we call lxc_container_put(). */ + * lxc.console.path here and set it NULL. lxc_console_peer_default() + * will then try to open /dev/tty. If the process doesn't have a + * controlling terminal we should still proceed. + */ free(conf->console.path); - conf->console.path = strdup("/dev/tty"); - if (!conf->console.path) - return -1; + conf->console.path = NULL; /* Create pty on the host. */ if (lxc_console_create(conf) < 0) @@ -318,7 +315,7 @@ conf->console.descr = &descr; /* Shift ttys to container. */ - if (ttys_shift_ids(conf) < 0) { + if (lxc_ttys_shift_ids(conf) < 0) { ERROR("Failed to shift tty into container"); goto err1; } @@ -374,6 +371,7 @@ { int ret = -1, r; int wexit = 0; + struct lxc_log log; pid_t pid; lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL}; @@ -389,8 +387,13 @@ if (!my_args.log_file) my_args.log_file = "none"; - r = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath[0]); + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + r = lxc_log_init(&log); if (r) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_autostart.c lxc-2.1.0/src/lxc/tools/lxc_autostart.c --- lxc-2.0.8/src/lxc/tools/lxc_autostart.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_autostart.c 2017-09-06 02:32:37.000000000 +0000 @@ -352,12 +352,19 @@ struct lxc_container **containers = NULL; struct lxc_list **c_groups_lists = NULL; struct lxc_list *cmd_group; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); - 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_cgroup.c lxc-2.1.0/src/lxc/tools/lxc_cgroup.c --- lxc-2.0.8/src/lxc/tools/lxc_cgroup.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_cgroup.c 2017-09-06 02:32:37.000000000 +0000 @@ -67,6 +67,7 @@ { char *state_object = NULL, *value = NULL; struct lxc_container *c; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); @@ -74,8 +75,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc-checkconfig.in lxc-2.1.0/src/lxc/tools/lxc-checkconfig.in --- lxc-2.0.8/src/lxc/tools/lxc-checkconfig.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc-checkconfig.in 2017-09-06 02:32:37.000000000 +0000 @@ -28,16 +28,27 @@ is_set $1 RES=$? - + RET=1 if [ $RES -eq 0 ]; then - $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL + $SETCOLOR_SUCCESS && echo -n "enabled" && $SETCOLOR_NORMAL + RET=0 else if [ ! -z "$mandatory" ] && [ "$mandatory" = yes ]; then - $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL + $SETCOLOR_FAILURE && echo -n "required" && $SETCOLOR_NORMAL else - $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL + $SETCOLOR_WARNING && echo -n "missing" && $SETCOLOR_NORMAL fi fi + return $RET +} + +is_probed() { + lsmod | grep $1 > /dev/null + if [ $? -eq 0 ]; then + echo -n ", loaded" + else + echo -n ", not loaded" + fi } if [ ! -f $CONFIG ]; then @@ -84,21 +95,26 @@ echo "--- Namespaces ---" echo -n "Namespaces: " && is_enabled CONFIG_NAMESPACES yes +echo echo -n "Utsname namespace: " && is_enabled CONFIG_UTS_NS +echo echo -n "Ipc namespace: " && is_enabled CONFIG_IPC_NS yes +echo echo -n "Pid namespace: " && is_enabled CONFIG_PID_NS yes +echo echo -n "User namespace: " && is_enabled CONFIG_USER_NS +echo if is_set CONFIG_USER_NS; then - if type newuidmap > /dev/null 2>&1; then - f=`type -P newuidmap` + if which newuidmap > /dev/null 2>&1; then + f=`which newuidmap` if [ ! -u "${f}" ]; then echo "Warning: newuidmap is not setuid-root" fi else echo "newuidmap is not installed" fi - if type newgidmap > /dev/null 2>&1; then - f=`type -P newgidmap` + if which newgidmap > /dev/null 2>&1; then + f=`which newgidmap` if [ ! -u "${f}" ]; then echo "Warning: newgidmap is not setuid-root" fi @@ -107,62 +123,118 @@ fi fi echo -n "Network namespace: " && is_enabled CONFIG_NET_NS +echo if ([ $KVER_MAJOR -lt 4 ]) || ([ $KVER_MAJOR -eq 4 ] && [ $KVER_MINOR -lt 7 ]); then echo -n "Multiple /dev/pts instances: " && is_enabled DEVPTS_MULTIPLE_INSTANCES fi echo + echo "--- Control groups ---" +echo -n "Cgroups: " && is_enabled CONFIG_CGROUPS +echo + print_cgroups() { # print all mountpoints for cgroup filesystems awk '$1 !~ /#/ && $3 == mp { print $2; } ; END { exit(0); } ' "mp=$1" "$2" ; } -CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -n 1` +CGROUP_V1_MNTS=`print_cgroups cgroup /proc/self/mounts` +echo +echo "Cgroup v1 mount points: " +echo "$CGROUP_V1_MNTS" +echo -echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes +CGROUP_V2_MNTS=`print_cgroups cgroup2 /proc/self/mounts` +echo "Cgroup v2 mount points: " +echo "$CGROUP_V2_MNTS" +echo +CGROUP_SYSTEMD_MNTPT=`echo "$CGROUP_V1_MNTS" | grep "/systemd"` +if [ -z "$CGROUP_SYSTEMD_MNTPT" ]; then + echo -n "Cgroup v1 systemd controller: " + "$SETCOLOR_FAILURE" && echo -n "missing" && $SETCOLOR_NORMAL + echo +fi + +CGROUP_FREEZER_MNTPT=`echo "$CGROUP_V1_MNTS" | grep "/freezer"` +if [ -z "$CGROUP_FREEZER_MNTPT" ]; then + echo -n "Cgroup v1 freezer controller: " + "$SETCOLOR_FAILURE" && echo -n "missing" && $SETCOLOR_NORMAL + echo +fi + +CGROUP_MNT_PATH=`echo "$CGROUP_V1_MNTS" | head -n 1` if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then - echo -n "Cgroup clone_children flag: " && + echo -n "Cgroup v1 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 + echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED +echo + echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT +echo + echo -n "Cgroup memory controller: " if ([ $KVER_MAJOR -ge 3 ] && [ $KVER_MINOR -ge 6 ]) || ([ $KVER_MAJOR -gt 3 ]); then is_enabled CONFIG_MEMCG else is_enabled CONFIG_CGROUP_MEM_RES_CTLR fi -is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS echo + +is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS && echo +echo + echo "--- Misc ---" -echo -n "Veth pair device: " && is_enabled CONFIG_VETH -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 -n "FUSE (for use with lxcfs): " && is_enabled CONFIG_FUSE_FS +echo -n "Veth pair device: " && is_enabled CONFIG_VETH && is_probed veth +echo +echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN && is_probed macvlan +echo +echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q && is_probed 8021q +echo +echo -n "Bridges: " && is_enabled CONFIG_BRIDGE && is_probed bridge +echo +echo -n "Advanced netfilter: " && is_enabled CONFIG_NETFILTER_ADVANCED && is_probed nf_tables +echo +echo -n "CONFIG_NF_NAT_IPV4: " && is_enabled CONFIG_NF_NAT_IPV4 && is_probed nf_nat_ipv4 +echo +echo -n "CONFIG_NF_NAT_IPV6: " && is_enabled CONFIG_NF_NAT_IPV6 && is_probed nf_nat_ipv6 +echo +echo -n "CONFIG_IP_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP_NF_TARGET_MASQUERADE && is_probed nf_nat_masquerade_ipv4 +echo +echo -n "CONFIG_IP6_NF_TARGET_MASQUERADE: " && is_enabled CONFIG_IP6_NF_TARGET_MASQUERADE && is_probed nf_nat_masquerade_ipv6 +echo +echo -n "CONFIG_NETFILTER_XT_TARGET_CHECKSUM: " && is_enabled CONFIG_NETFILTER_XT_TARGET_CHECKSUM && is_probed xt_CHECKSUM +echo -n "CONFIG_NETFILTER_XT_MATCH_COMMENT: " && is_enabled CONFIG_NETFILTER_XT_MATCH_COMMENT && is_probed xt_comment +echo +echo -n "FUSE (for use with lxcfs): " && is_enabled CONFIG_FUSE_FS && is_probed fuse +echo echo echo "--- Checkpoint/Restore ---" echo -n "checkpoint restore: " && is_enabled CONFIG_CHECKPOINT_RESTORE +echo echo -n "CONFIG_FHANDLE: " && is_enabled CONFIG_FHANDLE +echo echo -n "CONFIG_EVENTFD: " && is_enabled CONFIG_EVENTFD +echo echo -n "CONFIG_EPOLL: " && is_enabled CONFIG_EPOLL +echo echo -n "CONFIG_UNIX_DIAG: " && is_enabled CONFIG_UNIX_DIAG +echo echo -n "CONFIG_INET_DIAG: " && is_enabled CONFIG_INET_DIAG +echo echo -n "CONFIG_PACKET_DIAG: " && is_enabled CONFIG_PACKET_DIAG +echo echo -n "CONFIG_NETLINK_DIAG: " && is_enabled CONFIG_NETLINK_DIAG - +echo echo -n "File capabilities: " && \ ( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \ is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || \ diff -Nru lxc-2.0.8/src/lxc/tools/lxc_checkpoint.c lxc-2.1.0/src/lxc/tools/lxc_checkpoint.c --- lxc-2.0.8/src/lxc/tools/lxc_checkpoint.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_checkpoint.c 2017-09-06 02:32:37.000000000 +0000 @@ -36,6 +36,10 @@ static bool verbose = false; static bool do_restore = false; static bool daemonize_set = false; +static bool pre_dump = false; +static char *predump_dir = NULL; + +#define OPT_PREDUMP_DIR OPT_USAGE + 1 static const struct option my_longopts[] = { {"checkpoint-dir", required_argument, 0, 'D'}, @@ -44,6 +48,8 @@ {"restore", no_argument, 0, 'r'}, {"daemon", no_argument, 0, 'd'}, {"foreground", no_argument, 0, 'F'}, + {"pre-dump", no_argument, 0, 'p'}, + {"predump-dir", required_argument, 0, OPT_PREDUMP_DIR}, LXC_COMMON_OPTIONS }; @@ -63,6 +69,11 @@ return -1; } + if (pre_dump && do_restore) { + lxc_error(args, "-p not compatible with -r."); + return -1; + } + return 0; } @@ -91,6 +102,14 @@ args->daemonize = 0; daemonize_set = true; break; + case 'p': + pre_dump = true; + break; + case OPT_PREDUMP_DIR: + predump_dir = strdup(arg); + if (!predump_dir) + return -1; + break; } return 0; } @@ -111,6 +130,10 @@ -v, --verbose Enable verbose criu logs\n\ Checkpoint options:\n\ -s, --stop Stop the container after checkpointing.\n\ + -p, --pre-dump Only pre-dump the memory of the container.\n\ + Container keeps on running and following\n\ + checkpoints will only dump the changes.\n\ + --predump-dir=DIR path to images from previous dump (relative to -D)\n\ Restore options:\n\ -d, --daemon Daemonize the container (default)\n\ -F, --foreground Start with the current tty attached to /dev/console\n\ @@ -124,7 +147,9 @@ static bool checkpoint(struct lxc_container *c) { + struct migrate_opts opts; bool ret; + int mode; if (!c->is_running(c)) { fprintf(stderr, "%s not running, not checkpointing.\n", my_args.name); @@ -132,10 +157,24 @@ return false; } - ret = c->checkpoint(c, checkpoint_dir, stop, verbose); + memset(&opts, 0, sizeof(opts)); + + opts.directory = checkpoint_dir; + opts.stop = stop; + opts.verbose = verbose; + opts.predump_dir = predump_dir; + + if (pre_dump) + mode = MIGRATE_PRE_DUMP; + else + mode = MIGRATE_DUMP; + + ret = c->migrate(c, mode, &opts, sizeof(opts)); lxc_container_put(c); - if (!ret) { + /* the migrate() API does not negate the return code like + * checkpoint() and restore() does. */ + if (ret) { fprintf(stderr, "Checkpointing %s failed.\n", my_args.name); return false; } @@ -195,6 +234,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; bool ret; if (lxc_arguments_parse(&my_args, argc, argv)) @@ -203,8 +243,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_clone.c lxc-2.1.0/src/lxc/tools/lxc_clone.c --- lxc-2.0.8/src/lxc/tools/lxc_clone.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_clone.c 2017-09-06 02:32:37.000000000 +0000 @@ -53,7 +53,7 @@ while (isblank(*end)) end++; if (*end == '\0') - ret *= 1024ULL * 1024ULL; // MB by default + ret *= 1024ULL * 1024ULL; /* MB by default */ else if (*end == 'b' || *end == 'B') ret *= 1ULL; else if (*end == 'k' || *end == 'K') @@ -163,9 +163,10 @@ if (keepname) flags |= LXC_CLONE_KEEPNAME; if (keepmac) flags |= LXC_CLONE_KEEPMACADDR; - // vgname and fstype could be supported by sending them through the - // bdevdata. However, they currently are not yet. I'm not convinced - // they are worthwhile. + /* vgname and fstype could be supported by sending them through the + * bdevdata. However, they currently are not yet. I'm not convinced + * they are worthwhile. + */ if (vgname) { printf("Error: vgname not supported\n"); usage(argv[0]); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_console.c lxc-2.1.0/src/lxc/tools/lxc_console.c --- lxc-2.0.8/src/lxc/tools/lxc_console.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_console.c 2017-09-06 02:32:37.000000000 +0000 @@ -98,6 +98,7 @@ { int ret; struct lxc_container *c; + struct lxc_log log; ret = lxc_arguments_parse(&my_args, argc, argv); if (ret) @@ -106,8 +107,14 @@ 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]); + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + ret = lxc_log_init(&log); if (ret) return EXIT_FAILURE; lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_copy.c lxc-2.1.0/src/lxc/tools/lxc_copy.c --- lxc-2.0.8/src/lxc/tools/lxc_copy.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_copy.c 2017-09-06 02:32:37.000000000 +0000 @@ -37,13 +37,13 @@ #include #include "attach.h" -#include "bdev.h" #include "log.h" #include "confile.h" #include "arguments.h" #include "lxc.h" #include "conf.h" #include "state.h" +#include "storage.h" #include "utils.h" #ifndef HAVE_GETSUBOPT @@ -87,6 +87,7 @@ { "keepdata", no_argument, 0, 'D'}, { "keepname", no_argument, 0, 'K'}, { "keepmac", no_argument, 0, 'M'}, + { "tmpfs", no_argument, 0, 't'}, LXC_COMMON_OPTIONS }; @@ -119,6 +120,8 @@ -m, --mount directory to mount into container, either \n\ {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\ -B, --backingstorage=TYPE backingstorage type for the container\n\ + -t, --tmpfs place ephemeral container on a tmpfs\n\ + (WARNING: On reboot all changes made to the container will be lost.)\n\ -L, --fssize size of the new block device for block device containers\n\ -D, --keedata pass together with -e start a persistent snapshot \n\ -K, --keepname keep the hostname of the original container\n\ @@ -130,6 +133,7 @@ .task = CLONE, .daemonize = 1, .quiet = false, + .tmpfs = false, }; static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, @@ -149,6 +153,13 @@ char **args); static void free_mnts(void); static uint64_t get_fssize(char *s); + +/* Place an ephemeral container started with -e flag on a tmpfs. Restrictions + * are that you cannot request the data to be kept while placing the container + * on a tmpfs and that either overlay or aufs backing storage must be used. + */ +static char *mount_tmpfs(const char *oldname, const char *newname, + const char *path, struct lxc_arguments *arg); static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters); static int parse_aufs_mnt(char *mntstring, enum mnttype type); @@ -158,6 +169,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; int flags = 0; int ret = EXIT_FAILURE; @@ -167,8 +179,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); @@ -383,12 +401,13 @@ static int do_clone_ephemeral(struct lxc_container *c, struct lxc_arguments *arg, char **args, int flags) { + char *bdev; + char *premount; char randname[MAXPATHLEN]; unsigned int i; int ret = 0; bool bret = true, started = false; struct lxc_container *clone; - lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; attach_options.env_policy = LXC_ATTACH_CLEAR_ENV; @@ -410,6 +429,23 @@ if (!clone) return -1; + if (arg->tmpfs) { + bdev = c->lxc_conf->rootfs.bdev_type; + if (bdev && strcmp(bdev, "dir")) { + fprintf(stderr, "Cannot currently use tmpfs with %s storage backend.\n", bdev); + goto destroy_and_put; + } + + premount = mount_tmpfs(arg->name, arg->newname, arg->newpath, arg); + if (!premount) + goto destroy_and_put; + + bret = clone->set_config_item(clone, "lxc.hook.pre-mount", premount); + free(premount); + if (!bret) + goto destroy_and_put; + } + if (!arg->keepdata) if (!clone->set_config_item(clone, "lxc.ephemeral", "1")) goto destroy_and_put; @@ -437,6 +473,10 @@ if (!my_args.quiet) printf("Created %s as clone of %s\n", arg->newname, arg->name); + if (arg->tmpfs && !my_args.quiet) + printf("Container is placed on tmpfs.\nRebooting will cause " + "all changes made to it to be lost!\n"); + if (!arg->daemonize && arg->argc) { clone->want_daemonize(clone, true); arg->daemonize = 1; @@ -535,7 +575,7 @@ while (isblank(*end)) end++; if (*end == '\0') { - ret *= 1024ULL * 1024ULL; // MB by default + ret *= 1024ULL * 1024ULL; /* MB by default */ } else if (*end == 'b' || *end == 'B') { ret *= 1ULL; } else if (*end == 'k' || *end == 'K') { @@ -591,6 +631,9 @@ arg = "overlayfs"; args->bdevtype = arg; break; + case 't': + args->tmpfs = true; + break; case 'L': args->fssize = get_fssize(optarg); break; @@ -769,3 +812,89 @@ lxc_free_array((void **)mntarray, free); return -1; } + +/* For ephemeral snapshots backed by overlay or aufs filesystems, this function + * mounts a fresh tmpfs over the containers directory if the user requests it. + * Because we mount a fresh tmpfs over the directory of the container the + * updated /etc/hostname file created during the clone residing in the upperdir + * (currently named "delta0" by default) will be hidden. Hence, if the user + * requests that the old name is not to be kept for the clone, we recreate this + * file on the tmpfs. This should be all that is required to restore the exact + * behaviour we would get with a normal clone. + */ +static char *mount_tmpfs(const char *oldname, const char *newname, + const char *path, struct lxc_arguments *arg) +{ + int ret, fd; + size_t len; + char *premount = NULL; + FILE *fp; + + if (arg->tmpfs && arg->keepdata) { + fprintf(stderr, "%s\n", "A container can only be placed on a " + "tmpfs when storage backend is overlay " + "or aufs."); + goto err_free; + } + + if (arg->tmpfs && !arg->bdevtype) { + arg->bdevtype = "overlayfs"; + } else if (arg->tmpfs && arg->bdevtype && strcmp(arg->bdevtype, "overlayfs") && strcmp(arg->bdevtype, "aufs")) { + fprintf(stderr, "%s\n", "A container can only be placed on a " + "tmpfs when storage backend is overlay " + "or aufs."); + goto err_free; + } + + len = strlen(path) + strlen(newname) + strlen("pre-start-XXXXXX") + /* //\0 */ 3; + premount = malloc(len); + if (!premount) + goto err_free; + + ret = snprintf(premount, len, "%s/%s/pre-start-XXXXXX", path, newname); + if (ret < 0 || (size_t)ret >= len) + goto err_free; + + fd = mkstemp(premount); + if (fd < 0) + goto err_free; + + if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { + SYSERROR("Failed to set close-on-exec on file descriptor."); + goto err_close; + } + + if (chmod(premount, 0755) < 0) + goto err_close; + + fp = fdopen(fd, "r+"); + if (!fp) + goto err_close; + fd = -1; + + ret = fprintf(fp, "#! /bin/sh\n" + "mount -n -t tmpfs -o mode=0755 none %s/%s\n", + path, newname); + if (ret < 0) + goto err_close; + + if (!arg->keepname) { + ret = fprintf(fp, "mkdir -p %s/%s/delta0/etc\n" + "echo %s > %s/%s/delta0/etc/hostname\n", + path, newname, newname, path, newname); + if (ret < 0) + goto err_close; + } + + fclose(fp); + return premount; + +err_close: + if (fd > 0) + close(fd); + else + fclose(fp); +err_free: + free(premount); + return NULL; +} diff -Nru lxc-2.0.8/src/lxc/tools/lxc_create.c lxc-2.1.0/src/lxc/tools/lxc_create.c --- lxc-2.0.8/src/lxc/tools/lxc_create.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_create.c 2017-09-06 02:32:37.000000000 +0000 @@ -27,9 +27,10 @@ #include #include "arguments.h" -#include "bdev.h" #include "log.h" #include "lxc.h" +#include "storage.h" +#include "storage_utils.h" #include "utils.h" lxc_log_define(lxc_create_ui, lxc); @@ -48,7 +49,7 @@ while (isblank(*end)) end++; if (*end == '\0') - ret *= 1024ULL * 1024ULL; // MB by default + ret *= 1024ULL * 1024ULL; /* MB by default */ else if (*end == 'b' || *end == 'B') ret *= 1ULL; else if (*end == 'k' || *end == 'K') @@ -208,6 +209,7 @@ { struct lxc_container *c; struct bdev_specs spec; + struct lxc_log log; int flags = 0; if (lxc_arguments_parse(&my_args, argc, argv)) @@ -216,8 +218,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); @@ -240,10 +248,10 @@ if (strcmp(my_args.bdevtype, "none") == 0) my_args.bdevtype = "dir"; - // Final check whether the user gave use a valid bdev type. + /* Final check whether the user gave use a valid bdev type. */ if (strcmp(my_args.bdevtype, "best") && strcmp(my_args.bdevtype, "_unset") && - !is_valid_bdev_type(my_args.bdevtype)) { + !is_valid_storage_type(my_args.bdevtype)) { fprintf(stderr, "%s is not a valid backing storage type.\n", my_args.bdevtype); exit(EXIT_FAILURE); } diff -Nru lxc-2.0.8/src/lxc/tools/lxc_destroy.c lxc-2.1.0/src/lxc/tools/lxc_destroy.c --- lxc-2.0.8/src/lxc/tools/lxc_destroy.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_destroy.c 2017-09-06 02:32:37.000000000 +0000 @@ -67,6 +67,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; bool bret; if (lxc_arguments_parse(&my_args, argc, argv)) @@ -75,8 +76,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (my_args.quiet) @@ -164,7 +171,7 @@ if (ret < 0 || ret >= MAXPATHLEN) return false; - if (dir_exists(path)) { + if (rmdir(path) < 0 && errno != ENOENT) { if (!quiet) fprintf(stdout, "Destroying %s failed: %s has snapshots.\n", c->name, c->name); return false; @@ -264,7 +271,7 @@ if (ret < 0 || ret >= MAXPATHLEN) return false; - if (dir_exists(path)) + if (rmdir(path) < 0 && errno != ENOENT) bret = c->destroy_with_snapshots(c); else bret = do_destroy(c); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_device.c lxc-2.1.0/src/lxc/tools/lxc_device.c --- lxc-2.0.8/src/lxc/tools/lxc_device.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_device.c 2017-09-06 02:32:37.000000000 +0000 @@ -101,6 +101,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; char *cmd, *dev_name, *dst_name; bool ret = false; @@ -115,8 +116,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) goto err; lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_execute.c lxc-2.1.0/src/lxc/tools/lxc_execute.c --- lxc-2.0.8/src/lxc/tools/lxc_execute.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_execute.c 2017-09-06 02:32:37.000000000 +0000 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -104,9 +105,10 @@ int main(int argc, char *argv[]) { - char *rcfile; - struct lxc_conf *conf; + struct lxc_container *c; + struct lxc_log log; int ret; + bool bret; lxc_list_init(&defines); @@ -116,55 +118,51 @@ if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); - 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); - /* rcfile is specified in the cli option */ - if (my_args.rcfile) - rcfile = (char *)my_args.rcfile; - else { - int rc; - - rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name); - if (rc == -1) { - SYSERROR("failed to allocate memory"); - exit(EXIT_FAILURE); - } - - /* container configuration does not exist */ - if (access(rcfile, F_OK)) { - free(rcfile); - rcfile = NULL; - } - } - - conf = lxc_conf_init(); - if (!conf) { - ERROR("failed to initialize configuration"); + c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) { + ERROR("Failed to create lxc_container"); exit(EXIT_FAILURE); } - if (rcfile && lxc_config_read(rcfile, conf, NULL)) { - ERROR("failed to read configuration file"); - exit(EXIT_FAILURE); + if (my_args.rcfile) { + c->clear_config(c); + if (!c->load_config(c, my_args.rcfile)) { + ERROR("Failed to load rcfile"); + lxc_container_put(c); + exit(EXIT_FAILURE); + } + c->configfile = strdup(my_args.rcfile); + if (!c->configfile) { + ERROR("Out of memory setting new config filename"); + lxc_container_put(c); + exit(EXIT_FAILURE); + } } - if (lxc_config_define_load(&defines, conf)) - exit(EXIT_FAILURE); - if (my_args.uid) - conf->init_uid = my_args.uid; + c->lxc_conf->init_uid = my_args.uid; if (my_args.gid) - conf->init_gid = my_args.gid; - - ret = lxc_execute(my_args.name, my_args.argv, my_args.quiet, conf, my_args.lxcpath[0], false); + c->lxc_conf->init_gid = my_args.gid; - lxc_conf_free(conf); - - if (ret < 0) + c->daemonize = false; + bret = c->start(c, 1, my_args.argv); + ret = c->error_num; + lxc_container_put(c); + if (!bret) { + ERROR("Failed run an application inside container"); exit(EXIT_FAILURE); + } exit(ret); } diff -Nru lxc-2.0.8/src/lxc/tools/lxc_freeze.c lxc-2.1.0/src/lxc/tools/lxc_freeze.c --- lxc-2.0.8/src/lxc/tools/lxc_freeze.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_freeze.c 2017-09-06 02:32:37.000000000 +0000 @@ -57,6 +57,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); @@ -64,8 +65,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_info.c lxc-2.1.0/src/lxc/tools/lxc_info.c --- lxc-2.0.8/src/lxc/tools/lxc_info.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_info.c 2017-09-06 02:32:37.000000000 +0000 @@ -116,18 +116,18 @@ { if (val > 1 << 30) { snprintf(buf, bufsz, "%u.%2.2u GiB", - (int)(val >> 30), - (int)(val & ((1 << 30) - 1)) / 10737419); + (unsigned int)(val >> 30), + (unsigned int)(val & ((1 << 30) - 1)) / 10737419); } else if (val > 1 << 20) { - int x = val + 5243; /* for rounding */ + unsigned int x = val + 5243; /* for rounding */ snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (val > 1 << 10) { - int x = val + 5; /* for rounding */ + unsigned int x = val + 5; /* for rounding */ snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { - snprintf(buf, bufsz, "%u bytes", (int)val); + snprintf(buf, bufsz, "%u bytes", (unsigned int)val); } } @@ -155,15 +155,15 @@ char buf[256]; for(netnr = 0; ;netnr++) { - sprintf(buf, "lxc.network.%d.type", netnr); + sprintf(buf, "lxc.net.%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); + sprintf(buf, "lxc.net.%d.veth.pair", netnr); } else { - sprintf(buf, "lxc.network.%d.link", netnr); + sprintf(buf, "lxc.net.%d.link", netnr); } free(type); ifname = c->get_running_config_item(c, buf); @@ -394,6 +394,7 @@ int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(ret); @@ -401,8 +402,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_init.c lxc-2.1.0/src/lxc/tools/lxc_init.c --- lxc-2.0.8/src/lxc/tools/lxc_init.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_init.c 2017-09-06 02:32:37.000000000 +0000 @@ -33,8 +33,9 @@ #include #include +#include + #include "log.h" -#include "caps.h" #include "error.h" #include "initutils.h" @@ -80,9 +81,11 @@ int err; char **aargv; sigset_t mask, omask; + struct sigaction act; int i, have_status = 0, shutdown = 0; int opt; char *lxcpath = NULL, *name = NULL, *logpriority = NULL; + struct lxc_log log; while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) { switch(opt) { @@ -94,7 +97,7 @@ break; case 'q': quiet = 1; - break; + break; case 'P': lxcpath = optarg; break; @@ -104,14 +107,20 @@ } } - err = lxc_log_init(name, name ? NULL : "none", logpriority, - basename(argv[0]), quiet, lxcpath); + log.name = name; + log.file = name ? NULL : "none"; + log.level = logpriority; + log.prefix = basename(argv[0]); + log.quiet = quiet; + log.lxcpath = lxcpath; + + err = lxc_log_init(&log); if (err < 0) exit(EXIT_FAILURE); lxc_log_options_no_override(); if (!argv[optind]) { - ERROR("missing command to launch"); + ERROR("Missing command to launch"); exit(EXIT_FAILURE); } @@ -126,16 +135,27 @@ sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGBUS) || sigprocmask(SIG_SETMASK, &mask, &omask)) { - SYSERROR("failed to set signal mask"); + SYSERROR("Failed to set signal mask"); exit(EXIT_FAILURE); } - for (i = 1; i < NSIG; i++) { - struct sigaction act; + 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; + for (i = 1; i < NSIG; i++) { /* Exclude some signals: ILL, SEGV and BUS are likely to * reveal a bug and we want a core. STOP and KILL cannot be - * handled anyway: they're here for documentation. + * handled anyway: they're here for documentation. 32 and 33 + * are not defined. */ if (i == SIGILL || i == SIGSEGV || @@ -145,20 +165,8 @@ 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"); + SYSERROR("Failed to sigaction"); exit(EXIT_FAILURE); } } @@ -166,32 +174,32 @@ lxc_setup_fs(); pid = fork(); - if (pid < 0) exit(EXIT_FAILURE); if (!pid) { + int ret; /* 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"); + SYSERROR("Failed to set signal mask"); exit(EXIT_FAILURE); } - NOTICE("about to exec '%s'", aargv[0]); + NOTICE("About to exec '%s'", aargv[0]); - execvp(aargv[0], aargv); - ERROR("failed to exec: '%s' : %m", aargv[0]); - exit(err); + ret = execvp(aargv[0], aargv); + ERROR("Failed to exec: '%s' : %s", aargv[0], strerror(errno)); + exit(ret); } /* let's process the signals now */ if (sigdelset(&omask, SIGALRM) || sigprocmask(SIG_SETMASK, &omask, NULL)) { - SYSERROR("failed to set signal mask"); + SYSERROR("Failed to set signal mask"); exit(EXIT_FAILURE); } @@ -205,10 +213,8 @@ pid_t waited_pid; switch (was_interrupted) { - case 0: break; - case SIGPWR: case SIGTERM: if (!shutdown) { @@ -217,11 +223,9 @@ alarm(1); } break; - case SIGALRM: kill(-1, SIGKILL); break; - default: kill(pid, was_interrupted); break; @@ -235,7 +239,7 @@ if (errno == EINTR) continue; - ERROR("failed to wait child : %s", + ERROR("Failed to wait child : %s", strerror(errno)); goto out; } diff -Nru lxc-2.0.8/src/lxc/tools/lxc_ls.c lxc-2.1.0/src/lxc/tools/lxc_ls.c --- lxc-2.0.8/src/lxc/tools/lxc_ls.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_ls.c 2017-09-06 02:32:37.000000000 +0000 @@ -53,6 +53,7 @@ #define LS_RUNNING 4 #define LS_NESTING 5 #define LS_FILTER 6 +#define LS_DEFINED 7 #ifndef SOCK_CLOEXEC # define SOCK_CLOEXEC 02000000 @@ -167,6 +168,7 @@ {"running", no_argument, 0, LS_RUNNING}, {"frozen", no_argument, 0, LS_FROZEN}, {"stopped", no_argument, 0, LS_STOPPED}, + {"defined", no_argument, 0, LS_DEFINED}, {"nesting", optional_argument, 0, LS_NESTING}, {"groups", required_argument, 0, 'g'}, {"filter", required_argument, 0, LS_FILTER}, @@ -192,6 +194,7 @@ --running list only running containers\n\ --frozen list only frozen containers\n\ --stopped list only stopped containers\n\ + --defined list only defined containers\n\ --nesting=NUM list nested containers up to NUM (default is 5) levels of nesting\n\ --filter=REGEX filter container names by regular expression\n\ -g --groups comma separated list of groups a container must have to be displayed\n", @@ -203,6 +206,7 @@ int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; + struct lxc_log log; /* * The lxc parser requires that my_args.name is set. So let's satisfy * that condition by setting a dummy name which is never used. @@ -218,8 +222,14 @@ * We set the first argument that usually takes my_args.name to NULL so * that the log is only used when the user specifies a file. */ - if (lxc_log_init(NULL, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath[0])) + log.name = NULL; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); @@ -395,8 +405,9 @@ else if (!c) continue; - if (!c->is_defined(c)) + if (args->ls_defined && !c->is_defined(c)){ goto put_and_next; + } /* This does not allocate memory so no worries about freeing it * when we goto next or out. */ @@ -535,7 +546,12 @@ * need a path-extractor function. We face the same * problem with the ovl_mkdir() function in * lxcoverlay.{c,h}. */ - char *curr_path = ls_get_config_item(c, "lxc.rootfs", running); + char *curr_path = ls_get_config_item(c, "lxc.rootfs.path", running); + /* REMOVE IN LXC 3.0 + legacy rootfs key + */ + if (!curr_path) + curr_path = ls_get_config_item(c, "lxc.rootfs", running); if (!curr_path) goto put_and_next; @@ -661,18 +677,22 @@ */ static double ls_get_swap(struct lxc_container *c) { + char *stat, *swap, *tmp; unsigned long long int num = 0; - char *stat = ls_get_cgroup_item(c, "memory.stat"); + + stat = ls_get_cgroup_item(c, "memory.stat"); if (!stat) goto out; - char *swap = strstr(stat, "\nswap"); + swap = strstr(stat, "\nswap"); if (!swap) goto out; - swap = 1 + swap + 4 + 1; // start_of_swap_value = '\n' + strlen(swap) + ' ' + /* start_of_swap_value = '\n' + strlen(swap) + ' ' */ + swap = 1 + swap + 4 + 1; - char *tmp = strchr(swap, '\n'); // find end of swap value + /* find end of swap value */ + tmp = strchr(swap, '\n'); if (!tmp) goto out; @@ -928,6 +948,9 @@ case LS_STOPPED: args->ls_stopped = true; break; + case LS_DEFINED: + args->ls_defined = true; + break; case LS_NESTING: /* In case strtoul() receives a string that represents a * negative number it will return ULONG_MAX - the number that diff -Nru lxc-2.0.8/src/lxc/tools/lxc_monitor.c lxc-2.1.0/src/lxc/tools/lxc_monitor.c --- lxc-2.0.8/src/lxc/tools/lxc_monitor.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_monitor.c 2017-09-06 02:32:37.000000000 +0000 @@ -35,6 +35,7 @@ #include "log.h" #include "monitor.h" #include "arguments.h" +#include "lxccontainer.h" lxc_log_define(lxc_monitor_ui, lxc); @@ -91,6 +92,7 @@ struct pollfd *fds; nfds_t nfds; int len, rc_main, rc_snp, i; + struct lxc_log log; rc_main = EXIT_FAILURE; @@ -100,8 +102,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(rc_main); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_snapshot.c lxc-2.1.0/src/lxc/tools/lxc_snapshot.c --- lxc-2.0.8/src/lxc/tools/lxc_snapshot.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_snapshot.c 2017-09-06 02:32:37.000000000 +0000 @@ -27,10 +27,10 @@ #include -#include "bdev.h" #include "lxc.h" #include "log.h" #include "arguments.h" +#include "storage.h" #include "utils.h" lxc_log_define(lxc_snapshot_ui, lxc); @@ -81,6 +81,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; int ret; if (lxc_arguments_parse(&my_args, argc, argv)) @@ -89,8 +90,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_start.c lxc-2.1.0/src/lxc/tools/lxc_start.c --- lxc-2.0.8/src/lxc/tools/lxc_start.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_start.c 2017-09-06 02:32:37.000000000 +0000 @@ -80,16 +80,11 @@ goto err; } - *confpath = strdup(fullpath); - if (!*confpath) { - ERROR("failed to dup string '%s'", fullpath); - goto err; - } + *confpath = fullpath; } err = EXIT_SUCCESS; err: - free(fullpath); return err; } @@ -205,6 +200,7 @@ { int err = EXIT_FAILURE; struct lxc_conf *conf; + struct lxc_log log; char *const *args; char *rcfile = NULL; char *const default_args[] = { @@ -226,8 +222,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(err); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_stop.c lxc-2.1.0/src/lxc/tools/lxc_stop.c --- lxc-2.0.8/src/lxc/tools/lxc_stop.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_stop.c 2017-09-06 02:32:37.000000000 +0000 @@ -155,14 +155,21 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; bool s; int ret = EXIT_FAILURE; if (lxc_arguments_parse(&my_args, argc, argv)) exit(ret); - 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(ret); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_top.c lxc-2.1.0/src/lxc/tools/lxc_top.c --- lxc-2.0.8/src/lxc/tools/lxc_top.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_top.c 2017-09-06 02:32:37.000000000 +0000 @@ -21,11 +21,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include +#include #include #include #include #include +#include #include #include #include @@ -47,15 +50,24 @@ #define TERMBOLD ESC "[1m" #define TERMRVRS ESC "[7m" +struct blkio_stats { + uint64_t read; + uint64_t write; + uint64_t total; +}; + struct stats { uint64_t mem_used; uint64_t mem_limit; + uint64_t memsw_used; + uint64_t memsw_limit; uint64_t kmem_used; uint64_t kmem_limit; uint64_t cpu_use_nanos; uint64_t cpu_use_user; uint64_t cpu_use_sys; - uint64_t blkio; + struct blkio_stats io_service_bytes; + struct blkio_stats io_serviced; }; struct ct { @@ -63,10 +75,11 @@ struct stats *stats; }; +static int batch = 0; +static int delay_set = 0; static int delay = 3; static char sort_by = 'n'; static int sort_reverse = 0; - static struct termios oldtios; static struct ct *ct = NULL; static int ct_alloc_cnt = 0; @@ -75,9 +88,13 @@ { switch (c) { case 'd': + delay_set = 1; if (lxc_safe_int(arg, &delay) < 0) return -1; break; + case 'b': + batch=1; + break; case 's': sort_by = arg[0]; break; @@ -90,6 +107,7 @@ static const struct option my_longopts[] = { {"delay", required_argument, 0, 'd'}, + {"batch", no_argument, 0, 'b'}, {"sort", required_argument, 0, 's'}, {"reverse", no_argument, 0, 'r'}, LXC_COMMON_OPTIONS @@ -104,11 +122,13 @@ \n\ Options :\n\ -d, --delay delay in seconds between refreshes (default: 3.0)\n\ + -b, --batch output designed to capture to a file\n\ -s, --sort sort by [n,c,b,m] (default: n) where\n\ n = Name\n\ c = CPU use\n\ b = Block I/O use\n\ m = Memory use\n\ + s = Memory + Swap use\n\ k = Kernel memory use\n\ -r, --reverse sort in reverse (descending) order\n", .name = ".*", @@ -189,19 +209,19 @@ static void size_humanize(unsigned long long val, char *buf, size_t bufsz) { if (val > 1 << 30) { - snprintf(buf, bufsz, "%u.%2.2u GB", - (int)(val >> 30), - (int)(val & ((1 << 30) - 1)) / 10737419); + snprintf(buf, bufsz, "%u.%2.2u GiB", + (unsigned int)(val >> 30), + (unsigned int)(val & ((1 << 30) - 1)) / 10737419); } else if (val > 1 << 20) { - int x = val + 5243; /* for rounding */ - snprintf(buf, bufsz, "%u.%2.2u MB", + unsigned int x = val + 5243; /* for rounding */ + snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (val > 1 << 10) { - int x = val + 5; /* for rounding */ - snprintf(buf, bufsz, "%u.%2.2u KB", + unsigned int x = val + 5; /* for rounding */ + snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { - snprintf(buf, bufsz, "%3u.00 ", (int)val); + snprintf(buf, bufsz, "%3u.00 ", (unsigned int)val); } } @@ -262,39 +282,103 @@ return val; } +/* +examples: + blkio.throttle.io_serviced + 8:0 Read 4259 + 8:0 Write 835 + 8:0 Sync 292 + 8:0 Async 4802 + 8:0 Total 5094 + Total 5094 + + blkio.throttle.io_service_bytes + 8:0 Read 110309376 + 8:0 Write 39018496 + 8:0 Sync 2818048 + 8:0 Async 146509824 + 8:0 Total 149327872 + Total 149327872 +*/ +static void stat_get_blk_stats(struct lxc_container *c, const char *item, + struct blkio_stats *stats) { + char buf[4096]; + int i, len; + char **lines, **cols; + + len = c->get_cgroup_item(c, item, buf, sizeof(buf)); + if (len <= 0 || len >= sizeof(buf)) { + ERROR("unable to read cgroup item %s", item); + return; + } + + lines = lxc_string_split_and_trim(buf, '\n'); + if (!lines) + return; + + memset(stats, 0, sizeof(struct blkio_stats)); + for (i = 0; lines[i]; i++) { + cols = lxc_string_split_and_trim(lines[i], ' '); + if (!cols) + goto out; + if (strcmp(cols[1], "Read") == 0) + stats->read += strtoull(cols[2], NULL, 0); + else if (strcmp(cols[1], "Write") == 0) + stats->write += strtoull(cols[2], NULL, 0); + if (strcmp(cols[0], "Total") == 0) + stats->total = strtoull(cols[1], NULL, 0); + + lxc_free_array((void **)cols, free); + } +out: + lxc_free_array((void **)lines, free); + return; +} + static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total) { ct->c = c; ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes"); ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes"); + ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes"); + ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes"); ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes"); ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes"); ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage"); ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1); ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1); - ct->stats->blkio = stat_match_get_int(c, "blkio.throttle.io_service_bytes", "Total", 1); + stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes); + stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced); if (total) { total->mem_used = total->mem_used + ct->stats->mem_used; total->mem_limit = total->mem_limit + ct->stats->mem_limit; + total->memsw_used = total->memsw_used + ct->stats->memsw_used; + total->memsw_limit = total->memsw_limit + ct->stats->memsw_limit; total->kmem_used = total->kmem_used + ct->stats->kmem_used; total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit; total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos; total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user; total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys; - total->blkio = total->blkio + ct->stats->blkio; + total->io_service_bytes.total += ct->stats->io_service_bytes.total; + total->io_service_bytes.read += ct->stats->io_service_bytes.read; + total->io_service_bytes.write += ct->stats->io_service_bytes.write; } } static void stats_print_header(struct stats *stats) { printf(TERMRVRS TERMBOLD); - printf("%-18s %12s %12s %12s %14s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem"); + printf("%-18s %12s %12s %12s %36s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem"); + if (stats->memsw_used > 0) + printf(" %10s", "MemSw"); if (stats->kmem_used > 0) printf(" %10s", "KMem"); printf("\n"); - printf("%-18s %12s %12s %12s %14s %10s", "Name", "Used", "Sys", "User", "Total", "Used"); + printf("%-18s %12s %12s %12s %36s %10s", "Name", "Used", "Sys", "User", "Total(Read/Write)", "Used"); + if (stats->memsw_used > 0) + printf(" %10s", "Used"); if (stats->kmem_used > 0) printf(" %10s", "Used"); printf("\n"); @@ -304,24 +388,55 @@ static void stats_print(const char *name, const struct stats *stats, const struct stats *total) { - char blkio_str[20]; + char iosb_str[63]; + char iosb_total_str[20]; + char iosb_read_str[20]; + char iosb_write_str[20]; char mem_used_str[20]; + char memsw_used_str[20]; char kmem_used_str[20]; - - size_humanize(stats->blkio, blkio_str, sizeof(blkio_str)); - size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str)); - - printf("%-18.18s %12.2f %12.2f %12.2f %14s %10s", - name, - (float)stats->cpu_use_nanos / 1000000000, - (float)stats->cpu_use_sys / USER_HZ, - (float)stats->cpu_use_user / USER_HZ, - blkio_str, - mem_used_str); - if (total->kmem_used > 0) { - size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str)); - printf(" %10s", kmem_used_str); + struct timeval time_val; + unsigned long long time_ms; + int ret; + + if (!batch) { + size_humanize(stats->io_service_bytes.total, iosb_total_str, sizeof(iosb_total_str)); + size_humanize(stats->io_service_bytes.read, iosb_read_str, sizeof(iosb_read_str)); + size_humanize(stats->io_service_bytes.write, iosb_write_str, sizeof(iosb_write_str)); + size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str)); + + ret = snprintf(iosb_str, sizeof(iosb_str), "%s(%s/%s)", iosb_total_str, iosb_read_str, iosb_write_str); + if (ret < 0 || ret >= sizeof(iosb_str)) + WARN("snprintf'd too many characters: %d", ret); + + printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s", + name, + (float)stats->cpu_use_nanos / 1000000000, + (float)stats->cpu_use_sys / USER_HZ, + (float)stats->cpu_use_user / USER_HZ, + iosb_str, + mem_used_str); + + if (total->memsw_used > 0) { + size_humanize(stats->memsw_used, memsw_used_str, sizeof(memsw_used_str)); + printf(" %10s", memsw_used_str); + } + if (total->kmem_used > 0) { + size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str)); + printf(" %10s", kmem_used_str); + } + } else { + gettimeofday(&time_val, NULL); + time_ms = (unsigned long long) (time_val.tv_sec) * 1000 + (unsigned long long) (time_val.tv_usec) / 1000; + printf("%" PRIu64 ",%s,%" PRIu64 ",%" PRIu64 ",%" PRIu64 + ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64, + (uint64_t)time_ms, name, (uint64_t)stats->cpu_use_nanos, + (uint64_t)stats->cpu_use_sys, + (uint64_t)stats->cpu_use_user, (uint64_t)stats->io_service_bytes.total, + (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem_used, + (uint64_t)stats->memsw_used, (uint64_t)stats->kmem_used); } + } static int cmp_name(const void *sct1, const void *sct2) @@ -350,8 +465,8 @@ const struct ct *ct2 = sct2; if (sort_reverse) - return ct2->stats->blkio < ct1->stats->blkio; - return ct1->stats->blkio < ct2->stats->blkio; + return ct2->stats->io_service_bytes.total < ct1->stats->io_service_bytes.total; + return ct1->stats->io_service_bytes.total < ct2->stats->io_service_bytes.total; } static int cmp_memory(const void *sct1, const void *sct2) @@ -364,6 +479,16 @@ return ct1->stats->mem_used < ct2->stats->mem_used; } +static int cmp_memorysw(const void *sct1, const void *sct2) +{ + const struct ct *ct1 = sct1; + const struct ct *ct2 = sct2; + + if (sort_reverse) + return ct2->stats->memsw_used < ct1->stats->memsw_used; + return ct1->stats->memsw_used < ct2->stats->memsw_used; +} + static int cmp_kmemory(const void *sct1, const void *sct2) { const struct ct *ct1 = sct1; @@ -384,6 +509,7 @@ case 'c': cmp_func = cmp_cpuuse; break; case 'b': cmp_func = cmp_blkio; break; case 'm': cmp_func = cmp_memory; break; + case 's': cmp_func = cmp_memorysw; break; case 'k': cmp_func = cmp_kmemory; break; } qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func); @@ -458,6 +584,13 @@ goto err1; } + if (batch && !delay_set) { + delay = 300; + } + if (batch) { + printf("time_ms,container,cpu_nanos,cpu_sys_userhz,cpu_user_userhz,blkio_bytes,blkio_iops,mem_used_bytes,memsw_used_bytes,kernel_mem_used_bytes\n"); + } + for(;;) { struct lxc_container **active; int i, active_cnt; @@ -473,14 +606,18 @@ ct_sort(active_cnt); - printf(TERMCLEAR); - stats_print_header(&total); + if (!batch) { + printf(TERMCLEAR); + stats_print_header(&total); + } for (i = 0; i < active_cnt && i < ct_print_cnt; i++) { stats_print(ct[i].c->name, ct[i].stats, &total); printf("\n"); } - sprintf(total_name, "TOTAL %d of %d", i, active_cnt); - stats_print(total_name, &total, &total); + if (!batch) { + sprintf(total_name, "TOTAL %d of %d", i, active_cnt); + stats_print(total_name, &total, &total); + } fflush(stdout); for (i = 0; i < active_cnt; i++) { @@ -489,23 +626,28 @@ } in_char = '\0'; - ret = lxc_mainloop(&descr, 1000 * delay); - if (ret != 0 || in_char == 'q') - break; - switch(in_char) { - case 'r': - sort_reverse ^= 1; - break; - case 'n': - case 'c': - case 'b': - case 'm': - case 'k': - if (sort_by == in_char) + if (!batch) { + ret = lxc_mainloop(&descr, 1000 * delay); + if (ret != 0 || in_char == 'q') + break; + switch(in_char) { + case 'r': sort_reverse ^= 1; - else - sort_reverse = 0; - sort_by = in_char; + break; + case 'n': + case 'c': + case 'b': + case 'm': + case 's': + case 'k': + if (sort_by == in_char) + sort_reverse ^= 1; + else + sort_reverse = 0; + sort_by = in_char; + } + } else { + sleep(delay); } } ret = EXIT_SUCCESS; diff -Nru lxc-2.0.8/src/lxc/tools/lxc_unfreeze.c lxc-2.1.0/src/lxc/tools/lxc_unfreeze.c --- lxc-2.0.8/src/lxc/tools/lxc_unfreeze.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_unfreeze.c 2017-09-06 02:32:37.000000000 +0000 @@ -55,6 +55,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); @@ -62,8 +63,15 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_unshare.c lxc-2.1.0/src/lxc/tools/lxc_unshare.c --- lxc-2.0.8/src/lxc/tools/lxc_unshare.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_unshare.c 2017-09-06 02:32:37.000000000 +0000 @@ -137,7 +137,7 @@ exit(EXIT_FAILURE); } - // Setuid is useful even without a new user id space + /* Setuid is useful even without a new user id space. */ if (start_arg->setuid && setuid(uid)) { ERROR("failed to set uid %d: %s", uid, strerror(errno)); exit(EXIT_FAILURE); diff -Nru lxc-2.0.8/src/lxc/tools/lxc-update-config.in lxc-2.1.0/src/lxc/tools/lxc-update-config.in --- lxc-2.0.8/src/lxc/tools/lxc-update-config.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc-update-config.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,98 @@ +#!/bin/sh + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +set -e + +usage() +{ +cat <" keys we + # encounter with "lxc.network..". + echo "${LINE}" | grep -q "lxc.network.type" && IDX=$((IDX+1)) + sed -i -e "${LINE_NUM} s/\([[:blank:]*]\|#*\)\(lxc\.network\)\.\([^[:digit:]*]\)/\1lxc\.net\.${IDX}\.\3/g" "${CONFIGPATH}" +done < "${TMPFILE}" + +rm "${TMPFILE}" diff -Nru lxc-2.0.8/src/lxc/tools/lxc_usernsexec.c lxc-2.1.0/src/lxc/tools/lxc_usernsexec.c --- lxc-2.0.8/src/lxc/tools/lxc_usernsexec.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_usernsexec.c 2017-09-06 02:32:37.000000000 +0000 @@ -99,13 +99,13 @@ close(fd); } } -// Code copy end +/* Code copy end */ static int do_child(void *vargv) { char **argv = (char **)vargv; - // Assume we want to become root + /* Assume we want to become root */ if (setgid(0) < 0) { perror("setgid"); return -1; @@ -272,8 +272,8 @@ int pid; char *default_args[] = {"/bin/sh", NULL}; char buf[1]; - int pipe1[2], // child tells parent it has unshared - pipe2[2]; // parent tells child it is mapped and may proceed + int pipe1[2], /* child tells parent it has unshared */ + pipe2[2]; /* parent tells child it is mapped and may proceed */ memset(ttyname0, '\0', sizeof(ttyname0)); memset(ttyname1, '\0', sizeof(ttyname1)); @@ -326,7 +326,7 @@ exit(EXIT_FAILURE); } if ((pid = fork()) == 0) { - // Child. + /* Child. */ close(pipe1[0]); close(pipe2[1]); diff -Nru lxc-2.0.8/src/lxc/tools/lxc_wait.c lxc-2.1.0/src/lxc/tools/lxc_wait.c --- lxc-2.0.8/src/lxc/tools/lxc_wait.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/tools/lxc_wait.c 2017-09-06 02:32:37.000000000 +0000 @@ -83,6 +83,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; + struct lxc_log log; if (lxc_arguments_parse(&my_args, argc, argv)) exit(EXIT_FAILURE); @@ -90,8 +91,14 @@ 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])) + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) exit(EXIT_FAILURE); lxc_log_options_no_override(); diff -Nru lxc-2.0.8/src/lxc/utils.c lxc-2.1.0/src/lxc/utils.c --- lxc-2.0.8/src/lxc/utils.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -23,10 +23,13 @@ #include "config.h" +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ +#include #include #include #include #include +#include #include #include #include @@ -39,7 +42,6 @@ #include #include #include -#include #include #include "log.h" @@ -98,7 +100,7 @@ dir = opendir(dirname); if (!dir) { - ERROR("%s: failed to open %s", __func__, dirname); + ERROR("failed to open %s", dirname); return -1; } @@ -131,10 +133,10 @@ case ENOTDIR: ret = unlink(pathname); if (ret) - INFO("%s: failed to remove %s", __func__, pathname); + INFO("Failed to remove %s", pathname); break; default: - SYSERROR("%s: failed to rmdir %s", __func__, pathname); + SYSERROR("Failed to rmdir %s", pathname); failed = 1; break; } @@ -144,7 +146,7 @@ ret = lstat(pathname, &mystat); if (ret) { - ERROR("%s: failed to stat %s", __func__, pathname); + ERROR("Failed to stat %s", pathname); failed = 1; continue; } @@ -160,42 +162,44 @@ failed=1; } else { if (unlink(pathname) < 0) { - SYSERROR("%s: failed to delete %s", __func__, pathname); + SYSERROR("Failed to delete %s", pathname); failed=1; } } } if (rmdir(dirname) < 0 && !btrfs_try_remove_subvol(dirname) && !hadexclude) { - ERROR("%s: failed to delete %s", __func__, dirname); + ERROR("Failed to delete %s", dirname); failed=1; } ret = closedir(dir); if (ret) { - ERROR("%s: failed to close directory %s", __func__, dirname); + ERROR("Failed to close directory %s", dirname); failed=1; } return failed ? -1 : 0; } -/* we have two different magic values for overlayfs, yay */ +/* We have two different magic values for overlayfs, yay. */ +#ifndef OVERLAYFS_SUPER_MAGIC #define OVERLAYFS_SUPER_MAGIC 0x794c764f +#endif + +#ifndef OVERLAY_SUPER_MAGIC #define OVERLAY_SUPER_MAGIC 0x794c7630 -/* - * In overlayfs, st_dev is unreliable. so on overlayfs we don't do - * the lxc_rmdir_onedev() +#endif + +/* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the + * lxc_rmdir_onedev() */ static bool is_native_overlayfs(const char *path) { - struct statfs sb; - - if (statfs(path, &sb) < 0) - return false; - if (sb.f_type == OVERLAYFS_SUPER_MAGIC || - sb.f_type == OVERLAY_SUPER_MAGIC) + if (has_fs_type(path, OVERLAY_SUPER_MAGIC) || + has_fs_type(path, OVERLAYFS_SUPER_MAGIC)) return true; + return false; } @@ -212,7 +216,7 @@ if (lstat(path, &mystat) < 0) { if (errno == ENOENT) return 0; - ERROR("%s: failed to stat %s", __func__, path); + ERROR("Failed to stat %s", path); return -1; } @@ -489,6 +493,9 @@ /* child */ int child_std_end = STDOUT_FILENO; + close(parent_end); + parent_end = -1; + if (child_end != child_std_end) { /* dup2() doesn't dup close-on-exec flag */ dup2(child_end, child_std_end); @@ -722,47 +729,46 @@ return components; } -bool lxc_deslashify(char **path) +char *lxc_deslashify(const char *path) { - bool ret = false; - char *p; + char *dup, *p; char **parts = NULL; size_t n, len; - parts = lxc_normalize_path(*path); - if (!parts) - return false; + dup = strdup(path); + if (!dup) + return NULL; + + parts = lxc_normalize_path(dup); + if (!parts) { + free(dup); + return NULL; + } /* We'll end up here if path == "///" or path == "". */ if (!*parts) { - len = strlen(*path); + len = strlen(dup); if (!len) { - ret = true; - goto out; + lxc_free_array((void **)parts, free); + return dup; } - n = strcspn(*path, "/"); + n = strcspn(dup, "/"); if (n == len) { + free(dup); + lxc_free_array((void **)parts, free); + p = strdup("/"); if (!p) - goto out; - free(*path); - *path = p; - ret = true; - goto out; + return NULL; + + return p; } } - p = lxc_string_join("/", (const char **)parts, **path == '/'); - if (!p) - goto out; - - free(*path); - *path = p; - ret = true; - -out: + p = lxc_string_join("/", (const char **)parts, *dup == '/'); + free(dup); lxc_free_array((void **)parts, free); - return ret; + return p; } char *lxc_append_paths(const char *first, const char *second) @@ -1063,7 +1069,7 @@ ret = stat(path, &sb); if (ret < 0) - // could be something other than eexist, just say no + /* Could be something other than eexist, just say "no". */ return false; return S_ISDIR(sb.st_mode); } @@ -1119,7 +1125,7 @@ continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { - // this is '/'. is it shared? + /* This is '/'. Is it shared? */ p = strchr(p2 + 1, ' '); if (p && strstr(p, "shared:")) { fclose(f); @@ -1185,7 +1191,7 @@ continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { - // this is '/'. is it the ramfs? + /* This is '/'. Is it the ramfs? */ p = strchr(p2 + 1, '-'); if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) { free(line); @@ -1258,7 +1264,6 @@ const char *empty = "", *tmp; 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)) @@ -1290,9 +1295,7 @@ ERROR("pathname too long"); goto out1; } - - ret = stat(retv, &mystat); - if (ret == 0) + if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, LXCINITDIR, "/lxc/lxc-init"); @@ -1300,9 +1303,7 @@ ERROR("pathname too long"); goto out1; } - - ret = stat(retv, &mystat); - if (ret == 0) + if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/usr/lib/lxc/lxc-init", tmp); @@ -1310,8 +1311,7 @@ ERROR("pathname too long"); goto out1; } - ret = stat(retv, &mystat); - if (ret == 0) + if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/sbin/lxc-init", tmp); @@ -1319,8 +1319,7 @@ ERROR("pathname too long"); goto out1; } - ret = stat(retv, &mystat); - if (ret == 0) + if (access(retv, X_OK) == 0) return retv; /* @@ -1338,8 +1337,7 @@ WARN("Nonsense - name /lxc.init.static too long"); goto out1; } - ret = stat(retv, &mystat); - if (ret == 0) + if (access(retv, X_OK) == 0) return retv; out1: @@ -1574,20 +1572,21 @@ 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 + 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 */ + /* 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 + /* 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) { @@ -1687,8 +1686,10 @@ 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/ + int destfd, ret, saved_errno; + /* Only needs enough for /proc/self/fd/. */ + char srcbuf[50], destbuf[50]; + int srcfd = -1; const char *mntsrc = src; if (!rootfs) @@ -1757,9 +1758,8 @@ int lxc_mount_proc_if_needed(const char *rootfs) { char path[MAXPATHLEN]; - char link[20]; - int link_to_pid, linklen, ret; - int mypid; + int link_to_pid, linklen, mypid, ret; + char link[LXC_NUMSTRLEN64] = {0}; ret = snprintf(path, MAXPATHLEN, "%s/proc/self", rootfs); if (ret < 0 || ret >= MAXPATHLEN) { @@ -1767,10 +1767,7 @@ return -1; } - memset(link, 0, 20); - linklen = readlink(path, link, 20); - mypid = (int)getpid(); - INFO("I am %d, /proc/self points to \"%s\"", mypid, link); + linklen = readlink(path, link, LXC_NUMSTRLEN64); ret = snprintf(path, MAXPATHLEN, "%s/proc", rootfs); if (ret < 0 || ret >= MAXPATHLEN) { @@ -1783,24 +1780,29 @@ if (mkdir(path, 0755) && errno != EEXIST) return -1; goto domount; + } else if (linklen >= LXC_NUMSTRLEN64) { + link[linklen - 1] = '\0'; + ERROR("readlink returned truncated content: \"%s\"", link); + return -1; } + mypid = getpid(); + INFO("I am %d, /proc/self points to \"%s\"", mypid, link); + if (lxc_safe_int(link, &link_to_pid) < 0) return -1; - /* wrong /procs mounted */ - if (link_to_pid != mypid) { - /* ignore failure */ - umount2(path, MNT_DETACH); - goto domount; - } + /* correct procfs is already mounted */ + if (link_to_pid == mypid) + return 0; - /* the right proc is already mounted */ - return 0; + ret = umount2(path, MNT_DETACH); + if (ret < 0) + WARN("failed to umount \"%s\" with MNT_DETACH", path); domount: /* rootfs is NULL */ - if (!strcmp(rootfs,"")) + if (!strcmp(rootfs, "")) ret = mount("proc", path, "proc", 0, NULL); else ret = safe_mount("proc", path, "proc", 0, NULL, rootfs); @@ -1823,14 +1825,21 @@ int set_stdfds(int fd) { + int ret; + if (fd < 0) return -1; - if (dup2(fd, 0) < 0) + ret = dup2(fd, STDIN_FILENO); + if (ret < 0) return -1; - if (dup2(fd, 1) < 0) + + ret = dup2(fd, STDOUT_FILENO); + if (ret < 0) return -1; - if (dup2(fd, 2) < 0) + + ret = dup2(fd, STDERR_FILENO); + if (ret < 0) return -1; return 0; @@ -1921,12 +1930,14 @@ return bret; while (getline(&line, &n, f) != -1) { - if (!strncmp(line, "SigBlk:\t", 8)) - if (sscanf(line + 8, "%lx", &sigblk) != 1) - goto out; + if (strncmp(line, "SigBlk:\t", 8)) + continue; + + if (sscanf(line + 8, "%lx", &sigblk) != 1) + goto out; } - if (sigblk & signal) + if (sigblk & (1LU << (signal - 1))) bret = true; out: @@ -1998,12 +2009,18 @@ char *err = NULL; unsigned long int uli; + while (isspace(*numstr)) + numstr++; + + if (*numstr == '-') + return -EINVAL; + errno = 0; uli = strtoul(numstr, &err, 0); - if (errno > 0) - return -errno; + if (errno == ERANGE && uli == ULONG_MAX) + return -ERANGE; - if (!err || err == numstr || *err != '\0') + if (err == numstr || *err != '\0') return -EINVAL; if (uli > UINT_MAX) @@ -2013,6 +2030,29 @@ return 0; } +int lxc_safe_ulong(const char *numstr, unsigned long *converted) +{ + char *err = NULL; + unsigned long int uli; + + while (isspace(*numstr)) + numstr++; + + if (*numstr == '-') + return -EINVAL; + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno == ERANGE && uli == ULONG_MAX) + return -ERANGE; + + if (err == numstr || *err != '\0') + return -EINVAL; + + *converted = uli; + return 0; +} + int lxc_safe_int(const char *numstr, int *converted) { char *err = NULL; @@ -2020,13 +2060,16 @@ errno = 0; sli = strtol(numstr, &err, 0); - if (errno > 0) - return -errno; + if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) + return -ERANGE; + + if (errno != 0 && sli == 0) + return -EINVAL; - if (!err || err == numstr || *err != '\0') + if (err == numstr || *err != '\0') return -EINVAL; - if (sli > INT_MAX) + if (sli > INT_MAX || sli < INT_MIN) return -ERANGE; *converted = (int)sli; @@ -2040,14 +2083,14 @@ errno = 0; sli = strtol(numstr, &err, 0); - if (errno > 0) - return -errno; + if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) + return -ERANGE; - if (!err || err == numstr || *err != '\0') + if (errno != 0 && sli == 0) return -EINVAL; - if (sli > LONG_MAX) - return -ERANGE; + if (err == numstr || *err != '\0') + return -EINVAL; *converted = sli; return 0; @@ -2235,3 +2278,155 @@ return umounts; } + +int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) +{ + pid_t child; + int ret, fret, pipefd[2]; + ssize_t bytes; + + /* Make sure our callers do not receive unitialized memory. */ + if (buf_size > 0 && buf) + buf[0] = '\0'; + + if (pipe(pipefd) < 0) { + SYSERROR("failed to create pipe"); + return -1; + } + + child = fork(); + if (child < 0) { + close(pipefd[0]); + close(pipefd[1]); + SYSERROR("failed to create new process"); + return -1; + } + + if (child == 0) { + /* Close the read-end of the pipe. */ + close(pipefd[0]); + + /* Redirect std{err,out} to write-end of the + * pipe. + */ + ret = dup2(pipefd[1], STDOUT_FILENO); + if (ret >= 0) + ret = dup2(pipefd[1], STDERR_FILENO); + + /* Close the write-end of the pipe. */ + close(pipefd[1]); + + if (ret < 0) { + SYSERROR("failed to duplicate std{err,out} file descriptor"); + exit(EXIT_FAILURE); + } + + /* Does not return. */ + child_fn(args); + ERROR("failed to exec command"); + exit(EXIT_FAILURE); + } + + /* close the write-end of the pipe */ + close(pipefd[1]); + + bytes = read(pipefd[0], buf, (buf_size > 0) ? (buf_size - 1) : 0); + if (bytes > 0) + buf[bytes - 1] = '\0'; + + fret = wait_for_pid(child); + /* close the read-end of the pipe */ + close(pipefd[0]); + + return fret; +} + +char *must_make_path(const char *first, ...) +{ + va_list args; + char *cur, *dest; + size_t full_len = strlen(first); + + dest = must_copy_string(first); + + va_start(args, first); + while ((cur = va_arg(args, char *)) != NULL) { + full_len += strlen(cur); + if (cur[0] != '/') + full_len++; + dest = must_realloc(dest, full_len + 1); + if (cur[0] != '/') + strcat(dest, "/"); + strcat(dest, cur); + } + va_end(args); + + return dest; +} + +char *must_copy_string(const char *entry) +{ + char *ret; + + if (!entry) + return NULL; + do { + ret = strdup(entry); + } while (!ret); + + return ret; +} + +void *must_realloc(void *orig, size_t sz) +{ + void *ret; + + do { + ret = realloc(orig, sz); + } while (!ret); + + return ret; +} + +bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) +{ + return (fs->f_type == (fs_type_magic)magic_val); +} + +bool has_fs_type(const char *path, fs_type_magic magic_val) +{ + bool has_type; + int ret; + struct statfs sb; + + ret = statfs(path, &sb); + if (ret < 0) + return false; + + has_type = is_fs_type(&sb, magic_val); + if (!has_type && magic_val == RAMFS_MAGIC) + WARN("When the ramfs it a tmpfs statfs() might report tmpfs"); + + return has_type; +} + +bool lxc_nic_exists(char *nic) +{ +#define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1 + char path[__LXC_SYS_CLASS_NET_LEN]; + int ret; + struct stat sb; + + if (!strcmp(nic, "none")) + return true; + + ret = snprintf(path, __LXC_SYS_CLASS_NET_LEN, "/sys/class/net/%s", nic); + if (ret < 0 || (size_t)ret >= __LXC_SYS_CLASS_NET_LEN) + return false; + + ret = stat(path, &sb); + if (ret < 0) + return false; + + return true; +} diff -Nru lxc-2.0.8/src/lxc/utils.h lxc-2.1.0/src/lxc/utils.h --- lxc-2.0.8/src/lxc/utils.h 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/lxc/utils.h 2017-09-06 02:32:37.000000000 +0000 @@ -34,11 +34,18 @@ #include #include #include +#include #include #include +#include #include "initutils.h" +/* Define __S_ISTYPE if missing from the C library. */ +#ifndef __S_ISTYPE +#define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) +#endif + /* Useful macros */ /* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */ #define LXC_NUMSTRLEN64 21 @@ -85,7 +92,7 @@ #endif } #else -int unshare(int); +extern int unshare(int); #endif /* Define signalfd() if missing from the C library */ @@ -238,24 +245,30 @@ /* send and receive buffers completely */ extern ssize_t lxc_write_nointr(int fd, const void* buf, size_t count); extern ssize_t lxc_read_nointr(int fd, void* buf, size_t count); -extern ssize_t lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf); +extern ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, + const void *expected_buf); #if HAVE_LIBGNUTLS #define SHA_DIGEST_LENGTH 20 extern int sha1sum_file(char *fnam, unsigned char *md_value); #endif /* read and write whole files */ -extern int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool add_newline); +extern int lxc_write_to_file(const char *filename, const void *buf, + size_t count, bool add_newline); extern int lxc_read_from_file(const char *filename, void* buf, size_t count); /* convert variadic argument lists to arrays (for execl type argument lists) */ extern char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup); extern const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip); -/* Some simple string functions; if they return pointers, they are allocated buffers. */ -extern char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack); +/* Some simple string functions; if they return pointers, they are allocated + * buffers. + */ +extern char *lxc_string_replace(const char *needle, const char *replacement, + const char *haystack); extern bool lxc_string_in_array(const char *needle, const char **haystack); -extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix); +extern char *lxc_string_join(const char *sep, const char **parts, + bool use_as_prefix); /* Normalize and split path: Leading and trailing / are removed, multiple * / are compactified, .. and . are resolved (.. on the top level is considered * identical to .). @@ -268,13 +281,14 @@ */ extern char **lxc_normalize_path(const char *path); /* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ -extern bool lxc_deslashify(char **path); +extern char *lxc_deslashify(const char *path); extern char *lxc_append_paths(const char *first, const char *second); /* Note: the following two functions use strtok(), so they will never * consider an empty element, even if two delimiters are next to * each other. */ -extern bool lxc_string_in_list(const char *needle, const char *haystack, char sep); +extern bool lxc_string_in_list(const char *needle, const char *haystack, + char sep); extern char **lxc_string_split(const char *string, char sep); extern char **lxc_string_split_and_trim(const char *string, char sep); /* Append string to NULL-terminated string array. */ @@ -283,7 +297,8 @@ /* some simple array manipulation utilities */ typedef void (*lxc_free_fn)(void *); typedef void *(*lxc_dup_fn)(void *); -extern int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment); +extern int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, + size_t capacity_increment); extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); extern size_t lxc_array_len(void **array); @@ -296,7 +311,7 @@ /* munmap() wrapper. Use it to free memory mmap()ed with lxc_strmmap(). */ extern int lxc_strmunmap(void *addr, size_t length); -//initialize rand with urandom +/* initialize rand with urandom */ extern int randseed(bool); inline static bool am_unpriv(void) { @@ -311,48 +326,82 @@ extern bool dir_exists(const char *path); #define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) -uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval); +extern uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval); -int detect_shared_rootfs(void); -bool detect_ramfs_rootfs(void); -char *on_path(const char *cmd, const char *rootfs); -bool file_exists(const char *f); -bool cgns_supported(void); -char *choose_init(const char *rootfs); -int print_to_file(const char *file, const char *content); -bool switch_to_ns(pid_t pid, const char *ns); -int is_dir(const char *path); -char *get_template_path(const char *t); -int setproctitle(char *title); -int safe_mount(const char *src, const char *dest, const char *fstype, - unsigned long flags, const void *data, const char *rootfs); -int lxc_mount_proc_if_needed(const char *rootfs); -int open_devnull(void); -int set_stdfds(int fd); -int null_stdfds(void); -int lxc_count_file_lines(const char *fn); -int lxc_preserve_ns(const int pid, const char *ns); +extern int detect_shared_rootfs(void); +extern bool detect_ramfs_rootfs(void); +extern char *on_path(const char *cmd, const char *rootfs); +extern bool file_exists(const char *f); +extern bool cgns_supported(void); +extern char *choose_init(const char *rootfs); +extern int print_to_file(const char *file, const char *content); +extern bool switch_to_ns(pid_t pid, const char *ns); +extern int is_dir(const char *path); +extern char *get_template_path(const char *t); +extern int setproctitle(char *title); +extern int safe_mount(const char *src, const char *dest, const char *fstype, + unsigned long flags, const void *data, + const char *rootfs); +extern int lxc_mount_proc_if_needed(const char *rootfs); +extern int open_devnull(void); +extern int set_stdfds(int fd); +extern int null_stdfds(void); +extern int lxc_count_file_lines(const char *fn); +extern int lxc_preserve_ns(const int pid, const char *ns); /* Check whether a signal is blocked by a process. */ -bool task_blocking_signal(pid_t pid, int signal); +extern bool task_blocking_signal(pid_t pid, int signal); /* Helper functions to parse numbers. */ -int lxc_safe_uint(const char *numstr, unsigned int *converted); -int lxc_safe_int(const char *numstr, int *converted); -int lxc_safe_long(const char *numstr, long int *converted); +extern int lxc_safe_uint(const char *numstr, unsigned int *converted); +extern int lxc_safe_int(const char *numstr, int *converted); +extern int lxc_safe_long(const char *numstr, long int *converted); +extern int lxc_safe_ulong(const char *numstr, unsigned long *converted); /* Switch to a new uid and gid. */ -int lxc_switch_uid_gid(uid_t uid, gid_t gid); -int lxc_setgroups(int size, gid_t list[]); +extern int lxc_switch_uid_gid(uid_t uid, gid_t gid); +extern int lxc_setgroups(int size, gid_t list[]); /* Find an unused loop device and associate it with source. */ -int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags); +extern int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags); /* Clear all mounts on a given node. * >= 0 successfully cleared. The number returned is the number of umounts * performed. * < 0 error umounting. Return -errno. */ -int lxc_unstack_mountpoint(const char *path, bool lazy); +extern int lxc_unstack_mountpoint(const char *path, bool lazy); + +/* + * run_command runs a command and collect it's std{err,out} output in buf. + * + * @param[out] buf The buffer where the commands std{err,out] output will be + * read into. If no output was produced, buf will be memset + * to 0. + * @param[in] buf_size The size of buf. This function will reserve one byte for + * \0-termination. + * @param[in] child_fn The function to be run in the child process. This + * function must exec. + * @param[in] args Arguments to be passed to child_fn. + */ +extern int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), + void *args); + +/* Concatenate all passed-in strings into one path. Do not fail. If any piece + * is not prefixed with '/', add a '/'. + */ +extern char *must_make_path(const char *first, ...) __attribute__((sentinel)); + +/* return copy of string @entry; do not fail. */ +extern char *must_copy_string(const char *entry); + +/* Re-alllocate a pointer, do not fail */ +extern void *must_realloc(void *orig, size_t sz); + +/* __typeof__ should be safe to use with all compilers. */ +typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic; +extern bool has_fs_type(const char *path, fs_type_magic magic_val); +extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val); +extern bool lxc_nic_exists(char *nic); #endif /* __LXC_UTILS_H */ diff -Nru lxc-2.0.8/src/lxc/version.h lxc-2.1.0/src/lxc/version.h --- lxc-2.0.8/src/lxc/version.h 2017-05-11 17:23:23.000000000 +0000 +++ lxc-2.1.0/src/lxc/version.h 2017-09-06 02:32:53.000000000 +0000 @@ -25,9 +25,9 @@ #define LXC_DEVEL 0 #define LXC_VERSION_MAJOR 2 -#define LXC_VERSION_MINOR 0 -#define LXC_VERSION_MICRO 8 -#define LXC_VERSION_ABI "1.2.0" -#define LXC_VERSION "2.0.8" +#define LXC_VERSION_MINOR 1 +#define LXC_VERSION_MICRO 0 +#define LXC_VERSION_ABI "1.3.0" +#define LXC_VERSION "2.1.0" #endif diff -Nru lxc-2.0.8/src/python-lxc/examples/api_test.py lxc-2.1.0/src/python-lxc/examples/api_test.py --- lxc-2.0.8/src/python-lxc/examples/api_test.py 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/examples/api_test.py 2017-09-06 02:32:37.000000000 +0000 @@ -71,7 +71,7 @@ assert(container.defined) assert(container.name == CONTAINER_NAME - == container.get_config_item("lxc.utsname")) + == container.get_config_item("lxc.uts.name")) assert(container.name in lxc.list_containers()) ## Test the config @@ -90,7 +90,7 @@ print("Testing the networking") # A few basic checks of the current state -assert("name" in container.get_keys("lxc.network.0")) +assert("name" in container.get_keys("lxc.net.0")) assert(len(container.network) == 1) ## Starting the container @@ -131,8 +131,8 @@ ## Test running config assert(container.name == CONTAINER_NAME - == container.get_config_item("lxc.utsname") - == container.get_running_config_item("lxc.utsname")) + == container.get_config_item("lxc.uts.name") + == container.get_running_config_item("lxc.uts.name")) ## Testing cgroups a bit print("Testing cgroup API") diff -Nru lxc-2.0.8/src/python-lxc/lxc/__init__.py lxc-2.1.0/src/python-lxc/lxc/__init__.py --- lxc-2.0.8/src/python-lxc/lxc/__init__.py 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/lxc/__init__.py 2017-09-06 02:32:37.000000000 +0000 @@ -40,7 +40,7 @@ self.container = container self.index = index - for key in self.container.get_keys("lxc.network.%s" % self.index): + for key in self.container.get_keys("lxc.net.%s" % self.index): if "." in key: self.props[key.replace(".", "_")] = key else: @@ -98,18 +98,18 @@ def __clear_network_item(self, key): if key in ("ipv4", "ipv6"): - return self.container.clear_config_item("lxc.network.%s.%s" % ( + return self.container.clear_config_item("lxc.net.%s.%s" % ( self.index, key)) else: - return self.container.set_config_item("lxc.network.%s.%s" % ( + return self.container.set_config_item("lxc.net.%s.%s" % ( self.index, key), "") def __get_network_item(self, key): - return self.container.get_config_item("lxc.network.%s.%s" % ( + return self.container.get_config_item("lxc.net.%s.%s" % ( self.index, key)) def __set_network_item(self, key, value): - return self.container.set_config_item("lxc.network.%s.%s" % ( + return self.container.set_config_item("lxc.net.%s.%s" % ( self.index, key), value) @@ -124,7 +124,7 @@ return ContainerNetwork(self.container, index) def __len__(self): - values = self.container.get_config_item("lxc.network") + values = self.container.get_config_item("lxc.net") if values: return len(values) @@ -134,7 +134,7 @@ def add(self, network_type): index = len(self) - return self.container.set_config_item("lxc.network.%s.type" % index, + return self.container.set_config_item("lxc.net.%s.type" % index, network_type) def remove(self, index): @@ -142,7 +142,7 @@ if index >= count: raise IndexError("list index out of range") - return self.container.clear_config_item("lxc.network.%s" % index) + return self.container.clear_config_item("lxc.net.%s" % index) class Container(_lxc.Container): @@ -387,7 +387,7 @@ new_value = self.get_config_item(key) # loglevel is special and won't match the string we set - if key == "lxc.loglevel": + if key == "lxc.log.level": new_value = value if (isinstance(value, str) and isinstance(new_value, str) and diff -Nru lxc-2.0.8/src/python-lxc/lxc.c lxc-2.1.0/src/python-lxc/lxc.c --- lxc-2.0.8/src/python-lxc/lxc.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/lxc.c 2017-09-06 02:32:37.000000000 +0000 @@ -25,14 +25,91 @@ #include #include "structmember.h" #include -#include "lxc/utils.h" -#include "lxc/namespace.h" -#include "lxc/confile.h" #include #include +#include + +/* + * CLONE_* definitions copied from lxc/namespace.h + */ +#ifndef CLONE_FS +# define CLONE_FS 0x00000200 +#endif +#ifndef CLONE_NEWNS +# define CLONE_NEWNS 0x00020000 +#endif +#ifndef CLONE_NEWCGROUP +# define CLONE_NEWCGROUP 0x02000000 +#endif +#ifndef CLONE_NEWUTS +# define CLONE_NEWUTS 0x04000000 +#endif +#ifndef CLONE_NEWIPC +# define CLONE_NEWIPC 0x08000000 +#endif +#ifndef CLONE_NEWUSER +# define CLONE_NEWUSER 0x10000000 +#endif +#ifndef CLONE_NEWPID +# define CLONE_NEWPID 0x20000000 +#endif +#ifndef CLONE_NEWNET +# define CLONE_NEWNET 0x40000000 +#endif + +/* From sys/personality.h */ +#define PER_LINUX 0x0000 +#define PER_LINUX32 0x0008 /* Helper functions */ +/* Copied from lxc/utils.c */ +static 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; +} + +/* Copied from lxc/confile.c, with HAVE_SYS_PERSONALITY_H check removed */ +signed long lxc_config_parse_arch(const char *arch) +{ + struct per_name { + char *name; + unsigned long per; + } pername[] = { + { "x86", PER_LINUX32 }, + { "linux32", PER_LINUX32 }, + { "i386", PER_LINUX32 }, + { "i486", PER_LINUX32 }, + { "i586", PER_LINUX32 }, + { "i686", PER_LINUX32 }, + { "athlon", PER_LINUX32 }, + { "linux64", PER_LINUX }, + { "x86_64", PER_LINUX }, + { "amd64", PER_LINUX }, + }; + size_t len = sizeof(pername) / sizeof(pername[0]); + + size_t i; + + for (i = 0; i < len; i++) { + if (!strcmp(pername[i].name, arch)) + return pername[i].per; + } + + return -1; +} + char** convert_tuple_to_char_pointer_array(PyObject *argv) { int argc; diff -Nru lxc-2.0.8/src/python-lxc/Makefile.am lxc-2.1.0/src/python-lxc/Makefile.am --- lxc-2.0.8/src/python-lxc/Makefile.am 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/Makefile.am 2017-09-06 02:32:37.000000000 +0000 @@ -6,21 +6,28 @@ DISTSETUPOPTS= endif +if ENABLE_RPATH + RPATHOPTS=-R $(libdir) +else + RPATHOPTS= +endif + +CALL_SETUP_PY := cd @srcdir@ && $(PYTHON) setup.py build -b @abs_builddir@/build egg_info -e @abs_builddir@ + all: - $(PYTHON) setup.py build + $(CALL_SETUP_PY) build_ext -I @abs_top_srcdir@/src -L @abs_top_builddir@/src/lxc/.libs/ $(RPATHOPTS) --no-pkg-config + +DESTDIR = / # default install: - if [ "$(DESTDIR)" = "" ]; then \ - $(PYTHON) setup.py install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ - else \ - $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ - fi + $(CALL_SETUP_PY) install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS) --root=$(DESTDIR) clean-local: - rm -rf build + rm -rf @builddir@/build endif EXTRA_DIST = \ + setup.py \ lxc.c \ lxc/__init__.py \ examples/api_test.py \ diff -Nru lxc-2.0.8/src/python-lxc/Makefile.in lxc-2.1.0/src/python-lxc/Makefile.in --- lxc-2.0.8/src/python-lxc/Makefile.in 2017-05-11 17:23:10.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/Makefile.in 2017-09-06 02:32:42.000000000 +0000 @@ -101,7 +101,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h -CONFIG_CLEAN_FILES = setup.py +CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -123,7 +123,7 @@ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/setup.py.in +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -319,7 +319,12 @@ top_srcdir = @top_srcdir@ @ENABLE_PYTHON_TRUE@@HAVE_DEBIAN_FALSE@DISTSETUPOPTS = @ENABLE_PYTHON_TRUE@@HAVE_DEBIAN_TRUE@DISTSETUPOPTS = --install-layout=deb +@ENABLE_PYTHON_TRUE@@ENABLE_RPATH_FALSE@RPATHOPTS = +@ENABLE_PYTHON_TRUE@@ENABLE_RPATH_TRUE@RPATHOPTS = -R $(libdir) +@ENABLE_PYTHON_TRUE@CALL_SETUP_PY := cd @srcdir@ && $(PYTHON) setup.py build -b @abs_builddir@/build egg_info -e @abs_builddir@ +@ENABLE_PYTHON_TRUE@DESTDIR = / # default EXTRA_DIST = \ + setup.py \ lxc.c \ lxc/__init__.py \ examples/api_test.py \ @@ -358,8 +363,6 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo @@ -522,17 +525,13 @@ @ENABLE_PYTHON_TRUE@all: -@ENABLE_PYTHON_TRUE@ $(PYTHON) setup.py build +@ENABLE_PYTHON_TRUE@ $(CALL_SETUP_PY) build_ext -I @abs_top_srcdir@/src -L @abs_top_builddir@/src/lxc/.libs/ $(RPATHOPTS) --no-pkg-config @ENABLE_PYTHON_TRUE@install: -@ENABLE_PYTHON_TRUE@ if [ "$(DESTDIR)" = "" ]; then \ -@ENABLE_PYTHON_TRUE@ $(PYTHON) setup.py install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ -@ENABLE_PYTHON_TRUE@ else \ -@ENABLE_PYTHON_TRUE@ $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ -@ENABLE_PYTHON_TRUE@ fi +@ENABLE_PYTHON_TRUE@ $(CALL_SETUP_PY) install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS) --root=$(DESTDIR) @ENABLE_PYTHON_TRUE@clean-local: -@ENABLE_PYTHON_TRUE@ rm -rf build +@ENABLE_PYTHON_TRUE@ rm -rf @builddir@/build # 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. diff -Nru lxc-2.0.8/src/python-lxc/setup.py lxc-2.1.0/src/python-lxc/setup.py --- lxc-2.0.8/src/python-lxc/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/setup.py 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# +# python-lxc: Python bindings for LXC +# +# (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 os +import subprocess + +# Fix build when PIE is enabled (must run before setuptools import) +for var in ("LDFLAGS", "CFLAGS"): + current = os.environ.get(var, None) + if not current: + continue + + new = [] + for flag in current.split(" "): + if flag.lower() in ("-pie", "-fpie"): + if "-fPIC" not in new: + new.append("-fPIC") + continue + new.append(flag) + + os.environ[var] = " ".join(new) + +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext as BuildExtCommand + + +class LxcBuildExtCommand(BuildExtCommand): + user_options = BuildExtCommand.user_options + [ + ('no-pkg-config', None, + "don't use pkg-config to detect include/library paths") + ] + + def initialize_options(self): + super(LxcBuildExtCommand, self).initialize_options() + self.no_pkg_config = False + + def build_extensions(self): + if not self.no_pkg_config: + pkg_config_executable = os.environ.get('PKG_CONFIG_EXECUTABLE', + 'pkg-config') + + def get_pkg_config_var(name): + args = [pkg_config_executable, '--variable', name, 'lxc'] + output = subprocess.check_output(args, + universal_newlines=True) + return output.rstrip('\n') + + try: + includedir = get_pkg_config_var('includedir') + libdir = get_pkg_config_var('libdir') + + self.compiler.add_include_dir(includedir) + self.compiler.add_library_dir(libdir) + + except subprocess.CalledProcessError: + pass + + super(LxcBuildExtCommand, self).build_extensions() + + +setup(name='lxc', + version='0.1', + description='LXC', + packages=['lxc'], + package_dir={'lxc': 'lxc'}, + ext_modules=[Extension('_lxc', sources=['lxc.c'], libraries=['lxc'])], + cmdclass={'build_ext': LxcBuildExtCommand}, + ) diff -Nru lxc-2.0.8/src/python-lxc/setup.py.in lxc-2.1.0/src/python-lxc/setup.py.in --- lxc-2.0.8/src/python-lxc/setup.py.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/python-lxc/setup.py.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -# -# python-lxc: Python bindings for LXC -# -# (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 os, os.path - -# Fix build when PIE is enabled -for var in ("LDFLAGS", "CFLAGS"): - current = os.environ.get(var, None) - if not current: - continue - - new = [] - for flag in current.split(" "): - if flag.lower() in ("-pie", "-fpie"): - if "-fPIC" not in new: - new.append("-fPIC") - continue - new.append(flag) - - os.environ[var] = " ".join(new) - -from distutils.core import setup, Extension - -# Distutils doesn't cope well with source files that have relative paths going -# up in the directory tree: it tries to navigate outside of the build dir and -# fails miserably. Therefore, we will instead cd to the source directory, -# run this script from there, but write the build products to the correct path. -# -# Since we will be changing directories before building, we must transform -# all the path variables to their forms relative to srcdir. - -srcdir, builddir, top_srcdir, top_builddir = map(os.path.abspath, - ["@srcdir@", "@builddir@", "@top_srcdir@", "@top_builddir@"]) - -builddir, top_srcdir, top_builddir = map(lambda d: os.path.relpath(d, srcdir), - [builddir, top_srcdir, top_builddir]) - -os.chdir(srcdir) - -module = Extension('_lxc', sources=['lxc.c'], - include_dirs=[os.path.join(top_srcdir, 'src'), - os.path.join(top_builddir, 'src')], - library_dirs=[os.path.join(top_builddir, 'src/lxc/.libs/')], - libraries=['lxc']) - - -setup(name='_lxc', - version='0.1', - description='LXC', - packages=['lxc'], - package_dir={'lxc': 'lxc'}, - ext_modules=[module], - options={'build': {'build_base': os.path.join(builddir, 'build')}}) diff -Nru lxc-2.0.8/src/tests/aa.c lxc-2.1.0/src/tests/aa.c --- lxc-2.0.8/src/tests/aa.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/aa.c 2017-09-06 02:32:37.000000000 +0000 @@ -161,7 +161,7 @@ fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); goto err; } - if (!c->set_config_item(c, "lxc.network.type", "empty")) { + if (!c->set_config_item(c, "lxc.net.0.type", "empty")) { fprintf(stderr, "%s: %d: failed to set network type\n", __FILE__, __LINE__); goto err; } diff -Nru lxc-2.0.8/src/tests/attach.c lxc-2.1.0/src/tests/attach.c --- lxc-2.0.8/src/tests/attach.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/attach.c 2017-09-06 02:32:37.000000000 +0000 @@ -51,11 +51,11 @@ { if (lsm_enabled()) { if (!strcmp(lsm_name(), "SELinux")) { - lsm_config_key = "lxc.se_context"; + lsm_config_key = "lxc.selinux.context"; lsm_label = "unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023"; } else if (!strcmp(lsm_name(), "AppArmor")) { - lsm_config_key = "lxc.aa_profile"; + lsm_config_key = "lxc.apparmor.profile"; if (file_exists("/proc/self/ns/cgroup")) lsm_label = "lxc-container-default-cgns"; else @@ -403,6 +403,7 @@ if (ret < 0) return EXIT_FAILURE; + (void)rmdir(LXCPATH "/alternate-path-test"); TSTOUT("All tests passed\n"); return EXIT_SUCCESS; } diff -Nru lxc-2.0.8/src/tests/cgpath.c lxc-2.1.0/src/tests/cgpath.c --- lxc-2.0.8/src/tests/cgpath.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/cgpath.c 2017-09-06 02:32:37.000000000 +0000 @@ -143,7 +143,7 @@ c->destroy(c); c = lxc_container_new(name, lxcpath); } - c->set_config_item(c, "lxc.network.type", "empty"); + c->set_config_item(c, "lxc.net.0.type", "empty"); if (!c->createl(c, template, NULL, NULL, 0, NULL)) { TSTERR("creating container %s", name); goto out2; diff -Nru lxc-2.0.8/src/tests/concurrent.c lxc-2.1.0/src/tests/concurrent.c --- lxc-2.0.8/src/tests/concurrent.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/concurrent.c 2017-09-06 02:32:37.000000000 +0000 @@ -85,8 +85,8 @@ } if (debug) { - c->set_config_item(c, "lxc.loglevel", "DEBUG"); - c->set_config_item(c, "lxc.logfile", name); + c->set_config_item(c, "lxc.log.level", "DEBUG"); + c->set_config_item(c, "lxc.log", name); } if (strcmp(args->mode, "create") == 0) { diff -Nru lxc-2.0.8/src/tests/config_jump_table.c lxc-2.1.0/src/tests/config_jump_table.c --- lxc-2.0.8/src/tests/config_jump_table.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/tests/config_jump_table.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,94 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "confile.h" +#include "lxc/state.h" +#include "lxctest.h" + +int main(int argc, char *argv[]) +{ + int fulllen = 0, inlen = 0, ret = EXIT_FAILURE; + char *key, *keys, *saveptr = NULL; + + fulllen = lxc_list_config_items(NULL, inlen); + + keys = malloc(sizeof(char) * fulllen + 1); + if (!keys) { + lxc_error("%s\n", "failed to allocate memory"); + exit(ret); + } + + if (lxc_list_config_items(keys, fulllen) != fulllen) { + lxc_error("%s\n", "failed to retrieve configuration keys"); + goto on_error; + } + + for (key = strtok_r(keys, "\n", &saveptr); key != NULL; + key = strtok_r(NULL, "\n", &saveptr)) { + struct lxc_config_t *config; + config = lxc_get_config(key); + if (!config) { + lxc_error("configuration key \"%s\" not implemented in " + "jump table", + key); + goto on_error; + } + + if (!config->set) { + lxc_error("configuration key \"%s\" has no set method " + "in jump table", + key); + goto on_error; + } + + if (!config->get) { + lxc_error("configuration key \"%s\" has no get method " + "in jump table", + key); + goto on_error; + } + + if (!config->clr) { + lxc_error("configuration key \"%s\" has no clr (clear) " + "method in jump table", + key); + goto on_error; + } + } + + ret = EXIT_SUCCESS; + +on_error: + free(keys); + + exit(ret); + +} diff -Nru lxc-2.0.8/src/tests/console.c lxc-2.1.0/src/tests/console.c --- lxc-2.0.8/src/tests/console.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/console.c 2017-09-06 02:32:37.000000000 +0000 @@ -145,7 +145,8 @@ goto out2; } c->load_config(c, NULL); - c->set_config_item(c, "lxc.tty", TTYCNT_STR); + c->set_config_item(c, "lxc.tty.max", TTYCNT_STR); + c->set_config_item(c, "lxc.pty.max", "1024"); c->save_config(c, NULL); c->want_daemonize(c, true); if (!c->startl(c, 0, NULL)) { diff -Nru lxc-2.0.8/src/tests/createtest.c lxc-2.1.0/src/tests/createtest.c --- lxc-2.0.8/src/tests/createtest.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/createtest.c 2017-09-06 02:32:37.000000000 +0000 @@ -44,12 +44,12 @@ goto out; } - if (!c->set_config_item(c, "lxc.network.type", "veth")) { + if (!c->set_config_item(c, "lxc.net.0.type", "veth")) { fprintf(stderr, "%d: failed to set network type\n", __LINE__); goto out; } - c->set_config_item(c, "lxc.network.link", "lxcbr0"); - c->set_config_item(c, "lxc.network.flags", "up"); + c->set_config_item(c, "lxc.net.0.link", "lxcbr0"); + c->set_config_item(c, "lxc.net.0.flags", "up"); if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) { fprintf(stderr, "%d: failed to create a trusty container\n", __LINE__); goto out; diff -Nru lxc-2.0.8/src/tests/get_item.c lxc-2.1.0/src/tests/get_item.c --- lxc-2.0.8/src/tests/get_item.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/get_item.c 2017-09-06 02:32:37.000000000 +0000 @@ -26,7 +26,9 @@ #include #include #include + #include "lxc/state.h" +#include "lxctest.h" #define MYNAME "lxctest1" @@ -41,6 +43,29 @@ exit(EXIT_FAILURE); } + /* EXPECT SUCCESS: lxc.syslog with valid value. */ + if (!c->set_config_item(c, "lxc.syslog", "local0")) { + lxc_error("%s\n", "Failed to set lxc.syslog.\n"); + goto out; + } + ret = c->get_config_item(c, "lxc.syslog", v2, 255); + if (ret < 0) { + lxc_error("Failed to retrieve lxc.syslog: %d.\n", ret); + goto out; + } + if (strcmp(v2, "local0") != 0) { + lxc_error("Expected: local0 == %s.\n", v2); + goto out; + } + lxc_debug("Retrieving value for lxc.syslog correctly returned: %s.\n", v2); + + /* EXPECT FAILURE: lxc.syslog with invalid value. */ + if (c->set_config_item(c, "lxc.syslog", "NONSENSE")) { + lxc_error("%s\n", "Succeeded int setting lxc.syslog to invalid value \"NONSENSE\".\n"); + goto out; + } + lxc_debug("%s\n", "Successfully failed to set lxc.syslog to invalid value.\n"); + if (!c->set_config_item(c, "lxc.hook.pre-start", "hi there")) { fprintf(stderr, "%d: failed to set hook.pre-start\n", __LINE__); goto out; @@ -58,6 +83,11 @@ goto out; } fprintf(stderr, "%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2); + + + /* REMOVE IN LXC 3.0 + legacy lxc.tty key + */ if (!c->set_config_item(c, "lxc.tty", "4")) { fprintf(stderr, "%d: failed to set tty\n", __LINE__); goto out; @@ -149,6 +179,70 @@ } printf("lxc.mount.entry returned %d %s\n", ret, v2); + ret = c->get_config_item(c, "lxc.limit", v3, 2047); + if (ret != 0) { + fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret); + goto out; + } + + if (!c->set_config_item(c, "lxc.limit.nofile", "1234:unlimited")) { + fprintf(stderr, "%d: failed to set limit.nofile\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.limit.nofile", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.limit.nofile) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "1234:unlimited")) { + fprintf(stderr, "%d: lxc.limit.nofile returned wrong value: %d %s not 14 1234:unlimited\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.limit.nofile returned %d %s\n", ret, v2); + + if (!c->set_config_item(c, "lxc.limit.stack", "unlimited")) { + fprintf(stderr, "%d: failed to set limit.stack\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.limit.stack", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.limit.stack) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "unlimited")) { + fprintf(stderr, "%d: lxc.limit.stack returned wrong value: %d %s not 9 unlimited\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.limit.stack returned %d %s\n", ret, v2); + +#define LIMIT_STACK "lxc.limit.stack = unlimited\n" +#define ALL_LIMITS "lxc.limit.nofile = 1234:unlimited\n" LIMIT_STACK + ret = c->get_config_item(c, "lxc.limit", v3, 2047); + if (ret != sizeof(ALL_LIMITS)-1) { + fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, ALL_LIMITS)) { + fprintf(stderr, "%d: lxc.limit returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(ALL_LIMITS)-1, ALL_LIMITS); + goto out; + } + printf("lxc.limit returned %d %s\n", ret, v3); + + if (!c->clear_config_item(c, "lxc.limit.nofile")) { + fprintf(stderr, "%d: failed clearing limit.nofile\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.limit", v3, 2047); + if (ret != sizeof(LIMIT_STACK)-1) { + fprintf(stderr, "%d: get_config_item(limit) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, LIMIT_STACK)) { + fprintf(stderr, "%d: lxc.limit returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(LIMIT_STACK)-1, LIMIT_STACK); + goto out; + } + printf("lxc.limit returned %d %s\n", ret, v3); + if (!c->set_config_item(c, "lxc.aa_profile", "unconfined")) { fprintf(stderr, "%d: failed to set aa_profile\n", __LINE__); goto out; @@ -204,11 +298,27 @@ printf("%d: get_config_item(lxc.cap.drop) returned %d %s\n", __LINE__, ret, v2); ret = c->get_config_item(c, "lxc.network", v2, 255); if (ret < 0) { - fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret); + fprintf(stderr, "%d: get_config_item(lxc.network) returned %d\n", __LINE__, ret); goto out; } printf("%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2); + if (!c->set_config_item(c, "lxc.network.type", "veth")) { + fprintf(stderr, "%d: failed to set network.type\n", __LINE__); + goto out; + } + if (!c->set_config_item(c, "lxc.network.link", "lxcbr0")) { + fprintf(stderr, "%d: failed to set network.link\n", __LINE__); + goto out; + } + if (!c->set_config_item(c, "lxc.network.flags", "up")) { + fprintf(stderr, "%d: failed to set network.flags\n", __LINE__); + goto out; + } + if (!c->set_config_item(c, "lxc.network.hwaddr", "00:16:3e:xx:xx:xx")) { + fprintf(stderr, "%d: failed to set network.hwaddr\n", __LINE__); + goto out; + } if (!c->set_config_item(c, "lxc.network.ipv4", "10.2.3.4")) { fprintf(stderr, "%d: failed to set ipv4\n", __LINE__); goto out; @@ -302,6 +412,17 @@ fprintf(stderr, "%d: failed clearing lxc.hook\n", __LINE__); goto out; } + + if (!lxc_config_item_is_supported("lxc.arch")) { + fprintf(stderr, "%d: failed to report \"lxc.arch\" as supported configuration item\n", __LINE__); + goto out; + } + + if (lxc_config_item_is_supported("lxc.nonsense")) { + fprintf(stderr, "%d: failed to detect \"lxc.nonsense\" as unsupported configuration item\n", __LINE__); + goto out; + } + printf("All get_item tests passed\n"); ret = EXIT_SUCCESS; out: diff -Nru lxc-2.0.8/src/tests/getkeys.c lxc-2.1.0/src/tests/getkeys.c --- lxc-2.0.8/src/tests/getkeys.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/getkeys.c 2017-09-06 02:32:37.000000000 +0000 @@ -16,15 +16,17 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include -#include +#include #include #include +#include +#include +#include #include #include -#include -#include +#include + #include "lxc/state.h" #define MYNAME "lxctest1" @@ -64,6 +66,111 @@ goto out; } printf("get_keys for nic 1 returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.apparmor", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.selinux", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.mount", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.rootfs", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.uts", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.hook", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.cap", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.console", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.seccomp", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.signal", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.start", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.monitor", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.cgroup", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + ret = 0; out: diff -Nru lxc-2.0.8/src/tests/lxc-test-apparmor-mount lxc-2.1.0/src/tests/lxc-test-apparmor-mount --- lxc-2.0.8/src/tests/lxc-test-apparmor-mount 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-apparmor-mount 2017-09-06 02:32:37.000000000 +0000 @@ -50,7 +50,7 @@ run_cmd lxc-destroy -f -n $cname || true umount -l $MOUNTSR || true rmdir $dnam || true - pkill -u $(id -u $TUSER) -9 + pkill -u $(id -u $TUSER) -9 || true sed -i '/lxcunpriv/d' /run/lxc/nics /etc/lxc/lxc-usernet sed -i '/^lxcunpriv:/d' /etc/subuid /etc/subgid rm -Rf $HDIR /run/user/$(id -u $TUSER) @@ -100,10 +100,10 @@ mkdir -p $HDIR/.config/lxc/ cat > $HDIR/.config/lxc/default.conf << EOF -lxc.network.type = veth -lxc.network.link = lxcbr0 -lxc.id_map = u 0 910000 9999 -lxc.id_map = g 0 910000 9999 +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 +lxc.idmap = u 0 910000 9999 +lxc.idmap = g 0 910000 9999 EOF chown -R $TUSER: $HDIR @@ -170,7 +170,7 @@ run_cmd lxc-stop -n $cname -k echo "test regular unconfined container" -echo "lxc.aa_profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config +echo "lxc.apparmor.profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config run_cmd lxc-start -n $cname -d run_cmd lxc-wait -n $cname -s RUNNING pid=`run_cmd lxc-info -p -H -n $cname` @@ -185,7 +185,7 @@ mount --bind $dnam $MOUNTSR echo "test default confined container" -sed -i '/aa_profile/d' $HDIR/.local/share/lxc/$cname/config +sed -i '/apparmor.profile/d' $HDIR/.local/share/lxc/$cname/config run_cmd lxc-start -n $cname -d || true sleep 3 pid=`run_cmd lxc-info -p -H -n $cname` || true @@ -196,7 +196,7 @@ fi echo "test regular unconfined container" -echo "lxc.aa_profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config +echo "lxc.apparmor.profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config run_cmd lxc-start -n $cname -d run_cmd lxc-wait -n $cname -s RUNNING pid=`run_cmd lxc-info -p -H -n $cname` @@ -212,8 +212,8 @@ run_cmd lxc-stop -n $cname -k echo "testing override" -sed -i '/aa_profile/d' $HDIR/.local/share/lxc/$cname/config -echo "lxc.aa_allow_incomplete = 1" >> $HDIR/.local/share/lxc/$cname/config +sed -i '/apparmor.profile/d' $HDIR/.local/share/lxc/$cname/config +echo "lxc.apparmor.allow_incomplete = 1" >> $HDIR/.local/share/lxc/$cname/config run_cmd lxc-start -n $cname -d run_cmd lxc-wait -n $cname -s RUNNING pid=`run_cmd lxc-info -p -H -n $cname` diff -Nru lxc-2.0.8/src/tests/lxc-test-autostart lxc-2.1.0/src/tests/lxc-test-autostart --- lxc-2.0.8/src/tests/lxc-test-autostart 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-autostart 2017-09-06 02:32:37.000000000 +0000 @@ -55,8 +55,8 @@ done fi -lxc-create -t download -n $CONTAINER_NAME -- -d ubuntu -r $release -a $ARCH -CONTAINER_PATH=$(dirname $(lxc-info -n $CONTAINER_NAME -c lxc.rootfs -H)) +lxc-create -t download -n $CONTAINER_NAME -B dir -- -d ubuntu -r $release -a $ARCH +CONTAINER_PATH=$(dirname $(lxc-info -n $CONTAINER_NAME -c lxc.rootfs.path -H) | sed -e 's/dir://') cp $CONTAINER_PATH/config $CONTAINER_PATH/config.bak # Ensure it's not in lxc-autostart diff -Nru lxc-2.0.8/src/tests/lxc-test-checkpoint-restore lxc-2.1.0/src/tests/lxc-test-checkpoint-restore --- lxc-2.0.8/src/tests/lxc-test-checkpoint-restore 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-checkpoint-restore 2017-09-06 02:32:37.000000000 +0000 @@ -31,8 +31,8 @@ cat >> "$(lxc-config lxc.lxcpath)/$name/config" < $s/1.conf << EOF -lxc.network.type = veth -lxc.network.link = lxcbr0 +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 EOF # Simple nic with hwaddr; verify hwaddr changed cat > $s/2.conf << EOF -lxc.network.type = veth -lxc.network.link = lxcbr0 +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 EOF # No nics, but nic from include cat > $s/1.include << EOF -lxc.network.type = veth -lxc.network.link = lxcbr0 +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 lxc.hook.start = /bin/bash EOF cat > $s/3.conf << EOF @@ -108,8 +108,8 @@ # Figure out container dirname # We need this in 5.conf lxc-destroy -n lxctestb || true -lxc-create -t busybox -n lxctestb -CONTAINER_PATH=$(dirname $(lxc-info -n lxctestb -c lxc.rootfs -H)) +lxc-create -t busybox -n lxctestb -B dir +CONTAINER_PATH=$(dirname $(lxc-info -n lxctestb -c lxc.rootfs.path -H) | sed 's/dir://') lxc-destroy -n lxctestb # No nics, one clone hook in $container diff -Nru lxc-2.0.8/src/tests/lxc-test-createconfig lxc-2.1.0/src/tests/lxc-test-createconfig --- lxc-2.0.8/src/tests/lxc-test-createconfig 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-createconfig 2017-09-06 02:32:37.000000000 +0000 @@ -34,8 +34,8 @@ trap cleanup EXIT cat > $f << EOF -lxc.network.type = veth -lxc.network.hwaddr = 00:16:3e:xx:xx:xx +lxc.net.0.type = veth +lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx EOF lxc-create -t busybox -f $f -n lxctestc grep -q "xx:xx" /var/lib/lxc/lxctestc/config && { echo "hwaddr not expanded"; exit 1; } diff -Nru lxc-2.0.8/src/tests/lxc-test-no-new-privs lxc-2.1.0/src/tests/lxc-test-no-new-privs --- lxc-2.0.8/src/tests/lxc-test-no-new-privs 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-no-new-privs 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,104 @@ +#!/bin/bash + +# lxc: linux Container library + +# Authors: +# Christian Brauner +# +# This is a test script for PR_SET_NO_NEW_PRIVS + +# 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 -eux + +DONE=0 +cleanup() { + cd / + lxc-destroy -n c1 -f || true + if [ $DONE -eq 0 ]; then + echo "FAIL" + exit 1 + fi + echo "PASS" +} + +trap cleanup EXIT SIGHUP SIGINT SIGTERM + +mkdir -p /etc/lxc/ +cat > /etc/lxc/default.conf << EOF +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 +EOF + +ARCH=i386 +if type dpkg >/dev/null 2>&1; then + ARCH=$(dpkg --print-architecture) +fi + +lxc-create -t download -n c1 -- -d ubuntu -r xenial -a $ARCH +echo "lxc.no_new_privs = 1" >> /var/lib/lxc/c1/config + +lxc-start -n c1 +p1=$(lxc-info -n c1 -p -H) +[ "$p1" != "-1" ] || { echo "Failed to start container c1 (run $count)"; false; } +sleep 5s +lxc-attach -n c1 --clear-env -- apt update -y +lxc-attach -n c1 --clear-env -- apt install -y gcc make + +# Here documents don't seem to like sudo -i. +lxc-attach -n c1 --clear-env -- /bin/bash -c "cat < /nnptest.c +#include +#include +#include + +int main(int argc, char *argv[]) +{ + printf(\"%d\n\", geteuid()); +} +EOF" +lxc-attach -n c1 --clear-env -- cat /nnptest.c +lxc-attach -n c1 --clear-env -- make -C / nnptest +lxc-attach -n c1 --clear-env -- chmod u+s /nnptest + +# Check that lxc-attach obeys PR_SET_NO_NEW_PRIVS when it is set. +NNP_EUID=$(lxc-attach -n c1 --clear-env -- sudo -u ubuntu /nnptest) +if [ "$NNP_EUID" -ne 1000 ]; then + exit 1 +fi +lxc-stop -n c1 -k + +# Check that lxc-attach obeys PR_SET_NO_NEW_PRIVS when it is not set. +sed -i 's/lxc.no_new_privs = 1/lxc.no_new_privs = 0/' /var/lib/lxc/c1/config +lxc-start -n c1 +NNP_EUID=$(lxc-attach -n c1 --clear-env -- sudo -u ubuntu /nnptest) +if [ "$NNP_EUID" -ne 0 ]; then + exit 1 +fi +lxc-stop -n c1 -k + +# Check that lxc-execute and lxc-start obey PR_SET_NO_NEW_PRIVS when it is set. +NNP_EUID=$(lxc-execute -n c1 -- sudo -u ubuntu /nnptest) +if [ "$NNP_EUID" -ne 0 ]; then + exit 1 +fi + +# Check that lxc-execute and lxc-start obey PR_SET_NO_NEW_PRIVS when it is not set. +sed -i 's/lxc.no_new_privs = 0/lxc.no_new_privs = 1/' /var/lib/lxc/c1/config +NNP_EUID=$(lxc-execute -n c1 -- sudo -u ubuntu /nnptest) +if [ "$NNP_EUID" -ne 1000 ]; then + exit 1 +fi + +DONE=1 diff -Nru lxc-2.0.8/src/tests/lxc-test-rootfs lxc-2.1.0/src/tests/lxc-test-rootfs --- lxc-2.0.8/src/tests/lxc-test-rootfs 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-rootfs 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,66 @@ +#!/bin/bash + +# lxc: linux Container library + +# Authors: +# Feng Li + +# 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 -ex + +cleanup() { + set +e + lxc-destroy -n lxc-test-rootfs -f + if [ $PHASE != "done" ]; then + echo "rootfs test failed at $PHASE" + exit 1 + fi + echo "rootfs test passed" + exit 0 +} + +PHASE=setup +trap cleanup EXIT + +lxc-destroy -n lxc-test-rootfs -f || true +lxc-create -t busybox -n lxc-test-rootfs + +PHASE=ro_rootfs +echo "Starting phase $PHASE" +config=/var/lib/lxc/lxc-test-rootfs/config +sed -i '/lxc.rootfs.options/d' $config +echo "lxc.rootfs.options = ro" >> $config + +lxc-start -n lxc-test-rootfs +pid=`lxc-info -n lxc-test-rootfs -p -H` +ro=0 +mkdir /proc/$pid/root/rotest || ro=1 +[ $ro -ne 0 ] + +lxc-stop -n lxc-test-rootfs -k +PHASE=rw_rootfs +echo "Starting phase $PHASE" +sed -i '/lxc.rootfs.options/d' $config +echo "lxc.rootfs.options = rw" >> $config +lxc-start -n lxc-test-rootfs +pid=`lxc-info -n lxc-test-rootfs -p -H` +ro=0 +mkdir /proc/$pid/root/rwtest || ro=1 +[ $ro -ne 1 ] +rmdir /proc/$pid/root/rwtest +ro=0 + +PHASE=done diff -Nru lxc-2.0.8/src/tests/lxc-test-unpriv lxc-2.1.0/src/tests/lxc-test-unpriv --- lxc-2.0.8/src/tests/lxc-test-unpriv 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-unpriv 2017-09-06 02:32:37.000000000 +0000 @@ -71,7 +71,7 @@ run_cmd lxc-stop -n c2 -k || true run_cmd lxc-stop -n c1 -k || true - pkill -u $(id -u $TUSER) -9 + pkill -u $(id -u $TUSER) -9 || true sed -i '/lxcunpriv/d' /run/lxc/nics /etc/lxc/lxc-usernet sed -i '/^lxcunpriv:/d' /etc/subuid /etc/subgid @@ -116,10 +116,10 @@ mkdir -p $HDIR/.config/lxc/ cat > $HDIR/.config/lxc/default.conf << EOF -lxc.network.type = veth -lxc.network.link = lxcbr0 -lxc.id_map = u 0 910000 9999 -lxc.id_map = g 0 910000 9999 +lxc.net.0.type = veth +lxc.net.0.link = lxcbr0 +lxc.idmap = u 0 910000 9999 +lxc.idmap = g 0 910000 9999 EOF chown -R $TUSER: $HDIR diff -Nru lxc-2.0.8/src/tests/lxc-test-usernic.in lxc-2.1.0/src/tests/lxc-test-usernic.in --- lxc-2.0.8/src/tests/lxc-test-usernic.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-usernic.in 2017-09-06 02:32:37.000000000 +0000 @@ -80,9 +80,9 @@ mkdir -p /home/usernic-user/.config/lxc/ cat > /home/usernic-user/.config/lxc/default.conf << EOF -lxc.network.type = empty -lxc.id_map = u 0 910000 10000 -lxc.id_map = g 0 910000 10000 +lxc.net.0.type = empty +lxc.idmap = u 0 910000 10000 +lxc.idmap = g 0 910000 10000 EOF if which cgm >/dev/null 2>&1; then @@ -153,7 +153,7 @@ lxcname=b1 # Assign one veth, should fail as no allowed entries yet -if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx1"; then +if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx1"; then echo "FAIL: able to create nic with no entries" exit 1 fi @@ -164,24 +164,24 @@ echo "usernic-user veth usernic-br0 2" >> /etc/lxc/lxc-usernet # Assign one veth to second bridge, should fail -if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br1 xx1"; then +if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br1 xx1"; then echo "FAIL: able to create nic with no entries" exit 1 fi # Assign two veths, should succeed -if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx2"; then +if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx2"; then echo "FAIL: unable to create first nic" exit 1 fi -if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx3"; then +if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx3"; then echo "FAIL: unable to create second nic" exit 1 fi # Assign one more veth, should fail. -if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx4"; then +if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx4"; then echo "FAIL: able to create third nic" exit 1 fi @@ -191,7 +191,7 @@ run_cmd "lxc-start -n b1 -d" p1=$(run_cmd "lxc-info -n b1 -p -H") -if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx5"; then +if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx5"; then echo "FAIL: unable to create nic after destroying the old" cleanup 1 fi @@ -204,7 +204,7 @@ p2=$(lxc-info -n usernic-c1 -p -H) # assign veth to it - should fail -if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p2 veth usernic-br0 xx6"; then +if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p2 veth usernic-br0 xx6"; then echo "FAIL: able to attach nic to root-owned container" cleanup 1 fi diff -Nru lxc-2.0.8/src/tests/lxc-test-utils.c lxc-2.1.0/src/tests/lxc-test-utils.c --- lxc-2.0.8/src/tests/lxc-test-utils.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/lxc-test-utils.c 2017-09-06 02:32:37.000000000 +0000 @@ -41,33 +41,37 @@ void test_lxc_deslashify(void) { - char *s = strdup("/A///B//C/D/E/"); - if (!s) + char *s = "/A///B//C/D/E/"; + char *t; + + t = lxc_deslashify(s); + if (!t) exit(EXIT_FAILURE); - lxc_test_assert_abort(lxc_deslashify(&s)); - lxc_test_assert_abort(strcmp(s, "/A/B/C/D/E") == 0); - free(s); + lxc_test_assert_abort(strcmp(t, "/A/B/C/D/E") == 0); + free(t); + + s = "/A"; - s = strdup("/A"); - if (!s) + t = lxc_deslashify(s); + if (!t) exit(EXIT_FAILURE); - lxc_test_assert_abort(lxc_deslashify(&s)); - lxc_test_assert_abort(strcmp(s, "/A") == 0); - free(s); + lxc_test_assert_abort(strcmp(t, "/A") == 0); + free(t); - s = strdup(""); - if (!s) + s = ""; + t = lxc_deslashify(s); + if (!t) exit(EXIT_FAILURE); - lxc_test_assert_abort(lxc_deslashify(&s)); - lxc_test_assert_abort(strcmp(s, "") == 0); - free(s); + lxc_test_assert_abort(strcmp(t, "") == 0); + free(t); + + s = "//"; - s = strdup("//"); - if (!s) + t = lxc_deslashify(s); + if (!t) exit(EXIT_FAILURE); - lxc_test_assert_abort(lxc_deslashify(&s)); - lxc_test_assert_abort(strcmp(s, "/") == 0); - free(s); + lxc_test_assert_abort(strcmp(t, "/") == 0); + free(t); } /* /proc/int_as_str/ns/mnt\0 = (5 + 21 + 7 + 1) */ @@ -228,7 +232,22 @@ void test_lxc_safe_uint(void) { + int ret; unsigned int n; + char numstr[LXC_NUMSTRLEN64]; + + lxc_test_assert_abort((-EINVAL == lxc_safe_uint(" -123", &n))); + lxc_test_assert_abort((-EINVAL == lxc_safe_uint("-123", &n))); + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)UINT_MAX); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((0 == lxc_safe_uint(numstr, &n)) && n == UINT_MAX); + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)UINT_MAX + 1); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((-ERANGE == lxc_safe_uint(numstr, &n))); lxc_test_assert_abort((0 == lxc_safe_uint("1234345", &n)) && n == 1234345); lxc_test_assert_abort((0 == lxc_safe_uint(" 345", &n)) && n == 345); @@ -247,7 +266,29 @@ void test_lxc_safe_int(void) { + int ret; signed int n; + char numstr[LXC_NUMSTRLEN64]; + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)INT_MAX); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((0 == lxc_safe_int(numstr, &n)) && n == INT_MAX); + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRIu64, (uint64_t)INT_MAX + 1); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((-ERANGE == lxc_safe_int(numstr, &n))); + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRId64, (int64_t)INT_MIN); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((0 == lxc_safe_int(numstr, &n)) && n == INT_MIN); + + ret = snprintf(numstr, LXC_NUMSTRLEN64, "%" PRId64, (int64_t)INT_MIN - 1); + if (ret < 0 || ret >= LXC_NUMSTRLEN64) + exit(EXIT_FAILURE); + lxc_test_assert_abort((-ERANGE == lxc_safe_int(numstr, &n))); lxc_test_assert_abort((0 == lxc_safe_int("1234345", &n)) && n == 1234345); lxc_test_assert_abort((0 == lxc_safe_int(" 345", &n)) && n == 345); diff -Nru lxc-2.0.8/src/tests/Makefile.am lxc-2.1.0/src/tests/Makefile.am --- lxc-2.0.8/src/tests/Makefile.am 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/Makefile.am 2017-09-06 02:32:37.000000000 +0000 @@ -24,6 +24,9 @@ lxc_test_device_add_remove_SOURCES = device_add_remove.c lxc_test_apparmor_SOURCES = aa.c lxc_test_utils_SOURCES = lxc-test-utils.c lxctest.h +lxc_test_parse_config_file_SOURCES = parse_config_file.c lxctest.h +lxc_test_config_jump_table_SOURCES = config_jump_table.c lxctest.h +lxc_test_shortlived_SOURCES = shortlived.c AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ @@ -51,10 +54,15 @@ lxc-test-cgpath lxc-test-clonetest lxc-test-console \ lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \ lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \ - lxc-test-apparmor lxc-test-utils + lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \ + lxc-test-config-jump-table lxc-test-shortlived -bin_SCRIPTS = lxc-test-automount lxc-test-autostart lxc-test-cloneconfig \ - lxc-test-createconfig +bin_SCRIPTS = lxc-test-automount \ + lxc-test-autostart \ + lxc-test-cloneconfig \ + lxc-test-createconfig \ + lxc-test-no-new-privs \ + lxc-test-rootfs if DISTRO_UBUNTU bin_SCRIPTS += \ @@ -74,6 +82,7 @@ cgpath.c \ clonetest.c \ concurrent.c \ + config_jump_table.c \ console.c \ containertests.c \ createtest.c \ @@ -86,21 +95,26 @@ lxcpath.c \ lxc-test-lxc-attach \ lxc-test-automount \ + lxc-test-rootfs \ lxc-test-autostart \ lxc-test-apparmor-mount \ lxc-test-checkpoint-restore \ lxc-test-cloneconfig \ lxc-test-createconfig \ + lxc-test-no-new-privs \ lxc-test-snapdeps \ lxc-test-symlink \ lxc-test-ubuntu \ lxc-test-unpriv \ lxc-test-utils.c \ may_control.c \ + parse_config_file.c \ saveconfig.c \ + shortlived.c \ shutdowntest.c \ snapshot.c \ startone.c clean-local: rm -f lxc-test-utils-* + rm -f lxc-parse-config-file-* diff -Nru lxc-2.0.8/src/tests/Makefile.in lxc-2.1.0/src/tests/Makefile.in --- lxc-2.0.8/src/tests/Makefile.in 2017-05-11 17:23:10.000000000 +0000 +++ lxc-2.1.0/src/tests/Makefile.in 2017-09-06 02:32:42.000000000 +0000 @@ -112,7 +112,10 @@ @ENABLE_TESTS_TRUE@ lxc-test-attach$(EXEEXT) \ @ENABLE_TESTS_TRUE@ lxc-test-device-add-remove$(EXEEXT) \ @ENABLE_TESTS_TRUE@ lxc-test-apparmor$(EXEEXT) \ -@ENABLE_TESTS_TRUE@ lxc-test-utils$(EXEEXT) +@ENABLE_TESTS_TRUE@ lxc-test-utils$(EXEEXT) \ +@ENABLE_TESTS_TRUE@ lxc-test-parse-config-file$(EXEEXT) \ +@ENABLE_TESTS_TRUE@ lxc-test-config-jump-table$(EXEEXT) \ +@ENABLE_TESTS_TRUE@ lxc-test-shortlived$(EXEEXT) @DISTRO_UBUNTU_TRUE@@ENABLE_TESTS_TRUE@am__append_3 = \ @DISTRO_UBUNTU_TRUE@@ENABLE_TESTS_TRUE@ lxc-test-lxc-attach \ @DISTRO_UBUNTU_TRUE@@ENABLE_TESTS_TRUE@ lxc-test-apparmor-mount \ @@ -173,6 +176,15 @@ lxc_test_concurrent_LDADD = $(LDADD) @ENABLE_TESTS_TRUE@lxc_test_concurrent_DEPENDENCIES = \ @ENABLE_TESTS_TRUE@ ../lxc/liblxc.la +am__lxc_test_config_jump_table_SOURCES_DIST = config_jump_table.c \ + lxctest.h +@ENABLE_TESTS_TRUE@am_lxc_test_config_jump_table_OBJECTS = \ +@ENABLE_TESTS_TRUE@ config_jump_table.$(OBJEXT) +lxc_test_config_jump_table_OBJECTS = \ + $(am_lxc_test_config_jump_table_OBJECTS) +lxc_test_config_jump_table_LDADD = $(LDADD) +@ENABLE_TESTS_TRUE@lxc_test_config_jump_table_DEPENDENCIES = \ +@ENABLE_TESTS_TRUE@ ../lxc/liblxc.la am__lxc_test_console_SOURCES_DIST = console.c @ENABLE_TESTS_TRUE@am_lxc_test_console_OBJECTS = console.$(OBJEXT) lxc_test_console_OBJECTS = $(am_lxc_test_console_OBJECTS) @@ -241,6 +253,15 @@ lxc_test_may_control_LDADD = $(LDADD) @ENABLE_TESTS_TRUE@lxc_test_may_control_DEPENDENCIES = \ @ENABLE_TESTS_TRUE@ ../lxc/liblxc.la +am__lxc_test_parse_config_file_SOURCES_DIST = parse_config_file.c \ + lxctest.h +@ENABLE_TESTS_TRUE@am_lxc_test_parse_config_file_OBJECTS = \ +@ENABLE_TESTS_TRUE@ parse_config_file.$(OBJEXT) +lxc_test_parse_config_file_OBJECTS = \ + $(am_lxc_test_parse_config_file_OBJECTS) +lxc_test_parse_config_file_LDADD = $(LDADD) +@ENABLE_TESTS_TRUE@lxc_test_parse_config_file_DEPENDENCIES = \ +@ENABLE_TESTS_TRUE@ ../lxc/liblxc.la am__lxc_test_reboot_SOURCES_DIST = reboot.c @ENABLE_TESTS_TRUE@am_lxc_test_reboot_OBJECTS = reboot.$(OBJEXT) lxc_test_reboot_OBJECTS = $(am_lxc_test_reboot_OBJECTS) @@ -253,6 +274,13 @@ lxc_test_saveconfig_LDADD = $(LDADD) @ENABLE_TESTS_TRUE@lxc_test_saveconfig_DEPENDENCIES = \ @ENABLE_TESTS_TRUE@ ../lxc/liblxc.la +am__lxc_test_shortlived_SOURCES_DIST = shortlived.c +@ENABLE_TESTS_TRUE@am_lxc_test_shortlived_OBJECTS = \ +@ENABLE_TESTS_TRUE@ shortlived.$(OBJEXT) +lxc_test_shortlived_OBJECTS = $(am_lxc_test_shortlived_OBJECTS) +lxc_test_shortlived_LDADD = $(LDADD) +@ENABLE_TESTS_TRUE@lxc_test_shortlived_DEPENDENCIES = \ +@ENABLE_TESTS_TRUE@ ../lxc/liblxc.la am__lxc_test_shutdowntest_SOURCES_DIST = shutdowntest.c @ENABLE_TESTS_TRUE@am_lxc_test_shutdowntest_OBJECTS = \ @ENABLE_TESTS_TRUE@ shutdowntest.$(OBJEXT) @@ -340,14 +368,17 @@ am__v_CCLD_1 = SOURCES = $(lxc_test_apparmor_SOURCES) $(lxc_test_attach_SOURCES) \ $(lxc_test_cgpath_SOURCES) $(lxc_test_clonetest_SOURCES) \ - $(lxc_test_concurrent_SOURCES) $(lxc_test_console_SOURCES) \ - $(lxc_test_containertests_SOURCES) \ + $(lxc_test_concurrent_SOURCES) \ + $(lxc_test_config_jump_table_SOURCES) \ + $(lxc_test_console_SOURCES) $(lxc_test_containertests_SOURCES) \ $(lxc_test_createtest_SOURCES) $(lxc_test_destroytest_SOURCES) \ $(lxc_test_device_add_remove_SOURCES) \ $(lxc_test_get_item_SOURCES) $(lxc_test_getkeys_SOURCES) \ $(lxc_test_list_SOURCES) $(lxc_test_locktests_SOURCES) \ $(lxc_test_lxcpath_SOURCES) $(lxc_test_may_control_SOURCES) \ + $(lxc_test_parse_config_file_SOURCES) \ $(lxc_test_reboot_SOURCES) $(lxc_test_saveconfig_SOURCES) \ + $(lxc_test_shortlived_SOURCES) \ $(lxc_test_shutdowntest_SOURCES) $(lxc_test_snapshot_SOURCES) \ $(lxc_test_startone_SOURCES) $(lxc_test_utils_SOURCES) DIST_SOURCES = $(am__lxc_test_apparmor_SOURCES_DIST) \ @@ -355,6 +386,7 @@ $(am__lxc_test_cgpath_SOURCES_DIST) \ $(am__lxc_test_clonetest_SOURCES_DIST) \ $(am__lxc_test_concurrent_SOURCES_DIST) \ + $(am__lxc_test_config_jump_table_SOURCES_DIST) \ $(am__lxc_test_console_SOURCES_DIST) \ $(am__lxc_test_containertests_SOURCES_DIST) \ $(am__lxc_test_createtest_SOURCES_DIST) \ @@ -366,8 +398,10 @@ $(am__lxc_test_locktests_SOURCES_DIST) \ $(am__lxc_test_lxcpath_SOURCES_DIST) \ $(am__lxc_test_may_control_SOURCES_DIST) \ + $(am__lxc_test_parse_config_file_SOURCES_DIST) \ $(am__lxc_test_reboot_SOURCES_DIST) \ $(am__lxc_test_saveconfig_SOURCES_DIST) \ + $(am__lxc_test_shortlived_SOURCES_DIST) \ $(am__lxc_test_shutdowntest_SOURCES_DIST) \ $(am__lxc_test_snapshot_SOURCES_DIST) \ $(am__lxc_test_startone_SOURCES_DIST) \ @@ -614,6 +648,9 @@ @ENABLE_TESTS_TRUE@lxc_test_device_add_remove_SOURCES = device_add_remove.c @ENABLE_TESTS_TRUE@lxc_test_apparmor_SOURCES = aa.c @ENABLE_TESTS_TRUE@lxc_test_utils_SOURCES = lxc-test-utils.c lxctest.h +@ENABLE_TESTS_TRUE@lxc_test_parse_config_file_SOURCES = parse_config_file.c lxctest.h +@ENABLE_TESTS_TRUE@lxc_test_config_jump_table_SOURCES = config_jump_table.c lxctest.h +@ENABLE_TESTS_TRUE@lxc_test_shortlived_SOURCES = shortlived.c @ENABLE_TESTS_TRUE@AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ @ENABLE_TESTS_TRUE@ -DLXCPATH=\"$(LXCPATH)\" \ @ENABLE_TESTS_TRUE@ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ @@ -627,11 +664,13 @@ @ENABLE_TESTS_TRUE@ $(am__append_2) @ENABLE_TESTS_TRUE@bin_SCRIPTS = lxc-test-automount lxc-test-autostart \ @ENABLE_TESTS_TRUE@ lxc-test-cloneconfig lxc-test-createconfig \ +@ENABLE_TESTS_TRUE@ lxc-test-no-new-privs lxc-test-rootfs \ @ENABLE_TESTS_TRUE@ $(am__append_3) EXTRA_DIST = \ cgpath.c \ clonetest.c \ concurrent.c \ + config_jump_table.c \ console.c \ containertests.c \ createtest.c \ @@ -644,18 +683,22 @@ lxcpath.c \ lxc-test-lxc-attach \ lxc-test-automount \ + lxc-test-rootfs \ lxc-test-autostart \ lxc-test-apparmor-mount \ lxc-test-checkpoint-restore \ lxc-test-cloneconfig \ lxc-test-createconfig \ + lxc-test-no-new-privs \ lxc-test-snapdeps \ lxc-test-symlink \ lxc-test-ubuntu \ lxc-test-unpriv \ lxc-test-utils.c \ may_control.c \ + parse_config_file.c \ saveconfig.c \ + shortlived.c \ shutdowntest.c \ snapshot.c \ startone.c @@ -765,6 +808,10 @@ @rm -f lxc-test-concurrent$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_concurrent_OBJECTS) $(lxc_test_concurrent_LDADD) $(LIBS) +lxc-test-config-jump-table$(EXEEXT): $(lxc_test_config_jump_table_OBJECTS) $(lxc_test_config_jump_table_DEPENDENCIES) $(EXTRA_lxc_test_config_jump_table_DEPENDENCIES) + @rm -f lxc-test-config-jump-table$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lxc_test_config_jump_table_OBJECTS) $(lxc_test_config_jump_table_LDADD) $(LIBS) + lxc-test-console$(EXEEXT): $(lxc_test_console_OBJECTS) $(lxc_test_console_DEPENDENCIES) $(EXTRA_lxc_test_console_DEPENDENCIES) @rm -f lxc-test-console$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_console_OBJECTS) $(lxc_test_console_LDADD) $(LIBS) @@ -809,6 +856,10 @@ @rm -f lxc-test-may-control$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_may_control_OBJECTS) $(lxc_test_may_control_LDADD) $(LIBS) +lxc-test-parse-config-file$(EXEEXT): $(lxc_test_parse_config_file_OBJECTS) $(lxc_test_parse_config_file_DEPENDENCIES) $(EXTRA_lxc_test_parse_config_file_DEPENDENCIES) + @rm -f lxc-test-parse-config-file$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lxc_test_parse_config_file_OBJECTS) $(lxc_test_parse_config_file_LDADD) $(LIBS) + lxc-test-reboot$(EXEEXT): $(lxc_test_reboot_OBJECTS) $(lxc_test_reboot_DEPENDENCIES) $(EXTRA_lxc_test_reboot_DEPENDENCIES) @rm -f lxc-test-reboot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_reboot_OBJECTS) $(lxc_test_reboot_LDADD) $(LIBS) @@ -817,6 +868,10 @@ @rm -f lxc-test-saveconfig$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_saveconfig_OBJECTS) $(lxc_test_saveconfig_LDADD) $(LIBS) +lxc-test-shortlived$(EXEEXT): $(lxc_test_shortlived_OBJECTS) $(lxc_test_shortlived_DEPENDENCIES) $(EXTRA_lxc_test_shortlived_DEPENDENCIES) + @rm -f lxc-test-shortlived$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lxc_test_shortlived_OBJECTS) $(lxc_test_shortlived_LDADD) $(LIBS) + lxc-test-shutdowntest$(EXEEXT): $(lxc_test_shutdowntest_OBJECTS) $(lxc_test_shutdowntest_DEPENDENCIES) $(EXTRA_lxc_test_shutdowntest_DEPENDENCIES) @rm -f lxc-test-shutdowntest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_test_shutdowntest_OBJECTS) $(lxc_test_shutdowntest_LDADD) $(LIBS) @@ -879,6 +934,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgpath.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clonetest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/concurrent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_jump_table.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/containertests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/createtest.Po@am__quote@ @@ -891,8 +947,10 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxc-test-utils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxcpath.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/may_control.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_config_file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reboot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/saveconfig.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shortlived.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shutdowntest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snapshot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/startone.Po@am__quote@ @@ -1140,6 +1198,7 @@ clean-local: rm -f lxc-test-utils-* + rm -f lxc-parse-config-file-* # 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. diff -Nru lxc-2.0.8/src/tests/parse_config_file.c lxc-2.1.0/src/tests/parse_config_file.c --- lxc-2.0.8/src/tests/parse_config_file.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/tests/parse_config_file.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,1079 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "confile_utils.h" +#include "lxc/state.h" +#include "lxctest.h" + +static int set_get_compare_clear_save_load(struct lxc_container *c, + const char *key, const char *value, + const char *config_file, + bool compare) +{ + char retval[4096] = {0}; + int ret; + + if (!c->set_config_item(c, key, value)) { + lxc_error("failed to set config item \"%s\" to \"%s\"\n", key, + value); + return -1; + } + + ret = c->get_config_item(c, key, retval, sizeof(retval)); + if (ret < 0) { + lxc_error("failed to get config item \"%s\"\n", key); + return -1; + } + + if (compare) { + ret = strcmp(retval, value); + if (ret != 0) { + lxc_error( + "expected value \"%s\" and retrieved value \"%s\" " + "for config key \"%s\" do not match\n", + value, retval, key); + return -1; + } + } + + if (config_file) { + if (!c->save_config(c, config_file)) { + lxc_error("%s\n", "failed to save config file"); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + if (!c->load_config(c, config_file)) { + lxc_error("%s\n", "failed to load config file"); + return -1; + } + } + + if (!c->clear_config_item(c, key)) { + lxc_error("failed to clear config item \"%s\"\n", key); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + return 0; +} + +static int set_and_clear_complete_netdev(struct lxc_container *c) +{ + if (!c->set_config_item(c, "lxc.net.1.type", "veth")) { + lxc_error("%s\n", "lxc.net.1.type"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv4.address", "10.0.2.3/24")) { + lxc_error("%s\n", "lxc.net.1.ipv4.address"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv4.gateway", "10.0.2.2")) { + lxc_error("%s\n", "lxc.net.1.ipv4.gateway"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv6.address", + "2003:db8:1:0:214:1234:fe0b:3596/64")) { + lxc_error("%s\n", "lxc.net.1.ipv6.address"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv6.gateway", + "2003:db8:1:0::1")) { + lxc_error("%s\n", "lxc.net.1.ipv6.gateway"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.flags", "up")) { + lxc_error("%s\n", "lxc.net.1.flags"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.link", "br0")) { + lxc_error("%s\n", "lxc.net.1.link"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.veth.pair", "bla")) { + lxc_error("%s\n", "lxc.net.1.veth.pair"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.hwaddr", + "52:54:00:80:7a:5d")) { + lxc_error("%s\n", "lxc.net.1.hwaddr"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.mtu", "2000")) { + lxc_error("%s\n", "lxc.net.1.mtu"); + return -1; + } + + if (!c->clear_config_item(c, "lxc.net.1")) { + lxc_error("%s", "failed to clear \"lxc.net.1\"\n"); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + return 0; +} + +int test_idmap_parser(void) +{ + size_t i; + struct idmap_check { + bool is_valid; + const char *idmap; + }; + static struct idmap_check idmaps[] = { + /* valid idmaps */ + { true, "u 0 0 1" }, + { true, "g 0 0 1" }, + { true, "u 1 100001 999999999" }, + { true, "g 1 100001 999999999" }, + { true, "u 0 0 0" }, + { true, "g 0 0 0" }, + { true, "u 1000 165536 65536" }, + { true, "g 999 999 1" }, + { true, "u 0 5000 100000" }, + { true, "g 577 789 5" }, + { true, "u 65536 65536 1 " }, + /* invalid idmaps */ + { false, "1u 0 0 0" }, + { false, "1g 0 0 0a" }, + { false, "1 u 0 0 0" }, + { false, "1g 0 0 0 1" }, + { false, "1u a0 b0 c0 d1" }, + { false, "1g 0 b0 0 d1" }, + { false, "1u a0 0 c0 1" }, + { false, "g -1 0 -10" }, + { false, "a 1 0 10" }, + { false, "u 1 1 0 10" }, + { false, "g 1 0 10 z " }, + }; + + for (i = 0; i < sizeof(idmaps) / sizeof(struct idmap_check); i++) { + unsigned long hostid, nsid, range; + char type; + int ret; + ret = parse_idmaps(idmaps[i].idmap, &type, &nsid, &hostid, + &range); + if ((ret < 0 && idmaps[i].is_valid) || + (ret == 0 && !idmaps[i].is_valid)) { + lxc_error("failed to parse idmap \"%s\"\n", + idmaps[i].idmap); + return -1; + } + } + + return 0; +} + +static int set_get_compare_clear_save_load_network( + struct lxc_container *c, const char *key, const char *value, + const char *config_file, bool compare, const char *network_type) +{ + char retval[4096] = {0}; + int ret; + + if (!c->set_config_item(c, "lxc.net.0.type", network_type)) { + lxc_error("%s\n", "lxc.net.0.type"); + return -1; + } + + if (!c->set_config_item(c, key, value)) { + lxc_error("failed to set config item \"%s\" to \"%s\"\n", key, + value); + return -1; + } + + ret = c->get_config_item(c, key, retval, sizeof(retval)); + if (ret < 0) { + lxc_error("failed to get config item \"%s\"\n", key); + return -1; + } + + if (compare) { + ret = strcmp(retval, value); + if (ret != 0) { + lxc_error( + "expected value \"%s\" and retrieved value \"%s\" " + "for config key \"%s\" do not match\n", + value, retval, key); + return -1; + } + } + + if (config_file) { + if (!c->save_config(c, config_file)) { + lxc_error("%s\n", "failed to save config file"); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + if (!c->load_config(c, config_file)) { + lxc_error("%s\n", "failed to load config file"); + return -1; + } + } + + if (!c->clear_config_item(c, key)) { + lxc_error("failed to clear config item \"%s\"\n", key); + return -1; + } + + if (!c->clear_config_item(c, "lxc.net.0.type")) { + lxc_error("%s\n", "lxc.net.0.type"); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int fd = -1; + int ret = EXIT_FAILURE; + char tmpf[] = "lxc-parse-config-file-XXXXXX"; + char retval[4096] = {0}; + + fd = mkstemp(tmpf); + if (fd < 0) { + lxc_error("%s\n", "Could not create temporary file"); + exit(ret); + } + close(fd); + + c = lxc_container_new(tmpf, NULL); + if (!c) { + lxc_error("%s\n", "Failed to create new container"); + exit(EXIT_FAILURE); + } + + /* lxc.arch */ + if (set_get_compare_clear_save_load(c, "lxc.arch", "x86_64", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.arch"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy ps keys + */ + if (set_get_compare_clear_save_load(c, "lxc.pts", "1000", tmpf, true) < + 0) { + lxc_error("%s\n", "lxc.pts"); + goto non_test_error; + } + + /* lxc.pty.max */ + if (set_get_compare_clear_save_load(c, "lxc.pty.max", "1000", tmpf, true) < + 0) { + lxc_error("%s\n", "lxc.pty.max"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy tty.max keys + */ + if (set_get_compare_clear_save_load(c, "lxc.tty", "4", tmpf, true) < + 0) { + lxc_error("%s\n", "lxc.tty"); + goto non_test_error; + } + + /* lxc.tty.max */ + if (set_get_compare_clear_save_load(c, "lxc.tty.max", "4", tmpf, true) < + 0) { + lxc_error("%s\n", "lxc.tty.max"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy devttydir keys + */ + if (set_get_compare_clear_save_load(c, "lxc.devttydir", "not-dev", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.devttydir"); + goto non_test_error; + } + + /* lxc.tty.dir */ + if (set_get_compare_clear_save_load(c, "lxc.tty.dir", "not-dev", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.tty.dir"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy security keys + */ + if (set_get_compare_clear_save_load(c, "lxc.aa_profile", "unconfined", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.aa_profile"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy security keys + */ + if (set_get_compare_clear_save_load(c, "lxc.aa_allow_incomplete", "1", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.aa_allow_incomplete"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy security keys + */ + if (set_get_compare_clear_save_load(c, "lxc.se_context", "system_u:system_r:lxc_t:s0:c22", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.se_context"); + goto non_test_error; + } + + /* lxc.apparmor.profile */ + if (set_get_compare_clear_save_load(c, "lxc.apparmor.profile", "unconfined", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.apparmor.profile"); + goto non_test_error; + } + + /* lxc.apparmor.allow_incomplete */ + if (set_get_compare_clear_save_load(c, "lxc.apparmor.allow_incomplete", "1", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.apparmor.allow_incomplete"); + goto non_test_error; + } + + /* lxc.selinux.context */ + if (set_get_compare_clear_save_load(c, "lxc.selinux.context", "system_u:system_r:lxc_t:s0:c22", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.selinux.context"); + goto non_test_error; + } + + /* lxc.cgroup.cpuset.cpus */ + if (set_get_compare_clear_save_load(c, "lxc.cgroup.cpuset.cpus", + "1-100", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.cgroup.cpuset.cpus"); + goto non_test_error; + } + + /* lxc.cgroup */ + if (!c->set_config_item(c, "lxc.cgroup.cpuset.cpus", "1-100")) { + lxc_error("%s\n", "failed to set config item " + "\"lxc.cgroup.cpuset.cpus\" to \"1-100\""); + return -1; + } + + if (!c->set_config_item(c, "lxc.cgroup.memory.limit_in_bytes", + "123456789")) { + lxc_error( + "%s\n", + "failed to set config item " + "\"lxc.cgroup.memory.limit_in_bytes\" to \"123456789\""); + return -1; + } + + if (!c->get_config_item(c, "lxc.cgroup", retval, sizeof(retval))) { + lxc_error("%s\n", "failed to get config item \"lxc.cgroup\""); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + /* lxc.id_map + * We can't really save the config here since save_config() wants to + * chown the container's directory but we haven't created an on-disk + * container. So let's test set-get-clear. + */ + if (set_get_compare_clear_save_load( + c, "lxc.id_map", "u 0 100000 1000000000", NULL, false) < 0) { + lxc_error("%s\n", "lxc.id_map"); + goto non_test_error; + } + + if (!c->set_config_item(c, "lxc.id_map", "u 1 100000 10000000")) { + lxc_error("%s\n", "failed to set config item " + "\"lxc.id_map\" to \"u 1 100000 10000000\""); + return -1; + } + + if (!c->set_config_item(c, "lxc.id_map", "g 1 100000 10000000")) { + lxc_error("%s\n", "failed to set config item " + "\"lxc.id_map\" to \"g 1 100000 10000000\""); + return -1; + } + + if (!c->get_config_item(c, "lxc.id_map", retval, sizeof(retval))) { + lxc_error("%s\n", "failed to get config item \"lxc.cgroup\""); + return -1; + } + + /* lxc.idmap + * We can't really save the config here since save_config() wants to + * chown the container's directory but we haven't created an on-disk + * container. So let's test set-get-clear. + */ + if (set_get_compare_clear_save_load( + c, "lxc.idmap", "u 0 100000 1000000000", NULL, false) < 0) { + lxc_error("%s\n", "lxc.idmap"); + goto non_test_error; + } + + if (!c->set_config_item(c, "lxc.idmap", "u 1 100000 10000000")) { + lxc_error("%s\n", "failed to set config item " + "\"lxc.idmap\" to \"u 1 100000 10000000\""); + return -1; + } + + if (!c->set_config_item(c, "lxc.idmap", "g 1 100000 10000000")) { + lxc_error("%s\n", "failed to set config item " + "\"lxc.idmap\" to \"g 1 100000 10000000\""); + return -1; + } + + if (!c->get_config_item(c, "lxc.idmap", retval, sizeof(retval))) { + lxc_error("%s\n", "failed to get config item \"lxc.cgroup\""); + return -1; + } + + c->clear_config(c); + c->lxc_conf = NULL; + + /* REMOVE IN LXC 3.0 + legacy lxc.loglevel key + */ + if (set_get_compare_clear_save_load(c, "lxc.loglevel", "DEBUG", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.loglevel"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.logfile key + */ + if (set_get_compare_clear_save_load(c, "lxc.logfile", "/some/path", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.logfile"); + goto non_test_error; + } + + + /* lxc.log.level */ + if (set_get_compare_clear_save_load(c, "lxc.log.level", "DEBUG", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.log.level"); + goto non_test_error; + } + + /* lxc.log */ + if (set_get_compare_clear_save_load(c, "lxc.log.file", "/some/path", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.log.file"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.mount key + */ + if (set_get_compare_clear_save_load(c, "lxc.mount", "/some/path", NULL, + true) < 0) { + lxc_error("%s\n", "lxc.mount"); + goto non_test_error; + } + + /* lxc.mount.fstab */ + if (set_get_compare_clear_save_load(c, "lxc.mount.fstab", "/some/path", NULL, + true) < 0) { + lxc_error("%s\n", "lxc.mount.fstab"); + goto non_test_error; + } + + /* lxc.mount.auto + * Note that we cannot compare the values since the getter for + * lxc.mount.auto does not preserve ordering. + */ + if (set_get_compare_clear_save_load(c, "lxc.mount.auto", + "proc:rw sys:rw cgroup-full:rw", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.mount.auto"); + goto non_test_error; + } + + /* lxc.mount.entry + * Note that we cannot compare the values since the getter for + * lxc.mount.entry appends newlines. + */ + if (set_get_compare_clear_save_load( + c, "lxc.mount.entry", + "/dev/dri dev/dri none bind,optional,create=dir", tmpf, + false) < 0) { + lxc_error("%s\n", "lxc.mount.entry"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.rootfs key + */ + if (set_get_compare_clear_save_load(c, "lxc.rootfs", "/some/path", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.rootfs"); + goto non_test_error; + } + + /* lxc.rootfs.path */ + if (set_get_compare_clear_save_load(c, "lxc.rootfs.path", "/some/path", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.rootfs.path"); + goto non_test_error; + } + + /* lxc.rootfs.mount */ + if (set_get_compare_clear_save_load(c, "lxc.rootfs.mount", "/some/path", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.rootfs.mount"); + goto non_test_error; + } + + /* lxc.rootfs.options */ + if (set_get_compare_clear_save_load(c, "lxc.rootfs.options", + "ext4,discard", tmpf, true) < 0) { + lxc_error("%s\n", "lxc.rootfs.options"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.utsname key + */ + if (set_get_compare_clear_save_load(c, "lxc.utsname", "the-shire", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.utsname"); + goto non_test_error; + } + + /* lxc.uts.name */ + if (set_get_compare_clear_save_load(c, "lxc.uts.name", "the-shire", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.uts.name"); + goto non_test_error; + } + + /* lxc.hook.pre-start */ + if (set_get_compare_clear_save_load( + c, "lxc.hook.pre-start", "/some/pre-start", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.pre-start"); + goto non_test_error; + } + + /* lxc.hook.pre-mount */ + if (set_get_compare_clear_save_load( + c, "lxc.hook.pre-mount", "/some/pre-mount", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.pre-mount"); + goto non_test_error; + } + + /* lxc.hook.mount */ + if (set_get_compare_clear_save_load(c, "lxc.hook.mount", "/some/mount", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.mount"); + goto non_test_error; + } + + /* lxc.hook.autodev */ + if (set_get_compare_clear_save_load(c, "lxc.hook.autodev", + "/some/autodev", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.autodev"); + goto non_test_error; + } + + /* lxc.hook.start */ + if (set_get_compare_clear_save_load(c, "lxc.hook.start", "/some/start", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.start"); + goto non_test_error; + } + + /* lxc.hook.stop */ + if (set_get_compare_clear_save_load(c, "lxc.hook.stop", "/some/stop", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.stop"); + goto non_test_error; + } + + /* lxc.hook.post-stop */ + if (set_get_compare_clear_save_load( + c, "lxc.hook.post-stop", "/some/post-stop", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.post-stop"); + goto non_test_error; + } + + /* lxc.hook.clone */ + if (set_get_compare_clear_save_load(c, "lxc.hook.clone", "/some/clone", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.clone"); + goto non_test_error; + } + + /* lxc.hook.destroy */ + if (set_get_compare_clear_save_load(c, "lxc.hook.destroy", + "/some/destroy", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.hook.destroy"); + goto non_test_error; + } + + /* lxc.cap.drop */ + if (set_get_compare_clear_save_load(c, "lxc.cap.drop", + "sys_module mknod setuid net_raw", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.cap.drop"); + goto non_test_error; + } + + /* lxc.cap.keep */ + if (set_get_compare_clear_save_load(c, "lxc.cap.keep", + "sys_module mknod setuid net_raw", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.cap.keep"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.console key + */ + if (set_get_compare_clear_save_load(c, "lxc.console", "none", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.console"); + goto non_test_error; + } + + /* lxc.console.path */ + if (set_get_compare_clear_save_load(c, "lxc.console.path", "none", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.console.path"); + goto non_test_error; + } + + /* lxc.console.logfile */ + if (set_get_compare_clear_save_load(c, "lxc.console.logfile", + "/some/logfile", tmpf, true) < 0) { + lxc_error("%s\n", "lxc.console.logfile"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy seccomp key + */ + if (set_get_compare_clear_save_load( + c, "lxc.seccomp", "/some/seccomp/file", tmpf, true) < 0) { + lxc_error("%s\n", "lxc.seccomp"); + goto non_test_error; + } + + /* lxc.seccomp.profile */ + if (set_get_compare_clear_save_load( + c, "lxc.seccomp.profile", "/some/seccomp/file", tmpf, true) < 0) { + lxc_error("%s\n", "lxc.seccomp.profile"); + goto non_test_error; + } + + /* lxc.autodev */ + if (set_get_compare_clear_save_load(c, "lxc.autodev", "1", tmpf, true) < + 0) { + lxc_error("%s\n", "lxc.autodev"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.haltsignal key + */ + if (set_get_compare_clear_save_load(c, "lxc.haltsignal", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.haltsignal"); + goto non_test_error; + } + + /* lxc.signal.halt */ + if (set_get_compare_clear_save_load(c, "lxc.signal.halt", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.signal.halt"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.rebootsignal key + */ + if (set_get_compare_clear_save_load(c, "lxc.rebootsignal", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.rebootsignal"); + goto non_test_error; + } + + /* lxc.signal.reboot */ + if (set_get_compare_clear_save_load(c, "lxc.signal.reboot", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.signal.reboot"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.stopsignal key + */ + if (set_get_compare_clear_save_load(c, "lxc.stopsignal", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.stopsignal"); + goto non_test_error; + } + + /* lxc.signal.stop */ + if (set_get_compare_clear_save_load(c, "lxc.signal.stop", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.signal.stop"); + goto non_test_error; + } + + /* lxc.start.auto */ + if (set_get_compare_clear_save_load(c, "lxc.start.auto", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.start.auto"); + goto non_test_error; + } + + /* lxc.start.delay */ + if (set_get_compare_clear_save_load(c, "lxc.start.delay", "5", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.start.delay"); + goto non_test_error; + } + + /* lxc.start.order */ + if (set_get_compare_clear_save_load(c, "lxc.start.order", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.start.order"); + goto non_test_error; + } + + /* lxc.log.syslog */ + if (set_get_compare_clear_save_load(c, "lxc.log.syslog", "local0", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.log.syslog"); + goto non_test_error; + } + + /* lxc.utsname */ + if (set_get_compare_clear_save_load(c, "lxc.utsname", "get-schwifty", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.utsname"); + goto non_test_error; + } + + /* lxc.monitor.unshare */ + if (set_get_compare_clear_save_load(c, "lxc.monitor.unshare", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.monitor.unshare"); + goto non_test_error; + } + + /* lxc.group */ + if (set_get_compare_clear_save_load( + c, "lxc.group", "some,container,groups", tmpf, false) < 0) { + lxc_error("%s\n", "lxc.group"); + goto non_test_error; + } + + /* lxc.environment */ + if (set_get_compare_clear_save_load(c, "lxc.environment", "FOO=BAR", + tmpf, false) < 0) { + lxc_error("%s\n", "lxc.environment"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.init_cmd key + */ + if (set_get_compare_clear_save_load(c, "lxc.init_cmd", "/bin/bash", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.init_cmd"); + goto non_test_error; + } + + /* lxc.init.cmd */ + if (set_get_compare_clear_save_load(c, "lxc.init.cmd", "/bin/bash", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.init.cmd"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.init_uid key + */ + if (set_get_compare_clear_save_load(c, "lxc.init_uid", "1000", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.init_uid"); + goto non_test_error; + } + + /* lxc.init.uid */ + if (set_get_compare_clear_save_load(c, "lxc.init.uid", "1000", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.init.uid"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.init_gid key + */ + if (set_get_compare_clear_save_load(c, "lxc.init_gid", "1000", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.init_gid"); + goto non_test_error; + } + + /* lxc.init.gid */ + if (set_get_compare_clear_save_load(c, "lxc.init.gid", "1000", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.init.gid"); + goto non_test_error; + } + + /* lxc.ephemeral */ + if (set_get_compare_clear_save_load(c, "lxc.ephemeral", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.ephemeral"); + goto non_test_error; + } + + /* lxc.no_new_privs */ + if (set_get_compare_clear_save_load(c, "lxc.no_new_privs", "1", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.no_new_privs"); + goto non_test_error; + } + + /* REMOVE IN LXC 3.0 + legacy lxc.limit.* key + */ + if (set_get_compare_clear_save_load(c, "lxc.limit.nofile", "65536", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.limit.nofile"); + goto non_test_error; + } + + /* lxc.prlimit.nofile */ + if (set_get_compare_clear_save_load(c, "lxc.prlimit.nofile", "65536", + tmpf, true) < 0) { + lxc_error("%s\n", "lxc.prlimit.nofile"); + goto non_test_error; + } + + if (test_idmap_parser() < 0) { + lxc_error("%s\n", "failed to test parser for \"lxc.id_map\""); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.type", "veth", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.2.type", "none", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.2.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.3.type", "empty", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.3.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.4.type", "vlan", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.4.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.type", "macvlan", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.1000.type", "phys", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.1000.type"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.flags", "up", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.flags"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.name", "eth0", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.name"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.link", "bla", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.link"); + goto non_test_error; + } + + if (!set_get_compare_clear_save_load(c, "lxc.net.0.asdf", "veth", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.asdf"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load_network( + c, "lxc.net.0.macvlan.mode", "private", tmpf, true, + "macvlan")) { + lxc_error("%s\n", "lxc.net.0.macvlan.mode"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load_network( + c, "lxc.net.0.macvlan.mode", "vepa", tmpf, true, + "macvlan")) { + lxc_error("%s\n", "lxc.net.0.macvlan.mode"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load_network( + c, "lxc.net.0.macvlan.mode", "bridge", tmpf, true, + "macvlan")) { + lxc_error("%s\n", "lxc.net.0.macvlan.mode"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load_network( + c, "lxc.net.0.veth.pair", "clusterfuck", tmpf, true, + "veth")) { + lxc_error("%s\n", "lxc.net.0.veth.pair"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.script.up", + "/some/up/path", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.script.up"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.script.down", + "/some/down/path", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.script.down"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.hwaddr", + "52:54:00:80:7a:5d", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.hwaddr"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.mtu", "2000", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.mtu"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load_network(c, "lxc.net.0.vlan.id", + "2", tmpf, true, "vlan")) { + lxc_error("%s\n", "lxc.net.0.vlan.id"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv4.gateway", + "10.0.2.2", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv4.gateway"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv6.gateway", + "2003:db8:1::1", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv6.gateway"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv4.address", + "10.0.2.3/24", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv4.address"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load( + c, "lxc.net.0.ipv6.address", "2003:db8:1:0:214:1234:fe0b:3596/64", + tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv6.address"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.cgroup.dir", "lxd", tmpf, + true)) { + lxc_error("%s\n", "lxc.cgroup.dir"); + goto non_test_error; + } + + if (set_and_clear_complete_netdev(c) < 0) { + lxc_error("%s\n", "failed to clear whole network"); + goto non_test_error; + } + + ret = EXIT_SUCCESS; +non_test_error: + (void)unlink(tmpf); + (void)rmdir(dirname(c->configfile)); + lxc_container_put(c); + exit(ret); +} diff -Nru lxc-2.0.8/src/tests/reboot.c lxc-2.1.0/src/tests/reboot.c --- lxc-2.0.8/src/tests/reboot.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/reboot.c 2017-09-06 02:32:37.000000000 +0000 @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,7 +40,7 @@ int *cmd = arg; if (reboot(*cmd)) - printf("failed to reboot(%d): %m\n", *cmd); + printf("failed to reboot(%d): %s\n", *cmd, strerror(errno)); return 0; } @@ -51,12 +53,12 @@ ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd); if (ret < 0) { - printf("failed to clone: %m\n"); + printf("failed to clone: %s\n", strerror(errno)); return -1; } if (wait(&status) < 0) { - printf("unexpected wait error: %m\n"); + printf("unexpected wait error: %s\n", strerror(errno)); return -1; } diff -Nru lxc-2.0.8/src/tests/shortlived.c lxc-2.1.0/src/tests/shortlived.c --- lxc-2.0.8/src/tests/shortlived.c 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/src/tests/shortlived.c 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,188 @@ +/* liblxcapi + * + * Copyright © 2017 Christian Brauner . + * Copyright © 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "shortlived" + +static int destroy_container(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +static int create_container(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + const char *s; + bool b; + int i; + int ret = 0; + + ret = 1; + /* test a real container */ + c = lxc_container_new(MYNAME, NULL); + if (!c) { + fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + ret = create_container(); + if (ret) { + fprintf(stderr, "%d: failed to create a container\n", __LINE__); + goto out; + } + + b = c->is_defined(c); + if (!b) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + s = c->state(c); + if (!s || strcmp(s, "STOPPED")) { + fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined"); + goto out; + } + + b = c->load_config(c, NULL); + if (!b) { + fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name); + goto out; + } + + if (!c->set_config_item(c, "lxc.init.cmd", "echo hello")) { + fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__); + goto out; + } + + c->want_daemonize(c, true); + + /* Test whether we can start a really short-lived daemonized container. + */ + for (i = 0; i < 10; i++) { + if (!c->startl(c, 0, NULL)) { + fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name); + goto out; + } + sleep(1); + } + + if (!c->set_config_item(c, "lxc.init.cmd", "you-shall-fail")) { + fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__); + goto out; + } + + /* Test whether we catch the start failure of a really short-lived + * daemonized container. + */ + for (i = 0; i < 10; i++) { + if (c->startl(c, 0, NULL)) { + fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name); + goto out; + } + sleep(1); + } + + c->stop(c); + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; + +out: + if (c) { + c->stop(c); + destroy_container(); + } + lxc_container_put(c); + exit(ret); +} diff -Nru lxc-2.0.8/src/tests/shutdowntest.c lxc-2.1.0/src/tests/shutdowntest.c --- lxc-2.0.8/src/tests/shutdowntest.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/shutdowntest.c 2017-09-06 02:32:37.000000000 +0000 @@ -45,12 +45,12 @@ goto out; } - if (!c->set_config_item(c, "lxc.network.type", "veth")) { + if (!c->set_config_item(c, "lxc.net.0.type", "veth")) { fprintf(stderr, "%d: failed to set network type\n", __LINE__); goto out; } - c->set_config_item(c, "lxc.network.link", "lxcbr0"); - c->set_config_item(c, "lxc.network.flags", "up"); + c->set_config_item(c, "lxc.net.0.link", "lxcbr0"); + c->set_config_item(c, "lxc.net.0.flags", "up"); if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) { fprintf(stderr, "%d: failed to create a container\n", __LINE__); goto out; diff -Nru lxc-2.0.8/src/tests/snapshot.c lxc-2.1.0/src/tests/snapshot.c --- lxc-2.0.8/src/tests/snapshot.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/snapshot.c 2017-09-06 02:32:37.000000000 +0000 @@ -72,7 +72,7 @@ fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); (void) c->destroy_with_snapshots(c); } - if (!c->set_config_item(c, "lxc.network.type", "empty")) { + if (!c->set_config_item(c, "lxc.net.0.type", "empty")) { fprintf(stderr, "%s: %d: failed to set network type\n", __FILE__, __LINE__); goto err; } diff -Nru lxc-2.0.8/src/tests/startone.c lxc-2.1.0/src/tests/startone.c --- lxc-2.0.8/src/tests/startone.c 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/src/tests/startone.c 2017-09-06 02:32:37.000000000 +0000 @@ -154,8 +154,8 @@ goto out; } - if (!c->set_config_item(c, "lxc.utsname", "bobo")) { - fprintf(stderr, "%d: failed setting lxc.utsname\n", __LINE__); + if (!c->set_config_item(c, "lxc.uts.name", "bobo")) { + fprintf(stderr, "%d: failed setting lxc.uts.name\n", __LINE__); goto out; } diff -Nru lxc-2.0.8/templates/lxc-alpine.in lxc-2.1.0/templates/lxc-alpine.in --- lxc-2.0.8/templates/lxc-alpine.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-alpine.in 2017-09-06 02:32:37.000000000 +0000 @@ -43,7 +43,10 @@ 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" +12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub +73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041 alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub +9a4cd858d9710963848e6d5f555325dc199d1c952b01cf6e64da2c15deedbd97 alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub +780b3ed41786772cbc7b68136546fa3f897f28a23b30c72dde6225319c44cfff alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub" readonly APK_KEYS_URI='http://alpinelinux.org/keys' readonly DEFAULT_MIRROR_URL='http://dl-cdn.alpinelinux.org/alpine' @@ -126,6 +129,7 @@ aarch64 | arm64) echo 'aarch64';; armv7) echo 'armv7';; arm*) echo 'armhf';; + ppc64le) echo 'ppc64le';; *) return 1;; esac } @@ -231,7 +235,6 @@ local branch="$3" local extra_packages="$4" local apk_cache="$LXC_CACHE_DIR/apk/$arch" - local repo_url="$MIRROR_URL/$branch/main" if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$apk_cache" ]; then einfo "Cleaning cached APK packages for $arch" @@ -244,7 +247,10 @@ mkdir -p etc/apk ln -s "$apk_cache" etc/apk/cache - echo "$repo_url" > etc/apk/repositories + + local repo; for repo in main community; do + echo "$MIRROR_URL/$branch/$repo" >> etc/apk/repositories + done install_packages "$arch" "alpine-base $extra_packages" make_dev_nodes @@ -382,7 +388,7 @@ lxc.arch = $arch # Set hostname. - lxc.utsname = $hostname + lxc.uts.name = $hostname # If something doesn't work, try to comment this out. # Dropping sys_admin disables container root from doing a lot of things @@ -477,7 +483,7 @@ [ -n "$path" ] || die 1 'Missing required option --path' if [ -z "$rootfs" ] && [ -f "$path/config" ]; then - rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" + rootfs="$(sed -nE 's/^lxc.rootfs.path\s*=\s*(.*)$/\1/p' "$path/config")" fi if [ -z "$rootfs" ]; then rootfs="$path/rootfs" diff -Nru lxc-2.0.8/templates/lxc-altlinux.in lxc-2.1.0/templates/lxc-altlinux.in --- lxc-2.0.8/templates/lxc-altlinux.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-altlinux.in 2017-09-06 02:32:37.000000000 +0000 @@ -166,6 +166,15 @@ download_altlinux() { + if [ -z "$aptconfver" ]; then + case "$release" in + sisyphus) + aptconfver=apt-conf-sisyphus ;; + *) + aptconfver=apt-conf-branch ;; + esac + fi + # check the mini altlinux was not already downloaded INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT @@ -179,7 +188,7 @@ APT_GET="apt-get -o RPM::RootDir=$INSTALL_ROOT -y" PKG_LIST="$(grep -hs '^[^#]' "$profile_dir/$profile")" # if no configuration file $profile -- fall back to default list of packages - [ -z "$PKG_LIST" ] && PKG_LIST="interactivesystem apt apt-conf etcnet-full openssh-server systemd-sysvinit systemd-units systemd NetworkManager-daemon" + [ -z "$PKG_LIST" ] && PKG_LIST="interactivesystem apt $aptconfver etcnet-full openssh-server systemd-sysvinit systemd-units systemd NetworkManager-daemon" mkdir -p $INSTALL_ROOT/var/lib/rpm rpm --root $INSTALL_ROOT --initdb @@ -212,7 +221,7 @@ #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/ + rsync -SHaAX $cache/rootfs/ $rootfs_path/ return 0 } @@ -265,41 +274,41 @@ { mkdir -p $config_path - grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config cat <> $config_path/config -lxc.utsname = $name -lxc.tty = 4 -lxc.pts = 1024 +lxc.uts.name = $name +lxc.tty.max = 4 +lxc.pty.max = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined +#lxc.apparmor.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 +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = veth0 +#lxc.net.0.mtu = 1500 EOF if [ ! -z ${ipv4} ]; then cat <> $config_path/config -lxc.network.ipv4 = $ipv4 +lxc.net.0.ipv4.address = $ipv4 EOF fi if [ ! -z ${gw} ]; then cat <> $config_path/config -lxc.network.ipv4.gateway = $gw +lxc.net.0.ipv4.gateway = $gw EOF fi if [ ! -z ${ipv6} ]; then cat <> $config_path/config -lxc.network.ipv6 = $ipv6 +lxc.net.0.ipv6.address = $ipv6 EOF fi if [ ! -z ${gw6} ]; then cat <> $config_path/config -lxc.network.ipv6.gateway = $gw6 +lxc.net.0.ipv6.gateway = $gw6 EOF fi cat <> $config_path/config @@ -362,6 +371,7 @@ [-4|--ipv4=] [-6|--ipv6=] [-g|--gw=] [-d|--dns=] [-P|--profile=] [--rootfs=] + [-a|--apt-conf=] [-A|--arch=] [-h|--help] Mandatory args: @@ -375,6 +385,7 @@ -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 + -a,--apt-conf specify preferred 'apt-conf' package, eg. 'apt-conf-branch' -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 @@ -383,7 +394,7 @@ 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: -- "$@") +options=$(getopt -o hp:n:P:cR:4:6:g:d:a: -l help,rootfs:,path:,name:,profile:,clean,release:,ipv4:,ipv6:,gw:,dns:,apt-conf: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -404,6 +415,7 @@ -6|--ipv6) ipv6=$2; shift 2;; -g|--gw) gw=$2; shift 2;; -d|--dns) dns=$2; shift 2;; + -a|--apt-conf) aptconfver=$2; shift 2;; --) shift 1; break ;; *) break ;; esac @@ -448,10 +460,10 @@ exit 1 fi -# check for 'lxc.rootfs' passed in through default config by lxc-create +# check for 'lxc.rootfs.path' 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) + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) else rootfs_path=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-archlinux.in lxc-2.1.0/templates/lxc-archlinux.in --- lxc-2.0.8/templates/lxc-archlinux.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-archlinux.in 2017-09-06 02:32:37.000000000 +0000 @@ -121,10 +121,10 @@ ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf EOF # enable getty on active ttys - local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") - local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.devttydir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") + local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.max" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") + local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.dir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") local devtty="" - # bind getty instances to /dev//tty* if lxc.devttydir is set + # bind getty instances to /dev//tty* if lxc.tty.dir is set [ -n "${devttydir}" ] && devtty="${devttydir}-" if [ ${nttys:-0} -gt 1 ]; then ( cd "${rootfs_path}/etc/systemd/system/getty.target.wants" @@ -148,11 +148,11 @@ copy_configuration() { mkdir -p "${config_path}" local config="${config_path}/config" - echo "lxc.utsname = ${name}" >> "${config}" + echo "lxc.uts.name = ${name}" >> "${config}" grep -q "^lxc.arch" "${config}" 2>/dev/null \ || echo "lxc.arch = ${arch}" >> "${config}" - grep -q "^lxc.rootfs" "${config}" 2>/dev/null \ - || echo "lxc.rootfs = ${rootfs_path}" >> "${config}" + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null \ + || echo "lxc.rootfs.path = ${rootfs_path}" >> "${config}" [ -e "${shared_config}" ] \ && echo "lxc.include = ${shared_config}" >> "${config}" if [ $? -ne 0 ]; then diff -Nru lxc-2.0.8/templates/lxc-busybox.in lxc-2.1.0/templates/lxc-busybox.in --- lxc-2.0.8/templates/lxc-busybox.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-busybox.in 2017-09-06 02:32:37.000000000 +0000 @@ -339,17 +339,17 @@ rootfs=$2 name=$3 -grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config +grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config cat <> $path/config -lxc.haltsignal = SIGUSR1 -lxc.rebootsignal = SIGTERM -lxc.utsname = $name -lxc.tty = 1 -lxc.pts = 1 +lxc.signal.halt = SIGUSR1 +lxc.signal.reboot = SIGTERM +lxc.uts.name = $name +lxc.tty.max = 1 +lxc.pty.max = 1 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined +#lxc.apparmor.profile = unconfined lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0 @@ -427,8 +427,8 @@ # 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-centos.in lxc-2.1.0/templates/lxc-centos.in --- lxc-2.0.8/templates/lxc-centos.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-centos.in 2017-09-06 02:32:37.000000000 +0000 @@ -336,7 +336,7 @@ # /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. + # since lxc.tty.dir 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 @@ -421,9 +421,12 @@ else YUM="$YUM0" fi - PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils" + PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie" # use temporary repository definition + # always prefer the repo given by the user + # if no repo given, use mirrorlist.centos.org for i386 and x86_64 + # and http://mirror.centos.org/altarch/ otherwise REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo mkdir -p $(dirname $REPO_FILE) if [ -n "$repo" ]; then @@ -432,7 +435,7 @@ name=local repository baseurl="$repo" EOF -else + elif [ ${basearch} = 'i386' ] || [ ${basearch} = 'x86_64' ]; then cat < $REPO_FILE [base] name=CentOS-$release - Base @@ -442,6 +445,16 @@ name=CentOS-$release - Updates mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates EOF + else + cat < $REPO_FILE +[base] +name=CentOS-$release - Base +baseurl=http://mirror.centos.org/altarch/7/os/$basearch/ + +[updates] +name=CentOS-$release - Updates +baseurl=http://mirror.centos.org/altarch/7/updates/$basearch/ +EOF fi # create minimal device nodes, needed for "yum install" and "yum update" process @@ -520,7 +533,7 @@ #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/ + rsync -SHaAX $cache/rootfs/ $rootfs_path/ echo return 0 } @@ -585,8 +598,8 @@ { mkdir -p $config_path - grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo " -lxc.rootfs = $rootfs_path + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo " +lxc.rootfs.path = $rootfs_path " >> $config_path/config # The following code is to create static MAC addresses for each @@ -605,13 +618,13 @@ # Seems that \s doesn't work in brackets. KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') - if [[ "${KEY}" != "lxc.network.hwaddr" ]] + if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] then echo ${LINE} >> $config_path/config - if [[ "${KEY}" == "lxc.network.link" ]] + if [[ "${KEY}" == "lxc.net.0.link" ]] then - echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config fi fi done < $config_path/config.def @@ -628,22 +641,22 @@ # Append things which require expansion here... cat <> $config_path/config lxc.arch = $arch -lxc.utsname = $utsname +lxc.uts.name = $utsname # When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined +#lxc.apparmor.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 +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 # Additional example for veth network type # static MAC address, -#lxc.network.hwaddr = 00:16:3e:77:52:20 +#lxc.net.0.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 +#lxc.net.0.veth.pair = v-$name-e0 EOF @@ -836,8 +849,8 @@ # 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 + echo "This is not a CentOS or Red Hat host and release is missing, defaulting to 7, use -R|--release to specify release" + release=7 fi fi @@ -848,10 +861,10 @@ 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) + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \ + -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config) fi fi config_path=$path diff -Nru lxc-2.0.8/templates/lxc-cirros.in lxc-2.1.0/templates/lxc-cirros.in --- lxc-2.0.8/templates/lxc-cirros.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-cirros.in 2017-09-06 02:32:37.000000000 +0000 @@ -118,18 +118,17 @@ cat >> "$path/config" </dev/null 2>&1 && is_btrfs "$(dirname "$path")"; then + btrfs subvolume create "$path" + else + mkdir -p "$path" + fi +} + +try_rmsubvolume() +{ + path=$1 + [ -d "$path" ] || return 0 + if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume "$path"; then + btrfs subvolume delete "$path" + else + rm -rf "$path" + fi +} + cleanup() { - rm -rf "$cache/partial-$release-$arch" - rm -rf "$cache/rootfs-$release-$arch" + try_rmsubvolume "$cache/partial-$release-$arch" + try_rmsubvolume "$cache/rootfs-$release-$arch" } download_debian() @@ -277,6 +343,8 @@ cache=$1 arch=$2 release=$3 + interpreter="$4" + interpreter_path="$5" trap cleanup EXIT SIGHUP SIGINT SIGTERM @@ -299,7 +367,7 @@ | gpg --import --no-default-keyring --keyring="${releasekeyring}" fi # check the mini debian was not already downloaded - mkdir -p "$cache/partial-$release-$arch" + try_mksubvolume "$cache/partial-$release-$arch" if [ $? -ne 0 ]; then echo "Failed to create '$cache/partial-$release-$arch' directory" return 1 @@ -307,12 +375,33 @@ # 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 + if [ "$interpreter" = "" ] ; then + 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 + else + debootstrap --foreign --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 + mkdir -p "$(basename "$cache/partial-$release-$arch/$interpreter_path")" + cp "$interpreter" "$cache/partial-$release-$arch/$interpreter_path" + if [ $? -ne 0 ]; then + echo "failed to copy $interpreter to $cache/partial-$release-$arch/$interpreter_path" + return 1 + fi + chroot "$cache/partial-$release-$arch" debootstrap/debootstrap --second-stage + if [ $? -ne 0 ]; then + echo "failed to update the rootfs, aborting" + return 1 + fi fi mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" @@ -334,8 +423,18 @@ # 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 + try_mksubvolume "$rootfs" + if which btrfs >/dev/null 2>&1 && \ + is_btrfs_subvolume "$cache/rootfs-$release-$arch" && \ + is_btrfs_subvolume "$rootfs"; then + realrootfs="$(dirname "$config")"/rootfs + [ "$rootfs" = "$realrootfs" ] || umount "$rootfs" || return 1 + btrfs subvolume delete "$realrootfs" || return 1 + btrfs subvolume snapshot "$cache/rootfs-$release-$arch" "$realrootfs" || return 1 + [ "$rootfs" = "$realrootfs" ] || mount --bind "$realrootfs" "$rootfs" || return 1 + else + rsync -SHaAX "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 + fi return 0 } @@ -345,6 +444,9 @@ release=$2 arch=$3 cache="$4/debian" + interpreter="$5" + interpreter_path="$6" + flushcache=$7 mkdir -p $LOCALSTATEDIR/lock/subsys/ ( flock -x 9 @@ -353,9 +455,14 @@ return 1 fi + if [ "$flushcache" -eq 1 ]; then + echo "Flushing cache..." + cleanup + fi + echo "Checking cache download in $cache/rootfs-$release-$arch ... " if [ ! -e "$cache/rootfs-$release-$arch" ]; then - download_debian "$cache" "$arch" "$release" + download_debian "$cache" "$arch" "$release" "$interpreter" "$interpreter_path" if [ $? -ne 0 ]; then echo "Failed to download 'debian base'" return 1 @@ -386,9 +493,9 @@ # Generate the configuration file # if there is exactly one veth network entry, make sure it has an # associated hwaddr. - nics=$(grep -ce '^lxc\.network\.type[ \t]*=[ \t]*veth' "$path/config") + nics=$(grep -ce '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' "$path/config") if [ "$nics" -eq 1 ]; then - grep -q "^lxc.network.hwaddr" "$path/config" || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" "$path/config" + grep -q "^lxc.net.0.hwaddr" "$path/config" || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" "$path/config" fi ## Add all the includes @@ -404,12 +511,13 @@ ## 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" + grep -q "^lxc.rootfs.path" "$path/config" 2> /dev/null || echo "lxc.rootfs.path = $rootfs" >> "$path/config" cat <> $path/config -lxc.tty = $num_tty -lxc.utsname = $hostname +lxc.tty.max = $num_tty +lxc.uts.name = $hostname lxc.arch = $arch +lxc.pty.max = 1024 EOF if [ $? -ne 0 ]; then @@ -426,6 +534,7 @@ local release="$1"; shift local arch="$1"; shift local hostarch="$1"; shift + local interpreter="$1"; shift local packages="$*" # Disable service startup @@ -436,7 +545,7 @@ chmod +x "${rootfs}/usr/sbin/policy-rc.d" # If the container isn't running a native architecture, setup multiarch - if [ "${arch}" != "${hostarch}" ]; then + if [ "$interpreter" = "" -a "${arch}" != "${hostarch}" ]; then # Test if dpkg supports multiarch if ! chroot "$rootfs" dpkg --print-foreign-architectures 2>&1; then chroot "$rootfs" dpkg --add-architecture "${hostarch}" @@ -445,7 +554,7 @@ # Write a new sources.list containing both native and multiarch entries > "${rootfs}/etc/apt/sources.list" - if [ "${arch}" = "${hostarch}" ]; then + if [ "$interpreter" != "" -a "${arch}" = "${hostarch}" ]; then write_sourceslist "${rootfs}" "${release}" "${arch}" else write_sourceslist "${rootfs}" "${release}" @@ -461,6 +570,7 @@ # Re-enable service startup rm "${rootfs}/usr/sbin/policy-rc.d" + # end } @@ -497,14 +607,17 @@ Usage: $1 -h|--help -p|--path= [-c|--clean] [-a|--arch=] [-r|--release=] [--mirror=] [--security-mirror=] [--package=] + [-I|--interpreter-path=] + [-F | --flush-cache] [-S|--auth-key=] Options : -h, --help print this help text -p, --path=PATH directory where config and rootfs of this VM will be kept + -S, --auth-key=KEYFILE SSH public key to inject into the container as the root user. -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. + -r, --release=RELEASE Debian release. Can be one of: wheezy, jessie, stretch, buster, sid. Defaults to current stable. --mirror=MIRROR Debian mirror to use during installation. Overrides the MIRROR environment variable (see below). @@ -515,6 +628,9 @@ List of additional packages to install. Comma separated, without space. -c, --clean only clean up the cache and terminate --enable-non-free include also Debian's contrib and non-free repositories. + -I|--interpreter-path=INTERPRETER-PATH + Path of the binfmt interpreter to copy to the rootfs + -F | --flush-cache Flush the debian release cache Environment variables: @@ -527,7 +643,7 @@ return 0 } -options=$(getopt -o hp:n:a:r:c -l arch:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror: -- "$@") +options=$(getopt -o hp:n:a:r:cI:FS: -l arch:,auth-key:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror:,interpreter-path:,flush-cache -- "$@") if [ $? -ne 0 ]; then usage "$(basename "$0")" exit 1 @@ -543,6 +659,8 @@ arch="amd64" elif [ "$arch" = "armv7l" ]; then arch="armhf" +elif [ "$arch" = "aarch64" ]; then + arch="arm64" elif [ "$arch" = "ppc" ]; then arch="powerpc" elif [ "$arch" = "ppc64le" ]; then @@ -554,6 +672,7 @@ fi hostarch=$arch mainonly=1 +flushcache=0 while true do @@ -562,6 +681,9 @@ --) shift 1; break ;; -a|--arch) arch=$2; shift 2;; + -S|--auth-key) authkey=$2; shift 2;; + -I|--interpreter-path) + interpreter="$2"; shift 2;; -c|--clean) clean=1; shift 1;; --enable-non-free) mainonly=0; shift 1;; --mirror) MIRROR=$2; shift 2;; @@ -571,6 +693,7 @@ -r|--release) release=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; --security-mirror) SECURITY_MIRROR=$2; shift 2;; + -F|--flush-cache) flushcache=1; shift 1;; *) break ;; esac done @@ -588,20 +711,40 @@ arch=amd64 fi -if [ $hostarch = "i386" -a $arch = "amd64" ]; then - echo "can't create $arch container on $hostarch" - exit 1 -fi +if [ "$interpreter" = "" ] ; then + 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 = "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 + if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi + + if [ $hostarch = "mips" -a $arch != "mips" ] || \ + [ $hostarch = "mipsel" -a $arch != "mipsel" ] || \ + [ $hostarch = "mips64" -a $arch != "mips" -a $arch != "mips64" ] || \ + [ $hostarch = "mips64el" -a $arch != "mipsel" -a $arch != "mips64el" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi +else + if ! file -b "${interpreter}" |grep -q "statically linked" ; then + echo "'${interpreter}' must be statically linked" 1>&2 + exit 1 + fi + interpreter_path=$(find_interpreter "$interpreter") + if [ $? -ne 0 ] ; then + echo "no binfmt interpreter using $(basename "$interpreter")" 1>&2 + exit 1 + fi fi type debootstrap @@ -620,9 +763,22 @@ exit 1 fi +if [ -n "$authkey" ]; then + if [ ! -f "$authkey" ]; then + echo "SSH keyfile '$authkey' not found" + exit 1 + fi + # This is mostly to prevent accidental uage of the private key instead + # of the public key. + if [ "${authkey: -4}" != ".pub" ]; then + echo "SSH keyfile '$authkey' does not end with '.pub'" + exit 1 + fi +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') +valid_releases=('wheezy' 'jessie' 'stretch' 'buster' 'sid') if [[ ! "${valid_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]]; then echo "Invalid release ${release}, valid ones are: ${valid_releases[*]}" exit 1 @@ -631,21 +787,21 @@ # detect rootfs config="$path/config" if [ -z "$rootfs" ]; then - if grep -q '^lxc.rootfs' "$config" 2> /dev/null ; then - rootfs=$(awk -F= '/^lxc.rootfs[ \t]+=/{ print $2 }' "$config") + if grep -q '^lxc.rootfs.path' "$config" 2> /dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path[ \t]+=/{ print $2 }' "$config") else rootfs=$path/rootfs fi fi # determine the number of ttys - default is 4 -if grep -q '^lxc.tty' "$config" 2> /dev/null ; then - num_tty=$(awk -F= '/^lxc.tty[ \t]+=/{ print $2 }' "$config") +if grep -q '^lxc.tty.max' "$config" 2> /dev/null ; then + num_tty=$(awk -F= '/^lxc.tty.max[ \t]+=/{ print $2 }' "$config") else num_tty=4 fi -install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH" +install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH" "$interpreter" "$interpreter_path" "$flushcache" if [ $? -ne 0 ]; then echo "failed to install debian" exit 1 @@ -665,7 +821,7 @@ configure_debian_systemd "$path" "$rootfs" "$config" $num_tty -post_process "${rootfs}" "${release}" "${arch}" "${hostarch}" "${packages}" +post_process "${rootfs}" "${release}" ${arch} ${hostarch} "${interpreter}" "${packages}" if [ ! -z "$clean" ]; then clean || exit 1 diff -Nru lxc-2.0.8/templates/lxc-download.in lxc-2.1.0/templates/lxc-download.in --- lxc-2.0.8/templates/lxc-download.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-download.in 2017-09-06 02:32:37.000000000 +0000 @@ -28,7 +28,7 @@ # Defaults DOWNLOAD_ARCH= DOWNLOAD_BUILD= -DOWNLOAD_COMPAT_LEVEL=3 +DOWNLOAD_COMPAT_LEVEL=4 DOWNLOAD_DIST= DOWNLOAD_FLUSH_CACHE="false" DOWNLOAD_FORCE_CACHE="false" @@ -67,14 +67,14 @@ # Some useful functions cleanup() { - if [ -d "$DOWNLOAD_TEMP" ]; then - rm -Rf $DOWNLOAD_TEMP + if [ -d "${DOWNLOAD_TEMP}" ]; then + rm -Rf "${DOWNLOAD_TEMP}" fi } wget_wrapper() { for i in $(seq 3); do - if wget $@; then + if wget "$@"; then return 0 fi done @@ -83,26 +83,26 @@ } 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 ! 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 + 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 " 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 ! 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 @@ -116,30 +116,31 @@ } gpg_setup() { - if [ "$DOWNLOAD_VALIDATE" = "false" ]; then + if [ "${DOWNLOAD_VALIDATE}" = "false" ]; then return fi - if [ "$DOWNLOAD_READY_GPG" = "true" ]; then + 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" + 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 + if gpg --keyserver "${DOWNLOAD_KEYSERVER}" \ + --recv-keys "${DOWNLOAD_KEYID}" >/dev/null 2>&1; then success=1 break fi + break done - if [ -z "$success" ]; then + if [ -z "${success}" ]; then echo "ERROR: Unable to fetch GPG key from keyserver." exit 1 fi @@ -148,15 +149,15 @@ } gpg_validate() { - if [ "$DOWNLOAD_VALIDATE" = "false" ]; then - if [ "$DOWNLOAD_SHOW_GPG_WARNING" = "true" ]; then + 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 + if ! gpg --verify "$1" >/dev/null 2>&1; then echo "ERROR: Invalid signature for $1" 1>&2 exit 1 fi @@ -164,10 +165,16 @@ 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 + while read -r line; do + fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')" + if [ "${fields}" = "0 0 4294967295" ]; then + echo no; + return; + fi + if echo "${fields}" | grep -q " 0 1$"; then + echo userns-root; + return; + fi done < /proc/self/uid_map [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && \ @@ -180,11 +187,11 @@ if [ -e "${FILE_PATH}-${DOWNLOAD_MODE}" ]; then FILE_PATH="${FILE_PATH}-${DOWNLOAD_MODE}" fi - if [ -e "$FILE_PATH.${DOWNLOAD_COMPAT_LEVEL}" ]; then + if [ -e "${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}" ]; then FILE_PATH="${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}" fi - echo $FILE_PATH + echo "${FILE_PATH}" } usage() { @@ -224,11 +231,9 @@ return 0 } -options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ +if ! options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ server:,keyid:,keyserver:,no-validate,flush-cache,force-cache,name:,path:,\ -rootfs:,mapped-uid:,mapped-gid: -- "$@") - -if [ $? -ne 0 ]; then +rootfs:,mapped-uid:,mapped-gid: -- "$@"); then usage exit 1 fi @@ -238,36 +243,36 @@ 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;; + -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;; + --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 + if ! command -V "${bin}" >/dev/null 2>&1; then + echo "ERROR: Missing required tool: ${bin}" 1>&2 exit 1 fi done # Check for GPG -if [ "$DOWNLOAD_VALIDATE" = "true" ]; then - if ! type gpg >/dev/null 2>&1; then +if [ "${DOWNLOAD_VALIDATE}" = "true" ]; then + if ! command -V gpg >/dev/null 2>&1; then echo "ERROR: Missing recommended tool: gpg" 1>&2 echo "You can workaround this by using --no-validate." 1>&2 exit 1 @@ -275,18 +280,18 @@ 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 +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) +USERNS="$(in_userns)" -if [ "$USERNS" != "no" ]; then - if [ "$USERNS" = "yes" ]; then - if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then +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 @@ -298,112 +303,108 @@ fi fi -if [ -z "$DOWNLOAD_DIST" ] || [ -z "$DOWNLOAD_RELEASE" ] || \ - [ -z "$DOWNLOAD_ARCH" ]; then +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 +if ! command -V mktemp >/dev/null 2>&1; then DOWNLOAD_TEMP=/tmp/lxc-download.$$ - mkdir -p $DOWNLOAD_TEMP + mkdir -p "${DOWNLOAD_TEMP}" else DOWNLOAD_TEMP=$(mktemp -d) fi # Simply list images -if [ "$DOWNLOAD_LIST_IMAGES" = "true" ] || \ - [ "$DOWNLOAD_INTERACTIVE" = "true" ]; then +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} + 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 + 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 + 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 + while IFS=';' read -r f1 f2 f3 f4 f5 f6; do - printf "$1\t$2\t$3\t$4\t$5\n" - done < ${DOWNLOAD_TEMP}/index + [ -n "${DOWNLOAD_DIST}" ] && [ "$f1" != "${DOWNLOAD_DIST}" ] && continue + [ -n "${DOWNLOAD_RELEASE}" ] && [ "$f2" != "${DOWNLOAD_RELEASE}" ] && continue + [ -n "${DOWNLOAD_ARCH}" ] && [ "$f3" != "${DOWNLOAD_ARCH}" ] && continue + [ -n "${DOWNLOAD_VARIANT}" ] && [ "$f4" != "${DOWNLOAD_VARIANT}" ] && continue + [ -z "${f5}" ] || [ -z "${f6}" ] && continue + + printf "%s\t%s\t%s\t%s\t%s\n" "${f1}" "${f2}" "${f3}" "${f4}" "${f5}" + unset f1 f2 f3 f4 f5 f6 + done < "${DOWNLOAD_TEMP}/index" echo "---" - if [ "$DOWNLOAD_LIST_IMAGES" = "true" ]; then + if [ "${DOWNLOAD_LIST_IMAGES}" = "true" ]; then exit 1 fi # Interactive mode echo "" - if [ -z "$DOWNLOAD_DIST" ]; then - echo -n "Distribution: " - read DOWNLOAD_DIST + if [ -z "${DOWNLOAD_DIST}" ]; then + echo "Distribution: " + read -r DOWNLOAD_DIST fi - if [ -z "$DOWNLOAD_RELEASE" ]; then - echo -n "Release: " - read DOWNLOAD_RELEASE + if [ -z "${DOWNLOAD_RELEASE}" ]; then + echo "Release: " + read -r DOWNLOAD_RELEASE fi - if [ -z "$DOWNLOAD_ARCH" ]; then - echo -n "Architecture: " - read DOWNLOAD_ARCH + if [ -z "${DOWNLOAD_ARCH}" ]; then + echo "Architecture: " + read -r DOWNLOAD_ARCH fi echo "" fi # Setup the cache -if [ "$DOWNLOAD_TARGET" = "system" ]; then - LXC_CACHE_BASE="$LOCALSTATEDIR/cache/lxc/" +if [ "${DOWNLOAD_TARGET}" = "system" ]; then + LXC_CACHE_BASE="${LOCALSTATEDIR}/cache/lxc/" else - LXC_CACHE_BASE="$HOME/.cache/lxc/" + LXC_CACHE_BASE="${HOME}/.cache/lxc/" fi # Allow the setting of the LXC_CACHE_PATH with the usage of environment variables. -LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LXC_CACHE_BASE"} -LXC_CACHE_PATH=$LXC_CACHE_PATH/download/$DOWNLOAD_DIST -LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH/" -LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_VARIANT" +LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${LXC_CACHE_BASE}"}" +LXC_CACHE_PATH="${LXC_CACHE_PATH}/download/${DOWNLOAD_DIST}" +LXC_CACHE_PATH="${LXC_CACHE_PATH}/${DOWNLOAD_RELEASE}/${DOWNLOAD_ARCH}/" +LXC_CACHE_PATH="${LXC_CACHE_PATH}/${DOWNLOAD_VARIANT}" -if [ -d "$LXC_CACHE_PATH" ]; then - if [ "$DOWNLOAD_FLUSH_CACHE" = "true" ]; then +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 + 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 + if [ "$(cat "$(relevant_file expiry)")" -lt "$(date +%s)" ]; then echo "The cached copy has expired, re-downloading..." DOWNLOAD_USE_CACHE="false" fi @@ -412,88 +413,88 @@ fi # Download what's needed -if [ "$DOWNLOAD_USE_CACHE" = "false" ]; then +if [ "${DOWNLOAD_USE_CACHE}" = "false" ]; then # Initialize GPG gpg_setup # Grab the index - DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE} + 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 + 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 + 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 + while IFS=';' read -r f1 f2 f3 f4 f5 f6; do + + if [ "${f1}" != "${DOWNLOAD_DIST}" ] || \ + [ "${f2}" != "${DOWNLOAD_RELEASE}" ] || \ + [ "${f3}" != "${DOWNLOAD_ARCH}" ] || \ + [ "${f4}" != "${DOWNLOAD_VARIANT}" ] || \ + [ -z "${f6}" ]; then continue fi - DOWNLOAD_BUILD=$5 - DOWNLOAD_URL=$6 + DOWNLOAD_BUILD="${f5}" + DOWNLOAD_URL="${f6}" + + unset f1 f2 f3 f4 f5 f6 break - done < ${DOWNLOAD_TEMP}/index + done < "${DOWNLOAD_TEMP}/index" - if [ -z "$DOWNLOAD_URL" ]; then + 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 + 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 + 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 + 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 + 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 + if [ -n "${LXC_MAPPED_UID}" ] && [ "${LXC_MAPPED_UID}" != "-1" ]; then + # As the script is run in strict mode (set -eu), all commands + # exiting with non 0 would make the script stop. + # || true or || : (more portable) prevents that. + chown -R "${LXC_MAPPED_UID}" "${LXC_CACHE_BASE}" >/dev/null 2>&1 || : fi - if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then - chgrp -R $LXC_MAPPED_GID $LXC_CACHE_BASE >/dev/null 2>&1 || true + if [ -n "${LXC_MAPPED_GID}" ] && [ "${LXC_MAPPED_GID}" != "-1" ]; then + chgrp -R "${LXC_MAPPED_GID}" "${LXC_CACHE_BASE}" >/dev/null 2>&1 || : fi echo "The image cache is now ready" fi @@ -507,94 +508,102 @@ EXCLUDES="" excludelist=$(relevant_file excludes) if [ -f "${excludelist}" ]; then - while read line; do - EXCLUDES="$EXCLUDES --exclude=$line" - done < $excludelist + while read -r line; do + EXCLUDES="${EXCLUDES} --exclude=${line}" + done < "${excludelist}" fi +# Do not surround ${EXCLUDES} by quotes. This does not work. The solution could +# use array but this is not POSIX compliant. The only POSIX compliant solution +# is to use a function wrapper, but the latter can't be used here as the args +# are dynamic. We thus need to ignore the warning brought by shellcheck. +# shellcheck disable=SC2086 tar --anchored ${EXCLUDES} --numeric-owner -xpJf \ - ${LXC_CACHE_PATH}/rootfs.tar.xz -C ${LXC_ROOTFS} + "${LXC_CACHE_PATH}/rootfs.tar.xz" -C "${LXC_ROOTFS}" -mkdir -p ${LXC_ROOTFS}/dev/pts/ +mkdir -p "${LXC_ROOTFS}/dev/pts/" # Setup the configuration -configfile=$(relevant_file config) -fstab=$(relevant_file fstab) -if [ ! -e $configfile ]; then +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 +sed -i -e "/lxc.net.0/{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 +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 +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 +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 + 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 +if [ -e "${fstab}" ]; then + echo "lxc.mount.fstab = ${LXC_PATH}/fstab" >> "${LXC_PATH}/config" fi -echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config +echo "lxc.uts.name = ${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 + 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" +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) + while read -r line; do + fullpath="${LXC_ROOTFS}/${line}" + [ ! -e "${fullpath}" ] && continue + TEMPLATE_FILES="${TEMPLATE_FILES};${fullpath}" + done < "$(relevant_file templates)" fi # Replace variables in all templates -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 +OLD_IFS=${IFS} +IFS=";" +for file in ${TEMPLATE_FILES}; do + [ ! -f "${file}" ] && continue + + sed -i "s#LXC_NAME#${LXC_NAME}#g" "${file}" + sed -i "s#LXC_PATH#${LXC_PATH}#g" "${file}" + sed -i "s#LXC_ROOTFS#${LXC_ROOTFS}#g" "${file}" + sed -i "s#LXC_TEMPLATE_CONFIG#${LXC_TEMPLATE_CONFIG}#g" "${file}" + sed -i "s#LXC_HOOK_DIR#${LXC_HOOK_DIR}#g" "${file}" done +IFS=${OLD_IFS} # prevent mingetty from calling vhangup(2) since it fails with userns on CentOS / Oracle -if [ -f ${LXC_ROOTFS}/etc/init/tty.conf ]; then - sed -i 's|mingetty|mingetty --nohangup|' ${LXC_ROOTFS}/etc/init/tty.conf +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 +if [ -n "${LXC_MAPPED_UID}" ] && [ "${LXC_MAPPED_UID}" != "-1" ]; then + chown "${LXC_MAPPED_UID}" "${LXC_PATH}/config" "${LXC_PATH}/fstab" >/dev/null 2>&1 || : fi -if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then - chgrp $LXC_MAPPED_GID $LXC_PATH/config $LXC_PATH/fstab >/dev/null 2>&1 || true +if [ -n "${LXC_MAPPED_GID}" ] && [ "${LXC_MAPPED_GID}" != "-1" ]; then + chgrp "${LXC_MAPPED_GID}" "${LXC_PATH}/config" "${LXC_PATH}/fstab" >/dev/null 2>&1 || : fi if [ -e "$(relevant_file create-message)" ]; then diff -Nru lxc-2.0.8/templates/lxc-fedora.in lxc-2.1.0/templates/lxc-fedora.in --- lxc-2.0.8/templates/lxc-fedora.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-fedora.in 2017-09-06 02:32:37.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/bash # -# template script for generating fedora container for LXC +# template script for generating Fedora container for LXC # # @@ -11,6 +11,7 @@ # Daniel Lezcano # Ramez Hanna # Michael H. Warfield +# Reto Gantenbein # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,15 +20,30 @@ # 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 +# 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@ +# configurations +FEDORA_RELEASE_MIN=24 +FEDORA_RELEASE_DEFAULT=${FEDORA_RELEASE_DEFAULT:-25} +FEDORA_RSYNC_URL="${FEDORA_RSYNC_URL:-archives.fedoraproject.org::fedora-enchilada}" +MIRRORLIST_URL=${MIRRORLIST_URL:-https://mirrors.fedoraproject.org/mirrorlist} + +local_state_dir="@LOCALSTATEDIR@" +lxc_path="@LXCPATH@" +lxc_template_config="@LXCTEMPLATECONFIG@" +lxc_default_conf="@LXC_DEFAULT_CONFIG@" + +# allows the cache directory to be set by environment variable +LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${local_state_dir}/cache/lxc"}" + +# these are only going into comments in the resulting config... +lxc_network_type=veth +lxc_network_link=lxcbr0 # Some combinations of the tuning knobs below do not exactly make sense. # but that's ok. @@ -53,1218 +69,1018 @@ # # Make sure this is in single quotes to defer expansion to later! # :{root_password='Root-${name}-${RANDOM}'} -: ${root_password='Root-${name}-XXXXXX'} +: "${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'} +: "${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'} +: "${root_prompt_password='no'}" # Expire root password? Default to yes, but can be overridden from # the environment variable -: ${root_expire_password='yes'} +: "${root_expire_password='yes'}" -# These are only going into comments in the resulting config... -lxc_network_type=veth -lxc_network_link=lxcbr0 - -# is this fedora? -# Alow for weird remixes like the Raspberry Pi -# -# Use the Mitre standard CPE identifier for the release ID if possible... -# This may be in /etc/os-release or /etc/system-release-cpe. We -# should be able to use EITHER. Give preference to /etc/os-release for now. - -# Detect use under userns (unsupported) +# detect use under userns (unsupported) for arg in "$@"; do - [ "$arg" = "--" ] && break - if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + [ "${arg}" = "--" ] && break + if [ "${arg}" = "--mapped-uid" ] || [ "${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 +# 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 +# dnf package manager arguments +dnf_args=( --assumeyes --best --allowerasing --disablerepo=* --enablerepo=fedora --enablerepo=updates ) -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 +# This function is going to setup a minimal host-arch native Fedora bootstrap +# environment which will be used to create new containers on non-Fedora hosts. +# +bootstrap_fedora() +{ + local cache="${1}" -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" != "" ] + local bootstrap="${cache}/bootstrap" + if [ -d "${bootstrap}" ] then - is_fedora=true - fi -fi + echo "Existing Fedora bootstrap environment found. Testing ..." + if chroot_update_fedora "${bootstrap}" + then + CHROOT_DIR="${bootstrap}" + CHROOT_CMD="chroot ${CHROOT_DIR} " -configure_fedora() -{ + echo "Bootstrap environment appears to be functional." + return 0 + else + echo "Error: Bootstrap environment detected in ${bootstrap}" + echo "but appears to be non-functional. Please remove and restart." + return 1 + fi + fi - # disable selinux in fedora - mkdir -p $rootfs_path/selinux - echo 0 > $rootfs_path/selinux/enforce + echo "Setting up new Fedora ${FEDORA_RELEASE_DEFAULT} (${basearch}) bootstrap environment." - # 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 + [[ -d "${cache}" ]] || mkdir -p "${cache}" - # 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 + tmp_bootstrap_dir=$( mktemp -d --tmpdir="${cache}" bootstrap_XXXXXX ) + pushd "${tmp_bootstrap_dir}" >/dev/null - # 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 + mkdir squashfs liveos bootstrap - if [ -f ${rootfs_path}/etc/pam.d/crond ] + # download the LiveOS squashfs image + if [ ! -f "${cache}/install.img" ] then - sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond - fi + local os_path="linux/releases/${FEDORA_RELEASE_DEFAULT}/Everything/${basearch}/os" + local image_path="images/install.img" + local ret=1 - # 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 - ) + if [ -n "${rsync}" ] + then + echo "Syncing LiveOS squashfs image from ${FEDORA_RSYNC_URL} ... " + rsync --archive --info=progress "${FEDORA_RSYNC_URL}/${os_path}/${image_path}" . + ret=$? + else + if [ -z "${mirror}" ] + then + get_mirrors "${FEDORA_RELEASE_DEFAULT}" "${basearch}" || return $? + else + local mirror_url="${mirror}/${os_path}" + fi + for url in ${mirror:-${mirror_urls}} + do + echo "Downloading LiveOS squashfs image from ${url} ... " + curl --silent --show-error --fail --remote-name "${url}/${image_path}" + ret=$? + if [ ${ret} -ne 0 ] + then + continue + else + break + fi + done + fi + + if [ "${ret}" != 0 ] || [ ! -s install.img ] + then + echo "Error: Download of squashfs image failed." + return 1 + fi + mv install.img "${cache}/" + else + echo "Using cached LiveOS squashfs image." fi - if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] + echo "Mounting LiveOS squashfs file system." + if ! mount -o loop -t squashfs "${cache}/install.img" squashfs/ then - ( cd ${rootfs_path}/lib64/security/ - mv pam_loginuid.so pam_loginuid.so.disabled - ln -s pam_permit.so pam_loginuid.so - ) + echo " +Error: Mount of LiveOS squashfs image failed +-------------------------------------------- +You must have squashfs support available to mount image. LiveOS image is now +cached. Process may be rerun without penalty of downloading LiveOS again. If +LiveOS is corrupt, remove ${cache}/install.img +" + return 1 fi - # Set default localtime to the host localtime if not set... - if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] + # mount contained LiveOS + if ! mount -o loop squashfs/LiveOS/rootfs.img liveos then - # if /etc/localtime is a symlink, this should preserve it. - cp -a /etc/localtime ${rootfs_path}/etc/localtime + echo " +Error: Mount of LiveOS stage0 rootfs image failed +------------------------------------------------- +LiveOS download may be corrupt. Remove ${cache}/LiveOS +to force a new download. +" + return 1 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 + echo "Copying LiveOS content to bootstrap environment ... " + if ! rsync --archive --acls --hard-links --sparse liveos/. bootstrap/ + then + echo "Error: Build of bootstrap environment failed." + echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." + exit 1 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. + # unmount liveos mounts - we're done with liveos at this point + umount liveos + umount squashfs - # 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 + # customize bootstrap rootfs + pushd bootstrap >/dev/null - if [ ${root_display_password} = "yes" ] + # setup repositories in bootstrap chroot + CHROOT_DIR="$(pwd)" + CHROOT_CMD="chroot ${CHROOT_DIR} " + INSTALL_ROOT="/" + if ! setup_repositories "${cache}" "${basearch}" "${FEDORA_RELEASE_DEFAULT}" "${mirror}" then - echo "Setting root password to '$root_password'" + echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + exit 1 fi - if [ ${root_store_password} = "yes" ] + if [ -n "${mirror}" ] 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'" + set_dnf_mirror_url ./etc/yum.repos.d/fedora*.repo fi - echo "root:$root_password" | chroot $rootfs_path chpasswd + # make sure /etc/resolv.conf is up to date in the chroot + cp /etc/resolv.conf ./etc/ - if [ ${root_expire_password} = "yes" ] - then - # Also set this password as expired to force the user to change it! - chroot $rootfs_path passwd -e root - fi + # verify bootstrap chroot by running a dnf update + chroot_update_fedora "$(pwd)" + ret=$? - # 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 + popd >/dev/null - if [[ ! -e ${rootfs_path}/sbin/NetworkManager ]] + if [ "${ret}" != 0 ] 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 + echo "Error: Build of bootstrap environment failed." + echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." + return 1 fi - umount ${rootfs_path}/proc - umount ${rootfs_path}/dev + mv bootstrap "${cache}" - # silence some needless startup errors - touch ${rootfs_path}/etc/fstab + popd >/dev/null + rm -rf "${tmp_bootstrap_dir}" - # 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 + CHROOT_DIR="${bootstrap}" + CHROOT_CMD="chroot ${CHROOT_DIR} " + echo "Fedora bootstrap environment successfully created." return 0 } -configure_fedora_init() +chroot_mounts() { - 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 + test -n "${1}" && local chroot="${1}" || return 1 + + mount -t proc proc "${chroot}/proc" && + mount -o bind /dev "${chroot}/dev" } -configure_fedora_systemd() +chroot_umounts() { - 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 + test -n "${1}" && local chroot="${1}" || return 1 - # 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 ) + umount "${chroot}/proc" && + umount "${chroot}/dev" } -### 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() +clean_cache() { - 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} + local cache="${1}" - cd ${cache_base} + test ! -e "${cache}" && return 0 - # 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} ]] + # lock, so we won't purge while someone is creating a repository + ( + if ! flock -x 9 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 + echo "Error: Cache repository is busy." + exit 1 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 ) + echo -n "Purging the Fedora bootstrap and download cache ... " + rm --preserve-root --one-file-system -rf "${cache}" && echo "Done." || exit 1 - 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 + exit 0 -# 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 + ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" - 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... + return $? +} - # Right now, we are using Fedora 20 for the inial bootstrap. - # We could make this the "current" Fedora rev (F > 15). +# Customize container rootfs +# +configure_fedora() +{ + local rootfs="${1}" + local release="${2}" + local utsname="${3}" - rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/LiveOS . + # disable selinux + mkdir -p "${rootfs}/selinux" + echo 0 > "${rootfs}/selinux/enforce" - 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." + # also kill it in the /etc/selinux/config file if it's there... + if [ -f "${rootfs}/etc/selinux/config" ] + then + sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' "${rootfs}/etc/selinux/config" fi - mount -o loop ../LiveOS/squashfs.img squashfs - - if [[ $? != 0 ]] + # nice catch from Dwight Engen in the Oracle template. + # wantonly plagerized here with much appreciation. + if [ -f "${rootfs}/usr/sbin/selinuxenabled" ] 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 + rm -f "${rootfs}/usr/sbin/selinuxenabled" + ln -s /bin/false "${rootfs}/usr/sbin/selinuxenabled" fi - mount -o loop squashfs/LiveOS/rootfs.img stage0 + # set hostname + echo "${utsname}" > "${rootfs}/etc/hostname" - if [[ $? != 0 ]] + # set default localtime to the host localtime if not set... + if [ -e /etc/localtime ] && [ ! -e "${rootfs}/etc/localtime" ] 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 + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime "${rootfs}/etc/localtime" fi + # set minimal hosts + cat < "${rootfs}/etc/hosts" +127.0.0.1 localhost.localdomain localhost ${utsname} +::1 localhost6.localdomain6 localhost6 +EOF -### 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 + # 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.tty.dir is specified in the config. - echo "Updating Stage 1 image with full rpm and yum packages" + # allow root login on console, tty[1-4], and pts/0 for libvirt + cat <> "${rootfs}/etc/securetty" +# LXC (Linux Containers) +lxc/console +lxc/tty1 +lxc/tty2 +lxc/tty3 +lxc/tty4 +# For libvirt/Virtual Machine Monitor +pts/0 +EOF - # 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... + if [ "${root_display_password}" = yes ] + then + echo "Setting root password to '$root_password'" + fi + if [ "${root_store_password}" = yes ] + then + touch "${path}/tmp_root_pass" + chmod 600 "${path}/tmp_root_pass" + echo "${root_password}" > "${path}/tmp_root_pass" + echo "Storing root password in '${path}/tmp_root_pass'" + fi - 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]* . + echo "root:$root_password" | chroot "${rootfs}" chpasswd - # 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? + if [ "${root_expire_password}" = yes ] + then + # also set this password as expired to force the user to change it! + chroot "${rootfs}" passwd -e root + fi - # 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! + chroot_mounts "${rootfs}" -### 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 + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${rootfs}/etc/" - echo "Stage 1 creation complete. Building stage 2 Installation Bootstrap" + # rebuild the rpm database based on the target rpm version... + rm -f "${rootfs}"/var/lib/rpm/__db* + chroot "${rootfs}" rpm --rebuilddb - mount -o bind ../bootstrap run/install - rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/f/fedora-release-20* . + chroot_umounts "${rootfs}" - # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! - chroot . rpm --root /run/install --nodeps -ivh fedora-release-* + # default systemd target + chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target \ + /etc/systemd/system/default.target - # yum will take $basearch from host, so force the arch we want - sed -i "s|\$basearch|$basearch|" ./run/install/etc/yum.repos.d/* + # enable networking via systemd-networkd + test -d "${rootfs}/etc/systemd/network" || mkdir "${rootfs}/etc/systemd/network" + cat < "${rootfs}/etc/systemd/network/eth0.network" +[Match] +Name=eth0 - chroot . yum -y --nogpgcheck --installroot /run/install install python rpm yum +[Network] +DHCP=both +EOF + mkdir -p "${rootfs}/etc/systemd/system/socket.target.wants" + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.socket \ + /etc/systemd/system/socket.target.wants/systemd-networkd.socket + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.service \ + /etc/systemd/system/multi-user.target.wants/systemd-networkd.service + mkdir -p "${rootfs}/etc/systemd/system/network-online.target.wants" + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd-wait-online.service \ + /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service + + # disable traditional network init + chroot "${rootfs}" chkconfig network off + + # enable systemd-resolved + rm -f "${rootfs}/etc/resolv.conf" + chroot "${rootfs}" ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-resolved.service \ + /etc/systemd/system/multi-user.target.wants/systemd-resolved.service - umount run/install - umount proc - umount dev + # if desired, prevent systemd from over-mounting /tmp with tmpfs + if [ "${masktmp}" -eq 1 ] + then + chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/tmp.mount + fi -# 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. + return 0 +} - 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/ +copy_configuration() +{ + local rootfs="${1}" + local config="${2}" + local utsname="${3}" + + # include configuration from default.conf if available + grep -q "^lxc." "${lxc_default_conf}" > "${config}" 2>/dev/null + + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null || echo " +lxc.rootfs.path = ${rootfs} +" >> "${config}" - # yum will take $basearch from host, so force the arch we want - sed -i "s|\$basearch|$basearch|" ./etc/yum.repos.d/* + # 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}" "${config}.orig" - chroot . yum -y update + local line key + while read -r line + do + # This should catch variable expansions from the default config... + if expr "${line}" : '.*\$' > /dev/null 2>&1 + then + line=$(eval "echo \"${line}\"") + fi - RC=$? + # 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*=') - umount proc - umount dev + if [ "${key}" != "lxc.net.0.hwaddr" ] + then + echo "${line}" >> "${config}" - cd .. + if [ "${key}" == "lxc.net.0.link" ] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> "${config}" + fi + fi + done < "${config}.orig" + rm -f "${config}.orig" - if [[ ${RC} != 0 ]] + if [ -e "${lxc_template_config}/fedora.common.conf" ] then echo " -Build of Installation Bootstrap failed. Temp directory -not removed so it can be investigated. -" - return 255 +# Include common configuration +lxc.include = ${lxc_template_config}/fedora.common.conf +" >> "${config}" 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 + cat <> "${path}/config" +# Container specific configuration +lxc.arch = ${basearch} +lxc.uts.name = ${utsname} - return 0 -} +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined +# example simple networking setup, uncomment to enable +#lxc.net.0.type = ${lxc_network_type} +#lxc.net.0.flags = up +#lxc.net.0.link = ${lxc_network_link} +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.hwaddr = $(create_hwaddr) +# persistent veth device name on host side +# Note: This may potentially collide with other containers of same name! +#lxc.net.0.veth.pair = v-${name}-e0 +EOF -fedora_bootstrap_mounts() -{ - if [[ ${BOOTSTRAP} -ne 1 ]] + if [ $? -ne 0 ] then - return 0 + echo "Failed to add configuration" + return 1 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/ + return 0 } -fedora_bootstrap_umounts() +copy_fedora() { - if [[ ${BOOTSTRAP} -ne 1 ]] - then - return 0 - fi + local cache="${1}" + local rootfs="${2}" + echo -n "Copying ${cache} to ${rootfs} ... " + + mkdir -p "${rootfs}" && + rsync --archive --hard-links --sparse --acls --xattrs "${cache}/" "${rootfs}/" && + echo || return 1 - umount ${BOOTSTRAP_DIR}/proc - umount ${BOOTSTRAP_DIR}/dev - umount ${BOOTSTRAP_DIR}/run/install + return 0 } +# 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/' +} -# This is the code to create the initial roofs for Fedora. It may -# require a run time environment by calling the routines above... - +# Make sure a fully functional rootfs of the requested release and architecture +# will be setup in the given cache directory. If this is a Fedora host the +# commands will run natively otherwise in a minimal Fedora bootstrap chroot. +# download_fedora() { + local cache_rootfs="${1}" + local setup_rootfs="${cache_rootfs%%/rootfs}/partial" - # 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" + # suppress errors due to unknown locales + LC_ALL=C + LANG=en_US + + echo "Downloading ${basearch} rootfs for Fedora ${release} ..." + + # The following variables are going to be overwritten if the rootfs setup + # is run in a separate boostrap environment (can not build natively). + # These are the defaults for the non-boostrap (native) mode. + CHROOT_DIR= + CHROOT_CMD= + INSTALL_ROOT=${setup_rootfs} + + if [ ! "${is_fedora}" ] || [ "${fedora_host_ver}" -lt "${FEDORA_VERSION_MINIMAL}" ] + then + # if this is not a supported Fedora host, use minimal bootstrap chroot + echo "Non-Fedora host detected. Checking for bootstrap environment ... " + if ! bootstrap_fedora "${cache}" + then + echo "Error: Fedora Bootstrap setup failed" + return 1 + fi + echo "Using bootstrap environment at ${CHROOT_DIR}" + fi + + if ! mkdir -p "${setup_rootfs}" + then + echo "Error: Failed to create '${setup_rootfs}' directory." return 1 fi - # download a mini fedora into a cache - echo "Downloading fedora minimal ..." + trap revert_rootfs SIGHUP SIGINT SIGTERM - # 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. + mkdir -p "${setup_rootfs}/var/lib/rpm" - BOOTSTRAP_INSTALL_ROOT=${INSTALL_ROOT} - BOOTSTRAP_CHROOT= - BOOTSTRAP_DIR= + # if the setup is going to be run in a chroot, mount the related file systems + if [ -n "${CHROOT_DIR}" ] + then + chroot_mounts "${CHROOT_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" + # make sure rootfs is available in bootstrap chroot + INSTALL_ROOT="/run/install" + test -d "${CHROOT_DIR}${INSTALL_ROOT}" || mkdir -p "${CHROOT_DIR}${INSTALL_ROOT}" + mount -o bind "${setup_rootfs}" "${CHROOT_DIR}${INSTALL_ROOT}" + fi - if [[ ${release} -lt 17 ]] + if ! setup_repositories "${cache}" "${basearch}" "${release}" "${mirror}" 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" + echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + revert_rootfs >/dev/null + return 1 fi - if [[ ${release} -ge 21 ]] + # Unforunately "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" + if [ -n "${mirror}" ] + then + set_dnf_mirror_url "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" + fi + + # install minimal container file system + local pkg_list="dnf initscripts passwd vim-minimal openssh-server openssh-clients dhclient rootfiles policycoreutils fedora-release fedora-repos" + if ! ${CHROOT_CMD}dnf --installroot "${INSTALL_ROOT}" \ + --config="${INSTALL_ROOT}/dnf.conf" \ + --releasever "${release}" \ + ${dnf_args[@]} \ + install ${pkg_list} 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" + echo "Error: Failed to setup the rootfs in ${CHROOT_DIR}${INSTALL_ROOT}." + revert_rootfs >/dev/null + return 1 fi - DOWNLOAD_OK=no + unmount_installroot - # 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 + # from now on we'll work in the new rootfs + chroot_mounts "${setup_rootfs}" - echo "Failed to get a mirror on try $trynumber" - sleep 3 - done + # It might happen, that the dnf used above will write an incompatible + # RPM database for the version running in the rootfs. Rebuild it. + echo "Fixing up RPM databases" + rm -f "${setup_rootfs}"/var/lib/rpm/__db* + chroot "${setup_rootfs}" rpm --rebuilddb - # 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 + # Restrict locale for installed packages to en_US to shrink image size + # following: https://pagure.io/fedora-kickstarts/blob/master/f/fedora-cloud-base.ks + echo "Cleanup locales and language files ..." + find "${setup_rootfs}/usr/share/locale" -mindepth 1 -maxdepth 1 -type d \ + -not -name "${LANG}" -exec rm -rf {} + - 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 + chroot "${setup_rootfs}" localedef --list-archive | grep -v ^"${LANG}" | xargs \ + chroot "${setup_rootfs}" localedef --delete-from-archive - 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 + mv -f "${setup_rootfs}/usr/lib/locale/locale-archive" \ + "${setup_rootfs}/usr/lib/locale/locale-archive.tmpl" + chroot "${setup_rootfs}" build-locale-archive - # 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 + echo "%_install_langs C:en:${LANG}:${LANG}.UTF-8" > "${setup_rootfs}/etc/rpm/macros.image-language-conf" + chroot_umounts "${setup_rootfs}" - DOWNLOAD_OK=yes - break - done + # reset traps + trap SIGHUP + trap SIGINT + trap SIGTERM - if [ $DOWNLOAD_OK != yes ]; then - echo "Aborting" - return 1 - fi - - mkdir -p ${INSTALL_ROOT}/var/lib/rpm + # use generated rootfs as future cache + mv "${setup_rootfs}" "${cache_rootfs}" - if ! fedora_get_bootstrap - then - echo "Fedora Bootstrap setup failed" - return 1 - fi + echo "Download of Fedora rootfs complete." + return 0 +} - fedora_bootstrap_mounts +# Query the Fedora mirrorlist for several HTTPS mirrors +# +get_mirrors() +{ + local release="${1}" + local mirror_arch="${2}" - ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --initdb + for trynumber in 1 2 3 4 + do + [ "${trynumber}" != 1 ] && echo -n "Trying again ... " - # 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} + # choose some mirrors by parsing directory index + mirror_urls=$(curl --silent --show-error --fail "${MIRRORLIST_URL}?repo=fedora-${release}&arch=${mirror_arch}" | sed '/^https:/!d' | sed '2,6!d') - # 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 + # shellcheck disable=SC2181 + if [ $? -eq 0 ] && [ -n "${mirror_urls}" ] + then + break + fi - fedora_bootstrap_umounts + echo "Warning: Failed to get a mirror on try ${trynumber}." + sleep 3 + done - if [ ${RC} -ne 0 ]; then - echo "Failed to download the rootfs, aborting." + if [ -z "${mirror_urls}" ] + then + echo "Error: Failed to retrieve Fedora mirror URL. Please use '-m MIRROR' option." 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 a functional Fedora rootfs into the container root +# install_fedora() { - mkdir -p @LOCALSTATEDIR@/lock/subsys/ + local rootfs="${1}" + local cache="${2}" + local cache_rootfs="${cache}/${release}-${basearch}/rootfs" + + mkdir -p "${local_state_dir}/lock/subsys/" ( - flock -x 9 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." + if ! flock -x 9 + then + echo "Error: 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'" + echo "Checking cache download in ${cache_rootfs} ... " + if [ ! -e "${cache_rootfs}" ] + then + if ! download_fedora "${cache_rootfs}" + then + echo "Error: Failed to download Fedora ${release} (${basearch})" 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" + echo "Cache found at ${cache_rootfs}. Updating ..." + if ! chroot_update_fedora "${cache_rootfs}" + then + echo "Failed to update cached rootfs, continuing with previously cached version." else - echo "Update finished" + echo "Fedora update finished." fi fi - echo "Copy $cache/rootfs to $rootfs_path ... " - copy_fedora - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" + trap revert_container SIGHUP SIGINT SIGTERM + + if ! copy_fedora "${cache_rootfs}" "${rootfs}" + then + echo "Error: Failed to copy rootfs" return 1 fi + chroot_mounts "${rootfs}" + + # install additional user provided packages + if [ -n "${packages}" ] + then + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${rootfs}/etc/" + + echo "Installing user requested RPMs: ${packages}" + if ! chroot "${rootfs}" dnf install ${dnf_args[@]} $(tr ',' ' ' <<< "${packages}") + then + echo "Error: Installation of user provided packages failed." + echo "Cleaning up ... " + sleep 3 # wait for all file handles to properly close + chroot_umounts "${setup_rootfs}" + return 1 + fi + fi + + # cleanup dnf cache in new container + chroot "${rootfs}" dnf clean all + + sleep 3 # wait for all file handles to properly close + chroot_umounts "${rootfs}" + return 0 - ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora + ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" return $? } -# Generate a random hardware (MAC) address composed of FE followed by -# 5 random bytes... -create_hwaddr() +# Cleanup partial container +# +revert_container() { - openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' + echo "Interrupted, so cleaning up ..." + lxc-destroy -n "${name}" 2>/dev/null + # maybe was interrupted before copy config, try to prevent some mistakes + if [ -d "${path}" ] && + [ "${path}" != "/" ] && [ "${path}" != "/tmp" ] && [ "${path}" != "/bin" ] + then + rm -rf "${path}" + fi + echo "Exiting ..." + exit 1 } -copy_configuration() +# Cleanup partial rootfs cache +# +revert_rootfs() { - mkdir -p $config_path + echo "Interrupted, so cleaning up ..." + unmount_installroot + rm -rf "${setup_rootfs}" + echo "Exiting ..." + exit 1 +} - grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo " -lxc.rootfs = $rootfs_path -" >> $config_path/config +# Set dnf repository mirror in given repo files +# +set_dnf_mirror_url() +{ + sed -i -e 's/^\(metalink=.*\)$/#\1/g' "${@}" + sed -i -e '/baseurl/ s|^#||g' "${@}" + sed -i -e "/baseurl/ s|http://download.fedoraproject.org/pub/fedora|${mirror}|g" "${@}" +} - # 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 +# Setup dnf repository configuration. It can be run in a chroot by specifying +# $CHROOT_DIR (chroot directory) and $CHROOT_CMD (chroot command) and/or +# with an alternative RPM install root defined in $INSTALL_ROOT. +# +setup_repositories() +{ + local cache="${1}" + local target_arch="${2}" + local release="${3}" + local mirror="${4}" + + # download repository packages if not found in cache + pushd "${cache}" >/dev/null + if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || + [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] + then + # if no mirror given, get an appropriate mirror from the mirror list + if [ -z "${mirror}" ] then - LINE=$(eval "echo \"${LINE}\"") + get_mirrors "${release}" "${target_arch}" || return $? + else + # construct release-specific mirror url + mirror="${mirror}/linux/releases/${release}/Everything/${target_arch}/os" 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 + for mirror_url in ${mirror:-${mirror_urls}} + do + local release_url="${mirror_url}/Packages/f" - if [[ "${KEY}" == "lxc.network.link" ]] + for pkg in fedora-release-${release} fedora-repos-${release} + do + test -n "$(ls -1 ./${pkg}*.noarch.rpm 2>/dev/null)" && continue + + # query package name by parsing directory index + echo "Requesting '${pkg}' package version from ${release_url}." + pkg_name=$(curl --silent --show-error --fail "${release_url}/" | sed -n -e "/${pkg}/ s/.*href=\"\(${pkg}-.*\.noarch\.rpm\)\">.*/\1/p" | tail -1) + + # shellcheck disable=SC2181 + if [ $? -ne 0 ] || [ -z "${pkg_name}" ] + then + echo "Error: Failed to get '${pkg}' version from ${release_url}/." + continue + fi + + echo "Downloading '${release_url}/${pkg_name} ... " + if ! curl --silent --show-error --fail --remote-name "${release_url}/${pkg_name}" + then + echo "Error: Package download failed." + continue + fi + done + + # if we have both packages continue + if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || + [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] then - echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config + break fi - fi - done < $config_path/config.def - - rm -f $config_path/config.def + done + fi - if [ -e "@LXCTEMPLATECONFIG@/fedora.common.conf" ]; then - echo " -# Include common configuration -lxc.include = @LXCTEMPLATECONFIG@/fedora.common.conf -" >> $config_path/config + # copy packages to chroot file system + if [ -n "${CHROOT_DIR}" ] + then + cp ./fedora-release-${release}*.noarch.rpm "${CHROOT_DIR}" && + cp ./fedora-repos-${release}*.noarch.rpm "${CHROOT_DIR}" + else + local pkgdir="${cache}" fi - # Append things which require expansion here... - cat <> $config_path/config -lxc.arch = $arch -lxc.utsname = $utsname + # use '--nodeps' to work around 'fedora-release-24-*' bash dependency + ${CHROOT_CMD}rpm --root "${INSTALL_ROOT}" -ivh --nodeps "${pkgdir}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} + local ret=$? -# When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined + # dnf will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|${target_arch}|" ${CHROOT_DIR}${INSTALL_ROOT}/etc/yum.repos.d/* -# 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 + popd >/dev/null -EOF + if [ "${ret}" -ne 0 ] + then + echo "Failed to setup repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + exit 1 + fi - if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + # cleanup installed packages + if [ -n "${CHROOT_DIR}" ] + then + # shellcheck disable=SC2086 + rm -f "${CHROOT_DIR}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} fi return 0 } -clean() +# Run dnf update in the given chroot directory. +# +chroot_update_fedora() { + local chroot="${1}" + chroot_mounts "${chroot}" - if [ ! -e $cache ]; then - exit 0 - fi + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${chroot}/etc/" - # 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 + chroot "${chroot}" dnf -y update + local ret=$? - 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 + sleep 3 # wait for all file handles to properly close + + chroot_umounts "${chroot}" + + return ${ret} +} + +# Unmount installroot after bootstrapping or on error. +# +unmount_installroot() { + if [ -n "${CHROOT_DIR}" ] + then + sleep 3 # wait for all file handles to properly close + chroot_umounts "${CHROOT_DIR}" 2>/dev/null + umount "${CHROOT_DIR}${INSTALL_ROOT}" 2>/dev/null + fi } 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 +LXC Container configuration for Fedora images. + +Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME -t fedora [OPTION..] -- [TEMPLATE_OPTION..] + +Template options: + + -a, --arch Define what arch the container will be [i386,x86_64] + -c, --clean Clean bootstrap and download cache + -d, --debug Run with 'set -x' to debug errors + --fqdn Fully qualified domain name (FQDN) + -h, --help Print this help text + --mask-tmp Prevent systemd from over-mounting /tmp with tmpfs. + -M, --mirror=MIRROR Fedora mirror to use during installation. + -p, --path=PATH Path to where the container will be created, + defaults to ${lxc_path}. + -P, --packages=PKGS Comma-separated list of additional RPM packages to + install into the container. + -R, --release=RELEASE Fedora release number of the container, defaults + to host's release if the host is Fedora. + --rootfs=ROOTFS Path for the actual container root file system + --rsync Use rsync instead of HTTPS to download bootstrap + image (insecure). + +Environment variables: + + LXC_CACHE_PATH Cache directory for image bootstrap. Defaults to + '${LXC_CACHE_PATH}' + + MIRRORLIST_URL List of Fedora mirrors queried if no custom mirror is + given. Defaults to '${MIRRORLIST_URL}' + + FEDORA_RSYNC_URL Fedora rsync URL to use for bootstrap with '--rsync'. + Defaults to '${FEDORA_RSYNC_URL}' + + FEDORA_RELEASE_DEFAULT Set default Fedora release if not detected from the + host. Is set to '${FEDORA_RELEASE_DEFAULT}' + EOF return 0 } -options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,arch:,fqdn:,mask-tmp -- "$@") +options=$(getopt -o a:hp:n:cR:dP:M: -l help,path:,rootfs:,name:,clean,release:,arch:,debug,fqdn:,mask-tmp,mirror:,packages:,rsync -- "$@") +# shellcheck disable=SC2181 if [ $? -ne 0 ]; then - usage $(basename $0) + usage exit 1 fi arch=$(uname -m) +debug=0 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 ;; + case "${1}" in + -h|--help) usage; exit 0 ;; + -n|--name) name="${2}"; shift 2 ;; + -p|--path) path="${2}"; shift 2 ;; + --rootfs) rootfs="${2}"; shift 2 ;; + -a|--arch) newarch="${2}"; shift 2 ;; + -c|--clean) clean=1; shift 1 ;; + -d|--debug) debug=1; shift 1 ;; + --fqdn) utsname="${2}"; shift 2 ;; + --mask-tmp) masktmp=1; shift 1 ;; + -M|--mirror) mirror="${2}"; shift 2 ;; + -P|--packages) packages="${2}"; shift 2 ;; + -R|--release) release="${2}"; shift 2 ;; + --rsync) rsync=1; shift 1 ;; + --) shift 1; break ;; + *) break ;; esac done -if [ ! -z "$clean" -a -z "$path" ]; then - clean || exit 1 - exit 0 +if [ "${debug}" -eq 1 ] +then + set -x +fi + +# change to a safe directory +cd || exit $? + +# Is this Fedora? +# Allow 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. + +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 +fi + +if [ "${CPE_NAME}" = "" ] && [ -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 [ "${ID}" = "fedora" ] && [ -n "${CPE_NAME}" ] && [ -n "${VERSION_ID}" ] +then + fedora_host_ver=${VERSION_ID} + is_fedora=true 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 ;; -*) ;; +basearch=${arch} +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" ;; -*) ;; +case "${basearch}" in + ppc64|s390x) FEDORA_RSYNC_URL="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}" ] +if [ "${newarch}" != "" ] && [ "${newarch}" != "${arch}" ] then case "${newarch}" in i386|i586|i686) - if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] + if [ "${basearch}" = "i386" ] || [ "${basearch}" = "x86_64" ] then # Make the arch a generic x86 32 bit... - arch=${newarch} basearch=i386 else basearch=bad @@ -1277,14 +1093,11 @@ if [ "${basearch}" = "bad" ] then - echo "You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" + echo "Error: You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" exit 1 fi fi -# Allow the cache base to be set by environment variable -cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/fedora/$basearch - # Let's do something better for the initial root password. # It's not perfect but it will defeat common scanning brute force # attacks in the case where ssh is exposed. It will also be set to @@ -1294,16 +1107,16 @@ root_password=Root-${name}-${RANDOM} else # If it's got a ding in it, try and expand it! - if [ $(expr "${root_password}" : '.*$.') != 0 ] + 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 ] + if [ "$(expr "${root_password}" : '.*XXXX')" != 0 ] then - root_password=$(mktemp -u ${root_password}) + root_password=$(mktemp -u "${root_password}") fi fi @@ -1324,129 +1137,110 @@ # New behavior: # utsname and hostname = Container_Name.Domain_Name -if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then - if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then - utsname=${utsname}.$(dnsdomainname) +if [ "$(expr "${utsname}" : '.*\..*\.')" = 0 ] +then + if [ -n "$(dnsdomainname)" ] && [ "$(dnsdomainname)" != "localdomain" ] + then + utsname="${utsname}.$(dnsdomainname)" fi fi +# check if the pre-requisite binaries are available +prerequisite_pkgs=( curl openssl rsync ) 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\"" +for pkg in "${prerequisite_pkgs[@]}" +do + if ! type "${pkg}" >/dev/null 2>&1 + then + needed_pkgs="${pkg} ${needed_pkgs}" + fi +done +if [ -n "${needed_pkgs}" ] +then + echo "Error: Missing command(s): ${needed_pkgs}" exit 1 fi -if [ -z "$path" ]; then - path=$default_path/$name +if [ "$(id -u)" != "0" ] +then + echo "This script should be run as 'root'" + exit 1 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 +# cleanup cache if requested +cache="${LXC_CACHE_PATH}/fedora" +if [ -n "${clean}" ] +then + clean_cache "${cache}" || exit 1 + exit 0 fi -if [ "$(id -u)" != "0" ]; then - echo "This script should be run as 'root'" - exit 1 +# set container directory +if [ -z "${path}" ] +then + path="${lxc_path}/${name}" 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) +# set container rootfs and configuration path +config="${path}/config" +if [ -z "${rootfs}" ] +then + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' "${config}" 2>/dev/null + then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}") + else + rootfs="${path}/rootfs" 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 +# set release if not given +if [ -z "${release}" ] +then + if [ "${is_fedora}" ] && [ -n "${fedora_host_ver}" ] + then + echo "Detected Fedora ${fedora_host_ver} host. Set release to ${fedora_host_ver}." + release="${fedora_host_ver}" + else + echo "This is not a Fedora host or release is missing, defaulting release to ${FEDORA_RELEASE_DEFAULT}." + release="${FEDORA_RELEASE_DEFAULT}" + fi fi - -install_fedora -if [ $? -ne 0 ]; then - echo "failed to install fedora" +if [ "${release}" -lt "${FEDORA_RELEASE_MIN}" ] +then + echo "Error: Fedora release ${release} is not supported. Set -R at least to ${FEDORA_RELEASE_MIN}." exit 1 fi -configure_fedora -if [ $? -ne 0 ]; then - echo "failed to configure fedora for a container" +# bootstrap rootfs and copy to container file system +if ! install_fedora "${rootfs}" "${cache}" +then + echo "Error: Failed to create Fedora container" exit 1 fi -# If the systemd configuration directory exists - set it up for what we need. -if [ -d ${rootfs_path}/etc/systemd/system ] +# customize container file system +if ! configure_fedora "${rootfs}" "${release}" "${utsname}" then - configure_fedora_systemd + echo "Error: Failed to configure Fedora container" + exit 1 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 ] +# create container configuration (will be overwritten by newer lxc-create) +if ! copy_configuration "${rootfs}" "${config}" "${utsname}" then - configure_fedora_init + echo "Error: Failed write container configuration file" + exit 1 fi -if [ ! -z "$clean" ]; then +if [ -n "${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 +echo "Successfully created container '${name}'" -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" ] +if [ "${root_display_password}" = "yes" ] then echo "The temporary password for root is: '$root_password' @@ -1454,25 +1248,25 @@ " fi -if [ ${root_store_password} = "yes" ] +if [ "${root_store_password}" = "yes" ] then echo "The temporary root password is stored in: - '${config_path}/tmp_root_pass' + '${config}/tmp_root_pass' " fi -if [ ${root_prompt_password} = "yes" ] +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} passwd " - chroot ${rootfs_path} passwd + chroot "${rootfs}" passwd else - if [ ${root_expire_password} = "yes" ] + if [ "${root_expire_password}" = "yes" ] then - if ( mountpoint -q -- "${rootfs_path}" ) + if ( mountpoint -q -- "${rootfs}" ) then echo "To reset the root password, you can do: @@ -1482,14 +1276,16 @@ " else echo " -The root password is set up as "expired" and will require it to be changed +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 + chroot ${rootfs} passwd " fi fi fi + +# vim: set ts=4 sw=4 expandtab: diff -Nru lxc-2.0.8/templates/lxc-fedora-legacy.in lxc-2.1.0/templates/lxc-fedora-legacy.in --- lxc-2.0.8/templates/lxc-fedora-legacy.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/templates/lxc-fedora-legacy.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,1495 @@ +#!/bin/bash + +# +# template script for generating fedora container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano +# Ramez Hanna +# Michael H. Warfield + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#Configurations +default_path=@LXCPATH@ + +# Some combinations of the tuning knobs below do not exactly make sense. +# but that's ok. +# +# If the "root_password" is non-blank, use it, else set a default. +# This can be passed to the script as an environment variable and is +# set by a shell conditional assignment. Looks weird but it is what it is. +# +# If the root password contains a ding ($) then try to expand it. +# That will pick up things like ${name} and ${RANDOM}. +# If the root password contains more than 3 consecutive X's, pass it as +# a template to mktemp and take the result. +# +# If root_display_password = yes, display the temporary root password at exit. +# If root_store_password = yes, store it in the configuration directory +# If root_prompt_password = yes, invoke "passwd" to force the user to change +# the root password after the container is created. +# If root_expire_password = yes, you will be prompted to change the root +# password at the first login. +# +# These are conditional assignments... The can be overridden from the +# preexisting environment variables... +# +# Make sure this is in single quotes to defer expansion to later! +# :{root_password='Root-${name}-${RANDOM}'} +: ${root_password='Root-${name}-XXXXXX'} + +# Now, it doesn't make much sense to display, store, and force change +# together. But, we gotta test, right??? +: ${root_display_password='no'} +: ${root_store_password='yes'} +# Prompting for something interactive has potential for mayhem +# with users running under the API... Don't default to "yes" +: ${root_prompt_password='no'} + +# Expire root password? Default to yes, but can be overridden from +# the environment variable +: ${root_expire_password='yes'} + +# These are only going into comments in the resulting config... +lxc_network_type=veth +lxc_network_link=lxcbr0 + +# is this fedora? +# Alow for weird remixes like the Raspberry Pi +# +# Use the Mitre standard CPE identifier for the release ID if possible... +# This may be in /etc/os-release or /etc/system-release-cpe. We +# should be able to use EITHER. Give preference to /etc/os-release for now. + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +if [ -e /etc/os-release ] +then +# This is a shell friendly configuration file. We can just source it. +# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME + . /etc/os-release + echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" +fi + +if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] +then + CPE_NAME=$(head -n1 /etc/system-release-cpe) + CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') + if [ "${CPE_URI}" != "cpe:/o" ] + then + CPE_NAME= + else + echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" + # Probably a better way to do this but sill remain posix + # compatible but this works, shrug... + # Must be nice and not introduce convenient bashisms here. + ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') + VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') + fi +fi + +if [ "${CPE_NAME}" != "" -a "${ID}" = "fedora" -a "${VERSION_ID}" != "" ] +then + fedora_host_ver=${VERSION_ID} + is_fedora=true +elif [ -e /etc/redhat-release ] +then + # Only if all other methods fail, try to parse the redhat-release file. + fedora_host_ver=$( sed -e '/^Fedora /!d' -e 's/Fedora.*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release ) + if [ "$fedora_host_ver" != "" ] + then + is_fedora=true + fi +fi + +configure_fedora() +{ + + # disable selinux in fedora + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + # Also kill it in the /etc/selinux/config file if it's there... + if [[ -f $rootfs_path/etc/selinux/config ]] + then + sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config + fi + + # Nice catch from Dwight Engen in the Oracle template. + # Wantonly plagerized here with much appreciation. + if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then + mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig + ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled + fi + + # This is a known problem and documented in RedHat bugzilla as relating + # to a problem with auditing enabled. This prevents an error in + # the container "Cannot make/remove an entry for the specified session" + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd + + if [ -f ${rootfs_path}/etc/pam.d/crond ] + then + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond + fi + + # In addition to disabling pam_loginuid in the above config files + # we'll also disable it by linking it to pam_permit to catch any + # we missed or any that get installed after the container is built. + # + # Catch either or both 32 and 64 bit archs. + if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib64/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + # Set default localtime to the host localtime if not set... + if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] + then + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime ${rootfs_path}/etc/localtime + fi + + # Deal with some dain bramage in the /etc/init.d/halt script. + # Trim it and make it our own and link it in before the default + # halt script so we can intercept it. This also preventions package + # updates from interferring with our interferring with it. + # + # There's generally not much in the halt script that useful but what's + # in there from resetting the hardware clock down is generally very bad. + # So we just eliminate the whole bottom half of that script in making + # ourselves a copy. That way a major update to the init scripts won't + # trash what we've set up. + # + # This is mostly for legacy distros since any modern systemd Fedora + # release will not have this script so we won't try to intercept it. + if [ -f ${rootfs_path}/etc/init.d/halt ] + then + sed -e '/hwclock/,$d' \ + < ${rootfs_path}/etc/init.d/halt \ + > ${rootfs_path}/etc/init.d/lxc-halt + + echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt + chmod 755 ${rootfs_path}/etc/init.d/lxc-halt + + # Link them into the rc directories... + ( + cd ${rootfs_path}/etc/rc.d/rc0.d + ln -s ../init.d/lxc-halt S00lxc-halt + cd ${rootfs_path}/etc/rc.d/rc6.d + ln -s ../init.d/lxc-halt S00lxc-reboot + ) + fi + + # configure the network using the dhcp + cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=${utsname} +DHCP_HOSTNAME=\`hostname\` +NM_CONTROLLED=no +TYPE=Ethernet +MTU=${MTU} +EOF + + # set the hostname + cat < ${rootfs_path}/etc/sysconfig/network +NETWORKING=yes +HOSTNAME=${utsname} +EOF + + # set hostname on systemd Fedora systems + if [ $release -gt 14 ]; then + echo "${utsname}" > ${rootfs_path}/etc/hostname + fi + + # set minimal hosts + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $utsname +::1 localhost6.localdomain6 localhost6 +EOF + + # These mknod's really don't make any sense with modern releases of + # Fedora with systemd, devtmpfs, and autodev enabled. They are left + # here for legacy reasons and older releases with upstart and sysv init. + dev_path="${rootfs_path}/dev" + rm -rf $dev_path + mkdir -p $dev_path + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 666 ${dev_path}/random c 1 8 + mknod -m 666 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + mknod -m 666 ${dev_path}/tty0 c 4 0 + mknod -m 666 ${dev_path}/tty1 c 4 1 + mknod -m 666 ${dev_path}/tty2 c 4 2 + mknod -m 666 ${dev_path}/tty3 c 4 3 + mknod -m 666 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty + echo "lxc/console" >>${rootfs_path}/etc/securetty + echo "lxc/tty1" >>${rootfs_path}/etc/securetty + echo "lxc/tty2" >>${rootfs_path}/etc/securetty + echo "lxc/tty3" >>${rootfs_path}/etc/securetty + echo "lxc/tty4" >>${rootfs_path}/etc/securetty + echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty + echo "pts/0" >>${rootfs_path}/etc/securetty + + if [ ${root_display_password} = "yes" ] + then + echo "Setting root password to '$root_password'" + fi + if [ ${root_store_password} = "yes" ] + then + touch ${config_path}/tmp_root_pass + chmod 600 ${config_path}/tmp_root_pass + echo ${root_password} > ${config_path}/tmp_root_pass + echo "Storing root password in '${config_path}/tmp_root_pass'" + fi + + echo "root:$root_password" | chroot $rootfs_path chpasswd + + if [ ${root_expire_password} = "yes" ] + then + # Also set this password as expired to force the user to change it! + chroot $rootfs_path passwd -e root + fi + + # specifying this in the initial packages doesn't always work. + # Even though it should have... + echo "installing fedora-release package" + mount -o bind /dev ${rootfs_path}/dev + mount -t proc proc ${rootfs_path}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${rootfs_path}/etc/ + # Rebuild the rpm database based on the target rpm version... + rm -f ${rootfs_path}/var/lib/rpm/__db* + chroot ${rootfs_path} rpm --rebuilddb + chroot ${rootfs_path} yum -y install fedora-release + + if [[ ! -e ${rootfs_path}/sbin/NetworkManager ]] + then + # NetworkManager has not been installed. Use the + # legacy chkconfig command to enable the network startup + # scripts in the container. + chroot ${rootfs_path} chkconfig network on + fi + + umount ${rootfs_path}/proc + umount ${rootfs_path}/dev + + # silence some needless startup errors + touch ${rootfs_path}/etc/fstab + + # give us a console on /dev/console + sed -i 's/ACTIVE_CONSOLES=.*$/ACTIVE_CONSOLES="\/dev\/console \/dev\/tty[1-4]"/' \ + ${rootfs_path}/etc/sysconfig/init + + return 0 +} + +configure_fedora_init() +{ + sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit + sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit + # don't mount devpts, for pete's sake + sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.sysinit + sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit + chroot ${rootfs_path} chkconfig udev-post off + chroot ${rootfs_path} chkconfig network on + + if [ -d ${rootfs_path}/etc/init ] + then + # This is to make upstart honor SIGPWR. Should do no harm + # on systemd systems and some systems may have both. + cat <${rootfs_path}/etc/init/power-status-changed.conf +# power-status-changed - shutdown on SIGPWR +# +start on power-status-changed + +exec /sbin/shutdown -h now "SIGPWR received" +EOF + fi +} + +configure_fedora_systemd() +{ + rm -f ${rootfs_path}/etc/systemd/system/default.target + touch ${rootfs_path}/etc/fstab + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service + chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + # Make systemd honor SIGPWR + chroot ${rootfs_path} ln -s /usr/lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target + + # if desired, prevent systemd from over-mounting /tmp with tmpfs + if [ $masktmp -eq 1 ]; then + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/tmp.mount + fi + + #dependency on a device unit fails it specially that we disabled udev + # sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service + # + # Actually, the After=dev-%i.device line does not appear in the + # Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left + # over from an earlier version and it's not doing any harm. We do need + # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are + # started on the ttys in the container. Lets do it in an override copy of + # the service so it can still pass rpm verifies and not be automatically + # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ + + sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ + -e 's/After=dev-%i.device/After=/' \ + < ${rootfs_path}/lib/systemd/system/getty\@.service \ + > ${rootfs_path}/etc/systemd/system/getty\@.service + # Setup getty service on the 4 ttys we are going to allow in the + # default config. Number should match lxc.tty + ( cd ${rootfs_path}/etc/systemd/system/getty.target.wants + for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) +} + +### BEGIN Bootstrap Environment Code... Michael H. Warfield /\/\|=mhw=|\/\/ + +# Ok... Heads up. If you're reading these comments, you're either a +# template owner or someone wondering how the hell I did this (or, worse, +# someone in the future trying to maintain it). This code is slightly +# "evil coding bastard" code with one significant hack / dirty trick +# that you would probably miss just reading the code below. I'll mark +# it out with comments. +# +# Because of what this code does, it deserves a lot of comments so people +# can understand WHY I did it this way... +# +# Ultimate Objective - Build a Fedora container on a host system which does +# not have a (complete compatible) version of rpm and/or yum. That basically +# means damn near any distro other than Fedora and Ubuntu (which has rpm and +# yum available). Only requirements for this function are rsync and +# squashfs available to the kernel. If you don't have those, why are you +# even attempting to build containers? +# +# Challenge for this function - Bootstrap a Fedora install bootstrap +# run time environment which has all the pieces to run rpm and yum and +# from which we can build targets containers even where the host system +# has no support for rpm, yum, or fedora. +# +# Steps: +# Stage 0 - Download a Fedora LiveOS squashfs core (netinst core). +# Stage 1 - Extract filesystem from Stage 0 and update to full rpm & yum +# Stage 2 - Use Stage 1 to build a rootfs with python, rpm, and yum. +# +# Stage 2 becomes our bootstrap file system which can be cached +# and then used to build other arbitrary vesions of Fedora of a +# given architecture. Note that this only has to run once for +# Fedora on a given architecture since rpm and yum can build other +# versions. We'll arbitrarily pick Fedora 20 to build this. This +# will need to change as time goes on. + +# Programmers Note... A future fall back may be to download the netinst +# iso image instead of the LiveOS squasfs image and work from that. +# That may be more general but will introduce another substep +# (mounting the iso) to the stage0 setup. + +# This system is designed to be as autonomous as possible so all whitelists +# and controls are self-contained. + +# Initial testing - Whitelist nobody. Build for everybody... +# Initial deployment - Whitelist Fedora. +# Long term - Whitelist Fedora, Debian, Ubuntu, CentOs, Scientific, and NST. + +# List of distros which do not (should not) need a bootstrap (but we will test +# for rpm and yum none the less... OS SHOULD be taken from CPE values but +# Debian / Ubuntu doesn't support CPE yet. + +# BOOTSTRAP_WHITE_LIST="" +BOOTSTRAP_WHITE_LIST="fedora" +# BOOTSTRAP_WHITE_LIST="fedora debian ubuntu centos scientific sl nst" + +BOOTSTRAP=0 +BOOTSTRAP_DIR= +BOOTSTRAP_CHROOT= + +fedora_get_bootstrap() +{ + echo "Bootstrap Environment testing..." + + WHITE_LISTED=1 + + # We need rpm. No rpm - not possible to white list... + if ! which rpm > /dev/null 2>&1 + then + WHITE_LISTED=0 + fi + + # We need yum No yum - not possible to white list... + if ! which yum > /dev/null 2>&1 + then + WHITE_LISTED=0 + fi + + if [[ ${WHITE_LISTED} != 0 ]] + then + for OS in ${BOOTSTRAP_WHITE_LIST} + do + if [[ ${ID} = ${OS} ]] + then + echo " +OS ${ID} is whitelisted. Installation Bootstrap Environment not required. +" + return 0; + fi + done + fi + + echo " +Fedora Installation Bootstrap Build..." + + if ! which rsync > /dev/null 2>&1 + then + echo " +Unable to locate rsync. Cravely bailing out before even attempting to build +an Installation Bootstrap Please install rsync and then rerun this process. +" + + return 255 + fi + + [[ -d ${cache_base} ]] || mkdir -p ${cache_base} + + cd ${cache_base} + + # We know we don't have a cache directory of this version or we + # would have never reached this code to begin with. But we may + # have another Fedora cache directory from which we could run... + # We'll give a preference for close matches preferring higher over + # lower - which makes for really ugly code... + + # Is this a "bashism" that will need cleaning up???? + BOOTSTRAP_LIST="$(( $release + 1 ))/rootfs $(( $release - 1 ))/rootfs \ +$(( $release + 2 ))/rootfs $(( $release - 2 ))/rootfs \ +$(( $release + 3 ))/rootfs $(( $release - 3 ))/rootfs \ +bootstrap" + + for bootstrap in ${BOOTSTRAP_LIST} + do + if [[ -d ${bootstrap} ]] + then + echo " +Existing Bootstrap found. Testing..." + + mount -o bind /dev ${bootstrap}/dev + mount -t proc proc ${bootstrap}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${bootstrap}/etc/ + rm -f ${bootstrap}/var/lib/rpm/__db* + chroot ${bootstrap} rpm --rebuilddb + chroot ${bootstrap} yum -y update + RC=$? + umount ${bootstrap}/proc + umount ${bootstrap}/dev + + if [[ 0 == ${RC} ]] + then + BOOTSTRAP=1 + BOOTSTRAP_DIR="${cache_base}/${bootstrap}" + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + BOOTSTRAP_INSTALL_ROOT=/run/install + + echo " +Functional Installation Bootstrap exists and appears to be completed. +Will use existing Bootstrap: ${BOOTSTRAP_DIR} +" + return 0 + fi + echo " +Installation Bootstrap in ${BOOTSTRAP_DIR} exists +but appears to be non-functional. Skipping... It should be removed. +" + fi + done + + TMP_BOOTSTRAP_DIR=$( mktemp -d --tmpdir=${cache_base} bootstrap_XXXXXX ) + + cd ${TMP_BOOTSTRAP_DIR} + + mkdir squashfs stage0 stage1 bootstrap + +### Stage 0 setup. +# Download the LiveOS squashfs image +# mount image to "squashfs" +# mount contained LiveOS to stage0 + +# We're going to use the archives.fedoraproject.org mirror for the initial stages... +# 1 - It's generally up to date and complete +# 2 - It's has high bandwidth access +# 3 - It supports rsync and wildcarding (and we need both) +# 4 - Not all the mirrors carry the LiveOS images + + if [[ ! -f ../LiveOS/squashfs.img ]] + then + echo " +Downloading stage 0 LiveOS squashfs file system from archives.fedoraproject.org... +Have a beer or a cup of coffee. This will take a bit (~300MB). +" + sleep 3 # let him read it... + + # Right now, we are using Fedora 20 for the inial bootstrap. + # We could make this the "current" Fedora rev (F > 15). + + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/LiveOS . + + if [[ 0 == $? ]] + then + echo "Download of squashfs image complete." + mv LiveOS .. + else + echo " +Download of squashfs image failed. +" + return 255 + fi + else + echo "Using cached stage 0 LiveOS squashfs file system." + fi + + mount -o loop ../LiveOS/squashfs.img squashfs + + if [[ $? != 0 ]] + then + echo " +Mount of LiveOS squashfs image failed! You mush have squashfs support +available to mount image. Unable to continue. Correct and retry +process later! LiveOS image not removed. Process may be rerun +without penalty of downloading LiveOS again. If LiveOS is corrupt, +remove ${cache_base}/LiveOS before rerunning to redownload. +" + return 255 + fi + + mount -o loop squashfs/LiveOS/rootfs.img stage0 + + if [[ $? != 0 ]] + then + echo " +Mount of LiveOS stage0 rootfs image failed! LiveOS download may be corrupt. +Remove ${cache_base}/LiveOS to force a new download or +troubleshoot cached image and then rerun process. +" + return 255 + fi + + +### Stage 1 setup. +# Copy stage0 (which is ro) to stage1 area (rw) for modification. +# Unmount stage0 mounts - we're done with stage 0 at this point. +# Download our rpm and yum rpm packages. +# Force install of rpm and yum into stage1 image (dirty hack!) + + echo "Stage 0 complete, building Stage 1 image... +This will take a couple of minutes. Patience..." + + echo "Creating Stage 1 r/w copy of r/o Stage 0 squashfs image from LiveOS." + + rsync -aAHS stage0/. stage1/ + + umount stage0 + umount squashfs + + cd stage1 + + # Setup stage1 image with pieces to run installs... + + + mount -o bind /dev dev + mount -t proc proc proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf etc/ + + mkdir run/install + + echo "Updating Stage 1 image with full rpm and yum packages" + + # Retrieve our 2 rpm packages we need to force down the throat + # of this LiveOS image we're camped out on. This is the beginning + # of the butt ugly hack. Look close or you may missing it... + + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/r/rpm-[0-9]* \ + ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/y/yum-[0-9]* . + + # And here it is... + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + chroot . rpm -ivh --nodeps rpm-* yum-* + # Did you catch it? + + # The LiveOS image contains rpm (but not rpmdb) and yum (but not + # yummain.py - What the hell good does yum do with no + # yummain.py?!?! - Sigh...). It contains all the supporting + # pieces but the rpm database has not be initialized and it + # doesn't know all the dependences (seem to) have been met. + # So we do a "--nodeps" rpm install in the chrooted environment + # to force the installation of the full rpm and yum packages. + # + # For the purists - Yes, I know the rpm database is wildly out + # of whack now. That's why this is a butt ugly hack / dirty trick. + # But, this is just the stage1 image that we are going to discard as + # soon as the stage2 image is built, so we don't care. All we care + # is that the stage2 image ends up with all the pieces it need to + # run yum and rpm and that the stage2 rpm database is coherent. + # + # NOW we can really go to work! + +### Stage 2 setup. +# Download our Fedora Release rpm packages. +# Install fedora-release into bootstrap to initialize fs and databases. +# Install rpm, and yum into bootstrap image using yum + + echo "Stage 1 creation complete. Building stage 2 Installation Bootstrap" + + mount -o bind ../bootstrap run/install + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/f/fedora-release-20* . + + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + chroot . rpm --root /run/install --nodeps -ivh fedora-release-* + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ./run/install/etc/yum.repos.d/* + + chroot . yum -y --nogpgcheck --installroot /run/install install python rpm yum + + umount run/install + umount proc + umount dev + +# That's it! We should now have a viable installation BOOTSTRAP in +# bootstrap We'll do a yum update in that to verify and then +# move it to the cache location before cleaning up. + + cd ../bootstrap + mount -o bind /dev dev + mount -t proc proc proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf etc/ + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ./etc/yum.repos.d/* + + chroot . yum -y update + + RC=$? + + umount proc + umount dev + + cd .. + + if [[ ${RC} != 0 ]] + then + echo " +Build of Installation Bootstrap failed. Temp directory +not removed so it can be investigated. +" + return 255 + fi + + # We know have a working run time environment in rootfs... + mv bootstrap .. + cd .. + rm -rf ${TMP_BOOTSTRAP_DIR} + + echo " +Build of Installation Bootstrap complete! We now return you to your +normally scheduled template creation. +" + + BOOTSTRAP=1 + BOOTSTRAP_DIR="${cache_base}/bootstrap" + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + BOOTSTRAP_INSTALL_ROOT=/run/install + + return 0 +} + + +fedora_bootstrap_mounts() +{ + if [[ ${BOOTSTRAP} -ne 1 ]] + then + return 0 + fi + + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + + echo "Mounting Bootstrap mount points" + + [[ -d ${BOOTSTRAP_DIR}/run/install ]] || mkdir -p ${BOOTSTRAP_DIR}/run/install + + mount -o bind ${INSTALL_ROOT} ${BOOTSTRAP_DIR}/run/install + mount -o bind /dev ${BOOTSTRAP_DIR}/dev + mount -t proc proc ${BOOTSTRAP_DIR}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${BOOTSTRAP_DIR}/etc/ +} + +fedora_bootstrap_umounts() +{ + if [[ ${BOOTSTRAP} -ne 1 ]] + then + return 0 + fi + + umount ${BOOTSTRAP_DIR}/proc + umount ${BOOTSTRAP_DIR}/dev + umount ${BOOTSTRAP_DIR}/run/install +} + + +# This is the code to create the initial roofs for Fedora. It may +# require a run time environment by calling the routines above... + +download_fedora() +{ + + # check the mini fedora was not already downloaded + INSTALL_ROOT=$cache/partial + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + + # download a mini fedora into a cache + echo "Downloading fedora minimal ..." + + # These will get changed if it's decided that we need a + # boostrap environment (can not build natively). These + # are the defaults for the non-boostrap (native) mode. + + BOOTSTRAP_INSTALL_ROOT=${INSTALL_ROOT} + BOOTSTRAP_CHROOT= + BOOTSTRAP_DIR= + + PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils fedora-release" + MIRRORLIST_URL="http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$release&arch=$basearch" + + if [[ ${release} -lt 17 ]] + then + # The reflects the move of db_dump and db_load from db4_utils to + # libdb_utils in Fedora 17 and above and it's inclusion as a dep... + # Prior to Fedora 11, we need to explicitly include it! + PKG_LIST="${PKG_LIST} db4-utils" + fi + + if [[ ${release} -ge 21 ]] + then + # Since Fedora 21, a separate fedora-repos package is needed. + # Before, the information was conained in fedora-release. + PKG_LIST="${PKG_LIST} fedora-repos" + fi + + DOWNLOAD_OK=no + + # We're splitting the old loop into two loops plus a directory retrival. + # First loop... Try and retrive a mirror list with retries and a slight + # delay between attempts... + for trynumber in 1 2 3 4; do + [ $trynumber != 1 ] && echo "Trying again..." + # This code is mildly "brittle" in that it assumes a certain + # page format and parsing HTML. I've done worse. :-P + MIRROR_URLS=$(curl -s -S -f "$MIRRORLIST_URL" | sed -e '/^http:/!d' -e '2,6!d') + if [ $? -eq 0 ] && [ -n "$MIRROR_URLS" ] + then + break + fi + + echo "Failed to get a mirror on try $trynumber" + sleep 3 + done + + # This will fall through if we didn't get any URLS above + for MIRROR_URL in ${MIRROR_URLS} + do + if [ "$release" -gt "16" ]; then + RELEASE_URL="$MIRROR_URL/Packages/f" + else + RELEASE_URL="$MIRROR_URL/Packages/" + fi + + echo "Fetching release rpm name from $RELEASE_URL..." + # This code is mildly "brittle" in that it assumes a certain directory + # page format and parsing HTML. I've done worse. :-P + RELEASE_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-release-${release}-/!d" -e 's/.*.*//' ) + if [ $? -ne 0 -o "${RELEASE_RPM}" = "" ]; then + echo "Failed to identify fedora release rpm." + continue + fi + + echo "Fetching fedora release rpm from ${RELEASE_URL}/${RELEASE_RPM}......" + curl -L -f "${RELEASE_URL}/${RELEASE_RPM}" > ${INSTALL_ROOT}/${RELEASE_RPM} + if [ $? -ne 0 ]; then + echo "Failed to download fedora release rpm ${RELEASE_RPM}." + continue + fi + + # F21 and newer need fedora-repos in addition to fedora-release. + if [ "$release" -ge "21" ]; then + echo "Fetching repos rpm name from $RELEASE_URL..." + REPOS_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-repos-${release}-/!d" -e 's/.*.*//' ) + if [ $? -ne 0 -o "${REPOS_RPM}" = "" ]; then + echo "Failed to identify fedora repos rpm." + continue + fi + + echo "Fetching fedora repos rpm from ${RELEASE_URL}/${REPOS_RPM}..." + curl -L -f "${RELEASE_URL}/${REPOS_RPM}" > ${INSTALL_ROOT}/${REPOS_RPM} + if [ $? -ne 0 ]; then + echo "Failed to download fedora repos rpm ${RELEASE_RPM}." + continue + fi + fi + + + DOWNLOAD_OK=yes + break + done + + if [ $DOWNLOAD_OK != yes ]; then + echo "Aborting" + return 1 + fi + + mkdir -p ${INSTALL_ROOT}/var/lib/rpm + + if ! fedora_get_bootstrap + then + echo "Fedora Bootstrap setup failed" + return 1 + fi + + fedora_bootstrap_mounts + + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --initdb + + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --nodeps -ivh ${BOOTSTRAP_INSTALL_ROOT}/${RELEASE_RPM} + + # F21 and newer need fedora-repos in addition to fedora-release... + # Note that fedora-release and fedora-system have a mutual dependency. + # So installing the reops package after the release package we can + # spare one --nodeps. + if [ "$release" -ge "21" ]; then + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} -ivh ${BOOTSTRAP_INSTALL_ROOT}/${REPOS_RPM} + fi + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ${BOOTSTRAP_DIR}/${BOOTSTRAP_INSTALL_ROOT}/etc/yum.repos.d/* + + ${BOOTSTRAP_CHROOT}yum --installroot ${BOOTSTRAP_INSTALL_ROOT} -y --nogpgcheck install ${PKG_LIST} + + RC=$? + + if [[ ${BOOTSTRAP} -eq 1 ]] + then + # Here we have a bit of a sticky problem. We MIGHT have just installed + # this template cache using versions of yum and rpm in the bootstrap + # chroot that use a different database version than the target version. + # That can be a very big problem. Solution is to rebuild the rpmdatabase + # with the target database now that we are done building the cache. In the + # vast majority of cases, this is a do-not-care with no harm done if we + # didn't do it. But it catches several corner cases with older unsupported + # releases and it really doesn't cost us a lot of time for a one shot + # install that will never be done again for this rev. + # + # Thanks and appreciation to Dwight Engen and the Oracle template for the + # database rewrite hint! + + echo "Fixing up rpm databases" + + # Change to our target install directory (if we're not already + # there) just to simplify some of the logic to follow... + cd ${INSTALL_ROOT} + + rm -f var/lib/rpm/__db* + # Programmers Note (warning): + # + # Pay careful attention to the following commands! It + # crosses TWO chroot boundaries linked by a bind mount! + # In the bootstrap case, that's the bind mount of ${INSTALL_ROOT} + # to the ${BOOTSTRAP_CHROOT}/run/install directory! This is + # a deliberate hack across that bind mount to do a database + # translation between two environments, neither of which may + # be the host environment! It's ugly and hard to follow but, + # if you don't understand it, don't mess with it! The pipe + # is in host space between the two chrooted environments! + # This is also why we cd'ed into the INSTALL_ROOT directory + # in advance of this loop, so everything is relative to the + # current working directory and congruent with the same working + # space in both chrooted environments. The output into the new + # db is also done in INSTALL_ROOT space but works in either host + # space or INSTALL_ROOT space for the mv, so we don't care. It's + # just not obvious what's happening in the db_dump and db_load + # commands... + # + for db in var/lib/rpm/* ; do + ${BOOTSTRAP_CHROOT} db_dump ${BOOTSTRAP_INSTALL_ROOT}/$db | chroot . db_load $db.new + mv $db.new $db + done + # finish up by rebuilding the database... + # This should be redundant but we do it for completeness and + # any corner cases I may have missed... + mount -t proc proc proc + mount -o bind /dev dev + chroot . rpm --rebuilddb + umount dev + umount proc + fi + + fedora_bootstrap_umounts + + if [ ${RC} -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 +} + +copy_fedora() +{ + + # make a local copy of the minifedora + echo -n "Copying rootfs to $rootfs_path ..." + #cp -a $cache/rootfs-$basearch $rootfs_path || return 1 + # i prefer rsync (no reason really) + mkdir -p $rootfs_path + rsync -SHaAX $cache/rootfs/ $rootfs_path/ + echo + return 0 +} + +update_fedora() +{ + mount -o bind /dev ${cache}/rootfs/dev + mount -t proc proc ${cache}/rootfs/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${cache}/rootfs/etc/ + chroot ${cache}/rootfs yum -y update + umount ${cache}/rootfs/proc + umount ${cache}/rootfs/dev +} + +install_fedora() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_fedora + if [ $? -ne 0 ]; then + echo "Failed to download 'fedora base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_fedora + if [ $? -ne 0 ]; then + echo "Failed to update 'fedora base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_fedora + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora + + return $? +} + +# Generate a random hardware (MAC) address composed of FE followed by +# 5 random bytes... +create_hwaddr() +{ + openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' +} + +copy_configuration() +{ + mkdir -p $config_path + + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo " +lxc.rootfs.path = $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.net.0.hwaddr" ]] + then + echo "${LINE}" >> $config_path/config + + if [[ "${KEY}" == "lxc.net.0.link" ]] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config + fi + fi + done < $config_path/config.def + + rm -f $config_path/config.def + + if [ -e "@LXCTEMPLATECONFIG@/fedora.common.conf" ]; then + echo " +# Include common configuration +lxc.include = @LXCTEMPLATECONFIG@/fedora.common.conf +" >> $config_path/config + fi + + # Append things which require expansion here... + cat <> $config_path/config +lxc.arch = $arch +lxc.uts.name = $utsname + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +# example simple networking setup, uncomment to enable +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.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.net.0.veth.pair = v-$name-e0 + +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for Fedora-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] + [--fqdn=] [-a|--arch=] + [--mask-tmp] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container +Optional args: + -p,--path path to where the container will be created, + defaults to @LXCPATH@. + --rootfs path for actual rootfs. + -c,--clean clean the cache + -R,--release Fedora release for the new container. + Defaults to host's release if the host is Fedora. + --fqdn fully qualified domain name (FQDN) for DNS and system naming + -a,--arch Define what arch the container will be [i686,x86_64] + --mask-tmp Prevent systemd from over-mounting /tmp with tmpfs. + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,arch:,fqdn:,mask-tmp -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +arch=$(uname -m) +masktmp=0 + +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + -R|--release) release=$2; shift 2;; + -a|--arch) newarch=$2; shift 2;; + --fqdn) utsname=$2; shift 2;; + --mask-tmp) masktmp=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +basearch=${arch} +# Map a few architectures to their generic Fedora repository archs. +# The two ARM archs are a bit of a guesstimate for the v5 and v6 +# archs. V6 should have hardware floating point (Rasberry Pi). +# The "arm" arch is safer (no hardware floating point). So +# there may be cases where we "get it wrong" for some v6 other +# than RPi. +case "$arch" in +i686) basearch=i386 ;; +armv3l|armv4l|armv5l) basearch=arm ;; +armv6l|armv7l|armv8l) basearch=armhfp ;; +*) ;; +esac + +mirrorurl="archives.fedoraproject.org::fedora-archive" +case "$basearch" in +ppc64|s390x) mirrorurl="archives.fedoraproject.org::fedora-secondary" ;; +*) ;; +esac + +# Somebody wants to specify an arch. This is very limited case. +# i386/i586/i686 on i386/x86_64 +# - or - +# x86_64 on x86_64 +if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] +then + case "${newarch}" in + i386|i586|i686) + if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] + then + # Make the arch a generic x86 32 bit... + arch=${newarch} + basearch=i386 + else + basearch=bad + fi + ;; + *) + basearch=bad + ;; + esac + + if [ "${basearch}" = "bad" ] + then + echo "You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" + exit 1 + fi +fi + +# Allow the cache base to be set by environment variable +cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/fedora/$basearch + +# Let's do something better for the initial root password. +# It's not perfect but it will defeat common scanning brute force +# attacks in the case where ssh is exposed. It will also be set to +# expired, forcing the user to change it at first login. +if [ "${root_password}" = "" ] +then + root_password=Root-${name}-${RANDOM} +else + # If it's got a ding in it, try and expand it! + if [ $(expr "${root_password}" : '.*$.') != 0 ] + then + root_password=$(eval echo "${root_password}") + fi + + # If it has more than 3 consecutive X's in it, feed it + # through mktemp as a template. + if [ $(expr "${root_password}" : '.*XXXX') != 0 ] + then + root_password=$(mktemp -u ${root_password}) + fi +fi + +if [ -z "${utsname}" ]; then + utsname=${name} +fi + +# This follows a standard "resolver" convention that an FQDN must have +# at least two dots or it is considered a local relative host name. +# If it doesn't, append the dns domain name of the host system. +# +# This changes one significant behavior when running +# "lxc_create -n Container_Name" without using the +# --fqdn option. +# +# Old behavior: +# utsname and hostname = Container_Name +# New behavior: +# utsname and hostname = Container_Name.Domain_Name + +if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then + if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then + utsname=${utsname}.$(dnsdomainname) + fi +fi + +needed_pkgs="" + +type curl >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="curl $needed_pkgs" +fi +type openssl >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="openssl $needed_pkgs" +fi + +if [ -n "$needed_pkgs" ]; then + echo "Missing commands: $needed_pkgs" + echo "Please install these using \"sudo yum install $needed_pkgs\"" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path/$name +fi + +if [ -z "$release" ]; then + if [ "$is_fedora" -a "$fedora_host_ver" ]; then + release=$fedora_host_ver + else + echo "This is not a fedora host and release missing, defaulting to 22 use -R|--release to specify release" + release=22 + fi +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs_path" ]; then + rootfs_path=$path/rootfs + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \ + -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config) + fi +fi +config_path=$path +cache=$cache_base/$release + +revert() +{ + echo "Interrupted, so cleaning up" + lxc-destroy -n $name + # maybe was interrupted before copy config + rm -rf $path + echo "exiting..." + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +install_fedora +if [ $? -ne 0 ]; then + echo "failed to install fedora" + exit 1 +fi + +configure_fedora +if [ $? -ne 0 ]; then + echo "failed to configure fedora for a container" + exit 1 +fi + +# If the systemd configuration directory exists - set it up for what we need. +if [ -d ${rootfs_path}/etc/systemd/system ] +then + configure_fedora_systemd +fi + +# This configuration (rc.sysinit) is not inconsistent with the systemd stuff +# above and may actually coexist on some upgraded systems. Let's just make +# sure that, if it exists, we update this file, even if it's not used... +if [ -f ${rootfs_path}/etc/rc.sysinit ] +then + configure_fedora_init +fi + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi +echo " +Container rootfs and config have been created. +Edit the config file to check/enable networking setup. +" + +if [[ -d ${cache_base}/bootstrap ]] +then + echo "You have successfully built a Fedora container and cache. This cache may +be used to create future containers of various revisions. The directory +${cache_base}/bootstrap contains a bootstrap +which may no longer needed and can be removed. +" +fi + +if [[ -e ${cache_base}/LiveOS ]] +then + echo "A LiveOS directory exists at ${cache_base}/LiveOS. +This is only used in the creation of the bootstrap run-time-environment +and may be removed. +" +fi + +if [ ${root_display_password} = "yes" ] +then + echo "The temporary password for root is: '$root_password' + +You may want to note that password down before starting the container. +" +fi + +if [ ${root_store_password} = "yes" ] +then + echo "The temporary root password is stored in: + + '${config_path}/tmp_root_pass' +" +fi + +if [ ${root_prompt_password} = "yes" ] +then + echo "Invoking the passwd command in the container to set the root password. + + chroot ${rootfs_path} passwd +" + chroot ${rootfs_path} passwd +else + if [ ${root_expire_password} = "yes" ] + then + if ( mountpoint -q -- "${rootfs_path}" ) + then + echo "To reset the root password, you can do: + + lxc-start -n ${name} + lxc-attach -n ${name} -- passwd + lxc-stop -n ${name} +" + else + echo " +The root password is set up as "expired" and will require it to be changed +at first login, which you should do as soon as possible. If you lose the +root password or wish to change it without starting the container, you +can change it from the host by running the following command (which will +also reset the expired flag): + + chroot ${rootfs_path} passwd +" + fi + fi +fi diff -Nru lxc-2.0.8/templates/lxc-gentoo.in lxc-2.1.0/templates/lxc-gentoo.in --- lxc-2.0.8/templates/lxc-gentoo.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-gentoo.in 2017-09-06 02:32:37.000000000 +0000 @@ -282,9 +282,9 @@ 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 backingstore was specified, lxc.rootfs.path 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` + rootfs=`awk -F= '$1 ~ /^lxc.rootfs.path/ { print $2 }' "$path/config" 2>/dev/null` if [ -z "${rootfs}" ]; then #OK it's default rootfs="${path}/rootfs" @@ -517,7 +517,7 @@ value=$(echo "${line}" | sed 's/^.*_real_ugly_sep_42_//') #new nic ! - if [[ "${key}" == "lxc.network.type" ]]; then + if [[ "${key}" == "lxc.net.0.type" ]]; then #we don't know what to do with it. [[ "${value}" == "empty" ]] && continue @@ -535,15 +535,15 @@ nic_type="${value}" fi - if [[ "${key}" == "lxc.network.hwaddr" ]]; then + if [[ "${key}" == "lxc.net.0.hwaddr" ]]; then nic_hwaddr=1 fi - if [[ "${key}" =~ ^lxc.network.ipv(4|6) ]]; then + if [[ "${key}" =~ ^lxc.net.0.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 + if [[ "${key}" =~ ^lxc.net.0.name ]]; then nic_name="${value}" let nic_named=nic_named+1 fi @@ -583,10 +583,10 @@ 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" +lxc.net.0.type = veth +lxc.net.0.link = ${bridge} +lxc.net.0.flags = up +lxc.net.0.hwaddr = fe:xx:xx:xx:xx:xx" return 0 else store_user_message "No network interface for this container" @@ -681,16 +681,16 @@ # 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` + nics=`grep -e '^lxc\.net\.0\.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} + grep -q "^lxc.net.0.hwaddr" ${conf_file} || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" ${conf_file} fi - if grep -q "^lxc.rootfs" "${conf_file}" ; then + if grep -q "^lxc.rootfs.path" "${conf_file}" ; then #lxc-create already provided one conf_rootfs_line="" else - conf_rootfs_line="lxc.rootfs = $(readlink -f "${rootfs}")" + conf_rootfs_line="lxc.rootfs.path = $(readlink -f "${rootfs}")" fi if [[ "${arch}" == "x86" || "${arch}" == "amd64" ]]; then local conf_arch_line="lxc.arch = ${arch}" @@ -705,8 +705,8 @@ ${conf_arch_line} # set the hostname -lxc.utsname = ${name} -lxc.tty = ${tty} +lxc.uts.name = ${name} +lxc.tty.max = ${tty} ${conf_rootfs_line} ${portage_mount} diff -Nru lxc-2.0.8/templates/lxc-openmandriva.in lxc-2.1.0/templates/lxc-openmandriva.in --- lxc-2.0.8/templates/lxc-openmandriva.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-openmandriva.in 2017-09-06 02:32:37.000000000 +0000 @@ -155,7 +155,7 @@ echo -n "Copying rootfs to $rootfs_path ..." mkdir -p $rootfs_path - rsync -Ha $cache/rootfs/ $rootfs_path/ + rsync -SHaAX $cache/rootfs/ $rootfs_path/ return 0 } @@ -226,42 +226,42 @@ { mkdir -p $config_path - grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config cat <> $config_path/config -lxc.utsname = $name -lxc.tty = 4 -lxc.pts = 1024 +lxc.uts.name = $name +lxc.tty.max = 4 +lxc.pty.max = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed # When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined +#lxc.apparmor.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 +lxc.net.0.type = $lxc_network_type +lxc.net.0.flags = up +lxc.net.0.link = $lxc_network_link +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 EOF if [ ! -z ${ipv4} ]; then cat <> $config_path/config -lxc.network.ipv4 = $ipv4 +lxc.net.0.ipv4.address = $ipv4 EOF fi if [ ! -z ${gw} ]; then cat <> $config_path/config -lxc.network.ipv4.gateway = $gw +lxc.net.0.ipv4.gateway = $gw EOF fi if [ ! -z ${ipv6} ]; then cat <> $config_path/config -lxc.network.ipv6 = $ipv6 +lxc.net.0.ipv6.address = $ipv6 EOF fi if [ ! -z ${gw6} ]; then cat <> $config_path/config -lxc.network.ipv6.gateway = $gw6 +lxc.net.0.ipv6.gateway = $gw6 EOF fi cat <> $config_path/config @@ -424,10 +424,10 @@ exit 1 fi -# check for 'lxc.rootfs' passed in through default config by lxc-create +# check for 'lxc.rootfs.path' 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) + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) else rootfs_path=$path/$name/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-opensuse.in lxc-2.1.0/templates/lxc-opensuse.in --- lxc-2.0.8/templates/lxc-opensuse.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-opensuse.in 2017-09-06 02:32:37.000000000 +0000 @@ -12,6 +12,7 @@ # Frederic Crozat # Michael H. Warfield # Johannes Kastl +# Thomas Lamprecht # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -101,8 +102,10 @@ ln -s /dev/null $rootfs/etc/systemd/system/proc-sys-fs-binfmt_misc.automount ln -s /dev/null $rootfs/etc/systemd/system/console-shell.service ln -s /dev/null $rootfs/etc/systemd/system/systemd-vconsole-setup.service + # enable getty and console services sed -e 's/ConditionPathExists=.*//' $rootfs/usr/lib/systemd/system/getty@.service > $rootfs/etc/systemd/system/getty@.service ln -s getty@.service $rootfs/etc/systemd/system/getty@tty1.service + mkdir -p $rootfs/etc/systemd/system/getty.target.wants/ ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@console.service ln -s -f ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty1.service ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty2.service @@ -138,22 +141,33 @@ # 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 + + oss_repo_url="http://download.opensuse.org/distribution/$DISTRO/repo/oss/" + if [[ $DISTRO == "tumbleweed" ]]; then + oss_repo_url="http://download.opensuse.org/$DISTRO/repo/oss/" + fi + zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$oss_repo_url" repo-oss || return 1 + + update_repo_url="http://download.opensuse.org/update/$DISTRO/repo/oss" # Leap update repos were rearranged if [[ $DISTRO == "leap/4"* ]]; then - zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/update/$DISTRO/oss/ update || return 1 - else - zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1 + update_repo_url="http://download.opensuse.org/update/$DISTRO/oss/" fi - zypper --quiet --root $cache/partial-$arch-packages --non-interactive --gpg-auto-import-keys update || return 1 + # tumbleweed has no update repo + if [[ $DISTRO != "tumbleweed" ]]; then + zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$update_repo_url" update || return 1 + fi + + zypper --quiet --root $cache/partial-$arch-packages --non-interactive --gpg-auto-import-keys update || return 1 zypper --root $cache/partial-$arch-packages --non-interactive in --auto-agree-with-licenses --download-only zypper lxc patterns-openSUSE-base bash iputils sed tar rsyslog || return 1 + cat > $cache/partial-$arch-packages/opensuse.conf << EOF Preinstall: aaa_base bash coreutils diffutils Preinstall: filesystem fillup glibc grep insserv-compat perl-base -Preinstall: libbz2-1 libncurses5 pam -Preinstall: permissions libreadline6 rpm sed tar libz1 libselinux1 +Preinstall: libbz2-1 pam +Preinstall: permissions rpm sed tar libz1 libselinux1 Preinstall: liblzma5 libcap2 libacl1 libattr1 -Preinstall: libpopt0 libelf1 liblua5_1 +Preinstall: libpopt0 libelf1 Preinstall: libpcre1 RunScripts: aaa_base @@ -166,7 +180,7 @@ Support: udev Support: netcfg Support: hwinfo insserv-compat module-init-tools openSUSE-release openssh -Support: pwdutils rpcbind sysconfig +Support: pwdutils sysconfig Ignore: rpm:suse-build-key,build-key Ignore: systemd:systemd-presets-branding @@ -177,16 +191,21 @@ echo "Support: python3-base" >> $cache/partial-$arch-packages/opensuse.conf fi - # dhcpcd is not in the default repos since Leap 42.1 - if [[ $DISTRO != "leap/4"* ]] - then - echo "Support: dhcpcd" >> $cache/partial-$arch-packages/opensuse.conf + if [[ $DISTRO == "tumbleweed" ]]; then + echo "Preinstall: liblua5_3 libncurses6 libreadline7" >> $cache/partial-$arch-packages/opensuse.conf + else + echo "Preinstall: liblua5_1 libncurses5 libreadline6" >> $cache/partial-$arch-packages/opensuse.conf + echo "Support: rpcbind" >> $cache/partial-$arch-packages/opensuse.conf fi - # Leap doesn't seem to have iproute2 utils installed - if [[ $DISTRO == "leap/4"* ]] - then - echo "Support: net-tools iproute2" >> $cache/partial-$arch-packages/opensuse.conf + # dhcpcd is not in the default repos since Leap 42.1, neither in tumbleweed + if [[ $DISTRO != "leap/4"* ]] && [[ $DISTRO != "tumbleweed" ]]; then + echo "Support: dhcpcd" >> $cache/partial-$arch-packages/opensuse.conf + fi + + # Leap and tumbleweed doesn't seem to have iproute2 utils installed + if [[ $DISTRO == "leap/4"* ]] || [[ $DISTRO == "tumbleweed" ]]; then + echo "Support: net-tools iproute2" >> $cache/partial-$arch-packages/opensuse.conf fi if [ "$arch" = "i686" ]; then @@ -203,13 +222,16 @@ # openSUSE 13.2 has no noarch directory in update [ -d $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch ] || mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch - CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:$BUILD_DIR" $BUILD_DIR/init_buildsystem --clean --configdir $BUILD_DIR/configs --cachedir $cache/partial-$arch-cache --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 + repos=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/noarch") + if [[ $DISTRO != "tumbleweed" ]]; then # tumbleweed has no update repo + repos+=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch") + fi - if [[ $DISTRO == "leap/4"* ]]; then - chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/update/$DISTRO/oss update || return 1 - else - chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1 + CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:$BUILD_DIR" $BUILD_DIR/init_buildsystem --clean --configdir $BUILD_DIR/configs --cachedir $cache/partial-$arch-cache ${repos[*]} || return 1 + + chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$oss_repo_url" repo-oss || return 1 + if [[ $DISTRO != "tumbleweed" ]]; then + chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$update_repo_url" update || return 1 fi # really clean the image @@ -247,7 +269,7 @@ # make a local copy of the mini opensuse echo "Copying rootfs to $rootfs ..." mkdir -p $rootfs - rsync -Ha $cache/rootfs-$arch/ $rootfs/ || return 1 + rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 return 0 } @@ -299,8 +321,8 @@ rootfs=$2 name=$3 - grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo " -lxc.rootfs = $rootfs_path + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo " +lxc.rootfs.path = $rootfs_path " >> $path/config # The following code is to create static MAC addresses for each @@ -323,13 +345,13 @@ # Seems that \s doesn't work in brackets. KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') - if [[ "${KEY}" != "lxc.network.hwaddr" ]] + if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] then echo "${LINE}" >> $path/config - if [[ "${KEY}" == "lxc.network.link" ]] + if [[ "${KEY}" == "lxc.net.0.link" ]] then - echo "lxc.network.hwaddr = $(create_hwaddr)" >> $path/config + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $path/config fi fi done < $path/config.def @@ -346,24 +368,24 @@ # Append things which require expansion here... cat <> $path/config lxc.arch = $arch -lxc.utsname = $name +lxc.uts.name = $name lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed # When using LXC with apparmor, uncomment the next line to run unconfined: -lxc.aa_profile = unconfined +lxc.apparmor.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 +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 # Additional example for veth network type # static MAC address, -#lxc.network.hwaddr = 00:16:3e:77:52:20 +#lxc.net.0.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 +#lxc.net.0.veth.pair = v-$name-e0 EOF @@ -478,7 +500,15 @@ 42.2|leap/42.2|422) echo "Selected openSUSE Leap 42.2" DISTRO="leap/42.2" - ;; + ;; + 42.3|leap/42.3|423) + echo "Selected openSUSE Leap 42.3" + DISTRO="leap/42.3" + ;; + tumbleweed|factory) + echo "Selected openSUSE Leap Tumbleweed" + DISTRO="tumbleweed" + ;; *) echo "You have chosen an invalid release, quitting..." @@ -496,8 +526,8 @@ # 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-oracle.in lxc-2.1.0/templates/lxc-oracle.in --- lxc-2.0.8/templates/lxc-oracle.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-oracle.in 2017-09-06 02:32:37.000000000 +0000 @@ -359,7 +359,7 @@ # /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. + # since lxc.tty.dir 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 @@ -482,9 +482,9 @@ 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 +lxc.uts.name = $name EOF - grep -q "^lxc.rootfs" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs = $container_rootfs" >> $cfg_dir/config + grep -q "^lxc.rootfs.path" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs.path = $container_rootfs" >> $cfg_dir/config if [ $container_release_major != "4" ]; then echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config @@ -497,36 +497,36 @@ echo "# Networking" >>$cfg_dir/config # see if the default network settings were already specified - lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_type=`grep '^lxc.net.0.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_type" ]; then - echo "lxc.network.type = veth" >>$cfg_dir/config + echo "lxc.net.0.type = veth" >>$cfg_dir/config lxc_network_type=veth fi - lxc_network_link=`grep '^lxc.network.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_link=`grep '^lxc.net.0.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_link" ]; then - echo "lxc.network.link = lxcbr0" >>$cfg_dir/config + echo "lxc.net.0.link = lxcbr0" >>$cfg_dir/config lxc_network_link=lxcbr0 fi - lxc_network_hwaddr=`grep '^lxc.network.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_hwaddr=`grep '^lxc.net.0.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_hwaddr" ]; then # generate a hwaddr for the container # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n1 | awk '{print $2}' | cut -c1-6 | \ sed 's/\(..\)/\1:/g; s/.$//'`" - echo "lxc.network.hwaddr = $hwaddr" >>$cfg_dir/config + echo "lxc.net.0.hwaddr = $hwaddr" >>$cfg_dir/config fi - lxc_network_flags=`grep '^lxc.network.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_flags=`grep '^lxc.net.0.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_flags" ]; then - echo "lxc.network.flags = up" >>$cfg_dir/config + echo "lxc.net.0.flags = up" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" -lxc.network.name = eth0 -lxc.network.mtu = 1500 +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 EOF } diff -Nru lxc-2.0.8/templates/lxc-plamo.in lxc-2.1.0/templates/lxc-plamo.in --- lxc-2.0.8/templates/lxc-plamo.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-plamo.in 2017-09-06 02:32:37.000000000 +0000 @@ -233,7 +233,7 @@ copy_configuration() { ret=0 cat <<- EOF >> $path/config || let ret++ - lxc.utsname = $name + lxc.uts.name = $name lxc.arch = $arch EOF if [ -f "@LXCTEMPLATECONFIG@/plamo.common.conf" ] ; then @@ -346,8 +346,8 @@ 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` + if grep -q "^lxc.rootfs.path" $path/config ; then + rootfs=`awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config` else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-pld.in lxc-2.1.0/templates/lxc-pld.in --- lxc-2.0.8/templates/lxc-pld.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/templates/lxc-pld.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,484 @@ +#!/bin/sh + +# +# template script for generating PLD Linux container for LXC +# + +# +# lxc: Linux Container library + +# Authors: +# Elan Ruusamäe + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Configuration +arch=$(uname -m) +cache_base=@LOCALSTATEDIR@/cache/lxc/pld/$arch +default_path=@LXCPATH@ + +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 "${ID}" = "pld" -a "${VERSION_ID}" != "" ]; then + pld_host_ver=${VERSION_ID} + is_pld=true +elif [ -e /etc/pld-release ]; then + # Only if all other methods fail, try to parse the pld-release file. + pld_host_ver=$(sed -e '/PLD /!d' -e 's/^\([0-9.]*\)\sPLD.*/\1/' < /etc/pld-release) + if [ "$pld_host_ver" != "" ]; then + is_pld=true + fi +fi + +# Map a few architectures to their generic PLD Linux repository archs. +case "$pld_host_ver:$arch" in +3.0:i586) arch=i486 ;; +esac + +configure_pld() +{ + + # disable selinux + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + # configure the network using the dhcp + sed -i -e "s/^HOSTNAME=.*/HOSTNAME=${utsname}/" ${rootfs_path}/etc/sysconfig/network + + # set hostname on systemd + if [ $release = "3.0" ]; then + echo "${utsname}" > ${rootfs_path}/etc/hostname + fi + + # set minimal hosts + test -e $rootfs_path/etc/hosts || \ + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $utsname +::1 localhost6.localdomain6 localhost6 +EOF + + 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 + + if [ -n "${root_password}" ]; then + echo "setting root passwd to $root_password" + echo "root:$root_password" | chroot $rootfs_path chpasswd + fi + + return 0 +} + +configure_pld_init() +{ + # default powerfail action waits 2 minutes. for lxc we want it immediately + sed -i -e '/^pf::powerfail:/ s,/sbin/shutdown.*,/sbin/halt,' ${rootfs_path}/etc/inittab +} + +configure_pld_systemd() +{ + unlink ${rootfs_path}/etc/systemd/system/default.target + 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 + + # 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.max + for i in 1 2 3 4; do + ln -sf ../getty@.service ${rootfs_path}/etc/systemd/system/getty.target.wants/getty@tty${i}.service + done +} + +download_pld() +{ + + # check the mini pld 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 pld into a cache + echo "Downloading PLD Linux minimal ..." + POLDEK="poldek --root $INSTALL_ROOT --noask --nohold --noignore" + PKG_LIST="basesystem filesystem pld-release rpm poldek vserver-packages rc-scripts pwdutils mingetty" + + mkdir -p $INSTALL_ROOT@LOCALSTATEDIR@/lib/rpm + rpm --root $INSTALL_ROOT --initdb + $POLDEK -u $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_pld() +{ + + # make a local copy of the minipld + echo -n "Copying rootfs to $rootfs_path ..." + cp -a $cache/rootfs/* $rootfs_path || return 1 + return 0 +} + +update_pld() +{ + POLDEK="poldek --root $cache/rootfs --noask" + $POLDEK --upgrade-dist +} + +install_pld() +{ + 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_pld + if [ $? -ne 0 ]; then + echo "Failed to download 'pld base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_pld + if [ $? -ne 0 ]; then + echo "Failed to update 'pld base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_pld + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld + + return $? +} + +copy_configuration() +{ + + mkdir -p $config_path + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config + cat <> $config_path/config +# Most of below settings should be taken as defaults from +# lxc.include = /usr/share/lxc/config/common.conf +lxc.uts.name = $utsname +lxc.tty.max = 4 +lxc.pty.max = 1024 +# Consider if below line is right for systemd container +lxc.mount.fstab = $config_path/fstab +lxc.cap.drop = sys_module mac_admin mac_override sys_time + +lxc.autodev = $auto_dev + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +## Devices +# Allow all devices +#lxc.cgroup.devices.allow = a +# Deny all devices +lxc.cgroup.devices.deny = a +# Allow to mknod all devices (but not using them) +lxc.cgroup.devices.allow = c *:* m +lxc.cgroup.devices.allow = b *:* m + +# /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 < $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 PLD Linux $release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] [--fqdn=] [-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 will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case + --rootfs path for actual rootfs. + -c,--clean clean the cache + -R,--release PLD Linux release for the new container. if the host is PLD Linux, then it will default to the host's release. + --fqdn fully qualified domain name (FQDN) for DNS and system naming + -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,fqdn: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while :; 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=$2; shift 2;; + -R|--release) release=$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 + +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 [ -n "$(dnsdomainname)" ]; then + utsname=${utsname}.$(dnsdomainname) + fi +fi + +needed_pkgs="" +type poldek >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="poldek $needed_pkgs" +fi + +#type curl >/dev/null 2>&1 +#if [ $? -ne 0 ]; then +# needed_pkgs="curl $needed_pkgs" +#fi + +if [ -n "$needed_pkgs" ]; then + echo "Missing commands: $needed_pkgs" + echo "Please install these using \"sudo poldek -u $needed_pkgs\"" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path/$name +fi + +if [ -z "$release" ]; then + if [ "$is_pld" -a "$pld_host_ver" ]; then + release=$pld_host_ver + else + echo "This is not a PLD Linux host and release missing, defaulting to 3.0. use -R|--release to specify release" + release=3.0 + fi +fi + +# pld th have systemd. We need autodev enabled to keep systemd from causing problems. +if [ $release = 3.0 ]; then + auto_dev="0" +else + auto_dev="0" +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs" ]; then + rootfs_path=$path/rootfs + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + # TODO: should be lxc.rootfs.mount used instead? + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) + fi +else + rootfs_path=$rootfs +fi +config_path=$default_path/$name +cache=$cache_base/$release + +revert() +{ + echo "Interrupted, so cleaning up" + lxc-destroy -n $name + # maybe was interrupted before copy config + rm -rf $path + rm -rf $default_path/$name + echo "exiting..." + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "Failed write configuration file" + exit 1 +fi + +install_pld +if [ $? -ne 0 ]; then + echo "Failed to install PLD Linux" + exit 1 +fi + +configure_pld +if [ $? -ne 0 ]; then + echo "Failed to configure PLD Linux 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_pld_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_pld_init +fi + +if [ ! -z $clean ]; then + clean || exit 1 + exit 0 +fi +echo "container rootfs and config created" diff -Nru lxc-2.0.8/templates/lxc-sabayon.in lxc-2.1.0/templates/lxc-sabayon.in --- lxc-2.0.8/templates/lxc-sabayon.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/templates/lxc-sabayon.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,511 @@ +#!/bin/bash +# vim: set ts=4 sw=4 expandtab + +# Exit on error and treat unset variables as an error. +set -eu + +# +# LXC template for Sabayon OS, based of Alpine script. +# + +# Authors: +# Geaaru + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +#=========================== Constants ============================# + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' +readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' + + +# Temporary static MIRROR LIST. I will get list from online path on the near future. +readonly MIRRORS_LIST=" +http://mirror.it.sabayon.org/ +http://dl.sabayon.org/ +http://ftp.kddilabs.jp/Linux/packages/sabayonlinux/ +ftp://ftp.klid.dk/sabayonlinux/ +http://ftp.fsn.hu/pub/linux/distributions/sabayon/ +http://ftp.cc.uoc.gr/mirrors/linux/SabayonLinux/ +http://ftp.rnl.ist.utl.pt/pub/sabayon/ +ftp://ftp.nluug.nl/pub/os/Linux/distr/sabayonlinux/ +http://ftp.surfnet.nl/pub/os/Linux/distr/sabayonlinux/ +http://mirror.internode.on.net/pub/sabayon/ +http://mirror.yandex.ru/sabayon/ +http://sabayon.c3sl.ufpr.br/ +http://mirror.clarkson.edu/sabayon/ +http://na.mirror.garr.it/mirrors/sabayonlinux/" + +#======================== Global variables ========================# + +# Clean variables and set defaults. +arch="$(uname -m)" +debug='no' +flush_cache='no' +mirror_url= +name= +path= +release="DAILY" +rootfs= +unprivileged=false +mapped_uid= +mapped_gid= +flush_owner=false + +#======================== Helper Functions ========================# + +usage() { + cat <<-EOF +Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME [lxc-create-options] -- [template-options] + +Template options: + -a ARCH, --arch=ARCH The container architecture (e.g. x86_64, armv7); defaults + to the host arch. + -d, --debug Run this script in a debug mode (set -x and wget w/o -q). + -m URL --mirror=URL The Sabayon mirror to use; defaults to random mirror. + -u, --unprivileged Tuning of rootfs for unprivileged containers. + -r, --release Identify release to use. Default is DAILY. + --mapped-gid Group Id to use on unprivileged container + (based of value present on file /etc/subgid). + --mapped-uid User Id to use on unprivileged container + (based of value present on file /etc/subuid) + --flush-owner Only for directly creation of unprivileged containers + through lxc-create command. Execute fuidshift command. + Require --mapped-gid,--mapped-uid and --unprivileged + options. + +Environment variables: + RELEASE Release version of Sabayon. Default is ${RELEASE}. +EOF +} + +random_mirror_url() { + local url="" + + if [ $arch == 'amd64' ] ; then + url=$(echo $MIRRORS_LIST | sed -e 's/ /\n/g' | sort -R --random-source=/dev/urandom | head -n 1) + else + if [ $arch == 'armv7l' ] ; then + # Currently armv7l tarball is not on sabayon mirrored tree. + url="https://dockerbuilder.sabayon.org/" + fi + fi + + [ -n "$url" ] && echo "$url" +} + +die() { + local retval=$1; shift + + echo -e "==> $@\n" + exit $retval +} + +einfo() { + echo -e "==> $@\n" +} + +fetch() { + if [ "$debug" = 'yes' ]; then + wget -T 10 -O - $@ + else + wget -T 10 -O - -q $@ + fi +} + +parse_arch() { + case "$1" in + x86_64 | amd64) echo 'amd64';; + armv7 | armv7l) echo 'armv7l';; + #arm*) echo 'armhf';; + *) return 1;; + esac +} + +run_exclusively() { + + local lock_name="$1" + local timeout=$2 + local method=$3 + shift 3 + + mkdir -p "$LOCAL_STATE_DIR/lock/subsys" + + local retval + { + echo -n "Obtaining an exclusive lock..." + if ! flock -x 9; then + echo ' failed.' + return 1 + fi + echo ' done' + + ${method} $@ + retval=$? + } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-sabayon-$lock_name" + + return $retval +} + +create_url () { + + local url="" + # Example of amd64 tarball url + # http://mirror.yandex.ru/sabayon/iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz + + if [ $arch == 'amd64' ] ; then + + if [ $release = 'DAILY' ] ; then + url="${MIRROR_URL}iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz" + else + url="${MIRROR_URL}iso/monthly/Sabayon_Linux_${release}_amd64_tarball.tar.gz" + fi + else + # https://dockerbuilder.sabayon.org/Sabayon_Linux_16_armv7l.tar.bz2 + if [ $arch == 'armv7l' ] ; then + + # Currently $arch tarball is not on sabayon mirrored tree. + url="${MIRROR_URL}Sabayon_Linux_16_armv7l.tar.bz2" + + fi + fi + + echo $url +} + + +#=========================== Configure ===========================# + +unprivileged_rootfs() { + + pushd ${rootfs}/etc/systemd/system + + # Disable systemd-journald-audit.socket because it seems that doesn't + # start correctly on unprivileged container + ln -s /dev/null systemd-journald-audit.socket + + # Disable systemd-remount-fs.service because on unprivileged container + # systemd can't remount filesystem + ln -s /dev/null systemd-remount-fs.service + + # Remove mount of FUSE Control File system + ln -s /dev/null sys-fs-fuse-connections.mount + + # Change execution of service systemd-sysctl to avoid errors. + mkdir systemd-sysctl.service.d + cat < systemd-sysctl.service.d/00gentoo.conf +[Service] +ExecStart= +ExecStart=/usr/lib/systemd/systemd-sysctl --prefix=/etc/sysctl.d/ +EOF + + # Disable mount of hugepages + ln -s /dev/null dev-hugepages.mount + + popd + + pushd ${rootfs} + + # Disable sabayon-anti-fork-bomb limits (already apply to lxc container manager) + sed -i -e 's/^*/#*/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + sed -i -e 's/^root/#root/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + + popd + + return 0 +} + +unprivileged_shift_owner () { + + # I use /usr/bin/fuidshift from LXD project. + + einfo "Executing: fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ..." + + fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 || + die 1 "Error on change owners of ${rootfs} directory" + + einfo "Done." + + # Fix permission of container directory + chmod a+rx ${path} + + return 0 +} + +systemd_container_tuning () { + + # To avoid error on start systemd-tmpfiles-setup service + # it is needed clean journal directory + rm -rf ${rootfs}/var/log/journal/ + + # Remove LVM service. Normally not needed on container system. + rm -rf ${rootfs}/etc/systemd/system/sysinit.target.wants/lvm2-lvmetad.service + + # Comment unneeded entry on /etc/fstab + sed -e 's/\/dev/#\/dev/g' -i ${rootfs}/etc/fstab + + # Fix this stupid error until fix is available on sabayon image + # /usr/lib/systemd/system-generators/gentoo-local-generator: line 4: cd: /etc/local.d: No such file or directory + mkdir ${rootfs}/etc/local.d/ + + # Fix TERM variable for container console + mkdir container-getty\@0.service.d + cat < container-getty\@0.service.d/00gentoo.conf +[Service] +Environment=TERM= +Environment=TERM=linux +EOF + + return 0 +} + +configure_container() { + local config="$1" + local hostname="$2" + local arch="$3" + local privileged_options="" + local unprivileged_options="" + + if [[ $unprivileged && $unprivileged == true ]] ; then + if [[ $flush_owner == true ]] ; then + unprivileged_options=" +lxc.idmap = u 0 ${mapped_uid} 65536 +lxc.idmap = g 0 ${mapped_gid} 65536 +" + fi + + unprivileged_options=" +$unprivileged_options + +# Include common configuration. +lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.userns.conf +" + + else + privileged_options=" +## Allow any mknod (but not reading/writing the node) +lxc.cgroup.devices.allow = b *:* m +lxc.cgroup.devices.allow = c *:* m + +### /dev/pts/* +lxc.cgroup.devices.allow = c 136:* rwm +### /dev/tty +lxc.cgroup.devices.allow = c 5:0 rwm +### /dev/console +lxc.cgroup.devices.allow = c 5:1 rwm +### /dev/ptmx +lxc.cgroup.devices.allow = c 5:2 rwm +### fuse +lxc.cgroup.devices.allow = c 10:229 rwm + +" + fi + + cat <<-EOF >> "$config" +# Specify container architecture. +lxc.arch = $arch + +# Set hostname. +lxc.uts.name = $hostname + +# Include common configuration. +lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.common.conf + +$unprivileged_options +$privileged_options +EOF +} + + +#============================= Main ==============================# + +parse_cmdline() { + + # Parse command options. + local short_options="a:dm:n:p:r:hu" + local long_options="arch:,debug,mirror:,name:,path:,release:,rootfs:,mapped-uid:,mapped-gid:,flush-owner,help" + + options=$(getopt -u -q -a -o "$short_options" -l "$long_options" -- "$@") + + eval set -- "$options" + + # Process command options. + while [ $# -gt 0 ]; do + case $1 in + -a | --arch) + arch=$2 + shift + ;; + -d | --debug) + debug='yes' + ;; + -m | --mirror) + mirror_url=$2 + shift + ;; + -n | --name) + name=$2 + shift + ;; + -p | --path) + path=$2 + shift + ;; + -r | --release) + release=$2 + shift + ;; + --rootfs) + rootfs=$2 + shift + ;; + -u | --unprivileged) + unprivileged=true + ;; + -h | --help) + usage + exit 1 + ;; + --mapped-uid) + mapped_uid=$2 + shift + ;; + --mapped-gid) + mapped_gid=$2 + shift + ;; + --flush-owner) + flush_owner=true + ;; + --) + break + ;; + *) + einfo "Unknown option: $1" + usage + exit 1 + ;; + esac + shift + done + + if [ "$(id -u)" != "0" ]; then + die 1 "This script must be run as 'root'" + fi + + # Validate options. + [ -n "$name" ] || die 1 'Missing required option --name' + [ -n "$path" ] || die 1 'Missing required option --path' + + if [ -z "$rootfs" ] && [ -f "$path/config" ]; then + rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" + fi + if [ -z "$rootfs" ]; then + rootfs="$path/rootfs" + fi + + [ -z "$path" ] && die 1 "'path' parameter is required." + + arch=$(parse_arch "$arch") \ + || die 1 "Unsupported architecture: $arch" + + [[ $unprivileged == true && $flush_owner == true &&-z "$mapped_uid" ]] && \ + die 1 'Missing required option --mapped-uid with --unprivileged option' + + [[ $unprivileged == true && $flush_owner == true && -z "$mapped_gid" ]] && \ + die 1 'Missing required option --mapped-gid with --unprivileged option' + + [[ $flush_owner == true && $unprivileged == false ]] && \ + die 1 'flush-owner require --unprivileged option' + + return 0 +} + +main () { + + local tarball="" + + # Set global variables. + RELEASE="${RELEASE:-"DAILY"}" + ARCH="${ARCH:-`uname -m`}" + OS="${OS:-"sabayon"}" + + einfo "Processing command line arguments: $@" + + # Parse command line options + parse_cmdline "$@" + + DEBUG="$debug" + MIRROR_URL="${mirror_url:-$(random_mirror_url)}" + + einfo "Use arch = $arch, mirror_url = $MIRROR_URL, path = $path, name = $name, release = $release, unprivileged = $unprivileged, rootfs = $rootfs, mapped_uid = $mapped_uid, mapped_gid = $mapped_gid, flush_owner = $flush_owner" + + [ "$debug" = 'yes' ] && set -x + + # Download sabayon tarball + tarball=$(create_url) + einfo "Fetching tarball $tarball..." + + # TODO: use only a compression mode + if [ $arch == 'amd64' ] ; then + fetch "${tarball}" | tar -xpz -C "${rootfs}" + else + if [ $arch == 'armv7l' ] ; then + fetch "${tarball}" | tar -xpj -C "${rootfs}" + fi + fi + + einfo "Tarball ${tarball} Extracted." + + systemd_container_tuning + + # Fix container for unprivileged mode. + if [[ $unprivileged == true ]] ; then + unprivileged_rootfs + if [[ $flush_owner == true ]] ; then + unprivileged_shift_owner + fi + fi + + return 0 +} + + +einfo "Prepare creation of sabayon container with params: $@ ($#)" + +# Here we go! +run_exclusively 'main' 10 main "$@" +configure_container "$path/config" "$name" "$arch" + +einfo "Container's rootfs and config have been created" +cat <<-EOF + Edit the config file $path/config to check/enable networking setup. + The installed system is preconfigured for a loopback and single network + interface configured via DHCP. + + To start the container, run "lxc-start -n $name". + The root password is not set; to enter the container run "lxc-attach -n $name". + + Note: From kenel >= 4.6 for use unprivileged containers it is needed this mount on host: + + mkdir /sys/fs/cgroup/systemd + mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd +EOF diff -Nru lxc-2.0.8/templates/lxc-slackware.in lxc-2.1.0/templates/lxc-slackware.in --- lxc-2.0.8/templates/lxc-slackware.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-slackware.in 2017-09-06 02:32:37.000000000 +0000 @@ -638,10 +638,10 @@ cat <> $path/config -lxc.utsname = $name +lxc.uts.name = $name lxc.arch = $arch -lxc.mount = $rootfs/etc/fstab +lxc.mount.fstab = $rootfs/etc/fstab lxc.include = ${LXC_TEMPLATE_CONFIG}/slackware.common.conf EOF @@ -743,8 +743,8 @@ # 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-sparclinux.in lxc-2.1.0/templates/lxc-sparclinux.in --- lxc-2.0.8/templates/lxc-sparclinux.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-sparclinux.in 2017-09-06 02:32:37.000000000 +0000 @@ -229,7 +229,7 @@ # /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. + # since lxc.tty.dir 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 @@ -316,9 +316,9 @@ cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" # Container configuration for Linux for SPARC $container_release_major.$container_release_minor lxc.arch = $arch -lxc.utsname = $name +lxc.uts.name = $name EOF - grep -q "^lxc.rootfs" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs = $container_rootfs" >> $cfg_dir/config + grep -q "^lxc.rootfs.path" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs.path = $container_rootfs" >> $cfg_dir/config echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config @@ -326,36 +326,36 @@ echo "# Networking" >>$cfg_dir/config # see if the default network settings were already specified - lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_type=`grep '^lxc.net.0.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_type" ]; then - echo "lxc.network.type = veth" >>$cfg_dir/config + echo "lxc.net.0.type = veth" >>$cfg_dir/config lxc_network_type=veth fi - lxc_network_link=`grep '^lxc.network.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_link=`grep '^lxc.net.0.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_link" ]; then - echo "lxc.network.link = lxcbr0" >>$cfg_dir/config + echo "lxc.net.0.link = lxcbr0" >>$cfg_dir/config lxc_network_link=lxcbr0 fi - lxc_network_hwaddr=`grep '^lxc.network.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_hwaddr=`grep '^lxc.net.0.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_hwaddr" ]; then # generate a hwaddr for the container # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -n1 | awk '{print $2}' | cut -c1-6 | \ sed 's/\(..\)/\1:/g; s/.$//'`" - echo "lxc.network.hwaddr = $hwaddr" >>$cfg_dir/config + echo "lxc.net.0.hwaddr = $hwaddr" >>$cfg_dir/config fi - lxc_network_flags=`grep '^lxc.network.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + lxc_network_flags=`grep '^lxc.net.0.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_flags" ]; then - echo "lxc.network.flags = up" >>$cfg_dir/config + echo "lxc.net.0.flags = up" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" -lxc.network.name = eth0 -lxc.network.mtu = 1500 +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 EOF } diff -Nru lxc-2.0.8/templates/lxc-sshd.in lxc-2.1.0/templates/lxc-sshd.in --- lxc-2.0.8/templates/lxc-sshd.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-sshd.in 2017-09-06 02:32:37.000000000 +0000 @@ -127,14 +127,14 @@ init_path=$(realpath --relative-to=/ $(readlink -f /sbin/init)) - grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config cat <> $path/config -lxc.utsname = $name -lxc.pts = 1024 +lxc.uts.name = $name +lxc.pty.max = 1024 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: -#lxc.aa_profile = unconfined +#lxc.apparmor.profile = unconfined lxc.mount.entry = /dev dev none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 @@ -162,7 +162,7 @@ fi # if no .ipv4 section in config, then have the container run dhcp - grep -q "^lxc.network.ipv4" $path/config || touch $rootfs/run-dhcp + grep -q "^lxc.net.0.ipv4.address" $path/config || touch $rootfs/run-dhcp if [ "$(uname -m)" = "x86_64" ]; then cat <> $path/config @@ -250,8 +250,8 @@ # 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-ubuntu-cloud.in lxc-2.1.0/templates/lxc-ubuntu-cloud.in --- lxc-2.0.8/templates/lxc-ubuntu-cloud.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-ubuntu-cloud.in 2017-09-06 02:32:37.000000000 +0000 @@ -60,14 +60,14 @@ # 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` + nics=`grep -e '^lxc\.net\.0\.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 + grep -q "^lxc.net.0.hwaddr" $path/config || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config fi # Generate the configuration file ## Relocate all the network config entries - sed -i -e "/lxc.network/{w ${path}/config-network" -e "d}" $path/config + sed -i -e "/lxc.net.0/{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 @@ -89,9 +89,9 @@ 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 + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config cat <> $path/config -lxc.utsname = $name +lxc.uts.name = $name lxc.arch = $arch EOF @@ -263,8 +263,8 @@ # 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-ubuntu.in lxc-2.1.0/templates/lxc-ubuntu.in --- lxc-2.0.8/templates/lxc-ubuntu.in 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/lxc-ubuntu.in 2017-09-06 02:32:37.000000000 +0000 @@ -92,7 +92,15 @@ password=$5 # configure the network using the dhcp - cat < $rootfs/etc/network/interfaces + if chroot $rootfs which netplan >/dev/null 2>&1; then + cat < $rootfs/etc/netplan/10-lxc.yaml +network: + ethernets: + eth0: {dhcp4: true} + version: 2 +EOF + else + cat < $rootfs/etc/network/interfaces # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). @@ -103,6 +111,7 @@ auto eth0 iface eth0 inet dhcp EOF + fi # set the hostname cat < $rootfs/etc/hostname @@ -152,13 +161,20 @@ 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* - 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 + 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 @@ -359,7 +375,13 @@ debootstrap_parameters="$debootstrap_parameters --variant=$variant" fi if [ "$variant" = 'minbase' ]; then - packages_template="${packages_template},sudo,ifupdown,isc-dhcp-client" + packages_template="${packages_template},sudo" + # Newer releases use netplan, EOL releases not supported + case $release in + trusty|xenial|zesty) + packages_template="${packages_template},ifupdown,isc-dhcp-client" + ;; + esac fi echo "Installing packages in template: ${packages_template}" @@ -439,7 +461,7 @@ btrfs subvolume snapshot $cache/rootfs-$arch $realrootfs || return 1 [ "$rootfs" = "$realrootfs" ] || mount --bind $realrootfs $rootfs || return 1 else - rsync -Ha $cache/rootfs-$arch/ $rootfs/ || return 1 + rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 fi return 0 } @@ -503,14 +525,14 @@ # 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` + nics=`grep -e '^lxc\.net\.0\.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 + grep -q "^lxc.net.0.hwaddr" $path/config || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config fi # Generate the configuration file ## Relocate all the network config entries - sed -i -e "/lxc.network/{w ${path}/config-network" -e "d}" $path/config + sed -i -e "/lxc.net.0/{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 @@ -529,9 +551,9 @@ 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 + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config cat <> $path/config -lxc.utsname = $name +lxc.uts.name = $name lxc.arch = $arch EOF @@ -809,8 +831,8 @@ 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) + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) else rootfs=$path/rootfs fi diff -Nru lxc-2.0.8/templates/lxc-voidlinux.in lxc-2.1.0/templates/lxc-voidlinux.in --- lxc-2.0.8/templates/lxc-voidlinux.in 1970-01-01 00:00:00.000000000 +0000 +++ lxc-2.1.0/templates/lxc-voidlinux.in 2017-09-06 02:32:37.000000000 +0000 @@ -0,0 +1,199 @@ +#!/bin/bash + +# +# template script for generating Void Linux container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Gregor Reitzenstein + +# Based on lxc-archlinux template by: +# 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 + +# Utility functions + +# Check if array $2 contains item $1 +containsElement() { + local e + for e in "${@:2}"; do [[ "$1" == "$e" ]] && return 0; done + return 1 +} + +# 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 -ra result < <(echo "${1}") + IFS=${ifs} + return 0 +} + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +# defaults +default_path="/var/lib/lxc" +default_path="@LXCPATH@" +shared_config="@LXCTEMPLATECONFIG@/voidlinux.common.conf" +userns_config="@LXCTEMPLATECONFIG@/voidlinux.userns.conf" + +pkg_blacklist=("linux>=0" "e2fsprogs>=0" "btrfs-progs>=0" "xfsprogs>=0" "f2fs-tools>=0" "dosfstools>=0") +base_packages=() +for pkg in $(xbps-query -Mv --repository="http://repo2.voidlinux.eu/current/" -x base-system); do + containsElement "$pkg" "${pkg_blacklist[@]}" || base_packages+=($pkg) +done +declare -a additional_packages + +copy_configuration() { + mkdir -p "${config_path}" + local config="${config_path}/config" + echo "lxc.uts.name = ${name}" >> "${config}" + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null \ + || echo "lxc.rootfs.path = ${rootfs_path}" >> "${config}" + + # Detect if were in a UserNS and include the right config + if [ -z "${LXC_MAPPED_GID+x}" ] || [ -z "${LXC_MAPPED_UID+x}" ]; then + echo "lxc.include = ${userns_config}" >> "${config}" + else + echo "lxc.include = ${shared_config}" >> "${config}" + fi + + if [ $? -ne 0 ]; then + echo "Failed to configure container" + return 1 + fi + return 0 +} + +install_void() { + if ! yes | xbps-install -Sy -R http://repo2.voidlinux.eu/current -r "${rootfs_path}" "${base_packages[@]}" + then + echo "Failed to install container packages" + return 1 + fi +} + +usage() { + cat < [-p|--path=] [-a|--arch=] + [-r|--root_password=] [-P|--packages=] [-h|--help] + +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created (${default_path}) + --rootfs path for actual container rootfs, (${default_path}/rootfs) + -P,--packages preinstall additional packages, comma-separated list + -c,--config use specified pacman config when installing container packages + -a,--arch use specified architecture instead of host's architecture + -r,--root_password set container root password + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:P:n:c:r: -l help,rootfs:,path:,packages:,name:,config:,root_password:,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;; + -n|--name) name=${2}; shift 2;; + -c|--config) config_path=${2}; shift 2;; + --rootfs) rootfs_path=${2}; shift 2;; + -P|--packages) additional_packages=${2}; shift 2;; + -r|--root_password) root_passwd=${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 [ -z "${name}" ]; then + echo "missing required 'name' parameter" + exit 1 +fi + +type xbps-install >/dev/null 2>&1 +if [ ${?} -ne 0 ]; then + echo "'xbps-install' command is missing." +fi +type xbps-query >/dev/null 2>&1 +if [ ${?} -ne 0 ]; then + echo "'xbps-query' command is missing." +fi + +if [ -z "${rootfs_path}" ]; then + rootfs_path="${path}/rootfs" +fi +config_path="${path}" + +revert() { + echo "Interrupted, cleaning up" + lxc-destroy -n "${name}" + rm -rf "${path:?}/${name}" + rm -rf "${default_path:?}/${name}" + exit 1 +} +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "Failed to write configuration file" + rm -rf "${config_path}" + exit 1 +fi + +if [ ${#additional_packages[@]} -gt 0 ]; then + split_string "${additional_packages}" + base_packages+=(${result[@]}) +fi + +mkdir -p "${rootfs_path}" +install_void +if [ ${?} -ne 0 ]; then + echo "Failed to install Void Linux" + rm -rf "${config_path}" "${path}" + exit 1 +fi + + + +if [ -n "${root_passwd}" ]; then + echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd +fi + +cat << EOF +Void Linux Container ${name} has been successfully created. The configuration is +stored in ${config_path}/config. Please refer to https://wiki.voidlinux.eu for +information regarding Void Linux. +EOF diff -Nru lxc-2.0.8/templates/Makefile.am lxc-2.1.0/templates/Makefile.am --- lxc-2.0.8/templates/Makefile.am 2017-05-11 17:23:06.000000000 +0000 +++ lxc-2.1.0/templates/Makefile.am 2017-09-06 02:32:37.000000000 +0000 @@ -10,13 +10,17 @@ lxc-debian \ lxc-download \ lxc-fedora \ + lxc-fedora-legacy \ lxc-gentoo \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ lxc-plamo \ + lxc-pld \ lxc-slackware \ lxc-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud \ - lxc-sparclinux + lxc-sparclinux \ + lxc-voidlinux \ + lxc-sabayon diff -Nru lxc-2.0.8/templates/Makefile.in lxc-2.1.0/templates/Makefile.in --- lxc-2.0.8/templates/Makefile.in 2017-05-11 17:23:10.000000000 +0000 +++ lxc-2.1.0/templates/Makefile.in 2017-09-06 02:32:42.000000000 +0000 @@ -104,9 +104,9 @@ CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = lxc-alpine lxc-altlinux lxc-archlinux lxc-busybox \ lxc-centos lxc-cirros lxc-debian lxc-download lxc-fedora \ - lxc-gentoo lxc-openmandriva lxc-opensuse lxc-oracle lxc-plamo \ - lxc-slackware lxc-sshd lxc-ubuntu lxc-ubuntu-cloud \ - lxc-sparclinux + lxc-fedora-legacy lxc-gentoo lxc-openmandriva lxc-opensuse \ + lxc-oracle lxc-plamo lxc-pld lxc-slackware lxc-sshd lxc-ubuntu \ + lxc-ubuntu-cloud lxc-sparclinux lxc-voidlinux lxc-sabayon CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -161,12 +161,14 @@ $(srcdir)/lxc-altlinux.in $(srcdir)/lxc-archlinux.in \ $(srcdir)/lxc-busybox.in $(srcdir)/lxc-centos.in \ $(srcdir)/lxc-cirros.in $(srcdir)/lxc-debian.in \ - $(srcdir)/lxc-download.in $(srcdir)/lxc-fedora.in \ - $(srcdir)/lxc-gentoo.in $(srcdir)/lxc-openmandriva.in \ - $(srcdir)/lxc-opensuse.in $(srcdir)/lxc-oracle.in \ - $(srcdir)/lxc-plamo.in $(srcdir)/lxc-slackware.in \ - $(srcdir)/lxc-sparclinux.in $(srcdir)/lxc-sshd.in \ - $(srcdir)/lxc-ubuntu-cloud.in $(srcdir)/lxc-ubuntu.in + $(srcdir)/lxc-download.in $(srcdir)/lxc-fedora-legacy.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-pld.in $(srcdir)/lxc-sabayon.in \ + $(srcdir)/lxc-slackware.in $(srcdir)/lxc-sparclinux.in \ + $(srcdir)/lxc-sshd.in $(srcdir)/lxc-ubuntu-cloud.in \ + $(srcdir)/lxc-ubuntu.in $(srcdir)/lxc-voidlinux.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -371,16 +373,20 @@ lxc-debian \ lxc-download \ lxc-fedora \ + lxc-fedora-legacy \ lxc-gentoo \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ lxc-plamo \ + lxc-pld \ lxc-slackware \ lxc-sshd \ lxc-ubuntu \ lxc-ubuntu-cloud \ - lxc-sparclinux + lxc-sparclinux \ + lxc-voidlinux \ + lxc-sabayon all: all-am @@ -432,6 +438,8 @@ 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-fedora-legacy: $(top_builddir)/config.status $(srcdir)/lxc-fedora-legacy.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 @@ -442,6 +450,8 @@ 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-pld: $(top_builddir)/config.status $(srcdir)/lxc-pld.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-slackware: $(top_builddir)/config.status $(srcdir)/lxc-slackware.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-sshd: $(top_builddir)/config.status $(srcdir)/lxc-sshd.in @@ -452,6 +462,10 @@ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ lxc-sparclinux: $(top_builddir)/config.status $(srcdir)/lxc-sparclinux.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +lxc-voidlinux: $(top_builddir)/config.status $(srcdir)/lxc-voidlinux.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +lxc-sabayon: $(top_builddir)/config.status $(srcdir)/lxc-sabayon.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-templatesSCRIPTS: $(templates_SCRIPTS) @$(NORMAL_INSTALL) @list='$(templates_SCRIPTS)'; test -n "$(templatesdir)" || list=; \