lxc-3.0.3/0000755061062106075000000000000013375633377007335 500000000000000lxc-3.0.3/AUTHORS0000644061062106075000000000002113375633353010310 00000000000000IBM Corporation. lxc-3.0.3/autogen.sh0000755061062106075000000000203613375633353011251 00000000000000#!/bin/sh # # 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 set -x test -d autom4te.cache && rm -rf autom4te.cache libtoolize || exit 1 aclocal -I config || exit 1 autoheader || exit 1 autoconf || exit 1 automake --add-missing --copy || exit 1 lxc-3.0.3/src/0000755061062106075000000000000013375633377010124 500000000000000lxc-3.0.3/src/Makefile.in0000644061062106075000000005056613375633363012120 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ $(top_srcdir)/config/ax_check_compile_flag.m4 \ $(top_srcdir)/config/ax_check_link_flag.m4 \ $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/libtool.m4 \ $(top_srcdir)/config/ltoptions.m4 \ $(top_srcdir)/config/ltsugar.m4 \ $(top_srcdir)/config/ltversion.m4 \ $(top_srcdir)/config/lt~obsolete.m4 \ $(top_srcdir)/config/tls.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CAP_LIBS = @CAP_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIR = @DATADIR@ DEFAULT_CGROUP_PATTERN = @DEFAULT_CGROUP_PATTERN@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOCDIR = @DOCDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ HAVE_DOXYGEN = @HAVE_DOXYGEN@ INCLUDEDIR = @INCLUDEDIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LXCBINHOOKDIR = @LXCBINHOOKDIR@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_ABI = @LXC_ABI@ LXC_ABI_MAJOR = @LXC_ABI_MAJOR@ LXC_ABI_MICRO = @LXC_ABI_MICRO@ LXC_ABI_MINOR = @LXC_ABI_MINOR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ LXC_DEVEL = @LXC_DEVEL@ LXC_DISTRO_SYSCONF = @LXC_DISTRO_SYSCONF@ LXC_GENERATE_DATE = @LXC_GENERATE_DATE@ LXC_GLOBAL_CONF = @LXC_GLOBAL_CONF@ LXC_USERNIC_CONF = @LXC_USERNIC_CONF@ LXC_USERNIC_DB = @LXC_USERNIC_DB@ LXC_VERSION = @LXC_VERSION@ LXC_VERSION_BASE = @LXC_VERSION_BASE@ LXC_VERSION_BETA = @LXC_VERSION_BETA@ LXC_VERSION_MAJOR = @LXC_VERSION_MAJOR@ LXC_VERSION_MICRO = @LXC_VERSION_MICRO@ LXC_VERSION_MINOR = @LXC_VERSION_MINOR@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PAM_CFLAGS = @PAM_CFLAGS@ PAM_LIBS = @PAM_LIBS@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PREFIX = @PREFIX@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RUNTIME_PATH = @RUNTIME_PATH@ SBINDIR = @SBINDIR@ SECCOMP_CFLAGS = @SECCOMP_CFLAGS@ SECCOMP_LIBS = @SECCOMP_LIBS@ SED = @SED@ SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSCONFDIR = @SYSCONFDIR@ SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bashcompdir = @bashcompdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ db2xman = @db2xman@ docdir = @docdir@ docdtd = @docdtd@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pamdir = @pamdir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = lxc tests all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile config.h installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-hdr \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: lxc-3.0.3/src/Makefile.am0000644061062106075000000000002413375633353012066 00000000000000SUBDIRS = lxc tests lxc-3.0.3/src/config.h.in0000644061062106075000000001656213375633362012073 00000000000000/* src/config.h.in. Generated from configure.ac by autoheader. */ /* "Prefix for shared files." */ #undef DATADIR /* Define to 1 if you have the `confstr' function. */ #undef HAVE_CONFSTR /* Define to 1 if you have the declaration of `PR_CAPBSET_DROP', and to 0 if 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 /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the `endmntent' function. */ #undef HAVE_ENDMNTENT /* Define to 1 if you have the `faccessat' function. */ #undef HAVE_FACCESSAT /* Define to 1 if you have the `fgetln' function. */ #undef HAVE_FGETLN /* Define to 1 if you have the `getgrgid_r' function. */ #undef HAVE_GETGRGID_R /* Define to 1 if you have the `getline' function. */ #undef HAVE_GETLINE /* Define to 1 if you have the `getsubopt' function. */ #undef HAVE_GETSUBOPT /* Define to 1 if you have the `gettid' function. */ #undef HAVE_GETTID /* Define to 1 if you have the `hasmntopt' function. */ #undef HAVE_HASMNTOPT /* Have ifaddrs.h */ #undef HAVE_IFADDRS_H /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `keyctl' function. */ #undef HAVE_KEYCTL /* Define to 1 if you have the `cap' library (-lcap). */ #undef HAVE_LIBCAP /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the `seccomp' library (-lseccomp). */ #undef HAVE_LIBSECCOMP /* Define to 1 if you have the `util' library (-lutil). */ #undef HAVE_LIBUTIL /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_GENETLINK_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETLINK_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_UNISTD_H /* Define to 1 if you have the `memfd_create' function. */ #undef HAVE_MEMFD_CREATE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY /* 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 `prlimit64' function. */ #undef HAVE_PRLIMIT64 /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the `pthread_setcancelstate' function. */ #undef HAVE_PTHREAD_SETCANCELSTATE /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H /* Define to 1 if you have the `rand_r' function. */ #undef HAVE_RAND_R /* Define to 1 if the system has the type `scmp_filter_ctx'. */ #undef HAVE_SCMP_FILTER_CTX /* Define to 1 if you have the `sethostname' function. */ #undef HAVE_SETHOSTNAME /* Define to 1 if you have the `setmntent' function. */ #undef HAVE_SETMNTENT /* Define to 1 if you have the `setns' function. */ #undef HAVE_SETNS /* Have static libcap */ #undef HAVE_STATIC_LIBCAP /* Define to 1 if you have the `statvfs' function. */ #undef HAVE_STATVFS /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror_r' function. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if the system has the type `struct rtnl_link_stats64'. */ #undef HAVE_STRUCT_RTNL_LINK_STATS64 /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MEMFD_H /* 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 /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIMERFD_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define if the compiler supports __thread */ #undef HAVE_TLS /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `unshare' function. */ #undef HAVE_UNSHARE /* Define to 1 if you have the `utmpxname' function. */ #undef HAVE_UTMPXNAME /* Define to 1 if you have the header file. */ #undef HAVE_UTMPX_H /* bionic libc */ #undef IS_BIONIC /* Have cap_get_file */ #undef LIBCAP_SUPPORTS_FILE_CAPABILITIES /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #undef MAJOR_IN_MKDEV /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #undef MAJOR_IN_SYSMACROS /* Enabling mutex debugging */ #undef MUTEX_DEBUGGING /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Version number of package */ #undef VERSION /* Define to 1 if on MINIX. */ #undef _MINIX /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE /* Define to the compiler TLS keyword */ #undef thread_local lxc-3.0.3/src/include/0000755061062106075000000000000013375633377011547 500000000000000lxc-3.0.3/src/include/strlcpy.h0000644061062106075000000000165713375633353013343 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #ifndef _STRLCPY_H #define _STRLCPY_H #include extern size_t strlcpy(char *, const char *, size_t); #endif lxc-3.0.3/src/include/netns_ifaddrs.c0000644061062106075000000003102013375633353014444 00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "nl.h" #include "macro.h" #include "netns_ifaddrs.h" #ifndef NETNS_RTA #define NETNS_RTA(r) \ ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) #endif #define IFADDRS_HASH_SIZE 64 #define __NETLINK_ALIGN(len) (((len) + 3) & ~3) #define __NLMSG_OK(nlh, end) \ ((char *)(end) - (char *)(nlh) >= sizeof(struct nlmsghdr)) #define __NLMSG_NEXT(nlh) \ (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len)) #define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr))) #define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len) #define __NLMSG_RTA(nlh, len) \ ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \ __NETLINK_ALIGN(len))) #define __RTA_DATALEN(rta) ((rta)->rta_len - sizeof(struct rtattr)) #define __RTA_NEXT(rta) \ (struct rtattr *)((char *)(rta) + __NETLINK_ALIGN((rta)->rta_len)) #define __RTA_OK(nlh, end) \ ((char *)(end) - (char *)(rta) >= sizeof(struct rtattr)) #define __NLMSG_RTAOK(rta, nlh) __RTA_OK(rta, __NLMSG_DATAEND(nlh)) #define __IN6_IS_ADDR_LINKLOCAL(a) \ ((((uint8_t *)(a))[0]) == 0xfe && (((uint8_t *)(a))[1] & 0xc0) == 0x80) #define __IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *)(a))[1] & 0xf) == 0x2)) #define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr))) /* getifaddrs() reports hardware addresses with PF_PACKET that implies struct * sockaddr_ll. But e.g. Infiniband socket address length is longer than * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr - * callers should be able to still use it. */ struct sockaddr_ll_hack { unsigned short sll_family, sll_protocol; int sll_ifindex; unsigned short sll_hatype; unsigned char sll_pkttype, sll_halen; unsigned char sll_addr[24]; }; union sockany { struct sockaddr sa; struct sockaddr_ll_hack ll; struct sockaddr_in v4; struct sockaddr_in6 v6; }; struct ifaddrs_storage { struct netns_ifaddrs ifa; struct ifaddrs_storage *hash_next; union sockany addr, netmask, ifu; unsigned int index; char name[IFNAMSIZ + 1]; }; struct ifaddrs_ctx { struct ifaddrs_storage *first; struct ifaddrs_storage *last; struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE]; }; static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex) { uint8_t *dst; size_t len; switch (af) { case AF_INET: dst = (uint8_t *)&sa->v4.sin_addr; len = 4; break; case AF_INET6: dst = (uint8_t *)&sa->v6.sin6_addr; len = 16; if (__IN6_IS_ADDR_LINKLOCAL(addr) || __IN6_IS_ADDR_MC_LINKLOCAL(addr)) sa->v6.sin6_scope_id = ifindex; break; default: return; } if (addrlen < len) return; sa->sa.sa_family = af; memcpy(dst, addr, len); *r = &sa->sa; } static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen) { uint8_t addr[16] = {0}; int i; if ((size_t)prefixlen > 8 * sizeof(addr)) prefixlen = 8 * sizeof(addr); i = prefixlen / 8; memset(addr, 0xff, i); if ((size_t)i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8)); copy_addr(r, af, sa, addr, sizeof(addr), 0); } static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype) { if (addrlen > sizeof(sa->ll.sll_addr)) return; sa->ll.sll_family = AF_PACKET; sa->ll.sll_ifindex = ifindex; sa->ll.sll_hatype = hatype; sa->ll.sll_halen = addrlen; memcpy(sa->ll.sll_addr, addr, addrlen); *r = &sa->sa; } static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h) { struct ifaddrs_storage *ifs, *ifs0; struct rtattr *rta; int stats_len = 0; struct ifinfomsg *ifi = __NLMSG_DATA(h); struct ifaddrmsg *ifa = __NLMSG_DATA(h); struct ifaddrs_ctx *ctx = pctx; if (h->nlmsg_type == RTM_NEWLINK) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { #if HAVE_STRUCT_RTNL_LINK_STATS64 if (rta->rta_type != IFLA_STATS64) #else if (rta->rta_type != IFLA_STATS) #endif continue; stats_len = __RTA_DATALEN(rta); break; } #pragma GCC diagnostic pop } else { for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next) if (ifs0->index == ifa->ifa_index) break; if (!ifs0) return 0; } ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); if (!ifs) { errno = ENOMEM; return -1; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (h->nlmsg_type == RTM_NEWLINK) { ifs->index = ifi->ifi_index; ifs->ifa.ifa_ifindex = ifi->ifi_index; ifs->ifa.ifa_flags = ifi->ifi_flags; for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { switch (rta->rta_type) { case IFLA_IFNAME: if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { memcpy(ifs->name, __RTA_DATA(rta), __RTA_DATALEN(rta)); ifs->ifa.ifa_name = ifs->name; } break; case IFLA_ADDRESS: copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); break; case IFLA_BROADCAST: copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); break; #if HAVE_STRUCT_RTNL_LINK_STATS64 case IFLA_STATS64: ifs->ifa.ifa_stats_type = IFLA_STATS64; #else case IFLA_STATS: ifs->ifa.ifa_stats_type = IFLA_STATS; #endif memcpy(&ifs->ifa.ifa_stats, __RTA_DATA(rta), __RTA_DATALEN(rta)); break; case IFLA_MTU: memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta), sizeof(int)); break; case IFLA_TARGET_NETNSID: *netnsid_aware = true; break; } } if (ifs->ifa.ifa_name) { unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE; ifs->hash_next = ctx->hash[bucket]; ctx->hash[bucket] = ifs; } } else { ifs->ifa.ifa_name = ifs0->ifa.ifa_name; ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu; ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex; ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags; for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { switch (rta->rta_type) { case IFA_ADDRESS: /* If ifa_addr is already set we, received an * IFA_LOCAL before so treat this as * destination address. */ if (ifs->ifa.ifa_addr) copy_addr(&ifs->ifa.__ifa_dstaddr, ifa->ifa_family, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); else copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_BROADCAST: copy_addr(&ifs->ifa.__ifa_broadaddr, ifa->ifa_family, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_LOCAL: /* If ifa_addr is set and we get IFA_LOCAL, * assume we have a point-to-point network. * Move address to correct field. */ if (ifs->ifa.ifa_addr) { ifs->ifu = ifs->addr; ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa; memset(&ifs->addr, 0, sizeof(ifs->addr)); } copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_LABEL: if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { memcpy(ifs->name, __RTA_DATA(rta), __RTA_DATALEN(rta)); ifs->ifa.ifa_name = ifs->name; } break; case IFA_TARGET_NETNSID: *netnsid_aware = true; break; } } if (ifs->ifa.ifa_addr) { gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen); ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen; } } #pragma GCC diagnostic pop if (ifs->ifa.ifa_name) { if (!ctx->first) ctx->first = ifs; if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa; ctx->last = ifs; } else { free(ifs); } return 0; } static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr) { int ret; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = nlmsghdr, .iov_len = nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; ret = sendmsg(fd, &msg, MSG_NOSIGNAL); if (ret < 0) return -1; return ret; } static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af, __s32 netns_id, bool *netnsid_aware, int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h), void *ctx) { int r, property, ret; char *buf; struct nlmsghdr *hdr; struct ifinfomsg *ifi_msg; struct ifaddrmsg *ifa_msg; union { uint8_t buf[8192]; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; struct nlmsghdr reply; } u; char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + __NETLINK_ALIGN(sizeof(struct ifinfomsg)) + __NETLINK_ALIGN(1024)] = {0}; char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) + __NETLINK_ALIGN(1024)] = {0}; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (type == RTM_GETLINK) { buf = getlink_buf; hdr = (struct nlmsghdr *)buf; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg)); ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr); ifi_msg->ifi_family = af; property = IFLA_TARGET_NETNSID; } else if (type == RTM_GETADDR) { buf = getaddr_buf; hdr = (struct nlmsghdr *)buf; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg)); ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr); ifa_msg->ifa_family = af; property = IFA_TARGET_NETNSID; } else { errno = EINVAL; return -1; } #pragma GCC diagnostic pop hdr->nlmsg_type = type; hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; hdr->nlmsg_pid = 0; hdr->nlmsg_seq = seq; if (netns_id >= 0) addattr(hdr, 1024, property, &netns_id, sizeof(netns_id)); r = __ifaddrs_netlink_send(fd, hdr); if (r < 0) return -1; for (;;) { r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT); if (r <= 0) return -1; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]); hdr = __NLMSG_NEXT(hdr)) { if (hdr->nlmsg_type == NLMSG_DONE) return 0; if (hdr->nlmsg_type == NLMSG_ERROR) { errno = EINVAL; return -1; } ret = cb(ctx, netnsid_aware, hdr); if (ret) return ret; } #pragma GCC diagnostic pop } } static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id, bool *netnsid_aware, int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h), void *ctx) { int fd, r, saved_errno; bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false; fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); if (fd < 0) return -1; r = setsockopt(fd, SOL_NETLINK, NETLINK_DUMP_STRICT_CHK, &(int){1}, sizeof(int)); if (r < 0 && netns_id >= 0) { close(fd); *netnsid_aware = false; return -1; } r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id, &getlink_netnsid_aware, cb, ctx); if (!r) r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id, &getaddr_netnsid_aware, cb, ctx); saved_errno = errno; close(fd); errno = saved_errno; if (getaddr_netnsid_aware && getlink_netnsid_aware) *netnsid_aware = true; else *netnsid_aware = false; return r; } void netns_freeifaddrs(struct netns_ifaddrs *ifp) { struct netns_ifaddrs *n; while (ifp) { n = ifp->ifa_next; free(ifp); ifp = n; } } int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id, bool *netnsid_aware) { int r, saved_errno; struct ifaddrs_ctx _ctx; struct ifaddrs_ctx *ctx = &_ctx; memset(ctx, 0, sizeof *ctx); r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware, nl_msg_to_ifaddr, ctx); saved_errno = errno; if (r < 0) netns_freeifaddrs(&ctx->first->ifa); else *ifap = &ctx->first->ifa; errno = saved_errno; return r; } lxc-3.0.3/src/include/prlimit.h0000644061062106075000000000330013375633353013306 00000000000000/* * 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 lxc-3.0.3/src/include/openpty.c0000644061062106075000000000366413375633353013334 00000000000000 /* * openpty: glibc implementation * * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. * * Authors: * Zack Weinberg , 1998. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * 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 _XOPEN_SOURCE /* See feature_test_macros(7) */ #include #include #include #include #include #include #include #include #include #define _PATH_DEVPTMX "/dev/ptmx" int openpty (int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { char buf[PATH_MAX]; int master, slave; master = open(_PATH_DEVPTMX, O_RDWR); if (master == -1) return -1; if (grantpt(master)) goto fail; if (unlockpt(master)) goto fail; if (ptsname_r(master, buf, sizeof buf)) goto fail; slave = open(buf, O_RDWR | O_NOCTTY); if (slave == -1) goto fail; /* XXX Should we ignore errors here? */ if (termp) tcsetattr(slave, TCSAFLUSH, termp); if (winp) ioctl(slave, TIOCSWINSZ, winp); *amaster = master; *aslave = slave; if (name != NULL) strcpy(name, buf); return 0; fail: close(master); return -1; } lxc-3.0.3/src/include/getgrgid_r.h0000644061062106075000000000205413375633353013750 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #ifndef _GETGRGID_R_H #define _GETGRGID_R_H #include #include #include extern int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res); #endif /* _GETGRGID_R_H */ lxc-3.0.3/src/include/lxcmntent.c0000644061062106075000000001200613375633353013640 00000000000000/* Utilities for reading/writing fstab, mtab, etc. * Copyright (C) 1995-2000, 2001, 2002, 2003, 2006 * Free Software Foundation, Inc. * This file is part of the GNU C Library. * * The GNU C Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * The GNU C Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include "../lxc/macro.h" /* Since the values in a line are separated by spaces, a name cannot * contain a space. Therefore some programs encode spaces in names * by the strings "\040". We undo the encoding when reading an entry. * The decoding happens in place. */ static char *decode_name(char *buf) { char *rp = buf; char *wp = buf; do { if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0') { /* \040 is a SPACE. */ *wp++ = ' '; rp += 3; } else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '1') { /* \011 is a TAB. */ *wp++ = '\t'; rp += 3; } else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2') { /* \012 is a NEWLINE. */ *wp++ = '\n'; rp += 3; } else if (rp[0] == '\\' && rp[1] == '\\') { /* We have to escape \\ to be able to represent all characters. */ *wp++ = '\\'; rp += 1; } else if (rp[0] == '\\' && rp[1] == '1' && rp[2] == '3' && rp[3] == '4') { /* \134 is also \\. */ *wp++ = '\\'; rp += 3; } else { *wp++ = *rp; } } while (*rp++ != '\0'); return buf; } /* Read one mount table entry from STREAM. Returns a pointer to storage * reused on the next call, or null for EOF or error (use feof/ferror to check). */ struct mntent *getmntent_r(FILE *stream, struct mntent *mp, char *buffer, int bufsiz) { char *cp; char *head; do { char *end_ptr; if (!fgets(buffer, bufsiz, stream)) return NULL; end_ptr = strchr(buffer, '\n'); if (end_ptr != NULL) { /* chop newline */ *end_ptr = '\0'; } else { /* Not the whole line was read. Do it now but forget it. */ char tmp[1024] = {0}; while (fgets(tmp, sizeof tmp, stream)) if (strchr(tmp, '\n') != NULL) break; } head = buffer + strspn(buffer, " \t"); /* skip empty lines and comment lines: */ } while (head[0] == '\0' || head[0] == '#'); cp = strsep(&head, " \t"); mp->mnt_fsname = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_dir = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_type = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_opts = cp ? decode_name(cp) : (char *)""; if (head) { int ret = sscanf(head, " %d %d ", &mp->mnt_freq, &mp->mnt_passno); switch (ret) { case 0: mp->mnt_freq = 0; case 1: mp->mnt_passno = 0; case 2: break; } } else { mp->mnt_freq = 0; } return mp; } struct mntent *getmntent(FILE *stream) { static struct mntent m; static char *getmntent_buffer; if (!getmntent_buffer) { getmntent_buffer = (char *)malloc(LXC_MAX_BUFFER); if (!getmntent_buffer) return NULL; } return getmntent_r(stream, &m, getmntent_buffer, LXC_MAX_BUFFER); } /* Prepare to begin reading and/or writing mount table entries from the * beginning of FILE. MODE is as for `fopen'. */ FILE *setmntent(const char *file, const char *mode) { /* Extend the mode parameter with "c" to disable cancellation in the * I/O functions and "e" to set FD_CLOEXEC. */ size_t modelen = strlen(mode); char *newmode; newmode = alloca(modelen + 3); memcpy(newmode, mode, modelen); memcpy(newmode + modelen, "ce", 3); return fopen (file, newmode); } /* Close a stream opened with `setmntent'. */ int endmntent(FILE *stream) { /* SunOS 4.x allows for NULL stream */ if (stream) fclose(stream); /* SunOS 4.x says to always return 1 */ return 1; } /* Search MNT->mnt_opts for an option matching OPT. * Returns the address of the substring, or null if none found. */ char *hasmntopt(const struct mntent *mnt, const char *opt) { const size_t optlen = strlen(opt); char *rest = mnt->mnt_opts, *p; while ((p = strstr(rest, opt))) { if ((p == rest || p[-1] == ',') && (p[optlen] == '\0' || p[optlen] == '=' || p[optlen] == ',')) return p; rest = strchr(p, ','); if (!rest) break; ++rest; } return NULL; } lxc-3.0.3/src/include/strlcat.c0000644061062106075000000000210213375633353013274 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #include #include #include #ifndef HAVE_STRLCPY #include "strlcpy.h" #endif size_t strlcat(char *d, const char *s, size_t n) { size_t l = strnlen(d, n); if (l == n) return l + strlen(s); return l + strlcpy(d + l, s, n - l); } lxc-3.0.3/src/include/getline.c0000644061062106075000000000415213375633353013256 00000000000000/* * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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 AUTHOR 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 AUTHOR 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 /* * Emulate glibc getline() via BSD fgetln(). * Note that outsize is not changed unless memory is allocated. */ ssize_t getline(char **outbuf, size_t *outsize, FILE *fp) { size_t len; char *buf; buf = fgetln(fp, &len); if (buf == NULL) return (-1); /* Assumes realloc() accepts NULL for ptr (C99) */ if (*outbuf == NULL || *outsize < len + 1) { void *tmp = realloc(*outbuf, len + 1); if (tmp == NULL) return (-1); *outbuf = tmp; *outsize = len + 1; } memcpy(*outbuf, buf, len); (*outbuf)[len] = '\0'; return (len); } lxc-3.0.3/src/include/strlcpy.c0000644061062106075000000000204013375633353013321 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #include size_t strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); if (size) { size_t len = (ret >= size) ? size - 1 : ret; memcpy(dest, src, len); dest[len] = '\0'; } return ret; } lxc-3.0.3/src/include/strlcat.h0000644061062106075000000000166313375633353013314 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #ifndef _STRLCAT_H #define _STRLCAT_H #include extern size_t strlcat(char *d, const char *s, size_t n); #endif lxc-3.0.3/src/include/getline.h0000644061062106075000000000306513375633353013265 00000000000000/* * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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 AUTHOR 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 AUTHOR 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 _GETLINE_H #define _GETLINE_H #include extern ssize_t getline(char **outbuf, size_t *outsize, FILE *fp); #endif lxc-3.0.3/src/include/prlimit.c0000644061062106075000000000510713375633353013310 00000000000000/* * 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; } lxc-3.0.3/src/include/openpty.h0000644061062106075000000000246313375633353013335 00000000000000/* * openpty: glibc implementation * * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. * * Authors: * Zack Weinberg , 1998. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * 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 _OPENPTY_H #define _OPENPTY_H #include #include /* Create pseudo tty master slave pair with NAME and set terminal attributes according to TERMP and WINP and return handles for both ends in AMASTER and ASLAVE. */ extern int openpty (int *__amaster, int *__aslave, char *__name, const struct termios *__termp, const struct winsize *__winp); #endif lxc-3.0.3/src/include/getgrgid_r.c0000644061062106075000000002300513375633353013742 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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. * * This function has been copied from musl. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #define LOGIN_NAME_MAX 256 #define NSCDVERSION 2 #define GETPWBYNAME 0 #define GETPWBYUID 1 #define GETGRBYNAME 2 #define GETGRBYGID 3 #define GETINITGR 15 #define REQVERSION 0 #define REQTYPE 1 #define REQKEYLEN 2 #define REQ_LEN 3 #define PWVERSION 0 #define PWFOUND 1 #define PWNAMELEN 2 #define PWPASSWDLEN 3 #define PWUID 4 #define PWGID 5 #define PWGECOSLEN 6 #define PWDIRLEN 7 #define PWSHELLLEN 8 #define PW_LEN 9 #define GRVERSION 0 #define GRFOUND 1 #define GRNAMELEN 2 #define GRPASSWDLEN 3 #define GRGID 4 #define GRMEMCNT 5 #define GR_LEN 6 #define INITGRVERSION 0 #define INITGRFOUND 1 #define INITGRNGRPS 2 #define INITGR_LEN 3 #define FIX(x) (gr->gr_##x = gr->gr_##x - line + buf) static unsigned atou(char **s) { unsigned x; for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0'); return x; } static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem, struct group **res) { ssize_t l; char *s, *mems; size_t i; int rv = 0; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif for (;;) { if ((l = getline(line, size, f)) < 0) { rv = ferror(f) ? errno : 0; free(*line); *line = 0; gr = 0; goto end; } line[0][l - 1] = 0; s = line[0]; gr->gr_name = s++; if (!(s = strchr(s, ':'))) continue; *s++ = 0; gr->gr_passwd = s; if (!(s = strchr(s, ':'))) continue; *s++ = 0; gr->gr_gid = atou(&s); if (*s != ':') continue; *s++ = 0; mems = s; break; } for (*nmem = !!*s; *s; s++) if (*s == ',') ++*nmem; free(*mem); *mem = calloc(sizeof(char *), *nmem + 1); if (!*mem) { rv = errno; free(*line); *line = 0; gr = 0; goto end; } if (*mems) { mem[0][0] = mems; for (s = mems, i = 0; *s; s++) if (*s == ',') *s++ = 0, mem[0][++i] = s; mem[0][++i] = 0; } else { mem[0][0] = 0; } gr->gr_mem = *mem; end: #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif *res = gr; if (rv) errno = rv; return rv; } static char *itoa(char *p, uint32_t x) { // number of digits in a uint32_t + NUL p += 11; *--p = 0; do { *--p = '0' + x % 10; x /= 10; } while (x); return p; } static const struct { short sun_family; char sun_path[21]; } addr = {AF_UNIX, "/var/run/nscd/socket"}; static FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap) { size_t i; int fd; FILE *f = 0; int32_t req_buf[REQ_LEN] = {NSCDVERSION, req, strnlen(key, LOGIN_NAME_MAX) + 1}; struct msghdr msg = {.msg_iov = (struct iovec[]){{&req_buf, sizeof(req_buf)}, {(char *)key, strlen(key) + 1}}, .msg_iovlen = 2}; int errno_save = errno; *swap = 0; retry: memset(buf, 0, len); buf[0] = NSCDVERSION; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return NULL; if (!(f = fdopen(fd, "r"))) { close(fd); return 0; } if (req_buf[2] > LOGIN_NAME_MAX) return f; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { /* If there isn't a running nscd we simulate a "not found" * result and the caller is responsible for calling * fclose on the (unconnected) socket. The value of * errno must be left unchanged in this case. */ if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) { errno = errno_save; return f; } goto error; } if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0) goto error; if (!fread(buf, len, 1, f)) { /* If the VERSION entry mismatches nscd will disconnect. The * most likely cause is that the endianness mismatched. So, we * byteswap and try once more. (if we already swapped, just * fail out) */ if (ferror(f)) goto error; if (!*swap) { fclose(f); for (i = 0; i < sizeof(req_buf) / sizeof(req_buf[0]); i++) { req_buf[i] = bswap_32(req_buf[i]); } *swap = 1; goto retry; } else { errno = EIO; goto error; } } if (*swap) { for (i = 0; i < len / sizeof(buf[0]); i++) { buf[i] = bswap_32(buf[i]); } } /* The first entry in every nscd response is the version number. This * really shouldn't happen, and is evidence of some form of malformed * response. */ if (buf[0] != NSCDVERSION) { errno = EIO; goto error; } return f; error: fclose(f); return 0; } static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res) { FILE *f; int rv = 0; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif *res = 0; f = fopen("/etc/group", "rbe"); if (!f) { rv = errno; goto done; } while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) { if ((name && !strcmp(name, (*res)->gr_name)) || (!name && (*res)->gr_gid == gid)) { break; } } fclose(f); if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) { int32_t req = name ? GETGRBYNAME : GETGRBYGID; int32_t i; const char *key; int32_t groupbuf[GR_LEN] = {0}; size_t len = 0; size_t grlist_len = 0; char gidbuf[11] = {0}; int swap = 0; char *ptr; if (name) { key = name; } else { if (gid < 0 || gid > UINT32_MAX) { rv = 0; goto done; } key = itoa(gidbuf, gid); } f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap); if (!f) { rv = errno; goto done; } if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; } if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) { rv = EIO; goto cleanup_f; } if ((int64_t)groupbuf[GRNAMELEN] > (int64_t)(SIZE_MAX - groupbuf[GRPASSWDLEN])) { rv = ENOMEM; goto cleanup_f; } len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; for (i = 0; i < groupbuf[GRMEMCNT]; i++) { uint32_t name_len; if (fread(&name_len, sizeof name_len, 1, f) < 1) { rv = ferror(f) ? errno : EIO; goto cleanup_f; } if (swap) { name_len = bswap_32(name_len); } if (name_len > SIZE_MAX - grlist_len || name_len > SIZE_MAX - len) { rv = ENOMEM; goto cleanup_f; } len += name_len; grlist_len += name_len; } if (len > *size || !*buf) { char *tmp = realloc(*buf, len); if (!tmp) { rv = errno; goto cleanup_f; } *buf = tmp; *size = len; } if (!fread(*buf, len, 1, f)) { rv = ferror(f) ? errno : EIO; goto cleanup_f; } if (((size_t)(groupbuf[GRMEMCNT] + 1)) > *nmem) { if (((size_t)(groupbuf[GRMEMCNT] + 1)) > (SIZE_MAX / sizeof(char *))) { rv = ENOMEM; goto cleanup_f; } char **tmp = realloc(*mem, (groupbuf[GRMEMCNT] + 1) * sizeof(char *)); if (!tmp) { rv = errno; goto cleanup_f; } *mem = tmp; *nmem = groupbuf[GRMEMCNT] + 1; } if (groupbuf[GRMEMCNT]) { mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; for (ptr = mem[0][0], i = 0; ptr != mem[0][0] + grlist_len; ptr++) if (!*ptr) mem[0][++i] = ptr + 1; mem[0][i] = 0; if (i != groupbuf[GRMEMCNT]) { rv = EIO; goto cleanup_f; } } else { mem[0][0] = 0; } gr->gr_name = *buf; gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN]; gr->gr_gid = groupbuf[GRGID]; gr->gr_mem = *mem; if (gr->gr_passwd[-1] || gr->gr_passwd[groupbuf[GRPASSWDLEN] - 1]) { rv = EIO; goto cleanup_f; } if ((name && strcmp(name, gr->gr_name)) || (!name && gid != gr->gr_gid)) { rv = EIO; goto cleanup_f; } *res = gr; cleanup_f: fclose(f); goto done; } done: #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif if (rv) errno = rv; return rv; } static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) { char *line = 0; size_t len = 0; char **mem = 0; size_t nmem = 0; int rv = 0; size_t i; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res); if (*res && size < len + (nmem + 1) * sizeof(char *) + 32) { *res = 0; rv = ERANGE; } if (*res) { buf += (16 - (uintptr_t)buf) % 16; gr->gr_mem = (void *)buf; buf += (nmem + 1) * sizeof(char *); memcpy(buf, line, len); FIX(name); FIX(passwd); for (i = 0; mem[i]; i++) gr->gr_mem[i] = mem[i] - line + buf; gr->gr_mem[i] = 0; } free(mem); free(line); #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif if (rv) errno = rv; return rv; } int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) { return getgr_r(0, gid, gr, buf, size, res); } lxc-3.0.3/src/include/lxcmntent.h0000644061062106075000000000306413375633353013651 00000000000000/* Utilities for reading/writing fstab, mtab, etc. Copyright (C) 1995-2000, 2001, 2002, 2003, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public 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 _LXCMNTENT_H #define _LXCMNTENT_H #if IS_BIONIC struct mntent { char* mnt_fsname; char* mnt_dir; char* mnt_type; char* mnt_opts; int mnt_freq; int mnt_passno; }; extern struct mntent *getmntent (FILE *stream); extern struct mntent *getmntent_r (FILE *stream, struct mntent *mp, char *buffer, int bufsiz); #endif #if !defined(HAVE_SETMNTENT) || IS_BIONIC FILE *setmntent (const char *file, const char *mode); #endif #if !defined(HAVE_ENDMNTENT) || IS_BIONIC int endmntent (FILE *stream); #endif #if !defined(HAVE_HASMNTOPT) || IS_BIONIC extern char *hasmntopt (const struct mntent *mnt, const char *opt); #endif #endif lxc-3.0.3/src/include/netns_ifaddrs.h0000644061062106075000000000235513375633353014462 00000000000000#ifndef _LXC_NETNS_IFADDRS_H #define _LXC_NETNS_IFADDRS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include "netns_ifaddrs.h" struct netns_ifaddrs { struct netns_ifaddrs *ifa_next; /* Can - but shouldn't be - NULL. */ char *ifa_name; /* This field is not present struct ifaddrs. */ int ifa_ifindex; unsigned ifa_flags; /* This field is not present struct ifaddrs. */ int ifa_mtu; /* This field is not present struct ifaddrs. */ int ifa_prefixlen; struct sockaddr *ifa_addr; struct sockaddr *ifa_netmask; union { struct sockaddr *ifu_broadaddr; struct sockaddr *ifu_dstaddr; } ifa_ifu; /* These fields are not present struct ifaddrs. */ int ifa_stats_type; #if HAVE_STRUCT_RTNL_LINK_STATS64 struct rtnl_link_stats64 ifa_stats; #else struct rtnl_link_stats ifa_stats; #endif }; #define __ifa_broadaddr ifa_ifu.ifu_broadaddr #define __ifa_dstaddr ifa_ifu.ifu_dstaddr extern void netns_freeifaddrs(struct netns_ifaddrs *); extern int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id, bool *netnsid_aware); #ifdef __cplusplus } #endif #endif /* _LXC_NETNS_IFADDRS_H */ lxc-3.0.3/src/lxc/0000755061062106075000000000000013375633377010712 500000000000000lxc-3.0.3/src/lxc/rtnl.c0000644061062106075000000000443113375633353011751 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "config.h" #include "nl.h" #include "rtnl.h" extern int rtnetlink_open(struct rtnl_handler *handler) { return netlink_open(&handler->nlh, NETLINK_ROUTE); } extern int rtnetlink_close(struct rtnl_handler *handler) { return netlink_close(&handler->nlh); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" extern int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg) { return netlink_rcv(&handler->nlh, (struct nlmsg *)&rtnlmsg->nlmsghdr); } extern int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg) { return netlink_send(&handler->nlh, (struct nlmsg *)&rtnlmsg->nlmsghdr); } extern int rtnetlink_transaction(struct rtnl_handler *handler, struct rtnlmsg *request, struct rtnlmsg *answer) { return netlink_transaction(&handler->nlh, (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&answer->nlmsghdr); } #pragma GCC diagnostic pop extern struct rtnlmsg *rtnlmsg_alloc(size_t size) { /* size_t len; len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtnlmsghdr))) + size; return (struct rtnlmsg *)nlmsg_alloc(len); */ return NULL; } extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg) { free(rtnlmsg); } lxc-3.0.3/src/lxc/freezer.c0000644061062106075000000000510613375633353012434 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "cgroup.h" #include "commands.h" #include "config.h" #include "error.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "state.h" #include "string_utils.h" lxc_log_define(freezer, lxc); static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath) { int ret; char v[100]; struct cgroup_ops *cgroup_ops; const char *state = freeze ? "FROZEN" : "THAWED"; size_t state_len = 6; lxc_state_t new_state = freeze ? FROZEN : THAWED; cgroup_ops = cgroup_init(NULL); if (!cgroup_ops) return -1; ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, lxcpath); if (ret < 0) { cgroup_exit(cgroup_ops); ERROR("Failed to freeze %s", name); return -1; } for (;;) { ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, sizeof(v), name, lxcpath); if (ret < 0) { cgroup_exit(cgroup_ops); ERROR("Failed to get freezer state of %s", name); return -1; } v[99] = '\0'; v[lxc_char_right_gc(v, strlen(v))] = '\0'; ret = strncmp(v, state, state_len); if (ret == 0) { cgroup_exit(cgroup_ops); lxc_cmd_serve_state_clients(name, lxcpath, new_state); lxc_monitor_send_state(name, new_state, lxcpath); return 0; } sleep(1); } } int lxc_freeze(const char *name, const char *lxcpath) { lxc_cmd_serve_state_clients(name, lxcpath, FREEZING); lxc_monitor_send_state(name, FREEZING, lxcpath); return do_freeze_thaw(true, name, lxcpath); } int lxc_unfreeze(const char *name, const char *lxcpath) { return do_freeze_thaw(false, name, lxcpath); } lxc-3.0.3/src/lxc/attach.c0000644061062106075000000011467713375633353012254 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "attach.h" #include "caps.h" #include "cgroup.h" #include "commands.h" #include "conf.h" #include "config.h" #include "confile.h" #include "log.h" #include "lsm/lsm.h" #include "lxclock.h" #include "lxcseccomp.h" #include "macro.h" #include "mainloop.h" #include "namespace.h" #include "raw_syscalls.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #if HAVE_SYS_PERSONALITY_H #include #endif lxc_log_define(attach, lxc); /* 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 struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { int ret; bool found; FILE *proc_file; char proc_fn[LXC_PROC_STATUS_LEN]; size_t line_bufsz = 0; char *line = NULL; struct lxc_proc_context_info *info = NULL; /* Read capabilities. */ ret = snprintf(proc_fn, LXC_PROC_STATUS_LEN, "/proc/%d/status", pid); if (ret < 0 || ret >= LXC_PROC_STATUS_LEN) goto on_error; proc_file = fopen(proc_fn, "r"); if (!proc_file) { SYSERROR("Could not open %s", proc_fn); goto on_error; } info = calloc(1, sizeof(*info)); if (!info) { SYSERROR("Could not allocate memory"); fclose(proc_file); return NULL; } found = false; while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) { found = true; break; } } free(line); fclose(proc_file); if (!found) { ERROR("Could not read capability bounding set from %s", proc_fn); goto on_error; } info->lsm_label = lsm_process_label_get(pid); info->ns_inherited = 0; memset(info->ns_fd, -1, sizeof(int) * LXC_NS_MAX); return info; on_error: free(info); return NULL; } static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx) { int i; for (i = 0; i < LXC_NS_MAX; i++) { if (ctx->ns_fd[i] < 0) continue; close(ctx->ns_fd[i]); ctx->ns_fd[i] = -EBADF; } } static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx) { free(ctx->lsm_label); ctx->lsm_label = NULL; if (ctx->container) { lxc_container_put(ctx->container); ctx->container = NULL; } lxc_proc_close_ns_fd(ctx); free(ctx); } /** * in_same_namespace - Check whether two processes are in the same namespace. * @pid1 - PID of the first process. * @pid2 - PID of the second process. * @ns - Name of the namespace to check. Must correspond to one of the names * for the namespaces as shown in /proc/= 0) close(ns_fd1); if (ns_fd2 >= 0) close(ns_fd2); errno = saved_errno; return ret; } static int lxc_attach_to_ns(pid_t pid, struct lxc_proc_context_info *ctx) { int i, ret; for (i = 0; i < LXC_NS_MAX; i++) { if (ctx->ns_fd[i] < 0) continue; ret = setns(ctx->ns_fd[i], ns_info[i].clone_flag); if (ret < 0) { SYSERROR("Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); return -1; } DEBUG("Attached to %s namespace of %d", ns_info[i].proc_name, pid); } return 0; } static int lxc_attach_remount_sys_proc(void) { int ret; ret = unshare(CLONE_NEWNS); if (ret < 0) { SYSERROR("Failed to unshare mount namespace"); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } /* Assume /proc is always mounted, so remount it. */ ret = umount2("/proc", MNT_DETACH); if (ret < 0) { SYSERROR("Failed to unmount /proc"); return -1; } ret = mount("none", "/proc", "proc", 0, NULL); if (ret < 0) { SYSERROR("Failed to remount /proc"); return -1; } /* Try to umount /sys. If it's not a mount point, we'll get EINVAL, then * we ignore it because it may not have been mounted in the first place. */ ret = umount2("/sys", MNT_DETACH); if (ret < 0 && errno != EINVAL) { SYSERROR("Failed to unmount /sys"); return -1; } else if (ret == 0) { /* Remount it. */ ret = mount("none", "/sys", "sysfs", 0, NULL); if (ret < 0) { SYSERROR("Failed to remount /sys"); return -1; } } return 0; } static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) { int cap, last_cap; last_cap = lxc_caps_last_cap(); for (cap = 0; cap <= last_cap; cap++) { if (ctx->capability_mask & (1LL << cap)) continue; if (prctl(PR_CAPBSET_DROP, prctl_arg(cap), prctl_arg(0), prctl_arg(0), prctl_arg(0))) { SYSERROR("Failed to drop capability %d", cap); return -1; } TRACE("Dropped capability %d", cap); } return 0; } static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx, enum lxc_attach_env_policy_t policy, char **extra_env, char **extra_keep) { int ret; struct lxc_list *iterator; if (policy == LXC_ATTACH_CLEAR_ENV) { int path_kept = 0; char **extra_keep_store = NULL; if (extra_keep) { size_t count, i; for (count = 0; extra_keep[count]; count++) ; extra_keep_store = calloc(count, sizeof(char *)); if (!extra_keep_store) return -1; for (i = 0; i < count; i++) { char *v = getenv(extra_keep[i]); if (v) { extra_keep_store[i] = strdup(v); if (!extra_keep_store[i]) { while (i > 0) free(extra_keep_store[--i]); free(extra_keep_store); return -1; } if (strcmp(extra_keep[i], "PATH") == 0) path_kept = 1; } } } if (clearenv()) { if (extra_keep_store) { char **p; for (p = extra_keep_store; *p; p++) free(*p); free(extra_keep_store); } ERROR("Failed to clear environment"); return -1; } if (extra_keep_store) { size_t i; for (i = 0; extra_keep[i]; i++) { if (extra_keep_store[i]) { ret = setenv(extra_keep[i], extra_keep_store[i], 1); if (ret < 0) SYSWARN("Failed to set environment variable"); } free(extra_keep_store[i]); } free(extra_keep_store); } /* Always set a default path; shells and execlp tend to be fine * without it, but there is a disturbing number of C programs * out there that just assume that getenv("PATH") is never NULL * and then die a painful segfault death. */ if (!path_kept) { ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); if (ret < 0) SYSWARN("Failed to set environment variable"); } } ret = putenv("container=lxc"); if (ret < 0) { SYSWARN("Failed to set environment variable"); return -1; } /* Set container environment variables.*/ if (init_ctx && init_ctx->container && init_ctx->container->lxc_conf) { lxc_list_for_each(iterator, &init_ctx->container->lxc_conf->environment) { char *env_tmp; env_tmp = strdup((char *)iterator->elem); if (!env_tmp) return -1; ret = putenv(env_tmp); if (ret < 0) { SYSERROR("Failed to set environment variable: %s", (char *)iterator->elem); return -1; } } } /* Set extra environment variables. */ if (extra_env) { for (; *extra_env; extra_env++) { char *p; /* We just assume the user knows what they are doing, so * we don't do any checks. */ p = strdup(*extra_env); if (!p) return -1; ret = putenv(p); if (ret < 0) SYSWARN("Failed to set environment variable"); } } return 0; } static char *lxc_attach_getpwshell(uid_t uid) { int fd, ret; pid_t pid; int pipes[2]; FILE *pipe_f; bool found = false; size_t line_bufsz = 0; char *line = NULL, *result = NULL; /* We need to fork off a process that runs the getent program, and we * need to capture its output, so we use a pipe for that purpose. */ ret = pipe2(pipes, O_CLOEXEC); if (ret < 0) return NULL; pid = fork(); if (pid < 0) { close(pipes[0]); close(pipes[1]); return NULL; } if (!pid) { char uid_buf[32]; char *arguments[] = { "getent", "passwd", uid_buf, NULL }; close(pipes[0]); /* We want to capture stdout. */ ret = dup2(pipes[1], STDOUT_FILENO); close(pipes[1]); if (ret < 0) _exit(EXIT_FAILURE); /* Get rid of stdin/stderr, so we try to associate it with * /dev/null. */ fd = open_devnull(); if (fd < 0) { close(STDIN_FILENO); close(STDERR_FILENO); } else { (void)dup3(fd, STDIN_FILENO, O_CLOEXEC); (void)dup3(fd, STDOUT_FILENO, O_CLOEXEC); close(fd); } /* Finish argument list. */ ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long)uid); if (ret <= 0 || ret >= sizeof(uid_buf)) _exit(EXIT_FAILURE); /* Try to run getent program. */ (void)execvp("getent", arguments); _exit(EXIT_FAILURE); } close(pipes[1]); pipe_f = fdopen(pipes[0], "r"); while (getline(&line, &line_bufsz, pipe_f) != -1) { int i; long value; char *token; char *endptr = NULL, *saveptr = NULL; /* If we already found something, just continue to read * until the pipe doesn't deliver any more data, but * don't modify the existing data structure. */ if (found) continue; if (!line) continue; /* Trim line on the right hand side. */ for (i = strlen(line); i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) line[i - 1] = '\0'; /* Split into tokens: first: user name. */ token = strtok_r(line, ":", &saveptr); if (!token) continue; /* next: dummy password field */ token = strtok_r(NULL, ":", &saveptr); if (!token) continue; /* next: user id */ token = strtok_r(NULL, ":", &saveptr); value = token ? strtol(token, &endptr, 10) : 0; if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) continue; /* dummy sanity check: user id matches */ if ((uid_t)value != uid) continue; /* skip fields: gid, gecos, dir, go to next field 'shell' */ for (i = 0; i < 4; i++) { token = strtok_r(NULL, ":", &saveptr); if (!token) continue; } if (!token) continue; free(result); result = strdup(token); /* Sanity check that there are no fields after that. */ token = strtok_r(NULL, ":", &saveptr); if (token) continue; found = true; } free(line); fclose(pipe_f); ret = wait_for_pid(pid); if (ret < 0) { free(result); return NULL; } if (!found) { free(result); return NULL; } return result; } static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid) { FILE *proc_file; char proc_fn[LXC_PROC_STATUS_LEN]; int ret; char *line = NULL; size_t line_bufsz = 0; long value = -1; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; ret = snprintf(proc_fn, LXC_PROC_STATUS_LEN, "/proc/%d/status", 1); if (ret < 0 || ret >= LXC_PROC_STATUS_LEN) return; proc_file = fopen(proc_fn, "r"); if (!proc_file) return; while (getline(&line, &line_bufsz, proc_file) != -1) { /* Format is: real, effective, saved set user, fs we only care * about real uid. */ ret = sscanf(line, "Uid: %ld", &value); if (ret != EOF && ret == 1) { uid = (uid_t)value; } else { ret = sscanf(line, "Gid: %ld", &value); if (ret != EOF && ret == 1) gid = (gid_t)value; } if (uid != (uid_t)-1 && gid != (gid_t)-1) break; } fclose(proc_file); free(line); /* Only override arguments if we found something. */ if (uid != (uid_t)-1) *init_uid = uid; if (gid != (gid_t)-1) *init_gid = gid; /* TODO: we should also parse supplementary groups and use * setgroups() to set them. */ } static bool fetch_seccomp(struct lxc_container *c, lxc_attach_options_t *options) { int ret; bool bret; char *path; if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) { free(c->lxc_conf->seccomp); c->lxc_conf->seccomp = NULL; return true; } /* Remove current setting. */ if (!c->set_config_item(c, "lxc.seccomp.profile", "") && !c->set_config_item(c, "lxc.seccomp", "")) return false; /* Fetch the current profile path over the cmd interface. */ path = c->get_running_config_item(c, "lxc.seccomp.profile"); if (!path) { INFO("Failed to retrieve lxc.seccomp.profile"); path = c->get_running_config_item(c, "lxc.seccomp"); if (!path) { INFO("Failed to retrieve lxc.seccomp"); return true; } } /* Copy the value into the new lxc_conf. */ bret = c->set_config_item(c, "lxc.seccomp.profile", path); free(path); if (!bret) return false; /* Attempt to parse the resulting config. */ ret = lxc_read_seccomp_config(c->lxc_conf); if (ret < 0) { ERROR("Failed to retrieve seccomp policy"); return false; } INFO("Retrieved seccomp policy"); return true; } static bool no_new_privs(struct lxc_container *c, lxc_attach_options_t *options) { bool bret; char *val; /* Remove current setting. */ if (!c->set_config_item(c, "lxc.no_new_privs", "")) { INFO("Failed to unset 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 retrieve lxc.no_new_privs"); return false; } /* Set currently active setting. */ bret = c->set_config_item(c, "lxc.no_new_privs", val); free(val); return bret; } static signed long get_personality(const char *name, const char *lxcpath) { char *p; signed long ret; p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); if (!p) return -1; ret = lxc_config_parse_arch(p); free(p); return ret; } struct attach_clone_payload { int ipc_socket; int terminal_slave_fd; lxc_attach_options_t *options; struct lxc_proc_context_info *init_ctx; lxc_attach_exec_t exec_function; void *exec_payload; }; static void lxc_put_attach_clone_payload(struct attach_clone_payload *p) { if (p->ipc_socket >= 0) { close(p->ipc_socket); p->ipc_socket = -EBADF; } if (p->terminal_slave_fd >= 0) { close(p->terminal_slave_fd); p->terminal_slave_fd = -EBADF; } if (p->init_ctx) { lxc_proc_put_context_info(p->init_ctx); p->init_ctx = NULL; } } static int attach_child_main(struct attach_clone_payload *payload) { int fd, lsm_fd, ret; uid_t new_uid; gid_t new_gid; uid_t ns_root_uid = 0; gid_t ns_root_gid = 0; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; bool needs_lsm = (options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label; /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the * parent process, otherwise /proc may not properly reflect the new pid * namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) goto on_error; TRACE("Remounted \"/proc\" and \"/sys\""); } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { long new_personality; if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; ret = personality(new_personality); if (ret < 0) goto on_error; TRACE("Set new personality"); } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) goto on_error; TRACE("Dropped capabilities"); } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(init_ctx, options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) goto on_error; TRACE("Set up environment"); /* This remark only affects fully unprivileged containers: * Receive fd for LSM security module before we set{g,u}id(). The reason * is that on set{g,u}id() the kernel will a) make us undumpable and b) * we will change our effective uid. This means our effective uid will * be different from the effective uid of the process that created us * which means that this processs no longer has capabilities in our * namespace including CAP_SYS_PTRACE. This means we will not be able to * read and /proc/ files for the process anymore when /proc is * mounted with hidepid={1,2}. So let's get the lsm label fd before the * set{g,u}id(). */ if (needs_lsm) { ret = lxc_abstract_unix_recv_fds(payload->ipc_socket, &lsm_fd, 1, NULL, 0); if (ret <= 0) { if (ret < 0) SYSERROR("Failed to receive lsm label fd"); goto on_error; } TRACE("Received LSM label file descriptor %d from parent", lsm_fd); } if (options->stdin_fd > 0 && isatty(options->stdin_fd)) { ret = lxc_make_controlling_terminal(options->stdin_fd); if (ret < 0) goto on_error; } if (options->namespaces & CLONE_NEWUSER) { /* Check whether nsuid 0 has a mapping. */ ns_root_uid = get_ns_uid(0); /* Check whether nsgid 0 has a mapping. */ ns_root_gid = get_ns_gid(0); /* If there's no mapping for nsuid 0 try to retrieve the nsuid * init was started with. */ if (ns_root_uid == LXC_INVALID_UID) lxc_attach_get_init_uidgid(&ns_root_uid, &ns_root_gid); if (ns_root_uid == LXC_INVALID_UID) goto on_error; if (!lxc_switch_uid_gid(ns_root_uid, ns_root_gid)) goto on_error; } if (!lxc_setgroups(0, NULL) && errno != EPERM) goto on_error; /* Set {u,g}id. */ if (options->uid != LXC_INVALID_UID) new_uid = options->uid; else new_uid = ns_root_uid; if (options->gid != LXC_INVALID_GID) new_gid = options->gid; else new_gid = ns_root_gid; 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)) { ret = prctl(PR_SET_NO_NEW_PRIVS, prctl_arg(1), prctl_arg(0), prctl_arg(0), prctl_arg(0)); if (ret < 0) goto on_error; TRACE("Set PR_SET_NO_NEW_PRIVS"); } if (needs_lsm) { bool on_exec; /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; ret = lsm_process_label_set_at(lsm_fd, init_ctx->lsm_label, on_exec); close(lsm_fd); if (ret < 0) goto on_error; TRACE("Set %s LSM label to \"%s\"", lsm_name(), init_ctx->lsm_label); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp) { ret = lxc_seccomp_load(init_ctx->container->lxc_conf); if (ret < 0) goto on_error; TRACE("Loaded seccomp profile"); } close(payload->ipc_socket); payload->ipc_socket = -EBADF; lxc_proc_put_context_info(init_ctx); payload->init_ctx = NULL; /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO) (void)dup2(options->stdin_fd, STDIN_FILENO); if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO) (void)dup2(options->stdout_fd, STDOUT_FILENO); if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO) (void)dup2(options->stderr_fd, STDERR_FILENO); /* close the old fds */ if (options->stdin_fd > STDERR_FILENO) close(options->stdin_fd); if (options->stdout_fd > STDERR_FILENO) close(options->stdout_fd); if (options->stderr_fd > STDERR_FILENO) close(options->stderr_fd); /* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { ret = fd_cloexec(fd, false); if (ret < 0) { SYSERROR("Failed to clear FD_CLOEXEC from file descriptor %d", fd); goto on_error; } } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_terminal_prepare_login(payload->terminal_slave_fd); if (ret < 0) { SYSERROR("Failed to prepare terminal file descriptor %d", payload->terminal_slave_fd); goto on_error; } TRACE("Prepared terminal file descriptor %d", payload->terminal_slave_fd); } /* Avoid unnecessary syscalls. */ if (new_uid == ns_root_uid) new_uid = LXC_INVALID_UID; if (new_gid == ns_root_gid) new_gid = LXC_INVALID_GID; if (!lxc_switch_uid_gid(new_uid, new_gid)) goto on_error; /* We're done, so we can now do whatever the user intended us to do. */ _exit(payload->exec_function(payload->exec_payload)); on_error: lxc_put_attach_clone_payload(payload); _exit(EXIT_FAILURE); } static int lxc_attach_terminal(struct lxc_conf *conf, struct lxc_terminal *terminal) { int ret; lxc_terminal_init(terminal); ret = lxc_terminal_create(terminal); if (ret < 0) { ERROR("Failed to create terminal"); return -1; } /* Shift ttys to container. */ ret = lxc_terminal_map_ids(conf, terminal); if (ret < 0) { ERROR("Failed to chown terminal"); goto on_error; } return 0; on_error: lxc_terminal_delete(terminal); lxc_terminal_conf_free(terminal); return -1; } static int lxc_attach_terminal_mainloop_init(struct lxc_terminal *terminal, struct lxc_epoll_descr *descr) { int ret; ret = lxc_mainloop_open(descr); if (ret < 0) { ERROR("Failed to create mainloop"); return -1; } ret = lxc_terminal_mainloop_add(descr, terminal); if (ret < 0) { ERROR("Failed to add handlers to mainloop"); lxc_mainloop_close(descr); return -1; } return 0; } static inline void lxc_attach_terminal_close_master(struct lxc_terminal *terminal) { if (terminal->master < 0) return; close(terminal->master); terminal->master = -EBADF; } static inline void lxc_attach_terminal_close_slave(struct lxc_terminal *terminal) { if (terminal->slave < 0) return; close(terminal->slave); terminal->slave = -EBADF; } static inline void lxc_attach_terminal_close_peer(struct lxc_terminal *terminal) { if (terminal->peer < 0) return; close(terminal->peer); terminal->peer = -EBADF; } static inline void lxc_attach_terminal_close_log(struct lxc_terminal *terminal) { if (terminal->log_fd < 0) return; close(terminal->log_fd); terminal->log_fd = -EBADF; } int lxc_attach(const char *name, const char *lxcpath, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int i, ret, status; int ipc_sockets[2]; char *cwd, *new_cwd; signed long personality; pid_t attached_pid, init_pid, pid; struct lxc_proc_context_info *init_ctx; struct lxc_terminal terminal; struct lxc_conf *conf; struct attach_clone_payload payload = {0}; ret = access("/proc/self/ns", X_OK); if (ret) { SYSERROR("Does this kernel version support namespaces?"); return -1; } if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { ERROR("Failed to get init pid"); return -1; } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("Failed to get context of init process: %ld", (long)init_pid); return -1; } personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { ERROR("Failed to get personality of the container"); lxc_proc_put_context_info(init_ctx); return -1; } init_ctx->personality = personality; init_ctx->container = lxc_container_new(name, lxcpath); if (!init_ctx->container) { lxc_proc_put_context_info(init_ctx); return -1; } if (!init_ctx->container->lxc_conf) { init_ctx->container->lxc_conf = lxc_conf_init(); if (!init_ctx->container->lxc_conf) { lxc_proc_put_context_info(init_ctx); return -1; } } conf = init_ctx->container->lxc_conf; 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 * by asking lxc-start, if necessary. */ if (options->namespaces == -1) { options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath); /* call failed */ if (options->namespaces == -1) { ERROR("Failed to automatically determine the " "namespaces which the container uses"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } for (i = 0; i < LXC_NS_MAX; i++) { if (ns_info[i].clone_flag & CLONE_NEWCGROUP) if (!(options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) || !cgns_supported()) continue; if (ns_info[i].clone_flag & options->namespaces) continue; init_ctx->ns_inherited |= ns_info[i].clone_flag; } } pid = lxc_raw_getpid(); for (i = 0; i < LXC_NS_MAX; i++) { int j; if (options->namespaces & ns_info[i].clone_flag) init_ctx->ns_fd[i] = lxc_preserve_ns(init_pid, ns_info[i].proc_name); else if (init_ctx->ns_inherited & ns_info[i].clone_flag) init_ctx->ns_fd[i] = in_same_namespace(pid, init_pid, ns_info[i].proc_name); else continue; if (init_ctx->ns_fd[i] >= 0) continue; if (init_ctx->ns_fd[i] == -EINVAL) { DEBUG("Inheriting %s namespace from %d", ns_info[i].proc_name, pid); init_ctx->ns_inherited &= ~ns_info[i].clone_flag; continue; } /* We failed to preserve the namespace. */ SYSERROR("Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); /* Close all already opened file descriptors before we return an * error, so we don't leak them. */ for (j = 0; j < i; j++) close(init_ctx->ns_fd[j]); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_attach_terminal(conf, &terminal); if (ret < 0) { ERROR("Failed to setup new terminal"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } terminal.log_fd = options->log_fd; } else { lxc_terminal_init(&terminal); } /* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order * to make sure we don't irritate other threads that want to fork+exec * away * * IMPORTANT: if the initial process is multithreaded and another call * just fork()s away without exec'ing directly after, the socket fd will * exist in the forked process from the other thread and any close() in * our own child process will not really cause the socket to close * properly, potentially causing the parent to hang. * * For this reason, while IPC is still active, we have to use shutdown() * if the child exits prematurely in order to signal that the socket is * closed and cannot assume that the child exiting will automatically do * that. * * IPC mechanism: (X is receiver) * initial process intermediate attached * X <--- send pid of * attached proc, * then exit * send 0 ------------------------------------> X * [do initialization] * X <------------------------------------ send 1 * [add to cgroup, ...] * send 2 ------------------------------------> X * [set LXC_ATTACH_NO_NEW_PRIVS] * X <------------------------------------ send 3 * [open LSM label fd] * send 4 ------------------------------------> X * [set LSM label] * close socket close socket * run program */ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); if (ret < 0) { SYSERROR("Could not set up required IPC mechanism for attaching"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } /* Create intermediate subprocess, two reasons: * 1. We can't setns() in the child itself, since we want to make * sure we are properly attached to the pidns. * 2. Also, the initial thread has to put the attached process * into the cgroup, which we can only do if we didn't already * setns() (otherwise, user namespaces will hate us). */ pid = fork(); if (pid < 0) { SYSERROR("Failed to create first subprocess"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (pid) { int ret_parent = -1; pid_t to_cleanup_pid = pid; struct lxc_epoll_descr descr = {0}; /* close unneeded file descriptors */ close(ipc_sockets[1]); free(cwd); lxc_proc_close_ns_fd(init_ctx); if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_attach_terminal_close_slave(&terminal); /* Attach to cgroup, if requested. */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { struct cgroup_ops *cgroup_ops; cgroup_ops = cgroup_init(NULL); if (!cgroup_ops) goto on_error; if (!cgroup_ops->attach(cgroup_ops, name, lxcpath, pid)) goto on_error; cgroup_exit(cgroup_ops); TRACE("Moved intermediate process %d into container's cgroups", pid); } /* Setup /proc limits */ if (!lxc_list_empty(&conf->procs)) { ret = setup_proc_filesystem(&conf->procs, pid); if (ret < 0) goto on_error; } /* Setup resource limits */ if (!lxc_list_empty(&conf->limits)) { ret = setup_resource_limits(&conf->limits, pid); if (ret < 0) goto on_error; } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_attach_terminal_mainloop_init(&terminal, &descr); if (ret < 0) goto on_error; TRACE("Initialized terminal mainloop"); } /* Let the child process know to go ahead. */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret != sizeof(status)) goto close_mainloop; TRACE("Told intermediate process to start initializing"); /* Get pid of attached process from intermediate process. */ ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid)); if (ret != sizeof(attached_pid)) goto close_mainloop; TRACE("Received pid %d of attached process in parent pid namespace", attached_pid); /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */ if (options->stdin_fd == 0) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } /* Reap intermediate process. */ ret = wait_for_pid(pid); if (ret < 0) goto close_mainloop; TRACE("Intermediate process %d exited", pid); /* We will always have to reap the attached process now. */ to_cleanup_pid = attached_pid; /* Open LSM fd and send it to child. */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int ret = -1; int labelfd; bool on_exec; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; labelfd = lsm_process_label_fd_get(attached_pid, on_exec); if (labelfd < 0) goto close_mainloop; TRACE("Opened LSM label file descriptor %d", labelfd); /* Send child fd of the LSM security module to write to. */ ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); if (ret <= 0) { if (ret < 0) SYSERROR("Failed to send lsm label fd"); close(labelfd); goto close_mainloop; } close(labelfd); TRACE("Sent LSM label file descriptor %d to child", labelfd); } /* We're done, the child process should now execute whatever it * is that the user requested. The parent can now track it with * waitpid() or similar. */ *attached_process = attached_pid; /* Now shut down communication with child, we're done. */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); ipc_sockets[0] = -1; ret_parent = 0; to_cleanup_pid = -1; if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_mainloop(&descr, -1); if (ret < 0) { ret_parent = -1; to_cleanup_pid = attached_pid; } } close_mainloop: if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_mainloop_close(&descr); on_error: if (ipc_sockets[0] >= 0) { shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); } if (to_cleanup_pid > 0) (void)wait_for_pid(to_cleanup_pid); if (options->attach_flags & LXC_ATTACH_TERMINAL) { lxc_terminal_delete(&terminal); lxc_terminal_conf_free(&terminal); } lxc_proc_put_context_info(init_ctx); return ret_parent; } /* close unneeded file descriptors */ close(ipc_sockets[0]); ipc_sockets[0] = -EBADF; if (options->attach_flags & LXC_ATTACH_TERMINAL) { lxc_attach_terminal_close_master(&terminal); lxc_attach_terminal_close_peer(&terminal); lxc_attach_terminal_close_log(&terminal); } /* Wait for the parent to have setup cgroups. */ ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status)); if (ret != sizeof(status)) { shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } TRACE("Intermediate process starting to initialize"); /* Attach now, create another subprocess later, since pid namespaces * only really affect the children of the current process. */ ret = lxc_attach_to_ns(init_pid, init_ctx); if (ret < 0) { ERROR("Failed to enter namespaces"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } /* close namespace file descriptors */ lxc_proc_close_ns_fd(init_ctx); /* Attach succeeded, try to cwd. */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; if (new_cwd) { ret = chdir(new_cwd); if (ret < 0) WARN("Could not change directory to \"%s\"", new_cwd); } free(cwd); /* Create attached process. */ payload.ipc_socket = ipc_sockets[1]; payload.options = options; payload.init_ctx = init_ctx; payload.terminal_slave_fd = terminal.slave; payload.exec_function = exec_function; payload.exec_payload = exec_payload; pid = lxc_raw_clone(CLONE_PARENT); if (pid < 0) { SYSERROR("Failed to clone attached process"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } if (pid == 0) { if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = pthread_sigmask(SIG_SETMASK, &terminal.tty_state->oldmask, NULL); if (ret < 0) { SYSERROR("Failed to reset signal mask"); _exit(EXIT_FAILURE); } } ret = attach_child_main(&payload); if (ret < 0) ERROR("Failed to exec"); _exit(EXIT_FAILURE); } if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_attach_terminal_close_slave(&terminal); /* Tell grandparent the pid of the pid of the newly created child. */ ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ret != sizeof(pid)) { /* If this really happens here, this is very unfortunate, since * the parent will not know the pid of the attached process and * will not be able to wait for it (and we won't either due to * CLONE_PARENT) so the parent won't be able to reap it and the * attached process will remain a zombie. */ shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } TRACE("Sending pid %d of attached process", pid); /* The rest is in the hands of the initial and the attached process. */ lxc_proc_put_context_info(init_ctx); _exit(0); } int lxc_attach_run_command(void *payload) { int ret = -1; lxc_attach_command_t *cmd = payload; ret = execvp(cmd->program, cmd->argv); if (ret < 0) { switch (errno) { case ENOEXEC: ret = 126; break; case ENOENT: ret = 127; break; } } SYSERROR("Failed to exec \"%s\"", cmd->program); return ret; } int lxc_attach_run_shell(void* payload) { uid_t uid; struct passwd pwent; struct passwd *pwentp = NULL; char *user_shell; char *buf; size_t bufsize; int ret; /* Ignore payload parameter. */ (void)payload; uid = getuid(); bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (buf) { ret = getpwuid_r(uid, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) WARN("Could not find matched password record"); WARN("Failed to get password record - %u", uid); } } /* This probably happens because of incompatible nss implementations in * host and container (remember, this code is still using the host's * glibc but our mount namespace is in the container) we may try to get * the information by spawning a [getent passwd uid] process and parsing * the result. */ if (!pwentp) user_shell = lxc_attach_getpwshell(uid); else user_shell = pwent.pw_shell; if (user_shell) execlp(user_shell, user_shell, (char *)NULL); /* Executed if either no passwd entry or execvp fails, we will fall back * on /bin/sh as a default shell. */ execlp("/bin/sh", "/bin/sh", (char *)NULL); SYSERROR("Failed to execute shell"); if (!pwentp) free(user_shell); free(buf); return -1; } lxc-3.0.3/src/lxc/criu.h0000644061062106075000000000241613375633353011742 00000000000000/* * lxc: linux Container library * * Copyright © 2014-2015 Canonical Ltd. * * Authors: * Tycho Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CRIU_H #define __LXC_CRIU_H #include #include extern bool __criu_pre_dump(struct lxc_container *c, struct migrate_opts *opts); extern bool __criu_dump(struct lxc_container *c, struct migrate_opts *opts); extern bool __criu_restore(struct lxc_container *c, struct migrate_opts *opts); extern bool __criu_check_feature(uint64_t *features_to_check); #endif lxc-3.0.3/src/lxc/monitor.c0000644061062106075000000002302413375633353012460 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "config.h" #include "error.h" #include "log.h" #include "lxclock.h" #include "macro.h" #include "monitor.h" #include "state.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(monitor, lxc); /* routines used by monitor publishers (containers) */ int lxc_monitor_fifo_name(const char *lxcpath, char *fifo_path, size_t fifo_path_sz, int do_mkdirp) { int ret; char *rundir; rundir = get_rundir(); if (!rundir) return -1; if (do_mkdirp) { ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo", rundir, lxcpath); free(rundir); return -1; } ret = mkdir_p(fifo_path, 0755); if (ret < 0) { ERROR("Unable to create monitor fifo directory %s", fifo_path); free(rundir); return ret; } } ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s/monitor-fifo", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo", rundir, lxcpath); free(rundir); return -1; } free(rundir); return 0; } static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) { int fd,ret; char fifo_path[PATH_MAX]; BUILD_BUG_ON(sizeof(*msg) > PIPE_BUF); /* write not guaranteed atomic */ ret = lxc_monitor_fifo_name(lxcpath, fifo_path, sizeof(fifo_path), 0); if (ret < 0) return; /* Open the fifo nonblock in case the monitor is dead, we don't want the * open to wait for a reader since it may never come. */ fd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (fd < 0) { /* It is normal for this open() to fail with ENXIO when there is * no monitor running, so we don't log it. */ if (errno == ENXIO || errno == ENOENT) return; SYSWARN("Failed to open fifo to send message"); return; } if (fcntl(fd, F_SETFL, O_WRONLY) < 0) { close(fd); return; } ret = lxc_write_nointr(fd, msg, sizeof(*msg)); if (ret != sizeof(*msg)) { close(fd); SYSERROR("Failed to write to monitor fifo \"%s\"", fifo_path); return; } close(fd); } void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } void lxc_monitor_send_exit_code(const char *name, int exit_code, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_exit_code, .value = exit_code}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } /* routines used by monitor subscribers (lxc-monitor) */ int lxc_monitor_close(int fd) { return close(fd); } /* Enforces \0-termination for the abstract unix socket. This is not required * but allows us to print it out. * * Older version of liblxc only allowed for 105 bytes to be used for the * abstract unix domain socket name because the code for our abstract unix * socket handling performed invalid checks. Since we \0-terminate we could now * have a maximum of 106 chars. But to not break backwards compatibility we keep * the limit at 105. */ int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) { size_t len; int ret; char *path; uint64_t hash; /* addr.sun_path is only 108 bytes, so we hash the full name and * then append as much of the name as we can fit. */ memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; /* strlen("lxc/") + strlen("/monitor-sock") + 1 = 18 */ len = strlen(lxcpath) + 18; path = alloca(len); ret = snprintf(path, len, "lxc/%s/monitor-sock", lxcpath); if (ret < 0 || (size_t)ret >= len) { ERROR("Failed to create name for monitor socket"); return -1; } /* Note: snprintf() will \0-terminate addr->sun_path on the 106th byte * and so the abstract socket name has 105 "meaningful" characters. This * is absolutely intentional. For further info read the comment for this * function above! */ len = sizeof(addr->sun_path) - 1; hash = fnv_64a_buf(path, ret, FNV1A_64_INIT); ret = snprintf(addr->sun_path, len, "@lxc/%016" PRIx64 "/%s", hash, lxcpath); if (ret < 0) { ERROR("Failed to create hashed name for monitor socket"); goto on_error; } else if ((size_t)ret >= len) { errno = ENAMETOOLONG; SYSERROR("The name of monitor socket too long (%d bytes)", ret); goto on_error; } /* replace @ with \0 */ addr->sun_path[0] = '\0'; INFO("Using monitor socket name \"%s\" (length of socket name %zu must be <= %zu)", &addr->sun_path[1], strlen(&addr->sun_path[1]), sizeof(addr->sun_path) - 3); return 0; on_error: return -1; } int lxc_monitor_open(const char *lxcpath) { struct sockaddr_un addr; int fd; size_t retry; int backoff_ms[] = {10, 50, 100}; if (lxc_monitor_sock_name(lxcpath, &addr) < 0) return -1; DEBUG("Opening monitor socket %s with len %zu", &addr.sun_path[1], strlen(&addr.sun_path[1])); for (retry = 0; retry < sizeof(backoff_ms) / sizeof(backoff_ms[0]); retry++) { fd = lxc_abstract_unix_connect(addr.sun_path); if (fd != -1 || errno != ECONNREFUSED) break; SYSERROR("Failed to connect to monitor socket. Retrying in %d ms", backoff_ms[retry]); usleep(backoff_ms[retry] * 1000); } if (fd < 0) { SYSERROR("Failed to connect to monitor socket"); return -1; } return fd; } int lxc_monitor_read_fdset(struct pollfd *fds, nfds_t nfds, struct lxc_msg *msg, int timeout) { long i; int ret; ret = poll(fds, nfds, timeout * 1000); if (ret == -1) return -1; else if (ret == 0) return -2; /* timed out */ /* Only read from the first ready fd, the others will remain ready for * when this routine is called again. */ for (i = 0; i < nfds; i++) { if (fds[i].revents != 0) { fds[i].revents = 0; ret = recv(fds[i].fd, msg, sizeof(*msg), 0); if (ret <= 0) { SYSERROR("Failed to receive message. Did monitord die?"); return -1; } return ret; } } SYSERROR("No ready fd found"); return -1; } int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) { struct pollfd fds; fds.fd = fd; fds.events = POLLIN | POLLPRI; fds.revents = 0; return lxc_monitor_read_fdset(&fds, 1, msg, timeout); } int lxc_monitor_read(int fd, struct lxc_msg *msg) { return lxc_monitor_read_timeout(fd, msg, -1); } #define LXC_MONITORD_PATH LIBEXECDIR "/lxc/lxc-monitord" /* Used to spawn a monitord either on startup of a daemon container, or when * lxc-monitor starts. */ int lxc_monitord_spawn(const char *lxcpath) { int ret; int pipefd[2]; char pipefd_str[INTTYPE_TO_STRLEN(int)]; pid_t pid1, pid2; char *const args[] = { LXC_MONITORD_PATH, (char *)lxcpath, pipefd_str, NULL, }; /* double fork to avoid zombies when monitord exits */ pid1 = fork(); if (pid1 < 0) { SYSERROR("Failed to fork()"); return -1; } if (pid1) { DEBUG("Going to wait for pid %d", pid1); if (waitpid(pid1, NULL, 0) != pid1) return -1; DEBUG("Finished waiting on pid %d", pid1); return 0; } if (pipe(pipefd) < 0) { SYSERROR("Failed to create pipe"); _exit(EXIT_FAILURE); } pid2 = fork(); if (pid2 < 0) { SYSERROR("Failed to fork()"); _exit(EXIT_FAILURE); } if (pid2) { DEBUG("Trying to sync with child process"); char c; /* Wait for daemon to create socket. */ close(pipefd[1]); /* Sync with child, we're ignoring the return from read * because regardless if it works or not, either way we've * synced with the child process. the if-empty-statement * construct is to quiet the warn-unused-result warning. */ if (lxc_read_nointr(pipefd[0], &c, 1)) ; close(pipefd[0]); DEBUG("Successfully synced with child process"); _exit(EXIT_SUCCESS); } if (setsid() < 0) { SYSERROR("Failed to setsid()"); _exit(EXIT_FAILURE); } lxc_check_inherited(NULL, true, &pipefd[1], 1); if (null_stdfds() < 0) { SYSERROR("Failed to dup2() standard file descriptors to /dev/null"); _exit(EXIT_FAILURE); } close(pipefd[0]); ret = snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]); if (ret < 0 || ret >= sizeof(pipefd_str)) { ERROR("Failed to create pid argument to pass to monitord"); _exit(EXIT_FAILURE); } DEBUG("Using pipe file descriptor %d for monitord", pipefd[1]); execvp(args[0], args); SYSERROR("Failed to exec lxc-monitord"); _exit(EXIT_FAILURE); } lxc-3.0.3/src/lxc/lxc.h0000644061062106075000000000774513375633353011600 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXC_H #define __LXC_LXC_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "state.h" struct lxc_msg; struct lxc_conf; struct lxc_arguments; struct lxc_handler; /** Following code is for liblxc. lxc/lxc.h will contain exports of liblxc **/ /* * Start the specified command inside a system container * @name : the name of the container * @argv : an array of char * corresponding to the command line * @conf : configuration * @daemonize : whether or not the container is daemonized * Returns 0 on success, < 0 otherwise */ extern int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler, const char *lxcpath, bool daemonize, int *error_num); /* * Start the specified command inside an application container * @name : the name of the container * @argv : an array of char * corresponding to the command line * @quiet : if != 0 then lxc-init won't produce any output * @conf : configuration * @daemonize : whether or not the container is daemonized * Returns 0 on success, < 0 otherwise */ extern int lxc_execute(const char *name, char *const argv[], int quiet, struct lxc_handler *handler, const char *lxcpath, bool daemonize, int *error_num); /* * Close the fd associated with the monitoring * @fd : the file descriptor provided by lxc_monitor_open * Returns 0 on success, < 0 otherwise */ extern int lxc_monitor_close(int fd); /* * Freeze all the tasks running inside the container * @name : the container name * Returns 0 on success, < 0 otherwise */ extern int lxc_freeze(const char *name, const char *lxcpath); /* * Unfreeze all previously frozen tasks. * @name : the name of the container * Return 0 on success, < 0 otherwise */ extern int lxc_unfreeze(const char *name, const char *lxcpath); /* * Retrieve the container state * @name : the name of the container * Returns the state of the container on success, < 0 otherwise */ extern lxc_state_t lxc_state(const char *name, const char *lxcpath); /* * Create and return a new lxccontainer struct. */ extern struct lxc_container *lxc_container_new(const char *name, const char *configpath); /* * Returns 1 on success, 0 on failure. */ extern int lxc_container_get(struct lxc_container *c); /* * Put a lxccontainer struct reference. * Return -1 on error. * Return 0 if this was not the last reference. * If it is the last reference, free the lxccontainer and return 1. */ extern int lxc_container_put(struct lxc_container *c); /* * Get a list of valid wait states. * If states is NULL, simply return the number of states */ extern int lxc_get_wait_states(const char **states); /* * Add a dependency to a container */ extern int add_rdepend(struct lxc_conf *lxc_conf, char *rdepend); /* * Set a key/value configuration option. Requires that to take a lock on the * in-memory config of the container. */ extern int lxc_set_config_item_locked(struct lxc_conf *conf, const char *key, const char *v); #ifdef __cplusplus } #endif #endif lxc-3.0.3/src/lxc/commands_utils.c0000644061062106075000000001255413375633353014020 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include "af_unix.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "file_utils.h" #include "initutils.h" #include "log.h" #include "lxclock.h" #include "monitor.h" #include "state.h" #include "utils.h" lxc_log_define(commands_utils, lxc); int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout) { int ret; struct lxc_msg msg; struct timeval out; if (timeout >= 0) { memset(&out, 0, sizeof(out)); out.tv_sec = timeout; ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, sizeof(out)); if (ret < 0) { SYSERROR("Failed to set %ds timeout on container " "state socket", timeout); return -1; } } memset(&msg, 0, sizeof(msg)); ret = lxc_recv_nointr(state_client_fd, &msg, sizeof(msg), 0); if (ret < 0) { SYSERROR("Failed to receive message"); return -1; } TRACE("Received state %s from state client %d", lxc_state2str(msg.value), state_client_fd); return msg.value; } /* Register a new state client and retrieve state from command socket. */ int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int timeout) { int ret; int state_client_fd; ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd); if (ret < 0) return -1; if (ret < MAX_STATE) return ret; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); close(state_client_fd); return ret; } int lxc_make_abstract_socket_name(char *path, size_t pathlen, const char *lxcname, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { const char *name; char *offset; char *tmppath; size_t len; size_t tmplen; uint64_t hash; int ret; if (!path) return -1; offset = &path[1]; /* -2 here because this is an abstract unix socket so it needs a * leading \0, and we null terminate, so it needs a trailing \0. * Although null termination isn't required by the API, we do it anyway * because we print the sockname out sometimes. */ len = pathlen - 2; name = lxcname; if (!name) name = ""; if (hashed_sock_name != NULL) { ret = snprintf(offset, len, "lxc/%s/%s", hashed_sock_name, suffix); if (ret < 0 || ret >= len) { ERROR("Failed to create abstract socket name"); return -1; } return 0; } if (!lxcpath) { lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) { ERROR("Failed to allocate memory"); return -1; } } ret = snprintf(offset, len, "%s/%s/%s", lxcpath, name, suffix); if (ret < 0) { ERROR("Failed to create abstract socket name"); return -1; } if (ret < len) return 0; /* ret >= len; lxcpath or name is too long. hash both */ tmplen = strlen(name) + strlen(lxcpath) + 2; tmppath = alloca(tmplen); ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name); if (ret < 0 || (size_t)ret >= tmplen) { ERROR("Failed to create abstract socket name"); return -1; } hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT); ret = snprintf(offset, len, "lxc/%016" PRIx64 "/%s", hash, suffix); if (ret < 0 || ret >= len) { ERROR("Failed to create abstract socket name"); return -1; } return 0; } int lxc_cmd_connect(const char *name, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { int ret, client_fd; char path[LXC_AUDS_ADDR_LEN] = {0}; ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, hashed_sock_name, suffix); if (ret < 0) return -1; /* Get new client fd. */ client_fd = lxc_abstract_unix_connect(path); if (client_fd < 0) return -1; return client_fd; } int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, lxc_state_t states[MAX_STATE]) { int state; struct lxc_state_client *newclient; struct lxc_list *tmplist; newclient = malloc(sizeof(*newclient)); if (!newclient) return -ENOMEM; /* copy requested states */ memcpy(newclient->states, states, sizeof(newclient->states)); newclient->clientfd = state_client_fd; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) { free(newclient); return -ENOMEM; } state = handler->state; if (states[state] != 1) { lxc_list_add_elem(tmplist, newclient); lxc_list_add_tail(&handler->conf->state_clients, tmplist); } else { free(newclient); free(tmplist); return state; } TRACE("Added state client %d to state client list", state_client_fd); return MAX_STATE; } lxc-3.0.3/src/lxc/network.h0000644061062106075000000002273413375633353012476 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_NETWORK_H #define __LXC_NETWORK_H #include #include #include #include #include #include #include "list.h" struct lxc_conf; struct lxc_handler; struct lxc_netdev; enum { LXC_NET_EMPTY, LXC_NET_VETH, LXC_NET_MACVLAN, LXC_NET_PHYS, LXC_NET_VLAN, LXC_NET_NONE, LXC_NET_MAXCONFTYPE, }; /* * Defines the structure to configure an ipv4 address * @address : ipv4 address * @broadcast : ipv4 broadcast address * @mask : network mask */ struct lxc_inetdev { struct in_addr addr; struct in_addr bcast; unsigned int prefix; }; struct lxc_route { struct in_addr addr; }; /* * Defines the structure to configure an ipv6 address * @flags : set the address up * @address : ipv6 address * @broadcast : ipv6 broadcast address * @mask : network mask */ struct lxc_inet6dev { struct in6_addr addr; struct in6_addr mcast; struct in6_addr acast; unsigned int prefix; }; struct lxc_route6 { struct in6_addr addr; }; /* Contains information about the host side veth device. * @pair : Name of the host side veth device. * If the user requested that the host veth device be created with a * specific names this field will be set. If this field is set @veth1 * is not set. * @veth1 : Name of the host side veth device. * If the user did not request that the host veth device be created * with a specific name this field will be set. If this field is set * @pair is not set. * @ifindex : Ifindex of the network device. */ struct ifla_veth { char pair[IFNAMSIZ]; char veth1[IFNAMSIZ]; int ifindex; }; struct ifla_vlan { unsigned int flags; unsigned int fmask; unsigned short vid; unsigned short pad; }; struct ifla_macvlan { int mode; /* private, vepa, bridge, passthru */ }; /* Contains information about the physical network device as seen from the host. * @ifindex : The ifindex of the physical network device in the host's network * namespace. */ struct ifla_phys { int ifindex; }; union netdev_p { struct ifla_macvlan macvlan_attr; struct ifla_phys phys_attr; struct ifla_veth veth_attr; struct ifla_vlan vlan_attr; }; /* * Defines a structure to configure a network device * @idx : network counter * @ifindex : ifindex of the network device * Note that this is the ifindex of the network device in * the container's network namespace. If the network device * consists of a pair of network devices (e.g. veth pairs * attached to a network bridge) then this index cannot be * used to identify or modify the host veth device. See * struct ifla_veth for the host side information. * @type : network type (veth, macvlan, vlan, ...) * @flags : flag of the network device (IFF_UP, ... ) * @link : lxc.net.[i].link, name of bridge or host iface to attach * if any * @name : lxc.net.[i].name, name of iface on the container side * @hwaddr : mac address * @mtu : maximum transmission unit * @priv : information specific to the specificed network type * Note that this is a union so whether accessing a struct * is possible is dependent on the network type. * @ipv4 : a list of ipv4 addresses to be set on the network device * @ipv6 : a list of ipv6 addresses to be set on the network device * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered * from the associated @link * @ipv4_gateway : ipv4 gateway * @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered * from the associated @link * @ipv6_gateway : ipv6 gateway * @upscript : a script filename to be executed during interface * configuration * @downscript : a script filename to be executed during interface * destruction */ struct lxc_netdev { ssize_t idx; int ifindex; int type; int flags; char link[IFNAMSIZ]; char name[IFNAMSIZ]; char *hwaddr; char *mtu; union netdev_p priv; struct lxc_list ipv4; struct lxc_list ipv6; bool ipv4_gateway_auto; struct in_addr *ipv4_gateway; bool ipv6_gateway_auto; struct in6_addr *ipv6_gateway; char *upscript; char *downscript; }; /* Convert a string mac address to a socket structure. */ extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr); /* Move a device between namespaces. */ extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname); extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char *newname); /* Delete a network device. */ extern int lxc_netdev_delete_by_name(const char *name); extern int lxc_netdev_delete_by_index(int ifindex); /* Change the device name. */ extern int lxc_netdev_rename_by_name(const char *oldname, const char *newname); extern int lxc_netdev_rename_by_index(int ifindex, const char *newname); extern int netdev_set_flag(const char *name, int flag); /* Set the device network up or down. */ extern int lxc_netdev_isup(const char *name); extern int lxc_netdev_up(const char *name); extern int lxc_netdev_down(const char *name); /* Change the mtu size for the specified device. */ extern int lxc_netdev_set_mtu(const char *name, int mtu); /* Create a virtual network devices. */ extern int lxc_veth_create(const char *name1, const char *name2); extern int lxc_macvlan_create(const char *master, const char *name, int mode); extern int lxc_vlan_create(const char *master, const char *name, unsigned short vid); /* Set ip address. */ extern int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, struct in6_addr *mcast, struct in6_addr *acast, int prefix); extern int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, struct in_addr *bcast, int prefix); /* Get ip address. */ extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res); extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res); /* Set a destination route to an interface. */ extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest); extern int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest); /* Set default route. */ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); /* Attach an interface to the bridge. */ extern int lxc_bridge_attach(const char *bridge, const char *ifname); extern int lxc_ovs_delete_port(const char *bridge, const char *nic); extern bool is_ovs_bridge(const char *bridge); /* Create default gateway. */ extern int lxc_route_create_default(const char *addr, const char *ifname, int gateway); /* Delete default gateway. */ extern int lxc_route_delete_default(const char *addr, const char *ifname, int gateway); /* Activate neighbor proxying. */ extern int lxc_neigh_proxy_on(const char *name, int family); /* Disable neighbor proxying. */ extern int lxc_neigh_proxy_off(const char *name, int family); /* Generate a new unique network interface name. * Allocated memory must be freed by caller. */ extern char *lxc_mkifname(char *template); extern const char *lxc_net_type_to_str(int type); extern int setup_private_host_hw_addr(char *veth1); extern int netdev_get_mtu(int ifindex); extern int lxc_create_network_priv(struct lxc_handler *handler); extern int lxc_network_move_created_netdev_priv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid); extern void lxc_delete_network(struct lxc_handler *handler); extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_create_network_unpriv(const char *lxcpath, const char *lxcname, struct lxc_list *network, pid_t pid, unsigned int hook_version); 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); extern int lxc_netns_set_nsid(int netns_fd); extern int lxc_netns_get_nsid(__s32 fd); #endif /* __LXC_NETWORK_H */ lxc-3.0.3/src/lxc/start.h0000644061062106075000000001303613375633353012135 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_START_H #define __LXC_START_H #include #include #include #include #include #include "conf.h" #include "namespace.h" #include "state.h" struct lxc_handler { /* Record the clone for namespaces flags that the container requested. * * @ns_clone_flags * - All clone flags that were requested. * * @ns_on_clone_flags * - The clone flags for namespaces to actually use when calling * lxc_clone(): After the container has started ns_on_clone_flags will * list the clone flags that were unshare()ed rather then clone()ed * because of ordering requirements (e.g. e.g. CLONE_NEWNET and * CLONE_NEWUSER) or implementation details. * * @ns_keep_flags; * - The clone flags for the namespaces that the container will inherit * from the parent. They are not recorded in the handler itself but * are present in the container's config. * * @ns_share_flags; * - The clone flags for the namespaces that the container will share * with another process. They are not recorded in the handler itself * but are present in the container's config. */ struct /* lxc_ns */ { int ns_clone_flags; int ns_on_clone_flags; }; /* File descriptor to pin the rootfs for privileged containers. */ int pinfd; /* Signal file descriptor. */ int sigfd; /* List of file descriptors referring to the namespaces of the * container. Note that these are not necessarily identical to * the "clone_flags" handler field in case namespace inheritance is * requested. */ int nsfd[LXC_NS_MAX]; /* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data * between child and parent. */ int data_sock[2]; /* The socketpair() fds used to wait on successful daemonized startup. */ int state_socket_pair[2]; /* Socketpair to synchronize processes during container creation. */ int sync_sock[2]; /* Pointer to the name of the container. Do not free! */ const char *name; /* Pointer to the path the container. Do not free! */ const char *lxcpath; /* Whether the container's startup process euid is 0. */ bool am_root; /* Indicates whether should we close std{in,out,err} on start. */ bool daemonize; /* The child's pid. */ pid_t pid; /* Whether the child has already exited. */ bool init_died; /* The signal mask prior to setting up the signal file descriptor. */ sigset_t oldmask; /* The container's in-memory configuration. */ struct lxc_conf *conf; /* A set of operations to be performed at various stages of the * container's life. */ struct lxc_operations *ops; /* This holds the cgroup information. Note that the data here is * specific to the cgroup driver used. */ void *cgroup_data; /* Data to be passed to handler ops. */ void *data; /* Current state of the container. */ lxc_state_t state; /* The exit status of the container; not defined unless ->init_died == * true. */ int exit_status; struct cgroup_ops *cgroup_ops; }; struct execute_args { char *init_path; int init_fd; char *const *argv; int quiet; }; struct lxc_operations { int (*start)(struct lxc_handler *, void *); int (*post_start)(struct lxc_handler *, void *); }; extern int lxc_poll(const char *name, struct lxc_handler *handler); extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state); extern int lxc_serve_state_clients(const char *name, struct lxc_handler *handler, lxc_state_t state); extern void lxc_abort(const char *name, struct lxc_handler *handler); extern struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, const char *lxcpath, bool daemonize); extern void lxc_zero_handler(struct lxc_handler *handler); extern void lxc_free_handler(struct lxc_handler *handler); extern int lxc_init(const char *name, struct lxc_handler *handler); extern void lxc_fini(const char *name, struct lxc_handler *handler); /* lxc_check_inherited: Check for any open file descriptors and close them if * requested. * @param[in] conf The container's configuration. * @param[in] closeall Whether we should close all open file descriptors. * @param[in] fds_to_ignore Array of file descriptors to ignore. * @param[in] len_fds Length of fds_to_ignore array. */ extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int *fds_to_ignore, size_t len_fds); extern int __lxc_start(const char *, struct lxc_handler *, struct lxc_operations *, void *, const char *, bool, int *); extern int resolve_clone_flags(struct lxc_handler *handler); #endif lxc-3.0.3/src/lxc/Makefile.am0000644061062106075000000002641713375633353012672 00000000000000pkginclude_HEADERS = attach_options.h \ lxccontainer.h \ version.h noinst_HEADERS = attach.h \ caps.h \ cgroups/cgroup.h \ cgroups/cgroup_utils.h \ compiler.h \ conf.h \ confile.h \ confile_utils.h \ criu.h \ error.h \ file_utils.h \ ../include/netns_ifaddrs.h \ initutils.h \ list.h \ log.h \ lxc.h \ lxclock.h \ macro.h \ monitor.h \ namespace.h \ raw_syscalls.h \ start.h \ state.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/storage.h \ storage/storage_utils.h \ storage/zfs.h \ string_utils.h \ syscall_wrappers.h \ terminal.h \ ../tests/lxctest.h \ tools/arguments.h \ storage/storage_utils.h \ utils.h if IS_BIONIC noinst_HEADERS += ../include/lxcmntent.h \ ../include/openpty.h endif if !HAVE_PRLIMIT if HAVE_PRLIMIT64 noinst_HEADERS += ../include/prlimit.h endif endif if !HAVE_GETLINE if HAVE_FGETLN noinst_HEADERS += ../include/getline.h endif endif if !HAVE_GETSUBOPT noinst_HEADERS += tools/include/getsubopt.h endif if !HAVE_GETGRGID_R noinst_HEADERS += ../include/getgrgid_r.h endif sodir=$(libdir) LSM_SOURCES = lsm/lsm.c \ lsm/lsm.h \ lsm/nop.c if ENABLE_APPARMOR LSM_SOURCES += lsm/apparmor.c endif if ENABLE_SELINUX LSM_SOURCES += lsm/selinux.c endif lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = af_unix.c af_unix.h \ attach.c attach.h \ caps.c caps.h \ cgroups/cgfsng.c \ cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ compiler.h \ commands.c commands.h \ commands_utils.c commands_utils.h \ conf.c conf.h \ confile.c confile.h \ confile_utils.c confile_utils.h \ criu.c criu.h \ error.c error.h \ execute.c \ freezer.c \ file_utils.c file_utils.h \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ initutils.c initutils.h \ list.h \ log.c log.h \ lxc.h \ lxccontainer.c lxccontainer.h \ lxclock.c lxclock.h \ lxcseccomp.h \ macro.h \ mainloop.c mainloop.h \ namespace.c namespace.h \ nl.c nl.h \ network.c network.h \ monitor.c monitor.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ ringbuf.c ringbuf.h \ rtnl.c rtnl.h \ state.c state.h \ start.c start.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/storage.c storage/storage.h \ storage/storage_utils.c storage/storage_utils.h \ storage/zfs.c storage/zfs.h \ string_utils.c string_utils.h \ sync.c sync.h \ syscall_wrappers.h \ terminal.c \ utils.c utils.h \ version.h \ $(LSM_SOURCES) if IS_BIONIC liblxc_la_SOURCES += ../include/lxcmntent.c ../include/lxcmntent.h \ ../include/openpty.c ../include/openpty.h endif if !HAVE_GETGRGID_R liblxc_la_SOURCES += ../include/getgrgid_r.c ../include/getgrgid_r.h endif if !HAVE_GETLINE if HAVE_FGETLN liblxc_la_SOURCES += ../include/getline.c ../include/getline.h endif endif if !HAVE_PRLIMIT if HAVE_PRLIMIT64 liblxc_la_SOURCES += ../include/prlimit.c ../include/prlimit.h endif endif if ENABLE_SECCOMP liblxc_la_SOURCES += seccomp.c endif if !HAVE_STRLCPY liblxc_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT liblxc_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" \ -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ -DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \ -DSBINDIR=\"$(SBINDIR)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/lxc \ -I $(top_srcdir)/src/lxc/storage \ -I $(top_srcdir)/src/lxc/cgroups if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif if ENABLE_GNUTLS AM_CFLAGS += -DHAVE_LIBGNUTLS endif if ENABLE_SECCOMP AM_CFLAGS += -DHAVE_SECCOMP \ $(SECCOMP_CFLAGS) endif if ENABLE_SELINUX AM_CFLAGS += -DHAVE_SELINUX endif if USE_CONFIGPATH_LOGS AM_CFLAGS += -DUSE_CONFIGPATH_LOGS endif # build the shared library liblxc_la_CFLAGS = -fPIC \ -DPIC \ $(AM_CFLAGS) \ -pthread liblxc_la_LDFLAGS = -pthread \ -Wl,-no-undefined \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) \ $(GNUTLS_LIBS) \ $(SELINUX_LIBS) \ $(SECCOMP_LIBS) bin_SCRIPTS= if ENABLE_COMMANDS bin_SCRIPTS += cmd/lxc-checkconfig \ cmd/lxc-update-config endif if ENABLE_TOOLS bin_PROGRAMS = lxc-attach \ lxc-autostart \ lxc-cgroup \ lxc-checkpoint \ lxc-copy \ lxc-config \ lxc-console \ lxc-create \ lxc-destroy \ lxc-device \ lxc-execute \ lxc-freeze \ lxc-info \ lxc-ls \ lxc-monitor \ lxc-snapshot \ lxc-start \ lxc-stop \ lxc-top \ lxc-unfreeze \ lxc-unshare \ lxc-wait endif if ENABLE_COMMANDS if ENABLE_TOOLS bin_PROGRAMS += lxc-usernsexec else bin_PROGRAMS = lxc-usernsexec endif sbin_PROGRAMS = init.lxc pkglibexec_PROGRAMS = lxc-monitord \ lxc-user-nic endif AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif LDADD = liblxc.la \ @CAP_LIBS@ \ @GNUTLS_LIBS@ \ @SECCOMP_LIBS@ \ @SELINUX_LIBS@ if ENABLE_TOOLS lxc_attach_SOURCES = tools/lxc_attach.c \ tools/arguments.c tools/arguments.h lxc_autostart_SOURCES = tools/lxc_autostart.c \ tools/arguments.c tools/arguments.h lxc_cgroup_SOURCES = tools/lxc_cgroup.c \ tools/arguments.c tools/arguments.h lxc_config_SOURCES = tools/lxc_config.c \ tools/arguments.c tools/arguments.h lxc_console_SOURCES = tools/lxc_console.c \ tools/arguments.c tools/arguments.h lxc_destroy_SOURCES = tools/lxc_destroy.c \ tools/arguments.c tools/arguments.h lxc_device_SOURCES = tools/lxc_device.c \ tools/arguments.c tools/arguments.h lxc_execute_SOURCES = tools/lxc_execute.c \ tools/arguments.c tools/arguments.h lxc_freeze_SOURCES = tools/lxc_freeze.c \ tools/arguments.c tools/arguments.h lxc_info_SOURCES = tools/lxc_info.c \ tools/arguments.c tools/arguments.h lxc_monitor_SOURCES = tools/lxc_monitor.c \ macro.h \ tools/arguments.c tools/arguments.h lxc_ls_SOURCES = tools/lxc_ls.c \ tools/arguments.c tools/arguments.h lxc_copy_SOURCES = tools/lxc_copy.c \ tools/arguments.c tools/arguments.h \ storage/storage_utils.c storage/storage_utils.h lxc_start_SOURCES = tools/lxc_start.c \ tools/arguments.c tools/arguments.h lxc_stop_SOURCES = tools/lxc_stop.c \ tools/arguments.c tools/arguments.h lxc_top_SOURCES = tools/lxc_top.c \ tools/arguments.c tools/arguments.h lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c \ tools/arguments.c tools/arguments.h lxc_unshare_SOURCES = tools/lxc_unshare.c \ tools/arguments.c tools/arguments.h lxc_wait_SOURCES = tools/lxc_wait.c \ tools/arguments.c tools/arguments.h lxc_create_SOURCES = tools/lxc_create.c \ tools/arguments.c tools/arguments.h \ storage/storage_utils.c storage/storage_utils.h lxc_snapshot_SOURCES = tools/lxc_snapshot.c \ tools/arguments.c tools/arguments.h lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c \ tools/arguments.c tools/arguments.h endif if ENABLE_COMMANDS # Binaries shipping with liblxc init_lxc_SOURCES = cmd/lxc_init.c \ compiler.h \ error.h \ initutils.c initutils.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ string_utils.c string_utils.h lxc_monitord_SOURCES = cmd/lxc_monitord.c \ af_unix.c af_unix.h \ log.c log.h \ mainloop.c mainloop.h \ monitor.c monitor.h \ raw_syscalls.c raw_syscalls.h \ utils.c utils.h lxc_user_nic_SOURCES = cmd/lxc_user_nic.c \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ log.c log.h \ network.c network.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ syscall_wrappers.h lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \ conf.c conf.h \ list.h \ log.c log.h \ macro.h \ file_utils.c file_utils.h \ string_utils.c string_utils.h \ syscall_wrappers.h \ utils.c utils.h endif if ENABLE_TOOLS if !HAVE_GETSUBOPT lxc_copy_SOURCES += tools/include/getsubopt.c tools/include/getsubopt.h endif endif if ENABLE_COMMANDS if HAVE_STATIC_LIBCAP sbin_PROGRAMS += init.lxc.static init_lxc_static_SOURCES = cmd/lxc_init.c \ caps.c caps.h \ error.c error.h \ initutils.c initutils.h \ file_utils.c file_utils.h \ log.c log.h \ macro.h \ namespace.c namespace.h \ string_utils.c string_utils.h if !HAVE_GETLINE if HAVE_FGETLN init_lxc_static_SOURCES += ../include/getline.c ../include/getline.h endif endif if !HAVE_STRLCPY init_lxc_static_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT init_lxc_static_SOURCES += ../include/strlcat.c ../include/strlcat.h endif init_lxc_static_LDFLAGS = -all-static init_lxc_static_LDADD = @CAP_LIBS@ init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF endif endif if ENABLE_PAM if HAVE_PAM pam_LTLIBRARIES = pam_cgfs.la pam_cgfs_la_SOURCES = pam/pam_cgfs.c \ file_utils.c file_utils.h \ macro.h \ string_utils.c string_utils.h if !HAVE_STRLCAT pam_cgfs_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif if !HAVE_STRLCPY pam_cgfs_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif pam_cgfs_la_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF pam_cgfs_la_LIBADD = $(AM_LIBS) \ $(PAM_LIBS) \ -L$(top_srcdir) pam_cgfs_la_LDFLAGS = $(AM_LDFLAGS) \ -avoid-version \ -module \ -shared \ -Wl,-no-undefined endif endif install-exec-local: install-libLTLIBRARIES mkdir -p $(DESTDIR)$(datadir)/lxc install -c -m 644 lxc.functions $(DESTDIR)$(datadir)/lxc mv $(shell readlink -f $(DESTDIR)$(libdir)/liblxc.so) $(DESTDIR)$(libdir)/liblxc.so.@LXC_ABI@ rm -f $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.1 cd $(DESTDIR)$(libdir); \ ln -sf liblxc.so.@LXC_ABI@ liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)); \ ln -sf liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) liblxc.so install-exec-hook: chmod u+s $(DESTDIR)$(libexecdir)/lxc/lxc-user-nic uninstall-local: $(RM) $(DESTDIR)$(libdir)/liblxc.so* $(RM) $(DESTDIR)$(libdir)/liblxc.a if ENABLE_PAM if HAVE_PAM $(RM) $(DESTDIR)$(pamdir)/pam_cgfs.so* install-data-hook: install-pamLTLIBRARIES $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.la" $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.a" endif endif lxc-3.0.3/src/lxc/commands.c0000644061062106075000000007655113375633353012607 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "config.h" #include "confile.h" #include "log.h" #include "lxc.h" #include "lxclock.h" #include "mainloop.h" #include "monitor.h" #include "start.h" #include "terminal.h" #include "utils.h" /* * This file provides the different functions for clients to query/command the * server. The client is typically some lxc tool and the server is typically the * container (ie. lxc-start). * * Each command is transactional, the clients send a request to the server and * the server answers the request with a message giving the request's status * (zero or a negative errno value). Both the request and response may contain * additional data. * * Each command is wrapped in a ancillary message in order to pass a credential * making possible to the server to check if the client is allowed to ask for * this command or not. * * IMPORTANTLY: Note that semantics for current commands are fixed. If you wish * to make any changes to how, say, LXC_CMD_GET_CONFIG_ITEM works by adding * information to the end of cmd.data, then you must introduce a new * LXC_CMD_GET_CONFIG_ITEM_V2 define with a new number. You may wish to also * mark LXC_CMD_GET_CONFIG_ITEM deprecated in commands.h. * * This is necessary in order to avoid having a newly compiled lxc command * communicating with a running (old) monitor from crashing the running * container. */ lxc_log_define(commands, lxc); static const char *lxc_cmd_str(lxc_cmd_t cmd) { static const char *const cmdname[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = "console", [LXC_CMD_TERMINAL_WINCH] = "terminal_winch", [LXC_CMD_STOP] = "stop", [LXC_CMD_GET_STATE] = "get_state", [LXC_CMD_GET_INIT_PID] = "get_init_pid", [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags", [LXC_CMD_GET_CGROUP] = "get_cgroup", [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item", [LXC_CMD_GET_NAME] = "get_name", [LXC_CMD_GET_LXCPATH] = "get_lxcpath", [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client", [LXC_CMD_CONSOLE_LOG] = "console_log", [LXC_CMD_SERVE_STATE_CLIENTS] = "serve_state_clients", }; if (cmd >= LXC_CMD_MAX) return "Invalid request"; return cmdname[cmd]; } /* * lxc_cmd_rsp_recv: Receive a response to a command * * @sock : the socket connected to the container * @cmd : command to put response in * * Returns the size of the response message or < 0 on failure * * Note that if the command response datalen > 0, then data is * a malloc()ed buffer and should be free()ed by the caller. If * the response data is <= a void * worth of data, it will be * stored directly in data and datalen will be 0. * * As a special case, the response for LXC_CMD_CONSOLE is created * here as it contains an fd for the master pty passed through the * unix socket. */ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) { int ret, rspfd; struct lxc_cmd_rsp *rsp = &cmd->rsp; ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); if (ret < 0) { SYSWARN("Failed to receive response for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); if (errno == ECONNRESET) return -1; return -1; } TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd)); if (cmd->req.cmd == LXC_CMD_CONSOLE) { struct lxc_cmd_console_rsp_data *rspdata; /* recv() returns 0 bytes when a tty cannot be allocated, * rsp->ret is < 0 when the peer permission check failed */ if (ret == 0 || rsp->ret < 0) return 0; rspdata = malloc(sizeof(*rspdata)); if (!rspdata) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } rspdata->masterfd = rspfd; rspdata->ttynum = PTR_TO_INT(rsp->data); rsp->data = rspdata; } if (rsp->datalen == 0) { DEBUG("Response data length for command \"%s\" is 0", lxc_cmd_str(cmd->req.cmd)); return ret; } if ((rsp->datalen > LXC_CMD_DATA_MAX) && (cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) { ERROR("Response data for command \"%s\" is too long: %d bytes > %d", lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX); return -1; } if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) { rsp->data = malloc(rsp->datalen + 1); ((char *)rsp->data)[rsp->datalen] = '\0'; } else { rsp->data = malloc(rsp->datalen); } if (!rsp->data) { errno = ENOMEM; ERROR("Failed to allocate response buffer for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0); if (ret != rsp->datalen) { SYSERROR("Failed to receive response data for command \"%s\"", lxc_cmd_str(cmd->req.cmd)); return -1; } return ret; } /* * lxc_cmd_rsp_send: Send a command response * * @fd : file descriptor of socket to send response on * @rsp : response to send * * Returns 0 on success, < 0 on failure */ static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp) { ssize_t ret; errno = EMSGSIZE; ret = lxc_send_nointr(fd, rsp, sizeof(*rsp), MSG_NOSIGNAL); if (ret < 0 || (size_t)ret != sizeof(*rsp)) { SYSERROR("Failed to send command response %zd", ret); return -1; } if (!rsp->data || rsp->datalen <= 0) return 0; errno = EMSGSIZE; ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL); if (ret < 0 || ret != (ssize_t)rsp->datalen) { SYSWARN("Failed to send command response data %zd", ret); return -1; } return 0; } static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd, const char *lxcpath, const char *hashed_sock_name) { int client_fd, saved_errno; ssize_t ret = -1; client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name, "command"); if (client_fd < 0) return -1; ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, sizeof(cmd->req)); if (ret < 0 || (size_t)ret != sizeof(cmd->req)) goto on_error; if (cmd->req.datalen <= 0) return client_fd; errno = EMSGSIZE; ret = lxc_send_nointr(client_fd, (void *)cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL); if (ret < 0 || ret != (ssize_t)cmd->req.datalen) goto on_error; return client_fd; on_error: saved_errno = errno; close(client_fd); errno = saved_errno; return -1; } /* * lxc_cmd: Connect to the specified running container, send it a command * request and collect the response * * @name : name of container to connect to * @cmd : command with initialized request to send * @stopped : output indicator if the container was not running * @lxcpath : the lxcpath in which the container is running * * Returns the size of the response message on success, < 0 on failure * * Note that there is a special case for LXC_CMD_CONSOLE. For this command * the fd cannot be closed because it is used as a placeholder to indicate * that a particular tty slot is in use. The fd is also used as a signal to * the container that when the caller dies or closes the fd, the container * will notice the fd on its side of the socket in its mainloop select and * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be * returned in the cmd response structure. */ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, const char *lxcpath, const char *hashed_sock_name) { int client_fd, saved_errno; int ret = -1; bool stay_connected = false; if (cmd->req.cmd == LXC_CMD_CONSOLE || cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT) stay_connected = true; *stopped = 0; client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name); if (client_fd < 0) { SYSTRACE("Command \"%s\" failed to connect command socket", lxc_cmd_str(cmd->req.cmd)); if (errno == ECONNREFUSED || errno == EPIPE) *stopped = 1; return -1; } ret = lxc_cmd_rsp_recv(client_fd, cmd); if (ret < 0 && errno == ECONNRESET) *stopped = 1; if (!stay_connected || ret <= 0) { saved_errno = errno; close(client_fd); errno = saved_errno; return ret; } if (stay_connected && ret > 0) cmd->rsp.ret = client_fd; return ret; } int lxc_try_cmd(const char *name, const char *lxcpath) { int stopped, ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_INIT_PID }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (stopped) return 0; if (ret > 0 && cmd.rsp.ret < 0) { errno = cmd.rsp.ret; return -1; } if (ret > 0) return 0; /* At this point we weren't denied access, and the container *was* * started. There was some inexplicable error in the protocol. I'm not * clear on whether we should return -1 here, but we didn't receive a * -EACCES, so technically it's not that we're not allowed to control * the container - it's just not behaving. */ return 0; } /* Implementations of the commands and their callbacks */ /* * lxc_cmd_get_init_pid: Get pid of the container's init process * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the pid on success, < 0 on failure */ pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath) { int ret, stopped; intmax_t pid; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_INIT_PID }, .rsp = { .data = INTMAX_TO_PTR((intmax_t){-1}) } }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return -1; pid = PTR_TO_INTMAX(cmd.rsp.data); if (pid < 0) return -1; /* We need to assume that pid_t can actually hold any pid given to us * by the kernel. If it can't it's a libc bug. */ return (pid_t)pid; } static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { intmax_t pid = handler->pid; struct lxc_cmd_rsp rsp = { .data = INTMAX_TO_PTR(pid) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_clone_flags: Get clone flags container was spawned with * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the clone flags on success, < 0 on failure */ int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CLONE_FLAGS }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; return PTR_TO_INT(cmd.rsp.data); } static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->ns_clone_flags) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a * particular subsystem. This is the cgroup path relative to the root * of the cgroup filesystem. * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * @subsystem : the subsystem being asked about * * Returns the path on success, NULL on failure. The caller must free() the * returned path. */ char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, const char *subsystem) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CGROUP, .data = subsystem, .datalen = 0, }, }; cmd.req.data = subsystem; cmd.req.datalen = 0; if (subsystem) cmd.req.datalen = strlen(subsystem) + 1; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return NULL; if (ret == 0) return NULL; if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) return NULL; return cmd.rsp.data; } static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { const char *path; struct lxc_cmd_rsp rsp; struct cgroup_ops *cgroup_ops = handler->cgroup_ops; if (req->datalen > 0) path = cgroup_ops->get_cgroup(cgroup_ops, req->data); else path = cgroup_ops->get_cgroup(cgroup_ops, NULL); if (!path) return -1; rsp.ret = 0; rsp.datalen = strlen(path) + 1; rsp.data = (char *)path; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_config_item: Get config item the running container * * @name : name of container to connect to * @item : the configuration item to retrieve (ex: lxc.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 * returned item. */ char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM, .data = item, .datalen = strlen(item) + 1, }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int cilen; char *cidata; struct lxc_config_t *item; struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); item = lxc_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 (item->get(req->data, cidata, cilen + 1, handler->conf, NULL) != cilen) goto err1; cidata[cilen] = '\0'; rsp.data = cidata; rsp.datalen = cilen + 1; rsp.ret = 0; goto out; err1: rsp.ret = -1; out: return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_state: Get current state of the container * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the state on success, < 0 on failure */ int lxc_cmd_get_state(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_STATE } }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0 && stopped) return STOPPED; if (ret < 0) return -1; if (!ret) { WARN("Container \"%s\" has stopped before sending its state", name); return -1; } DEBUG("Container \"%s\" is in \"%s\" state", name, lxc_state2str(PTR_TO_INT(cmd.rsp.data))); return PTR_TO_INT(cmd.rsp.data); } static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) }; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_stop: Stop the container previously started with lxc_start. All * the processes running inside this container will be killed. * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns 0 on success, < 0 on failure */ int lxc_cmd_stop(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_STOP }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) { if (stopped) { INFO("Container \"%s\" is already stopped", name); return 0; } return -1; } /* We do not expect any answer, because we wait for the connection to be * closed. */ if (ret > 0) { errno = -cmd.rsp.ret; SYSERROR("Failed to stop container \"%s\"", name); return -1; } INFO("Container \"%s\" has stopped", name); return 0; } static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; int stopsignal = SIGKILL; struct cgroup_ops *cgroup_ops = handler->cgroup_ops; if (handler->conf->stopsignal) stopsignal = handler->conf->stopsignal; memset(&rsp, 0, sizeof(rsp)); rsp.ret = kill(handler->pid, stopsignal); if (!rsp.ret) { /* We can't just use lxc_unfreeze() since we are already in the * context of handling the STOP cmd in lxc-start, and calling * lxc_unfreeze() would do another cmd (GET_CGROUP) which would * deadlock us. */ if (!cgroup_ops->get_cgroup(cgroup_ops, "freezer")) return 0; if (cgroup_ops->unfreeze(cgroup_ops)) return 0; ERROR("Failed to unfreeze container \"%s\"", handler->name); rsp.ret = -1; } return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_terminal_winch: To process as if a SIGWINCH were received * * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns 0 on success, < 0 on failure */ int lxc_cmd_terminal_winch(const char *name, const char *lxcpath) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_TERMINAL_WINCH }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; return 0; } static int lxc_cmd_terminal_winch_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp = { .data = 0 }; lxc_terminal_sigwinch(SIGWINCH); return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_console: Open an fd to a tty in the container * * @name : name of container to connect to * @ttynum : in: the tty to open or -1 for next available * : out: the tty allocated * @fd : out: file descriptor for master side of pty * @lxcpath : the lxcpath in which the container is running * * Returns fd holding tty allocated on success, < 0 on failure */ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) { int ret, stopped; struct lxc_cmd_console_rsp_data *rspdata; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; if (cmd.rsp.ret < 0) { errno = -cmd.rsp.ret; SYSERROR("Denied access to tty"); ret = -1; goto out; } if (ret == 0) { ERROR("tty number %d invalid, busy or all ttys busy", *ttynum); ret = -1; goto out; } rspdata = cmd.rsp.data; if (rspdata->masterfd < 0) { ERROR("Unable to allocate fd for tty %d", rspdata->ttynum); goto out; } ret = cmd.rsp.ret; /* socket fd */ *fd = rspdata->masterfd; *ttynum = rspdata->ttynum; INFO("Alloced fd %d for tty %d via socket %d", *fd, rspdata->ttynum, ret); out: free(cmd.rsp.data); return ret; } static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int masterfd, ret; struct lxc_cmd_rsp rsp; int ttynum = PTR_TO_INT(req->data); masterfd = lxc_terminal_allocate(handler->conf, fd, &ttynum); if (masterfd < 0) goto out_close; memset(&rsp, 0, sizeof(rsp)); rsp.data = INT_TO_PTR(ttynum); ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)); if (ret < 0) { SYSERROR("Failed to send tty to client"); lxc_terminal_free(handler->conf, fd); goto out_close; } return 0; out_close: /* Special indicator to lxc_cmd_handler() to close the fd and do * related cleanup. */ return 1; } /* * lxc_cmd_get_name: Returns the name of the container * * @hashed_sock_name: hashed socket name * * Returns the name on success, NULL on failure. */ char *lxc_cmd_get_name(const char *hashed_sock_name) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_NAME}, }; ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); rsp.data = (char *)handler->name; rsp.datalen = strlen(handler->name) + 1; rsp.ret = 0; return lxc_cmd_rsp_send(fd, &rsp); } /* * lxc_cmd_get_lxcpath: Returns the lxcpath of the container * * @hashed_sock_name: hashed socket name * * Returns the lxcpath on success, NULL on failure. */ char *lxc_cmd_get_lxcpath(const char *hashed_sock_name) { int ret, stopped; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_GET_LXCPATH}, }; ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); if (ret < 0) return NULL; if (cmd.rsp.ret == 0) return cmd.rsp.data; return NULL; } static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; memset(&rsp, 0, sizeof(rsp)); rsp.ret = 0; rsp.data = (char *)handler->lxcpath; rsp.datalen = strlen(handler->lxcpath) + 1; return lxc_cmd_rsp_send(fd, &rsp); } int lxc_cmd_add_state_client(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int *state_client_fd) { int state, stopped; ssize_t ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_ADD_STATE_CLIENT, .data = states, .datalen = (sizeof(lxc_state_t) * MAX_STATE) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (states[STOPPED] != 0 && stopped != 0) return STOPPED; if (ret < 0) { if (errno != ECONNREFUSED) SYSERROR("Failed to execute command"); return -1; } /* We should now be guaranteed to get an answer from the state sending * function. */ if (cmd.rsp.ret < 0) { errno = -cmd.rsp.ret; SYSERROR("Failed to receive socket fd"); return -1; } state = PTR_TO_INT(cmd.rsp.data); if (state < MAX_STATE) { TRACE("Container is already in requested state %s", lxc_state2str(state)); close(cmd.rsp.ret); return state; } *state_client_fd = cmd.rsp.ret; TRACE("Added state client %d to state client list", cmd.rsp.ret); return MAX_STATE; } static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int ret; struct lxc_cmd_rsp rsp = {0}; if (req->datalen < 0) goto reap_client_fd; if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE)) goto reap_client_fd; if (!req->data) goto reap_client_fd; rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data); if (rsp.ret < 0) goto reap_client_fd; rsp.data = INT_TO_PTR(rsp.ret); ret = lxc_cmd_rsp_send(fd, &rsp); if (ret < 0) goto reap_client_fd; return 0; reap_client_fd: /* Special indicator to lxc_cmd_handler() to close the fd and do related * cleanup. */ return 1; } int lxc_cmd_console_log(const char *name, const char *lxcpath, struct lxc_console_log *log) { int ret, stopped; struct lxc_cmd_console_log data; struct lxc_cmd_rr cmd; data.clear = log->clear; data.read = log->read; data.read_max = *log->read_max; cmd.req.cmd = LXC_CMD_CONSOLE_LOG; cmd.req.data = &data; cmd.req.datalen = sizeof(struct lxc_cmd_console_log); ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) return ret; /* There is nothing to be read from the buffer. So clear any values we * where passed to clearly indicate to the user that nothing went wrong. */ if (cmd.rsp.ret == -ENODATA || cmd.rsp.ret == -EFAULT || cmd.rsp.ret == -ENOENT) { *log->read_max = 0; log->data = NULL; } /* This is a proper error so don't touch any values we were passed. */ if (cmd.rsp.ret < 0) return cmd.rsp.ret; *log->read_max = cmd.rsp.datalen; log->data = cmd.rsp.data; return 0; } static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; uint64_t buffer_size = handler->conf->console.buffer_size; const struct lxc_cmd_console_log *log = req->data; struct lxc_ringbuf *buf = &handler->conf->console.ringbuf; rsp.ret = -EFAULT; rsp.datalen = 0; rsp.data = NULL; if (buffer_size <= 0) goto out; if (log->read || log->write_logfile) rsp.datalen = lxc_ringbuf_used(buf); if (log->read) rsp.data = lxc_ringbuf_get_read_addr(buf); if (log->read_max > 0 && (log->read_max <= rsp.datalen)) rsp.datalen = log->read_max; /* there's nothing to read */ rsp.ret = -ENODATA; if (log->read && (buf->r_off == buf->w_off)) goto out; rsp.ret = 0; if (log->clear) lxc_ringbuf_clear(buf); /* clear the ringbuffer */ else if (rsp.datalen > 0) lxc_ringbuf_move_read_addr(buf, rsp.datalen); out: return lxc_cmd_rsp_send(fd, &rsp); } int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, lxc_state_t state) { int stopped; ssize_t ret; struct lxc_cmd_rr cmd = { .req = { .cmd = LXC_CMD_SERVE_STATE_CLIENTS, .data = INT_TO_PTR(state) }, }; ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) { SYSERROR("Failed to execute command"); return -1; } return 0; } static int lxc_cmd_serve_state_clients_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int ret; lxc_state_t state = PTR_TO_INT(req->data); struct lxc_cmd_rsp rsp = {0}; ret = lxc_serve_state_clients(handler->name, handler, state); if (ret < 0) goto reap_client_fd; ret = lxc_cmd_rsp_send(fd, &rsp); if (ret < 0) goto reap_client_fd; return 0; reap_client_fd: /* Special indicator to lxc_cmd_handler() to close the fd and do related * cleanup. */ return 1; } static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *); callback cb[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, [LXC_CMD_TERMINAL_WINCH] = lxc_cmd_terminal_winch_callback, [LXC_CMD_STOP] = lxc_cmd_stop_callback, [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, [LXC_CMD_CONSOLE_LOG] = lxc_cmd_console_log_callback, [LXC_CMD_SERVE_STATE_CLIENTS] = lxc_cmd_serve_state_clients_callback, }; if (req->cmd >= LXC_CMD_MAX) { ERROR("Undefined command id %d", req->cmd); return -1; } return cb[req->cmd](fd, req, handler); } static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct lxc_epoll_descr *descr, const lxc_cmd_t cmd) { struct lxc_state_client *client; struct lxc_list *cur, *next; lxc_terminal_free(handler->conf, fd); lxc_mainloop_del_handler(descr, fd); if (cmd != LXC_CMD_ADD_STATE_CLIENT) { close(fd); return; } lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { client = cur->elem; if (client->clientfd != fd) continue; /* kick client from list */ lxc_list_del(cur); close(client->clientfd); free(cur->elem); free(cur); /* No need to walk the whole list. If we found the state client * fd there can't be a second one. */ break; } } static int lxc_cmd_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int ret; struct lxc_cmd_req req; void *reqdata = NULL; struct lxc_handler *handler = data; ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); if (ret < 0) { SYSERROR("Failed to receive data on command socket for command " "\"%s\"", lxc_cmd_str(req.cmd)); if (errno == EACCES) { /* We don't care for the peer, just send and close. */ struct lxc_cmd_rsp rsp = {.ret = ret}; lxc_cmd_rsp_send(fd, &rsp); } goto out_close; } if (ret == 0) goto out_close; if (ret != sizeof(req)) { WARN("Failed to receive full command request. Ignoring request " "for \"%s\"", lxc_cmd_str(req.cmd)); ret = -1; goto out_close; } if ((req.datalen > LXC_CMD_DATA_MAX) && (req.cmd != LXC_CMD_CONSOLE_LOG)) { ERROR("Received command data length %d is too large for " "command \"%s\"", req.datalen, lxc_cmd_str(req.cmd)); errno = EFBIG; ret = -EFBIG; goto out_close; } if (req.datalen > 0) { /* LXC_CMD_CONSOLE_LOG needs to be able to allocate data * that exceeds LXC_CMD_DATA_MAX: use malloc() for that. */ if (req.cmd == LXC_CMD_CONSOLE_LOG) reqdata = malloc(req.datalen); else reqdata = alloca(req.datalen); if (!reqdata) { ERROR("Failed to allocate memory for \"%s\" command", lxc_cmd_str(req.cmd)); errno = ENOMEM; ret = -ENOMEM; goto out_close; } ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0); if (ret != req.datalen) { WARN("Failed to receive full command request. Ignoring " "request for \"%s\"", lxc_cmd_str(req.cmd)); ret = LXC_MAINLOOP_ERROR; goto out_close; } req.data = reqdata; } ret = lxc_cmd_process(fd, &req, handler); if (ret) { /* This is not an error, but only a request to close fd. */ ret = LXC_MAINLOOP_CONTINUE; goto out_close; } out: if (req.cmd == LXC_CMD_CONSOLE_LOG && reqdata) free(reqdata); return ret; out_close: lxc_cmd_fd_cleanup(fd, handler, descr, req.cmd); goto out; } static int lxc_cmd_accept(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { int connection; int opt = 1, ret = -1; connection = accept(fd, NULL, 0); if (connection < 0) { SYSERROR("Failed to accept connection to run command"); return LXC_MAINLOOP_ERROR; } ret = fcntl(connection, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set close-on-exec on incoming command connection"); goto out_close; } ret = setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); if (ret < 0) { SYSERROR("Failed to enable necessary credentials on command socket"); goto out_close; } ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data); if (ret) { ERROR("Failed to add command handler"); goto out_close; } out: return ret; out_close: close(connection); goto out; } int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix) { int fd, ret; char path[LXC_AUDS_ADDR_LEN] = {0}; ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, NULL, suffix); if (ret < 0) return -1; TRACE("Creating abstract unix socket \"%s\"", &path[1]); fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); if (fd < 0) { SYSERROR("Failed to create command socket %s", &path[1]); if (errno == EADDRINUSE) ERROR("Container \"%s\" appears to be already running", name); return -1; } ret = fcntl(fd, F_SETFD, FD_CLOEXEC); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC on command socket file descriptor"); close(fd); return -1; } return fd; } int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler) { int ret; int fd = handler->conf->maincmd_fd; ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); if (ret < 0) { ERROR("Failed to add handler for command socket"); close(fd); } return ret; } lxc-3.0.3/src/lxc/state.c0000644061062106075000000000606513375633353012117 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "start.h" #include "utils.h" lxc_log_define(state, lxc); static const char *const strstate[] = { "STOPPED", "STARTING", "RUNNING", "STOPPING", "ABORTING", "FREEZING", "FROZEN", "THAWED", }; const char *lxc_state2str(lxc_state_t state) { if (state < STOPPED || state > MAX_STATE - 1) return NULL; return strstate[state]; } lxc_state_t lxc_str2state(const char *state) { size_t len; lxc_state_t i; len = sizeof(strstate)/sizeof(strstate[0]); for (i = 0; i < len; i++) if (!strcmp(strstate[i], state)) return i; ERROR("invalid state '%s'", state); return -1; } lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { return lxc_cmd_get_state(name, lxcpath); } static int fillwaitedstates(const char *strstates, lxc_state_t *states) { char *token; char *strstates_dup; int state; strstates_dup = strdup(strstates); if (!strstates_dup) return -1; lxc_iterate_parts(token, strstates_dup, "|") { state = lxc_str2state(token); if (state < 0) { free(strstates_dup); return -1; } states[state] = 1; } free(strstates_dup); return 0; } int lxc_wait(const char *lxcname, const char *states, int timeout, const char *lxcpath) { int state = -1; lxc_state_t s[MAX_STATE] = {0}; if (fillwaitedstates(states, s)) return -1; for (;;) { struct timespec onesec = { .tv_sec = 1, .tv_nsec = 0, }; state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout); if (state >= 0) break; if (errno != ECONNREFUSED) { SYSERROR("Failed to receive state from monitor"); return -1; } if (timeout > 0) timeout--; if (timeout == 0) return -1; (void)nanosleep(&onesec, NULL); } TRACE("Retrieved state of container %s", lxc_state2str(state)); if (!s[state]) return -1; return 0; } lxc-3.0.3/src/lxc/lxc.functions.in0000644061062106075000000000210013375633353013742 00000000000000# # lxc: linux Container library # Authors: # Serge Hallyn # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This file contains helpers for the various lxc shell scripts globalconf=@LXC_GLOBAL_CONF@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ lxc_path=`lxc-config lxc.lxcpath` lxc_vg=`lxc-config lxc.bdev.lvm.vg` lxc_zfsroot=`lxc-config lxc.bdev.zfs.root` lxc-3.0.3/src/lxc/raw_syscalls.c0000644061062106075000000000506713375633353013506 00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include "config.h" #include "macro.h" #include "raw_syscalls.h" int lxc_raw_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags) { #ifdef __NR_execveat syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); #else errno = ENOSYS; #endif return -1; } /* * This is based on raw_clone in systemd but adapted to our needs. This uses * copy on write semantics and doesn't pass a stack. CLONE_VM is tricky and * doesn't really matter to us so disallow it. * * The nice thing about this is that we get fork() behavior. That is * lxc_raw_clone() returns 0 in the child and the child pid in the parent. */ pid_t lxc_raw_clone(unsigned long flags) { /* * These flags don't interest at all so we don't jump through any hoops * of retrieving them and passing them to the kernel. */ errno = EINVAL; if ((flags & (CLONE_VM | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS))) return -EINVAL; #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) /* On s390/s390x and cris the order of the first and second arguments * of the system call is reversed. */ return (int)syscall(__NR_clone, NULL, flags | SIGCHLD); #elif defined(__sparc__) && defined(__arch64__) { /* * sparc64 always returns the other process id in %o0, and a * boolean flag whether this is the child or the parent in %o1. * Inline assembly is needed to get the flag returned in %o1. */ int in_child; int child_pid; asm volatile("mov %2, %%g1\n\t" "mov %3, %%o0\n\t" "mov 0 , %%o1\n\t" "t 0x6d\n\t" "mov %%o1, %0\n\t" "mov %%o0, %1" : "=r"(in_child), "=r"(child_pid) : "i"(__NR_clone), "r"(flags | SIGCHLD) : "%o1", "%o0", "%g1"); if (in_child) return 0; else return child_pid; } #elif defined(__ia64__) /* On ia64 the stack and stack size are passed as separate arguments. */ return (int)syscall(__NR_clone, flags | SIGCHLD, NULL, prctl_arg(0)); #else return (int)syscall(__NR_clone, flags | SIGCHLD, NULL); #endif } pid_t lxc_raw_clone_cb(int (*fn)(void *), void *args, unsigned long flags) { pid_t pid; pid = lxc_raw_clone(flags); if (pid < 0) return -1; /* * exit() is not thread-safe and might mess with the parent's signal * handlers and other stuff when exec() fails. */ if (pid == 0) _exit(fn(args)); return pid; } lxc-3.0.3/src/lxc/af_unix.c0000644061062106075000000001520613375633353012425 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "raw_syscalls.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(af_unix, lxc); static ssize_t lxc_abstract_unix_set_sockaddr(struct sockaddr_un *addr, const char *path) { size_t len; if (!addr || !path) { errno = EINVAL; return -1; } /* Clear address structure */ memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; len = strlen(&path[1]); /* do not enforce \0-termination */ if (len >= INT_MAX || len >= sizeof(addr->sun_path)) { errno = ENAMETOOLONG; return -1; } /* do not enforce \0-termination */ memcpy(&addr->sun_path[1], &path[1], len); return len; } int lxc_abstract_unix_open(const char *path, int type, int flags) { int fd, ret; ssize_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, type, 0); if (fd < 0) return -1; if (!path) return fd; len = lxc_abstract_unix_set_sockaddr(&addr, path); if (len < 0) { int saved_errno = errno; close(fd); errno = saved_errno; return -1; } ret = bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { int saved_errno = errno; close(fd); errno = saved_errno; return -1; } if (type == SOCK_STREAM) { ret = listen(fd, 100); if (ret < 0) { int saved_errno = errno; close(fd); errno = saved_errno; return -1; } } return fd; } void lxc_abstract_unix_close(int fd) { close(fd); } int lxc_abstract_unix_connect(const char *path) { int fd, ret; ssize_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; len = lxc_abstract_unix_set_sockaddr(&addr, path); if (len < 0) { int saved_errno = errno; close(fd); errno = saved_errno; return -1; } ret = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { int saved_errno = errno; close(fd); errno = saved_errno; return -1; } return fd; } int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) { errno = ENOMEM; return -1; } msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = sendmsg(fd, &msg, MSG_NOSIGNAL); free(cmsgbuf); return ret; } int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) { errno = ENOMEM; return -1; } msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(fd, &msg, 0); if (ret <= 0) goto out; cmsg = CMSG_FIRSTHDR(&msg); memset(recvfds, -1, num_recvfds * sizeof(int)); if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); out: free(cmsgbuf); return ret; } int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred = { .pid = lxc_raw_getpid(), .uid = getuid(), .gid = getgid(), }; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; return sendmsg(fd, &msg, MSG_NOSIGNAL); } int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred; int ret; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(fd, &msg, 0); if (ret <= 0) goto out; cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); if (cred.uid && (cred.uid != getuid() || cred.gid != getgid())) { INFO("Message denied for '%d/%d'", cred.uid, cred.gid); errno = EACCES; return -1; } } out: return ret; } lxc-3.0.3/src/lxc/mainloop.c0000644061062106075000000000701013375633353012604 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include "config.h" #include "mainloop.h" struct mainloop_handler { lxc_mainloop_callback_t callback; int fd; void *data; }; #define MAX_EVENTS 10 int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) { int i, nfds, ret; struct mainloop_handler *handler; struct epoll_event events[MAX_EVENTS]; for (;;) { nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms); if (nfds < 0) { if (errno == EINTR) continue; return -1; } for (i = 0; i < nfds; i++) { handler = events[i].data.ptr; /* If the handler returns a positive value, exit the * mainloop. */ ret = handler->callback(handler->fd, events[i].events, handler->data, descr); if (ret == LXC_MAINLOOP_CLOSE) return 0; } if (nfds == 0) return 0; if (lxc_list_empty(&descr->handlers)) return 0; } } int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, void *data) { struct epoll_event ev; struct mainloop_handler *handler; struct lxc_list *item; handler = malloc(sizeof(*handler)); if (!handler) return -1; handler->callback = callback; handler->fd = fd; handler->data = data; ev.events = EPOLLIN; ev.data.ptr = handler; if (epoll_ctl(descr->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) goto out_free_handler; item = malloc(sizeof(*item)); if (!item) goto out_free_handler; item->elem = handler; lxc_list_add(&descr->handlers, item); return 0; out_free_handler: free(handler); return -1; } int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd) { struct mainloop_handler *handler; struct lxc_list *iterator; lxc_list_for_each(iterator, &descr->handlers) { handler = iterator->elem; if (handler->fd == fd) { /* found */ if (epoll_ctl(descr->epfd, EPOLL_CTL_DEL, fd, NULL)) return -1; lxc_list_del(iterator); free(iterator->elem); free(iterator); return 0; } } return -1; } int lxc_mainloop_open(struct lxc_epoll_descr *descr) { /* hint value passed to epoll create */ descr->epfd = epoll_create1(EPOLL_CLOEXEC); if (descr->epfd < 0) return -1; lxc_list_init(&descr->handlers); return 0; } int lxc_mainloop_close(struct lxc_epoll_descr *descr) { struct lxc_list *iterator, *next; iterator = descr->handlers.next; while (iterator != &descr->handlers) { next = iterator->next; lxc_list_del(iterator); free(iterator->elem); free(iterator); iterator = next; } if (descr->epfd >= 0) return close(descr->epfd); return 0; } lxc-3.0.3/src/lxc/lxccontainer.h0000644061062106075000000010316313375633353013472 00000000000000/*! \file * * liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONTAINER_H #define __LXC_CONTAINER_H #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define LXC_CLONE_KEEPNAME (1 << 0) /*!< Do not edit the rootfs to change the hostname */ #define LXC_CLONE_KEEPMACADDR (1 << 1) /*!< Do not change the MAC address on network interfaces */ #define LXC_CLONE_SNAPSHOT (1 << 2) /*!< Snapshot the original filesystem(s) */ #define LXC_CLONE_KEEPBDEVTYPE (1 << 3) /*!< Use the same bdev type */ #define LXC_CLONE_MAYBE_SNAPSHOT (1 << 4) /*!< Snapshot only if bdev supports it, else copy */ #define LXC_CLONE_MAXFLAGS (1 << 5) /*!< Number of \c LXC_CLONE_* flags */ #define LXC_CREATE_QUIET (1 << 0) /*!< Redirect \c stdin to \c /dev/zero and \c stdout and \c stderr to \c /dev/null */ #define LXC_CREATE_MAXFLAGS (1 << 1) /*!< Number of \c LXC_CREATE* flags */ struct bdev_specs; struct lxc_snapshot; struct lxc_lock; struct migrate_opts; struct lxc_console_log; /*! * An LXC container. * * Note that changing the order of struct members is an API change, as callers * will end up having the wrong offset when calling a function. So when making * changes, whenever possible stick to simply appending new members. */ struct lxc_container { /* private fields */ /*! * \private * Name of container. */ char *name; /*! * \private * Full path to configuration file. */ char *configfile; /*! * \private * File to store pid. */ char *pidfile; /*! * \private * Container semaphore lock. */ struct lxc_lock *slock; /*! * \private * Container private lock. */ struct lxc_lock *privlock; /*! * \private * Number of references to this container. * \note protected by privlock. */ int numthreads; /*! * \private * Container configuration. * * \internal FIXME: do we want the whole lxc_handler? */ struct lxc_conf *lxc_conf; /* public fields */ /*! Human-readable string representing last error */ char *error_string; /*! Last error number */ int error_num; /*! Whether container wishes to be daemonized */ bool daemonize; /*! Full path to configuration file */ char *config_path; /*! * \brief Determine if \c /var/lib/lxc/$name/config exists. * * \param c Container. * * \return \c true if container is defined, else \c false. */ bool (*is_defined)(struct lxc_container *c); /*! * \brief Determine state of container. * * \param c Container. * * \return Static upper-case string representing state of container. * * \note Returned string must not be freed. */ const char *(*state)(struct lxc_container *c); /*! * \brief Determine if container is running. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*is_running)(struct lxc_container *c); /*! * \brief Freeze running container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*freeze)(struct lxc_container *c); /*! * \brief Thaw a frozen container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*unfreeze)(struct lxc_container *c); /*! * \brief Determine process ID of the containers init process. * * \param c Container. * * \return pid of init process as seen from outside the * container. */ pid_t (*init_pid)(struct lxc_container *c); /*! * \brief Load the specified configuration for the container. * * \param c Container. * \param alt_file Full path to alternate configuration file, or * \c NULL to use the default configuration file. * * \return \c true on success, else \c false. */ bool (*load_config)(struct lxc_container *c, const char *alt_file); /*! * \brief Start the container. * * \param c Container. * \param useinit Use lxcinit rather than \c /sbin/init. * \param argv Array of arguments to pass to init. * * \return \c true on success, else \c false. */ bool (*start)(struct lxc_container *c, int useinit, char * const argv[]); /*! * \brief Start the container (list variant). * * \param c Container. * \param useinit Use lxcinit rather than \c /sbin/init. * \param ... Command-line to pass to init (must end in \c NULL). * * \return \c true on success, else \c false. * * \note Identical to \ref start except that that the init * arguments are specified via a list rather than an array of * pointers. */ bool (*startl)(struct lxc_container *c, int useinit, ...); /*! * \brief Stop the container. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*stop)(struct lxc_container *c); /*! * \brief Change whether the container wants to run disconnected * from the terminal. * * \param c Container. * \param state Value for the daemonize bit (0 or 1). * * \return \c true on success, else \c false. */ bool (*want_daemonize)(struct lxc_container *c, bool state); /*! * \brief Change whether the container wishes all file descriptors * to be closed on startup. * * \param c Container. * \param state Value for the close_all_fds bit (0 or 1). * * \return \c true on success, else \c false. */ bool (*want_close_all_fds)(struct lxc_container *c, bool state); /*! * \brief Return current config file name. * * \param c Container. * * \return config file name, or \c NULL on error. * * \note The result is allocated, so the caller must free the result. */ char *(*config_file_name)(struct lxc_container *c); /*! * \brief Wait for container to reach a particular state. * * \param c Container. * \param state State to wait for. * \param timeout Timeout in seconds. * * \return \c true if state reached within \p timeout, else \c false. * * \note A \p timeout of \c -1 means wait forever. A \p timeout * of \c 0 means do not wait. */ bool (*wait)(struct lxc_container *c, const char *state, int timeout); /*! * \brief Set a key/value configuration option. * * \param c Container. * \param key Name of option to set. * \param value Value of \p name to set. * * \return \c true on success, else \c false. */ bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value); /*! * \brief Delete the container. * * \param c Container. * * \return \c true on success, else \c false. * * \note Container must be stopped and have no dependent snapshots. */ bool (*destroy)(struct lxc_container *c); /*! * \brief Save configuration to a file. * * \param c Container. * \param alt_file Full path to file to save configuration in. * * \return \c true on success, else \c false. */ bool (*save_config)(struct lxc_container *c, const char *alt_file); /*! * \brief Create a container. * * \param c Container (with lxcpath, name and a starting * configuration set). * \param t Template to execute to instantiate the root * filesystem and adjust the configuration. * \param bdevtype Backing store type to use (if \c NULL, \c dir will be used). * \param specs Additional parameters for the backing store (for * example LVM volume group to use). * \param flags \c LXC_CREATE_* options (currently only \ref * LXC_CREATE_QUIET is supported). * \param argv Arguments to pass to the template, terminated by \c NULL (if no * arguments are required, just pass \c NULL). * * \return \c true on success, else \c false. */ bool (*create)(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]); /*! * \brief Create a container (list variant). * * \param c Container (with lxcpath, name and a starting * configuration set). * \param t Template to execute to instantiate the root * filesystem and adjust the configuration. * \param bdevtype Backing store type to use (if \c NULL, \c dir will be used). * \param specs Additional parameters for the backing store (for * example LVM volume group to use). * \param flags \c LXC_CREATE_* options (currently only \ref * LXC_CREATE_QUIET is supported). * \param ... Command-line to pass to init (must end in \c NULL). * * \return \c true on success, else \c false. * * \note Identical to \ref create except that the template * arguments are specified as a list rather than an array of * pointers. */ bool (*createl)(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, ...); /*! * \brief Rename a container * * \param c Container. * \param newname New name to be used for the container. * * \return \c true on success, else \c false. */ bool (*rename)(struct lxc_container *c, const char *newname); /*! * \brief Request the container reboot by sending it \c SIGINT. * * \param c Container. * * \return \c true if reboot request successful, else \c false. */ bool (*reboot)(struct lxc_container *c); /*! * \brief Request the container shutdown by sending it \c * SIGPWR. * * \param c Container. * \param timeout Seconds to wait before returning false. * (-1 to wait forever, 0 to avoid waiting). * * \return \c true if the container was shutdown successfully, else \c false. */ bool (*shutdown)(struct lxc_container *c, int timeout); /*! * \brief Completely clear the containers in-memory configuration. * * \param c Container. */ void (*clear_config)(struct lxc_container *c); /*! * \brief Clear a configuration item. * * \param c Container. * \param key Name of option to clear. * * \return \c true on success, else \c false. * * \note Analog of \ref set_config_item. */ bool (*clear_config_item)(struct lxc_container *c, const char *key); /*! * \brief Retrieve the value of a config item. * * \param c Container. * \param key Name of option to get. * \param[out] retv Caller-allocated buffer to write value of \p key * into (or \c NULL to determine length of value). * \param inlen Length of \p retv (may be zero). * * \return Length of config items value, or < 0 on error. * * \note The caller can (and should) determine how large a buffer to allocate for * \p retv by initially passing its value as \c NULL and considering the return value. * This function can then be called again passing a newly-allocated suitably-sized buffer. * \note If \p retv is NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, nothing will be written to \p retv and still return * the length of config item value. */ int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen); /*! * \brief Retrieve the value of a config item from running container. * * \param c Container. * \param key Name of option to get. * * \return the item or NULL on error. * * \note Returned string must be freed by the caller. */ char* (*get_running_config_item)(struct lxc_container *c, const char *key); /*! * \brief Retrieve a list of config item keys given a key * prefix. * * \param c Container. * \param key Name of option to get. * \param[out] retv Caller-allocated buffer to write list of keys to * (or \c NULL to determine overall length of keys list). * \param inlen Length of \p retv (may be zero). * * \return Length of keys list, or < 0 on error. * * \note The list values written to \p retv are separated by * a newline character ('\\n'). * \note The caller can (and should) determine how large a buffer to allocate for * \p retv by initially passing its value as \c NULL and considering the return value. * This function can then be called again passing a newly-allocated suitably-sized buffer. * \note If \p retv is NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, the value written * to \p retv will be truncated. */ int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen); /*! * \brief Obtain a list of network interfaces. * \param c Container. * * \return Newly-allocated array of network interfaces, or \c * NULL on error. * * \note The returned array is allocated, so the caller must free it. * \note The returned array is terminated with a \c NULL entry. */ char** (*get_interfaces)(struct lxc_container *c); /*! * \brief Determine the list of container IP addresses. * * \param c Container. * \param interface Network interface name to consider. * \param family Network family (for example "inet", "inet6"). * \param scope IPv6 scope id (ignored if \p family is not "inet6"). * * \return Newly-allocated array of network interfaces, or \c * NULL on error. * * \note The returned array is allocated, so the caller must free it. * \note The returned array is terminated with a \c NULL entry. */ char** (*get_ips)(struct lxc_container *c, const char* interface, const char* family, int scope); /*! * \brief Retrieve the specified cgroup subsystem value for the container. * * \param c Container. * \param subsys cgroup subsystem to retrieve. * \param[out] retv Caller-allocated buffer to write value of \p * subsys into (or \c NULL to determine length of value). * \param inlen length of \p retv (may be zero). * * \return Length of \p subsys value, or < 0 on error. * * \note If \p retv is \c NULL, \p inlen is ignored. * \note If \p inlen is smaller than required, the value written * to \p retv will be truncated. */ int (*get_cgroup_item)(struct lxc_container *c, const char *subsys, char *retv, int inlen); /*! * \brief Set the specified cgroup subsystem value for the container. * * \param c Container. * \param subsys cgroup subsystem to consider. * \param value Value to set for \p subsys. * * \return \c true on success, else \c false. */ bool (*set_cgroup_item)(struct lxc_container *c, const char *subsys, const char *value); /*! * \brief Determine full path to the containers configuration file. * Each container can have a custom configuration path. However * by default it will be set to either the \c LXCPATH configure * variable, or the lxcpath value in the \c LXC_GLOBAL_CONF configuration * file (i.e. \c /etc/lxc/lxc.conf). * The value for a specific container can be changed using * \ref set_config_path. There is no other way to specify this in general at the moment. * * \param c Container. * * \return Static string representing full path to configuration * file. * * \note Returned string must not be freed. */ const char *(*get_config_path)(struct lxc_container *c); /*! * \brief Set the full path to the containers configuration * file. * * \param c Container. * \param path Full path to configuration file. * * \return \c true on success, else \c false. */ bool (*set_config_path)(struct lxc_container *c, const char *path); /*! * \brief Copy a stopped container. * * \param c Original container. * \param newname New name for the container. If \c NULL, the same * name is used and a new lxcpath MUST be specified. * \param lxcpath lxcpath in which to create the new container. If * \c NULL, the original container's lxcpath will be used. * (XXX: should we use the default instead?) * \param flags Additional \c LXC_CLONE* flags to change the cloning behaviour: * - \ref LXC_CLONE_KEEPNAME * - \ref LXC_CLONE_KEEPMACADDR * - \ref LXC_CLONE_SNAPSHOT * \param bdevtype Optionally force the cloned bdevtype to a specified plugin. * By default the original is used (subject to snapshot requirements). * \param bdevdata Information about how to create the new storage * (i.e. fstype and fsdata). * \param newsize In case of a block device backing store, an * optional size. If \c 0, the original backing store's size will * be used if possible. Note this only applies to the rootfs. For * any other filesystems, the original size will be duplicated. * \param hookargs Additional arguments to pass to the clone hook script. * * \return Newly-allocated copy of container \p c, or \p NULL on * error. * * \note If devtype was not specified, and \p flags contains \ref * LXC_CLONE_SNAPSHOT then use the native \p bdevtype if possible, * else use an overlayfs. */ struct lxc_container *(*clone)(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs); /*! * \brief Allocate a console tty for the container. * * \param c Container. * \param[in,out] ttynum Terminal number to attempt to allocate, * or \c -1 to allocate the first available tty. * \param[out] masterfd File descriptor referring to the master side of the pty. * * \return tty file descriptor number on success, or \c -1 on * failure. * * \note On successful return, \p ttynum will contain the tty number * that was allocated. * \note The returned file descriptor is used to keep the tty * allocated. The caller should call close(2) on the returned file * descriptor when no longer required so that it may be allocated * by another caller. */ int (*console_getfd)(struct lxc_container *c, int *ttynum, int *masterfd); /*! * \brief Allocate and run a console tty. * * \param c Container. * \param ttynum Terminal number to attempt to allocate, \c -1 to * allocate the first available tty or \c 0 to allocate the * console. * \param stdinfd File descriptor to read input from. * \param stdoutfd File descriptor to write output to. * \param stderrfd File descriptor to write error output to. * \param escape The escape character (1 == 'a', 2 == 'b', ...). * * \return \c 0 on success, \c -1 on failure. * * \note This function will not return until the console has been * exited by the user. */ int (*console)(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape); /*! * \brief Create a sub-process attached to a container and run * a function inside it. * * \param c Container. * \param exec_function Function to run. * \param exec_payload Data to pass to \p exec_function. * \param options \ref lxc_attach_options_t. * \param[out] attached_process Process ID of process running inside * container \p c that is running \p exec_function. * * \return \c 0 on success, \c -1 on error. */ int (*attach)(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process); /*! * \brief Run a program inside a container and wait for it to exit. * * \param c Container. * \param options See \ref attach options. * \param program Full path inside container of program to run. * \param argv Array of arguments to pass to \p program. * * \return \c waitpid(2) status of exited process that ran \p * program, or \c -1 on error. */ int (*attach_run_wait)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]); /*! * \brief Run a program inside a container and wait for it to exit (list variant). * * \param c Container. * \param options See \ref attach options. * \param program Full path inside container of program to run. * \param ... Command-line to pass to \p program (must end in \c NULL). * * \return \c waitpid(2) status of exited process that ran \p * program, or \c -1 on error. */ int (*attach_run_waitl)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...); /*! * \brief Create a container snapshot. * * Assuming default paths, snapshots will be created as * \c /var/lib/lxc/\/snaps/snap\ * where \c \ represents the container name and \c \ * represents the zero-based snapshot number. * * \param c Container. * \param commentfile Full path to file containing a description * of the snapshot. * * \return -1 on error, or zero-based snapshot number. * * \note \p commentfile may be \c NULL but this is discouraged. */ int (*snapshot)(struct lxc_container *c, const char *commentfile); /*! * \brief Obtain a list of container snapshots. * * \param c Container. * \param[out] snapshots Dynamically-allocated Array of lxc_snapshot's. * * \return Number of snapshots. * * \note The array returned in \p snapshots is allocated, so the caller must free it. * \note To free an individual snapshot as returned in \p * snapshots, call the snapshots \c free function (see \c src/tests/snapshot.c for an example). */ int (*snapshot_list)(struct lxc_container *c, struct lxc_snapshot **snapshots); /*! * \brief Create a new container based on a snapshot. * * The restored container will be a copy (not snapshot) of the snapshot, * and restored in the lxcpath of the original container. * \param c Container. * \param snapname Name of snapshot. * \param newname Name to be used for the restored snapshot. * \return \c true on success, else \c false. * \warning If \p newname is the same as the current container * name, the container will be destroyed. However, this will * fail if the snapshot is overlay-based, since the snapshots * will pin the original container. * \note As an example, if the container exists as \c /var/lib/lxc/c1, snapname might be \c 'snap0' * (representing \c /var/lib/lxc/c1/snaps/snap0). If \p newname is \p c2, * then \c snap0 will be copied to \c /var/lib/lxc/c2. */ bool (*snapshot_restore)(struct lxc_container *c, const char *snapname, const char *newname); /*! * \brief Destroy the specified snapshot. * * \param c Container. * \param snapname Name of snapshot. * * \return \c true on success, else \c false. */ bool (*snapshot_destroy)(struct lxc_container *c, const char *snapname); /*! * \brief Determine if the caller may control the container. * * \param c Container. * * \return \c false if there is a control socket for the * container monitor and the caller may not access it, otherwise * returns \c true. */ bool (*may_control)(struct lxc_container *c); /*! * \brief Add specified device to the container. * * \param c Container. * \param src_path Full path of the device. * \param dest_path Alternate path in the container (or \p NULL * to use \p src_path). * * \return \c true on success, else \c false. */ bool (*add_device_node)(struct lxc_container *c, const char *src_path, const char *dest_path); /*! * \brief Remove specified device from the container. * * \param c Container. * \param src_path Full path of the device. * \param dest_path Alternate path in the container (or \p NULL * to use \p src_path). * * \return \c true on success, else \c false. */ bool (*remove_device_node)(struct lxc_container *c, const char *src_path, const char *dest_path); /* Post LXC-1.0 additions */ /*! * \brief Add specified netdev to the container. * * \param c Container. * \param dev name of net device. * * \return \c true on success, else \c false. */ bool (*attach_interface)(struct lxc_container *c, const char *dev, const char *dst_dev); /*! * \brief Remove specified netdev from the container. * * \param c Container. * \param dev name of net device. * * \return \c true on success, else \c false. */ bool (*detach_interface)(struct lxc_container *c, const char *dev, const char *dst_dev); /*! * \brief Checkpoint a container. * * \param c Container. * \param directory The directory to dump the container to. * \param stop Whether or not to stop the container after checkpointing. * \param verbose Enable criu's verbose logs. * * \return \c true on success, else \c false. * present at compile time). */ bool (*checkpoint)(struct lxc_container *c, char *directory, bool stop, bool verbose); /*! * \brief Restore a container from a checkpoint. * * \param c Container. * \param directory The directory to restore the container from. * \param verbose Enable criu's verbose logs. * * \return \c true on success, else \c false. * */ bool (*restore)(struct lxc_container *c, char *directory, bool verbose); /*! * \brief Delete the container and all its snapshots. * * \param c Container. * * \return \c true on success, else \c false. * * \note Container must be stopped. */ bool (*destroy_with_snapshots)(struct lxc_container *c); /*! * \brief Destroy all the container's snapshot. * * \param c Container. * * \return \c true on success, else \c false. */ bool (*snapshot_destroy_all)(struct lxc_container *c); /* Post LXC-1.1 additions */ /*! * \brief An API call to perform various migration operations * * \param cmd One of the MIGRATE_ constants. * \param opts A migrate_opts struct filled with relevant options. * \param size The size of the migrate_opts struct, i.e. sizeof(struct migrate_opts). * * \return \c 0 on success, nonzero on failure. */ int (*migrate)(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size); /*! * \brief Query the console log of a container. * * \param c Container. * \param opts A lxc_console_log struct filled with relevant options. * * \return \c 0 on success, nonzero on failure. */ int (*console_log)(struct lxc_container *c, struct lxc_console_log *log); /*! * \brief Request the container reboot by sending it \c SIGINT. * * \param c Container. * \param timeout Seconds to wait before returning false. * (-1 to wait forever, 0 to avoid waiting). * * \return \c true if the container was rebooted successfully, else \c false. */ bool (*reboot2)(struct lxc_container *c, int timeout); }; /*! * \brief An LXC container snapshot. */ struct lxc_snapshot { char *name; /*!< Name of snapshot */ char *comment_pathname; /*!< Full path to snapshots comment file (may be \c NULL) */ char *timestamp; /*!< Time snapshot was created */ char *lxcpath; /*!< Full path to LXCPATH for snapshot */ /*! * \brief De-allocate the snapshot. * \param s snapshot. */ void (*free)(struct lxc_snapshot *s); }; /*! * \brief Specifications for how to create a new backing store */ struct bdev_specs { char *fstype; /*!< Filesystem type */ uint64_t fssize; /*!< Filesystem size in bytes */ struct { char *zfsroot; /*!< ZFS root path */ } zfs; struct { char *vg; /*!< LVM Volume Group name */ char *lv; /*!< LVM Logical Volume name */ char *thinpool; /*!< LVM thin pool to use, if any */ } lvm; char *dir; /*!< Directory path */ struct { char *rbdname; /*!< RBD image name */ char *rbdpool; /*!< Ceph pool name */ } rbd; }; /*! * \brief Commands for the migrate API call. */ enum { MIGRATE_PRE_DUMP, MIGRATE_DUMP, MIGRATE_RESTORE, MIGRATE_FEATURE_CHECK, }; /*! * \brief Available feature checks. */ #define FEATURE_MEM_TRACK (1ULL << 0) #define FEATURE_LAZY_PAGES (1ULL << 1) /*! * \brief Options for the migrate API call. */ struct migrate_opts { /* new members should be added at the end */ char *directory; bool verbose; bool stop; /* stop the container after dump? */ char *predump_dir; /* relative to directory above */ char *pageserver_address; /* where should memory pages be send? */ char *pageserver_port; /* This flag indicates whether or not the container's rootfs will have * the same inodes on checkpoint and restore. In the case of e.g. zfs * send or btrfs send, or an LVM snapshot, this will be true, but it * won't if e.g. you rsync the filesystems between two machines. */ bool preserves_inodes; /* Path to an executable script that will be registered as a criu * "action script" */ char *action_script; /* If CRIU >= 2.4 is detected the option to skip in-flight connections * will be enabled by default. The flag 'disable_skip_in_flight' will * unconditionally disable this feature. In-flight connections are * not fully established TCP connections: SYN, SYN-ACK */ bool disable_skip_in_flight; /* This is the maximum file size for deleted files (which CRIU calls * "ghost" files) that will be handled. 0 indicates the CRIU default, * which at this time is 1MB. */ uint64_t ghost_limit; /* Some features cannot be checked by comparing the CRIU version. * Features like dirty page tracking or userfaultfd depend on * the architecture/kernel/criu combination. This is a bitmask * in which the desired feature checks can be encoded. */ uint64_t features_to_check; }; struct lxc_console_log { /* Clear the console log. */ bool clear; /* Retrieve the console log. */ bool read; /* This specifies the maximum size to read from the ringbuffer. Setting * it to 0 means that the a read can be as big as the whole ringbuffer. * On return callers can check how many bytes were actually read. * If "read" and "clear" are set to false and a non-zero value is * specified then up to "read_max" bytes of data will be discarded from * the ringbuffer. */ uint64_t *read_max; /* Data that was read from the ringbuffer. If "read_max" is 0 on return * "data" is invalid. */ char *data; }; /*! * \brief Create a new container. * * \param name Name to use for container. * \param configpath Full path to configuration file to use. * * \return Newly-allocated container, or \c NULL on error. */ struct lxc_container *lxc_container_new(const char *name, const char *configpath); /*! * \brief Add a reference to the specified container. * * \param c Container. * * \return \c true on success, \c false on error. */ int lxc_container_get(struct lxc_container *c); /*! * \brief Drop a reference to the specified container. * * \param c Container. * * \return \c 0 on success, \c 1 if reference was successfully dropped * and container has been freed, and \c -1 on error. * * \warning If \c 1 is returned, \p c is no longer valid. */ int lxc_container_put(struct lxc_container *c); /*! * \brief Obtain a list of all container states. * \param[out] states Caller-allocated array to hold all states (may be \c NULL). * * \return Number of container states. * * \note Passing \c NULL for \p states allows the caller to first * calculate how many states there are before calling the function again, the second time * providing a suitably-sized array to store the static string pointers * in. * \note The \p states array should be freed by the caller, but not the strings the elements point to. */ int lxc_get_wait_states(const char **states); /*! * \brief Get the value for a global config key * * \param key The name of the config key * * \return String representing the current value for the key. */ const char *lxc_get_global_config_item(const char *key); /*! * \brief Determine version of LXC. * \return Static string representing version of LXC in use. * * \note Returned string must not be freed. */ const char *lxc_get_version(void); /*! * \brief Get a list of defined containers in a lxcpath. * * \param lxcpath lxcpath under which to look. * \param names If not \c NULL, then a list of container names will be returned here. * \param cret If not \c NULL, then a list of lxc_containers will be returned here. * * \return Number of containers found, or \c -1 on error. * * \note Values returned in \p cret are sorted by container name. */ int list_defined_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); /*! * \brief Get a list of active containers for a given lxcpath. * * \param lxcpath Full \c LXCPATH path to consider. * \param[out] names Dynamically-allocated array of container names. * \param[out] cret Dynamically-allocated list of containers. * * \return Number of containers found, or -1 on error. * * \note Some of the containers may not be "defined". * \note Values returned in \p cret are sorted by container name. * \note \p names and \p cret may both (or either) be specified as \c NULL. * \note \p names and \p cret must be freed by the caller. */ int list_active_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); /*! * \brief Get a complete list of all containers for a given lxcpath. * * \param lxcpath Full \c LXCPATH path to consider. * \param[out] names Dynamically-allocated array of container name. * \param[out] cret Dynamically-allocated list of containers. * * \return Number of containers, or -1 on error. * * \note Some of the containers may not be "defined". * \note Values returned in \p cret are sorted by container name. * \note \p names and \p cret may both (or either) be specified as \c NULL. * \note \p names and \p cret must be freed by the caller. */ int list_all_containers(const char *lxcpath, char ***names, struct lxc_container ***cret); 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 #endif lxc-3.0.3/src/lxc/nl.c0000644061062106075000000001736513375633353011415 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "nl.h" lxc_log_define(nl, lxc); extern size_t nlmsg_len(const struct nlmsg *nlmsg) { return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN; } extern void *nlmsg_data(struct nlmsg *nlmsg) { char *data = ((char *)nlmsg) + NLMSG_HDRLEN; if (!nlmsg_len(nlmsg)) return NULL; return data; } static int nla_put(struct nlmsg *nlmsg, int attr, const void *data, size_t len) { struct rtattr *rta; size_t rtalen = RTA_LENGTH(len); size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen); if (tlen > nlmsg->cap) return -ENOMEM; rta = NLMSG_TAIL(nlmsg->nlmsghdr); rta->rta_type = attr; rta->rta_len = rtalen; if (data && len) memcpy(RTA_DATA(rta), data, len); nlmsg->nlmsghdr->nlmsg_len = tlen; return 0; } extern int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size) { return nla_put(nlmsg, attr, data, size); } extern int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string) { return nla_put(nlmsg, attr, string, strlen(string) + 1); } extern int nla_put_u32(struct nlmsg *nlmsg, int attr, int value) { return nla_put(nlmsg, attr, &value, sizeof(value)); } extern int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value) { return nla_put(nlmsg, attr, &value, 2); } extern int nla_put_attr(struct nlmsg *nlmsg, int attr) { return nla_put(nlmsg, attr, NULL, 0); } struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr) { struct rtattr *rtattr = NLMSG_TAIL(nlmsg->nlmsghdr); if (nla_put_attr(nlmsg, attr)) return NULL; return rtattr; } void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr) { attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr; } extern struct nlmsg *nlmsg_alloc(size_t size) { struct nlmsg *nlmsg; size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size); nlmsg = (struct nlmsg *)malloc(sizeof(struct nlmsg)); if (!nlmsg) return NULL; nlmsg->nlmsghdr = (struct nlmsghdr *)malloc(len); if (!nlmsg->nlmsghdr) goto errout; memset(nlmsg->nlmsghdr, 0, len); nlmsg->cap = len; nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN; return nlmsg; errout: free(nlmsg); return NULL; } extern void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len) { void *buf; size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len; size_t tlen = NLMSG_ALIGN(len); if (nlmsg_len + tlen > nlmsg->cap) return NULL; buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len; nlmsg->nlmsghdr->nlmsg_len += tlen; if (tlen > len) memset(buf + len, 0, tlen - len); return buf; } extern struct nlmsg *nlmsg_alloc_reserve(size_t size) { struct nlmsg *nlmsg; nlmsg = nlmsg_alloc(size); if (!nlmsg) return NULL; /* Just set message length to cap directly. */ nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap; return nlmsg; } extern void nlmsg_free(struct nlmsg *nlmsg) { if (!nlmsg) return; free(nlmsg->nlmsghdr); free(nlmsg); } extern int __netlink_recv(struct nl_handler *handler, struct nlmsghdr *nlmsghdr) { int ret; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = nlmsghdr, .iov_len = nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; again: ret = recvmsg(handler->fd, &msg, 0); if (ret < 0) { if (errno == EINTR) goto again; return -1; } if (!ret) return 0; if (msg.msg_flags & MSG_TRUNC && (ret == nlmsghdr->nlmsg_len)) { errno = EMSGSIZE; ret = -1; } return ret; } extern int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer) { return __netlink_recv(handler, answer->nlmsghdr); } extern int __netlink_send(struct nl_handler *handler, struct nlmsghdr *nlmsghdr) { int ret; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = nlmsghdr, .iov_len = nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; ret = sendmsg(handler->fd, &msg, MSG_NOSIGNAL); if (ret < 0) return -1; return ret; } extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg) { return __netlink_send(handler, nlmsg->nlmsghdr); } extern int __netlink_transaction(struct nl_handler *handler, struct nlmsghdr *request, struct nlmsghdr *answer) { int ret; ret = __netlink_send(handler, request); if (ret < 0) return -1; ret = __netlink_recv(handler, answer); if (ret < 0) return -1; ret = 0; if (answer->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(answer); errno = -err->error; if (err->error < 0) ret = -1; } return ret; } extern int netlink_transaction(struct nl_handler *handler, struct nlmsg *request, struct nlmsg *answer) { return __netlink_transaction(handler, request->nlmsghdr, answer->nlmsghdr); } extern int netlink_open(struct nl_handler *handler, int protocol) { socklen_t socklen; int sndbuf = 32768; int rcvbuf = 32768; int err; memset(handler, 0, sizeof(*handler)); handler->fd = socket(AF_NETLINK, SOCK_RAW, protocol); if (handler->fd < 0) return -errno; if (setsockopt(handler->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) goto err_with_errno; if (setsockopt(handler->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,sizeof(rcvbuf)) < 0) goto err_with_errno; memset(&handler->local, 0, sizeof(handler->local)); handler->local.nl_family = AF_NETLINK; handler->local.nl_groups = 0; if (bind(handler->fd, (struct sockaddr*)&handler->local, sizeof(handler->local)) < 0) goto err_with_errno; socklen = sizeof(handler->local); if (getsockname(handler->fd, (struct sockaddr*)&handler->local, &socklen) < 0) goto err_with_errno; if (socklen != sizeof(handler->local)) { err = -EINVAL; goto errclose; } if (handler->local.nl_family != AF_NETLINK) { err = -EINVAL; goto errclose; } handler->seq = time(NULL); return 0; err_with_errno: err = -errno; errclose: close(handler->fd); return err; } extern int netlink_close(struct nl_handler *handler) { close(handler->fd); handler->fd = -1; return 0; } int addattr(struct nlmsghdr *n, size_t maxlen, int type, const void *data, size_t alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; errno = EMSGSIZE; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) return -1; rta = NLMSG_TAIL(n); rta->rta_type = type; rta->rta_len = len; if (alen) memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return 0; } lxc-3.0.3/src/lxc/lxclock.c0000644061062106075000000001600413375633353012430 00000000000000/* liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "lxclock.h" #include "utils.h" #ifdef MUTEX_DEBUGGING #include #endif #define MAX_STACKDEPTH 25 lxc_log_define(lxclock, lxc); #ifdef MUTEX_DEBUGGING static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; static inline void dump_stacktrace(void) { void *array[MAX_STACKDEPTH]; size_t size; char **strings; size_t i; size = backtrace(array, MAX_STACKDEPTH); strings = backtrace_symbols(array, size); /* Using fprintf here as our logging module is not thread safe. */ fprintf(stderr, "\tObtained %zu stack frames\n", size); for (i = 0; i < size; i++) fprintf(stderr, "\t\t%s\n", strings[i]); free(strings); } #else static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; static inline void dump_stacktrace(void) {;} #endif static void lock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_lock(l); if (ret != 0) { SYSERROR("Failed to acquire mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static void unlock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_unlock(l); if (ret != 0) { SYSERROR("Failed to release mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static char *lxclock_name(const char *p, const char *n) { int ret; size_t len; char *dest, *rundir; /* lockfile will be: * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root * or * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root */ /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */ len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3; rundir = get_rundir(); if (!rundir) return NULL; len += strlen(rundir); dest = malloc(len); if (!dest) { free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p); if (ret < 0 || (size_t)ret >= len) { free(dest); free(rundir); return NULL; } ret = mkdir_p(dest, 0755); if (ret < 0) { free(dest); free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n); free(rundir); if (ret < 0 || (size_t)ret >= len) { free(dest); return NULL; } return dest; } static sem_t *lxc_new_unnamed_sem(void) { int ret; sem_t *s; s = malloc(sizeof(*s)); if (!s) return NULL; ret = sem_init(s, 0, 1); if (ret < 0) { free(s); return NULL; } return s; } struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) { struct lxc_lock *l; l = malloc(sizeof(*l)); if (!l) goto on_error; if (!name) { l->type = LXC_LOCK_ANON_SEM; l->u.sem = lxc_new_unnamed_sem(); if (!l->u.sem) { free(l); l = NULL; } goto on_error; } l->type = LXC_LOCK_FLOCK; l->u.f.fname = lxclock_name(lxcpath, name); if (!l->u.f.fname) { free(l); l = NULL; goto on_error; } l->u.f.fd = -1; on_error: return l; } int lxclock(struct lxc_lock *l, int timeout) { struct flock lk; int ret = -1, saved_errno = errno; switch(l->type) { case LXC_LOCK_ANON_SEM: if (!timeout) { ret = sem_wait(l->u.sem); if (ret < 0) saved_errno = errno; } else { struct timespec ts; ret = clock_gettime(CLOCK_REALTIME, &ts); if (ret < 0) { ret = -2; goto on_error; } ts.tv_sec += timeout; ret = sem_timedwait(l->u.sem, &ts); if (ret < 0) saved_errno = errno; } break; case LXC_LOCK_FLOCK: ret = -2; if (timeout) { ERROR("Timeouts are not supported with file locks"); goto on_error; } if (!l->u.f.fname) { ERROR("No filename set for file lock"); goto on_error; } if (l->u.f.fd == -1) { l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR); if (l->u.f.fd == -1) { SYSERROR("Failed to open \"%s\"", l->u.f.fname); saved_errno = errno; goto on_error; } } memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX); saved_errno = errno; } break; } on_error: errno = saved_errno; return ret; } int lxcunlock(struct lxc_lock *l) { struct flock lk; int ret = 0, saved_errno = errno; switch (l->type) { case LXC_LOCK_ANON_SEM: if (!l->u.sem) { ret = -2; } else { ret = sem_post(l->u.sem); saved_errno = errno; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_UNLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB); saved_errno = errno; } close(l->u.f.fd); l->u.f.fd = -1; } else { ret = -2; } break; } errno = saved_errno; return ret; } /* * lxc_putlock() is only called when a container_new() fails, * or during container_put(), which is already guaranteed to * only be done by one task. * So the only exclusion we need to provide here is for regular * thread safety (i.e. file descriptor table changes). */ void lxc_putlock(struct lxc_lock *l) { if (!l) return; switch(l->type) { case LXC_LOCK_ANON_SEM: if (l->u.sem) { sem_destroy(l->u.sem); free(l->u.sem); l->u.sem = NULL; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { close(l->u.f.fd); l->u.f.fd = -1; } free(l->u.f.fname); l->u.f.fname = NULL; break; } free(l); } void process_lock(void) { lock_mutex(&thread_mutex); } void process_unlock(void) { unlock_mutex(&thread_mutex); } int container_mem_lock(struct lxc_container *c) { return lxclock(c->privlock, 0); } void container_mem_unlock(struct lxc_container *c) { lxcunlock(c->privlock); } int container_disk_lock(struct lxc_container *c) { int ret; ret = lxclock(c->privlock, 0); if (ret < 0) return ret; ret = lxclock(c->slock, 0); if (ret < 0) { lxcunlock(c->privlock); return ret; } return 0; } void container_disk_unlock(struct lxc_container *c) { lxcunlock(c->slock); lxcunlock(c->privlock); } lxc-3.0.3/src/lxc/list.h0000644061062106075000000001036213375633353011752 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LIST_H #define __LXC_LIST_H #include struct lxc_list { void *elem; struct lxc_list *next; struct lxc_list *prev; }; #define lxc_init_list(l) \ { \ .next = l, .prev = l \ } /* * Iterate through an lxc list. An example for an idiom would be: * * struct lxc_list *iterator; * lxc_list_for_each(iterator, list) { * type *tmp; * tmp = iterator->elem; * } */ #define lxc_list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; __iterator != __list; \ __iterator = __iterator->next) /* Iterate safely through an lxc list. An example for an appropriate use case * would be: * * struct lxc_list *cur, *next; * lxc_list_for_each_safe(cur, list, next) { * type *tmp; * tmp = cur->elem; * } */ #define lxc_list_for_each_safe(__iterator, __list, __next) \ for (__iterator = (__list)->next, __next = __iterator->next; \ __iterator != __list; __iterator = __next, __next = __next->next) /* Initialize list. */ static inline void lxc_list_init(struct lxc_list *list) { list->elem = NULL; list->next = list->prev = list; } /* Add an element to a list. See lxc_list_add() and lxc_list_add_tail() for an * idiom. */ static inline void lxc_list_add_elem(struct lxc_list *list, void *elem) { list->elem = elem; } /* Retrieve first element of list. */ static inline void *lxc_list_first_elem(struct lxc_list *list) { return list->next->elem; } /* Retrieve last element of list. */ static inline void *lxc_list_last_elem(struct lxc_list *list) { return list->prev->elem; } /* Determine if list is empty. */ static inline int lxc_list_empty(struct lxc_list *list) { return list == list->next; } /* Workhorse to be called from lxc_list_add() and lxc_list_add_tail(). */ static inline void __lxc_list_add(struct lxc_list *new, struct lxc_list *prev, struct lxc_list *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* Idiom to add an element to the beginning of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) * return 1; * lxc_list_add_elem(tmp, elem); * lxc_list_add(list, tmp); */ static inline void lxc_list_add(struct lxc_list *head, struct lxc_list *list) { __lxc_list_add(list, head, head->next); } /* Idiom to add an element to the end of an lxc list: * * struct lxc_list *tmp = malloc(sizeof(*tmp)); * if (tmp == NULL) * return 1; * lxc_list_add_elem(tmp, elem); * lxc_list_add_tail(list, tmp); */ static inline void lxc_list_add_tail(struct lxc_list *head, struct lxc_list *list) { __lxc_list_add(list, head->prev, head); } /* Idiom to 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); * } */ static inline void lxc_list_del(struct lxc_list *list) { struct lxc_list *next, *prev; next = list->next; prev = list->prev; next->prev = prev; prev->next = next; } /* Return length of the list. */ static inline size_t lxc_list_len(struct lxc_list *list) { size_t i = 0; struct lxc_list *iter; lxc_list_for_each(iter, list) { i++; } return i; } #endif /* __LXC_LIST_H */ lxc-3.0.3/src/lxc/lsm/0000755061062106075000000000000013375633377011505 500000000000000lxc-3.0.3/src/lxc/lsm/lsm.c0000644061062106075000000001005413375633353012356 00000000000000/* * lxc: linux Container library * * Authors: * Copyright © 2012 Serge Hallyn * Copyright © 2012 Canonical Ltd. * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include "conf.h" #include "config.h" #include "log.h" #include "lsm.h" lxc_log_define(lsm, lxc); static struct lsm_drv *drv = NULL; extern struct lsm_drv *lsm_apparmor_drv_init(void); extern struct lsm_drv *lsm_selinux_drv_init(void); extern struct lsm_drv *lsm_nop_drv_init(void); __attribute__((constructor)) void lsm_init(void) { if (drv) { INFO("LSM security driver %s", drv->name); return; } #if HAVE_APPARMOR drv = lsm_apparmor_drv_init(); #endif #if HAVE_SELINUX if (!drv) drv = lsm_selinux_drv_init(); #endif if (!drv) drv = lsm_nop_drv_init(); INFO("Initialized LSM security driver %s", drv->name); } int lsm_enabled(void) { if (drv) return drv->enabled(); return 0; } const char *lsm_name(void) { if (drv) return drv->name; return "none"; } char *lsm_process_label_get(pid_t pid) { if (!drv) { ERROR("LSM driver not inited"); return NULL; } return drv->process_label_get(pid); } int lsm_process_label_fd_get(pid_t pid, bool on_exec) { int ret = -1; int labelfd = -1; const char *name; char path[LXC_LSMATTRLEN]; name = lsm_name(); if (strcmp(name, "nop") == 0) return 0; if (strcmp(name, "none") == 0) return 0; /* We don't support on-exec with AppArmor */ if (strcmp(name, "AppArmor") == 0) on_exec = 0; if (on_exec) ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/exec", pid); else ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/current", pid); if (ret < 0 || ret >= LXC_LSMATTRLEN) return -1; labelfd = open(path, O_RDWR); if (labelfd < 0) { SYSERROR("Unable to %s LSM label file descriptor", name); return -1; } return labelfd; } int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec) { int ret = -1; const char *name; name = lsm_name(); if (strcmp(name, "nop") == 0) return 0; if (strcmp(name, "none") == 0) return 0; /* We don't support on-exec with AppArmor */ if (strcmp(name, "AppArmor") == 0) on_exec = false; if (strcmp(name, "AppArmor") == 0) { size_t len; char *command; if (on_exec) { ERROR("Changing AppArmor profile on exec not supported"); return -1; } len = strlen(label) + strlen("changeprofile ") + 1; command = malloc(len); if (!command) goto on_error; ret = snprintf(command, len, "changeprofile %s", label); if (ret < 0 || (size_t)ret >= len) { int saved_errno = errno; free(command); errno = saved_errno; goto on_error; } ret = lxc_write_nointr(label_fd, command, len - 1); free(command); } else if (strcmp(name, "SELinux") == 0) { ret = lxc_write_nointr(label_fd, label, strlen(label)); } else { errno = EINVAL; ret = -1; } if (ret < 0) { on_error: SYSERROR("Failed to set %s label \"%s\"", name, label); return -1; } INFO("Set %s label to \"%s\"", name, label); return 0; } int lsm_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec) { if (!drv) { ERROR("LSM driver not inited"); return -1; } return drv->process_label_set(label, conf, use_default, on_exec); } lxc-3.0.3/src/lxc/lsm/nop.c0000644061062106075000000000266513375633353012370 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include "config.h" #include "lsm/lsm.h" static char *nop_process_label_get(pid_t pid) { return NULL; } static int nop_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec) { return 0; } static int nop_enabled(void) { return 0; } static struct lsm_drv nop_drv = { .name = "nop", .enabled = nop_enabled, .process_label_get = nop_process_label_get, .process_label_set = nop_process_label_set, }; struct lsm_drv *lsm_nop_drv_init(void) { return &nop_drv; } lxc-3.0.3/src/lxc/lsm/selinux.c0000644061062106075000000000604313375633353013255 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include "conf.h" #include "config.h" #include "log.h" #include "lsm.h" #define DEFAULT_LABEL "unconfined_t" lxc_log_define(selinux, lsm); /* * selinux_process_label_get: Get SELinux context of a process * * @pid : the pid to get, or 0 for self * * Returns the context of the given pid. The caller must free() * the returned string. * * Note that this relies on /proc being available. */ static char *selinux_process_label_get(pid_t pid) { security_context_t ctx; char *label; if (getpidcon_raw(pid, &ctx) < 0) { SYSERROR("failed to get SELinux context for pid %d", pid); return NULL; } label = strdup((char *)ctx); freecon(ctx); return label; } /* * selinux_process_label_set: Set SELinux context of a process * * @label : label string * @conf : the container configuration to use if @label is NULL * @default : use the default context if @label is NULL * @on_exec : the new context will take effect on exec(2) not immediately * * Returns 0 on success, < 0 on failure * * Notes: This relies on /proc being available. */ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf, bool use_default, bool on_exec) { int ret; const char *label; label = inlabel ? inlabel : conf->lsm_se_context; if (!label) { if (!use_default) return -EINVAL; label = DEFAULT_LABEL; } if (strcmp(label, "unconfined_t") == 0) return 0; if (on_exec) ret = setexeccon_raw((char *)label); else ret = setcon_raw((char *)label); if (ret < 0) { SYSERROR("Failed to set SELinux%s context to \"%s\"", on_exec ? " exec" : "", label); return -1; } INFO("Changed SELinux%s context to \"%s\"", on_exec ? " exec" : "", label); return 0; } static struct lsm_drv selinux_drv = { .name = "SELinux", .enabled = is_selinux_enabled, .process_label_get = selinux_process_label_get, .process_label_set = selinux_process_label_set, }; struct lsm_drv *lsm_selinux_drv_init(void) { if (!is_selinux_enabled()) return NULL; return &selinux_drv; } lxc-3.0.3/src/lxc/lsm/apparmor.c0000644061062106075000000001414013375633353013404 00000000000000/* apparmor * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include "conf.h" #include "config.h" #include "log.h" #include "lsm.h" #include "parse.h" #include "raw_syscalls.h" #include "utils.h" lxc_log_define(apparmor, lsm); /* set by lsm_apparmor_drv_init if true */ static int aa_enabled = 0; static int mount_features_enabled = 0; #define AA_DEF_PROFILE "lxc-container-default" #define AA_DEF_PROFILE_CGNS "lxc-container-default-cgns" #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" #define AA_UNCHANGED "unchanged" static bool check_mount_feature_enabled(void) { return mount_features_enabled == 1; } static void load_mount_features_enabled(void) { struct stat statbuf; int ret; ret = stat(AA_MOUNT_RESTR, &statbuf); if (ret == 0) mount_features_enabled = 1; } /* aa_getcon is not working right now. Use our hand-rolled version below */ static int apparmor_enabled(void) { FILE *fin; char e; int ret; fin = fopen_cloexec(AA_ENABLED_FILE, "r"); if (!fin) return 0; ret = fscanf(fin, "%c", &e); fclose(fin); if (ret == 1 && e == 'Y') { load_mount_features_enabled(); return 1; } return 0; } static char *apparmor_process_label_get(pid_t pid) { char path[100], *space; int ret; char *buf = NULL, *newbuf; int sz = 0; FILE *f; ret = snprintf(path, 100, "/proc/%d/attr/current", pid); if (ret < 0 || ret >= 100) { ERROR("path name too long"); return NULL; } again: f = fopen_cloexec(path, "r"); if (!f) { SYSERROR("opening %s", path); free(buf); return NULL; } sz += 1024; newbuf = realloc(buf, sz); if (!newbuf) { free(buf); ERROR("out of memory"); fclose(f); return NULL; } buf = newbuf; memset(buf, 0, sz); ret = fread(buf, 1, sz - 1, f); fclose(f); if (ret < 0) { ERROR("reading %s", path); free(buf); return NULL; } if (ret >= sz) goto again; space = strchr(buf, '\n'); if (space) *space = '\0'; space = strchr(buf, ' '); if (space) *space = '\0'; return buf; } /* * Probably makes sense to reorganize these to only read * the label once */ static bool apparmor_am_unconfined(void) { char *p = apparmor_process_label_get(lxc_raw_getpid()); bool ret = false; if (!p || strcmp(p, "unconfined") == 0) ret = true; free(p); return ret; } /* aa stacking is not yet supported */ static bool aa_stacking_supported(void) { return false; } static bool aa_needs_transition(char *curlabel) { if (!curlabel) return false; if (strcmp(curlabel, "unconfined") == 0) return false; if (strcmp(curlabel, "/usr/bin/lxc-start") == 0) return false; return true; } /* * apparmor_process_label_set: Set AppArmor process profile * * @label : the profile to set * @conf : the container configuration to use if @label is NULL * @default : use the default profile if @label is NULL * @on_exec : this is ignored. Apparmor profile will be changed immediately * * Returns 0 on success, < 0 on failure * * Notes: This relies on /proc being available. */ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf, bool use_default, bool on_exec) { int label_fd, ret; pid_t tid; const char *label = inlabel ? inlabel : conf->lsm_aa_profile; char *curlabel; if (!aa_enabled) return 0; /* user may request that we just ignore apparmor */ if (label && strcmp(label, AA_UNCHANGED) == 0) { INFO("apparmor profile unchanged per user request"); return 0; } curlabel = apparmor_process_label_get(lxc_raw_getpid()); if (!aa_stacking_supported() && aa_needs_transition(curlabel)) { /* we're already confined, and stacking isn't supported */ if (!label || strcmp(curlabel, label) == 0) { /* no change requested */ free(curlabel); return 0; } ERROR("already apparmor confined, but new label requested."); free(curlabel); return -1; } free(curlabel); if (!label) { if (use_default) { if (cgns_supported()) label = AA_DEF_PROFILE_CGNS; else label = AA_DEF_PROFILE; } else label = "unconfined"; } if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) { WARN("Incomplete AppArmor support in your kernel"); if (!conf->lsm_aa_allow_incomplete) { ERROR("If you really want to start this container, set"); ERROR("lxc.apparmor.allow_incomplete = 1"); ERROR("in your container configuration file"); return -1; } } if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) { INFO("apparmor profile unchanged"); return 0; } tid = lxc_raw_gettid(); label_fd = lsm_process_label_fd_get(tid, on_exec); if (label_fd < 0) { SYSERROR("Failed to change apparmor profile to %s", label); return -1; } ret = lsm_process_label_set_at(label_fd, label, on_exec); close(label_fd); if (ret < 0) { ERROR("Failed to change apparmor profile to %s", label); return -1; } INFO("Changed apparmor profile to %s", label); return 0; } static struct lsm_drv apparmor_drv = { .name = "AppArmor", .enabled = apparmor_enabled, .process_label_get = apparmor_process_label_get, .process_label_set = apparmor_process_label_set, }; struct lsm_drv *lsm_apparmor_drv_init(void) { if (!apparmor_enabled()) return NULL; aa_enabled = 1; return &apparmor_drv; } lxc-3.0.3/src/lxc/lsm/lsm.h0000644061062106075000000000314413375633353012365 00000000000000/* * lxc: linux Container library * * Copyright © 2013 Oracle. * * Authors: * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LSM_H #define __LXC_LSM_H struct lxc_conf; #include #include "macro.h" #include "utils.h" struct lsm_drv { const char *name; int (*enabled)(void); char *(*process_label_get)(pid_t pid); int (*process_label_set)(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec); }; extern void lsm_init(void); extern int lsm_enabled(void); extern const char *lsm_name(void); extern char *lsm_process_label_get(pid_t pid); extern int lsm_process_label_set(const char *label, struct lxc_conf *conf, bool use_default, bool on_exec); extern int lsm_process_label_fd_get(pid_t pid, bool on_exec); extern int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec); #endif /* __LXC_LSM_H */ lxc-3.0.3/src/lxc/conf.c0000644061062106075000000034236313375633353011730 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #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 "af_unix.h" #include "caps.h" #include "cgroup.h" #include "conf.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "error.h" #include "log.h" #include "lsm/lsm.h" #include "lxclock.h" #include "lxcseccomp.h" #include "macro.h" #include "namespace.h" #include "network.h" #include "parse.h" #include "raw_syscalls.h" #include "ringbuf.h" #include "start.h" #include "storage.h" #include "storage/overlay.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #ifdef MAJOR_IN_MKDEV #include #endif #ifdef HAVE_STATVFS #include #endif #if HAVE_PTY_H #include #else #include <../include/openpty.h> #endif #if HAVE_LIBCAP #include #endif #if HAVE_SYS_PERSONALITY_H #include #endif #ifndef HAVE_STRLCAT #include "include/strlcat.h" #endif #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #if !defined(HAVE_PRLIMIT) && defined(HAVE_PRLIMIT64) #include <../include/prlimit.h> #endif lxc_log_define(conf, lxc); /* The lxc_conf of the container currently being worked on in an API call. * This is used in the error calls. */ #ifdef HAVE_TLS thread_local struct lxc_conf *current_config; #else struct lxc_conf *current_config; #endif char *lxchook_names[NUM_LXC_HOOKS] = { "pre-start", "pre-mount", "mount", "autodev", "start", "stop", "post-stop", "clone", "destroy", "start-host" }; struct mount_opt { char *name; int clear; int flag; }; struct caps_opt { char *name; int value; }; struct limit_opt { char *name; int value; }; static struct mount_opt mount_opt[] = { { "async", 1, MS_SYNCHRONOUS }, { "atime", 1, MS_NOATIME }, { "bind", 0, MS_BIND }, { "defaults", 0, 0 }, { "dev", 1, MS_NODEV }, { "diratime", 1, MS_NODIRATIME }, { "dirsync", 0, MS_DIRSYNC }, { "exec", 1, MS_NOEXEC }, { "lazytime", 0, MS_LAZYTIME }, { "mand", 0, MS_MANDLOCK }, { "noatime", 0, MS_NOATIME }, { "nodev", 0, MS_NODEV }, { "nodiratime", 0, MS_NODIRATIME }, { "noexec", 0, MS_NOEXEC }, { "nomand", 1, MS_MANDLOCK }, { "norelatime", 1, MS_RELATIME }, { "nostrictatime", 1, MS_STRICTATIME }, { "nosuid", 0, MS_NOSUID }, { "rbind", 0, MS_BIND|MS_REC }, { "relatime", 0, MS_RELATIME }, { "remount", 0, MS_REMOUNT }, { "ro", 0, MS_RDONLY }, { "rw", 1, MS_RDONLY }, { "strictatime", 0, MS_STRICTATIME }, { "suid", 1, MS_NOSUID }, { "sync", 0, MS_SYNCHRONOUS }, { NULL, 0, 0 }, }; static struct mount_opt propagation_opt[] = { { "private", 0, MS_PRIVATE }, { "shared", 0, MS_SHARED }, { "slave", 0, MS_SLAVE }, { "unbindable", 0, MS_UNBINDABLE }, { "rprivate", 0, MS_PRIVATE|MS_REC }, { "rshared", 0, MS_SHARED|MS_REC }, { "rslave", 0, MS_SLAVE|MS_REC }, { "runbindable", 0, MS_UNBINDABLE|MS_REC }, { NULL, 0, 0 }, }; static struct caps_opt caps_opt[] = { #if HAVE_LIBCAP { "chown", CAP_CHOWN }, { "dac_override", CAP_DAC_OVERRIDE }, { "dac_read_search", CAP_DAC_READ_SEARCH }, { "fowner", CAP_FOWNER }, { "fsetid", CAP_FSETID }, { "kill", CAP_KILL }, { "setgid", CAP_SETGID }, { "setuid", CAP_SETUID }, { "setpcap", CAP_SETPCAP }, { "linux_immutable", CAP_LINUX_IMMUTABLE }, { "net_bind_service", CAP_NET_BIND_SERVICE }, { "net_broadcast", CAP_NET_BROADCAST }, { "net_admin", CAP_NET_ADMIN }, { "net_raw", CAP_NET_RAW }, { "ipc_lock", CAP_IPC_LOCK }, { "ipc_owner", CAP_IPC_OWNER }, { "sys_module", CAP_SYS_MODULE }, { "sys_rawio", CAP_SYS_RAWIO }, { "sys_chroot", CAP_SYS_CHROOT }, { "sys_ptrace", CAP_SYS_PTRACE }, { "sys_pacct", CAP_SYS_PACCT }, { "sys_admin", CAP_SYS_ADMIN }, { "sys_boot", CAP_SYS_BOOT }, { "sys_nice", CAP_SYS_NICE }, { "sys_resource", CAP_SYS_RESOURCE }, { "sys_time", CAP_SYS_TIME }, { "sys_tty_config", CAP_SYS_TTY_CONFIG }, { "mknod", CAP_MKNOD }, { "lease", CAP_LEASE }, #ifdef CAP_AUDIT_READ { "audit_read", CAP_AUDIT_READ }, #endif #ifdef CAP_AUDIT_WRITE { "audit_write", CAP_AUDIT_WRITE }, #endif #ifdef CAP_AUDIT_CONTROL { "audit_control", CAP_AUDIT_CONTROL }, #endif { "setfcap", CAP_SETFCAP }, { "mac_override", CAP_MAC_OVERRIDE }, { "mac_admin", CAP_MAC_ADMIN }, #ifdef CAP_SYSLOG { "syslog", CAP_SYSLOG }, #endif #ifdef CAP_WAKE_ALARM { "wake_alarm", CAP_WAKE_ALARM }, #endif #ifdef CAP_BLOCK_SUSPEND { "block_suspend", CAP_BLOCK_SUSPEND }, #endif #endif }; static 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) { int ret; char *output; struct lxc_popen_FILE *f; f = lxc_popen(buffer); if (!f) { SYSERROR("Failed to popen() %s", buffer); return -1; } output = malloc(LXC_LOG_BUFFER_SIZE); if (!output) { ERROR("Failed to allocate memory for %s", buffer); lxc_pclose(f); return -1; } while (fgets(output, LXC_LOG_BUFFER_SIZE, f->f)) DEBUG("Script %s with output: %s", buffer, output); free(output); ret = lxc_pclose(f); if (ret == -1) { SYSERROR("Script exited with error"); return -1; } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { ERROR("Script exited with status %d", WEXITSTATUS(ret)); return -1; } else if (WIFSIGNALED(ret)) { ERROR("Script terminated by signal %d", WTERMSIG(ret)); return -1; } return 0; } int run_script_argv(const char *name, unsigned int hook_version, const char *section, const char *script, const char *hookname, char **argv) { int buf_pos, i, ret; char *buffer; int fret = -1; size_t size = 0; if (hook_version == 0) INFO("Executing script \"%s\" for container \"%s\", config " "section \"%s\"", script, name, section); else INFO("Executing script \"%s\" for container \"%s\"", script, name); for (i = 0; argv && argv[i]; i++) size += strlen(argv[i]) + 1; size += STRLITERALLEN("exec"); size++; size += strlen(script); size++; if (size > INT_MAX) return -EFBIG; if (hook_version == 0) { size += strlen(hookname); size++; size += strlen(name); size++; size += strlen(section); size++; if (size > INT_MAX) return -EFBIG; } buffer = malloc(size); if (!buffer) return -ENOMEM; if (hook_version == 0) buf_pos = snprintf(buffer, size, "exec %s %s %s %s", script, name, section, hookname); else buf_pos = snprintf(buffer, size, "exec %s", script); if (buf_pos < 0 || (size_t)buf_pos >= size) { ERROR("Failed to create command line for script \"%s\"", script); goto on_error; } if (hook_version == 1) { ret = setenv("LXC_HOOK_TYPE", hookname, 1); if (ret < 0) { SYSERROR("Failed to set environment variable: " "LXC_HOOK_TYPE=%s", hookname); goto on_error; } TRACE("Set environment variable: LXC_HOOK_TYPE=%s", hookname); ret = setenv("LXC_HOOK_SECTION", section, 1); if (ret < 0) { SYSERROR("Failed to set environment variable: " "LXC_HOOK_SECTION=%s", section); goto on_error; } TRACE("Set environment variable: LXC_HOOK_SECTION=%s", section); if (strcmp(section, "net") == 0) { char *parent; if (!argv || !argv[0]) goto on_error; ret = setenv("LXC_NET_TYPE", argv[0], 1); if (ret < 0) { SYSERROR("Failed to set environment variable: " "LXC_NET_TYPE=%s", argv[0]); goto on_error; } TRACE("Set environment variable: LXC_NET_TYPE=%s", argv[0]); parent = argv[1] ? argv[1] : ""; if (strcmp(argv[0], "macvlan") == 0) { ret = setenv("LXC_NET_PARENT", parent, 1); if (ret < 0) { SYSERROR("Failed to set environment " "variable: LXC_NET_PARENT=%s", parent); goto on_error; } TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); } else if (strcmp(argv[0], "phys") == 0) { ret = setenv("LXC_NET_PARENT", parent, 1); if (ret < 0) { SYSERROR("Failed to set environment " "variable: LXC_NET_PARENT=%s", parent); goto on_error; } TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); } else if (strcmp(argv[0], "veth") == 0) { char *peer = argv[2] ? argv[2] : ""; ret = setenv("LXC_NET_PEER", peer, 1); if (ret < 0) { SYSERROR("Failed to set environment " "variable: LXC_NET_PEER=%s", peer); goto on_error; } TRACE("Set environment variable: LXC_NET_PEER=%s", peer); ret = setenv("LXC_NET_PARENT", parent, 1); if (ret < 0) { SYSERROR("Failed to set environment " "variable: LXC_NET_PARENT=%s", parent); goto on_error; } TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); } } } for (i = 0; argv && argv[i]; i++) { size_t len = size - buf_pos; ret = snprintf(buffer + buf_pos, len, " %s", argv[i]); if (ret < 0 || (size_t)ret >= len) { ERROR("Failed to create command line for script \"%s\"", script); goto on_error; } buf_pos += ret; } fret = run_buffer(buffer); on_error: free(buffer); return fret; } int run_script(const char *name, const char *section, const char *script, ...) { int ret; char *buffer, *p; va_list ap; size_t size = 0; INFO("Executing script \"%s\" for container \"%s\", config section \"%s\"", script, name, section); va_start(ap, script); while ((p = va_arg(ap, char *))) size += strlen(p) + 1; va_end(ap); size += STRLITERALLEN("exec"); size += strlen(script); size += strlen(name); size += strlen(section); size += 4; if (size > INT_MAX) return -1; buffer = alloca(size); ret = snprintf(buffer, size, "exec %s %s %s", script, name, section); if (ret < 0 || ret >= size) return -1; va_start(ap, script); while ((p = va_arg(ap, char *))) { int len = size - ret; int rc; rc = snprintf(buffer + ret, len, " %s", p); if (rc < 0 || rc >= len) { va_end(ap); return -1; } ret += rc; } va_end(ap); return run_buffer(buffer); } /* pin_rootfs * if rootfs is a directory, then open ${rootfs}/.lxc-keep for writing for * the duration of the container run, to prevent the container from marking * the underlying fs readonly on shutdown. unlink the file immediately so * no name pollution is happens. * don't unlink on NFS to avoid random named stale handles. * return -1 on error. * return -2 if nothing needed to be pinned. * return an open fd (>=0) if we pinned it. */ int pin_rootfs(const char *rootfs) { int fd, ret; char absrootfspin[PATH_MAX]; char *absrootfs; struct stat s; struct statfs sfs; if (rootfs == NULL || strlen(rootfs) == 0) return -2; absrootfs = realpath(rootfs, NULL); if (!absrootfs) return -2; ret = stat(absrootfs, &s); if (ret < 0) { free(absrootfs); return -1; } if (!S_ISDIR(s.st_mode)) { free(absrootfs); return -2; } ret = snprintf(absrootfspin, PATH_MAX, "%s/.lxc-keep", absrootfs); free(absrootfs); if (ret < 0 || ret >= PATH_MAX) return -1; fd = open(absrootfspin, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); if (fd < 0) return fd; ret = fstatfs (fd, &sfs); if (ret < 0) return fd; if (sfs.f_type == NFS_SUPER_MAGIC) { DEBUG("Rootfs on NFS, not unlinking pin file \"%s\"", absrootfspin); return fd; } (void)unlink(absrootfspin); return fd; } /* If we are asking to remount something, make sure that any NOEXEC etc are * honored. */ unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags) { #ifdef HAVE_STATVFS int ret; struct statvfs sb; unsigned long required_flags = 0; if (!s) s = d; if (!s) return flags; ret = statvfs(s, &sb); if (ret < 0) return flags; if (flags & MS_REMOUNT) { if (sb.f_flag & MS_NOSUID) required_flags |= MS_NOSUID; if (sb.f_flag & MS_NODEV) required_flags |= MS_NODEV; if (sb.f_flag & MS_RDONLY) required_flags |= MS_RDONLY; if (sb.f_flag & MS_NOEXEC) required_flags |= MS_NOEXEC; } if (sb.f_flag & MS_NOATIME) required_flags |= MS_NOATIME; if (sb.f_flag & MS_NODIRATIME) required_flags |= MS_NODIRATIME; if (sb.f_flag & MS_LAZYTIME) required_flags |= MS_LAZYTIME; if (sb.f_flag & MS_RELATIME) required_flags |= MS_RELATIME; if (sb.f_flag & MS_STRICTATIME) required_flags |= MS_STRICTATIME; return flags | required_flags; #else return flags; #endif } static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler) { int i, r; static struct { int match_mask; int match_flag; const char *source; const char *destination; const char *fstype; unsigned long flags; const char *options; } default_mounts[] = { /* Read-only bind-mounting... In older kernels, doing that * required to do one MS_BIND mount and then * MS_REMOUNT|MS_RDONLY the same one. According to mount(2) * manpage, MS_BIND honors MS_RDONLY from kernel 2.6.26 * onwards. However, this apparently does not work on kernel * 3.8. Unfortunately, on that very same kernel, doing the same * trick as above doesn't seem to work either, there one needs * to ALSO specify MS_BIND for the remount, otherwise the * entire fs is remounted read-only or the mount fails because * it's busy... MS_REMOUNT|MS_BIND|MS_RDONLY seems to work for * kernels as low as 2.6.32... */ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, /* proc/tty is used as a temporary placeholder for proc/sys/net which we'll move back in a few steps */ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys/net", "%r/proc/tty", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys", "%r/proc/sys", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sys", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/tty", "%r/proc/sys/net", NULL, MS_MOVE, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sysrq-trigger", "%r/proc/sysrq-trigger", NULL, MS_BIND, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sysrq-trigger", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW, "sysfs", "%r/sys", "sysfs", 0, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO, "sysfs", "%r/sys", "sysfs", MS_RDONLY, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/sys", "sysfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, NULL, "%r/sys", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/sys/devices/virtual/net", "sysfs", 0, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/sys/devices/virtual/net/devices/virtual/net", "%r/sys/devices/virtual/net", NULL, MS_BIND, NULL }, { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, NULL, "%r/sys/devices/virtual/net", NULL, MS_REMOUNT|MS_BIND|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL }, { 0, 0, NULL, NULL, NULL, 0, NULL } }; for (i = 0; default_mounts[i].match_mask; i++) { int saved_errno; unsigned long mflags; char *destination = NULL; char *source = NULL; if ((flags & default_mounts[i].match_mask) != default_mounts[i].match_flag) continue; if (default_mounts[i].source) { /* will act like strdup if %r is not present */ source = lxc_string_replace("%r", conf->rootfs.path ? conf->rootfs.mount : "", default_mounts[i].source); if (!source) return -1; } if (!default_mounts[i].destination) { ERROR("BUG: auto mounts destination %d was NULL", i); free(source); return -1; } /* will act like strdup if %r is not present */ destination = lxc_string_replace("%r", conf->rootfs.path ? conf->rootfs.mount : "", default_mounts[i].destination); if (!destination) { saved_errno = errno; free(source); errno = saved_errno; return -1; } mflags = add_required_remount_flags(source, destination, default_mounts[i].flags); r = safe_mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options, conf->rootfs.path ? conf->rootfs.mount : NULL); saved_errno = errno; if (r < 0 && errno == ENOENT) { INFO("Mount source or target for \"%s\" on \"%s\" does " "not exist. Skipping", source, destination); r = 0; } else if (r < 0) { SYSERROR("Failed to mount \"%s\" on \"%s\" with flags %lu", source, destination, mflags); } free(source); free(destination); if (r < 0) { errno = saved_errno; return -1; } } if (flags & LXC_AUTO_CGROUP_MASK) { int cg_flags; cg_flags = flags & (LXC_AUTO_CGROUP_MASK & ~LXC_AUTO_CGROUP_FORCE); /* If the type of cgroup mount was not specified, it depends on * the container's capabilities as to what makes sense: if we * have CAP_SYS_ADMIN, the read-only part can be remounted * read-write anyway, so we may as well default to read-write; * then the admin will not be given a false sense of security. * (And if they really want mixed r/o r/w, then they can * explicitly specify :mixed.) OTOH, if the container lacks * CAP_SYS_ADMIN, do only default to :mixed, because then the * container can't remount it read-write. */ if (cg_flags == LXC_AUTO_CGROUP_NOSPEC || cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC) { int has_sys_admin = 0; if (!lxc_list_empty(&conf->keepcaps)) has_sys_admin = in_caplist(CAP_SYS_ADMIN, &conf->keepcaps); else has_sys_admin = !in_caplist(CAP_SYS_ADMIN, &conf->caps); if (cg_flags == LXC_AUTO_CGROUP_NOSPEC) cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_RW : LXC_AUTO_CGROUP_MIXED; else cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_FULL_RW : LXC_AUTO_CGROUP_FULL_MIXED; } if (flags & LXC_AUTO_CGROUP_FORCE) cg_flags |= LXC_AUTO_CGROUP_FORCE; if (!handler->cgroup_ops->mount(handler->cgroup_ops, handler, conf->rootfs.path ? conf->rootfs.mount : "", cg_flags)) { SYSERROR("Failed to mount \"/sys/fs/cgroup\""); return -1; } } return 0; } static int setup_utsname(struct utsname *utsname) { int ret; if (!utsname) return 0; ret = sethostname(utsname->nodename, strlen(utsname->nodename)); if (ret < 0) { SYSERROR("Failed to set the hostname to \"%s\"", utsname->nodename); return -1; } INFO("Set hostname to \"%s\"", utsname->nodename); return 0; } struct dev_symlinks { const char *oldpath; const char *name; }; static const struct dev_symlinks dev_symlinks[] = { { "/proc/self/fd", "fd" }, { "/proc/self/fd/0", "stdin" }, { "/proc/self/fd/1", "stdout" }, { "/proc/self/fd/2", "stderr" }, }; static int lxc_setup_dev_symlinks(const struct lxc_rootfs *rootfs) { int i, ret; char path[PATH_MAX]; struct stat s; for (i = 0; i < sizeof(dev_symlinks) / sizeof(dev_symlinks[0]); i++) { const struct dev_symlinks *d = &dev_symlinks[i]; ret = snprintf(path, sizeof(path), "%s/dev/%s", rootfs->path ? rootfs->mount : "", d->name); if (ret < 0 || ret >= PATH_MAX) return -1; /* Stat the path first. If we don't get an error accept it as * is and don't try to create it */ ret = stat(path, &s); if (ret == 0) continue; ret = symlink(d->oldpath, path); if (ret && errno != EEXIST) { if (errno == EROFS) { WARN("Failed to create \"%s\". Read-only filesystem", path); } else { SYSERROR("Failed to create \"%s\"", path); return -1; } } } return 0; } /* Build a space-separate list of ptys to pass to systemd. */ static bool append_ttyname(char **pp, char *name) { char *p; size_t size; if (!*pp) { *pp = malloc(strlen(name) + strlen("container_ttys=") + 1); if (!*pp) return false; sprintf(*pp, "container_ttys=%s", name); return true; } size = strlen(*pp) + strlen(name) + 2; p = realloc(*pp, size); if (!p) return false; *pp = p; (void)strlcat(p, " ", size); (void)strlcat(p, name, size); return true; } static int lxc_setup_ttys(struct lxc_conf *conf) { int i, ret; const struct lxc_tty_info *ttys = &conf->ttys; char *ttydir = ttys->dir; char path[PATH_MAX], lxcpath[PATH_MAX]; if (!conf->rootfs.path) return 0; for (i = 0; i < ttys->max; i++) { struct lxc_terminal_info *tty = &ttys->tty[i]; ret = snprintf(path, sizeof(path), "/dev/tty%d", i + 1); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; if (ttydir) { /* create dev/lxc/tty%d" */ ret = snprintf(lxcpath, sizeof(lxcpath), "/dev/%s/tty%d", ttydir, i + 1); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = mknod(lxcpath, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"%s\"", lxcpath); return -1; } ret = unlink(path); if (ret < 0 && errno != ENOENT) { SYSERROR("Failed to unlink \"%s\"", path); return -1; } ret = mount(tty->name, lxcpath, "none", MS_BIND, 0); if (ret < 0) { SYSWARN("Failed to bind mount \"%s\" onto \"%s\"", tty->name, lxcpath); continue; } DEBUG("Bind mounted \"%s\" onto \"%s\"", tty->name, lxcpath); ret = snprintf(lxcpath, sizeof(lxcpath), "%s/tty%d", ttydir, i + 1); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = symlink(lxcpath, path); if (ret < 0) { SYSERROR("Failed to create symlink \"%s\" -> \"%s\"", path, lxcpath); return -1; } } else { /* If we populated /dev, then we need to create * /dev/ttyN */ ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0) /* this isn't fatal, continue */ SYSERROR("Failed to create \"%s\"", path); ret = mount(tty->name, path, "none", MS_BIND, 0); if (ret < 0) { SYSERROR("Failed to mount '%s'->'%s'", tty->name, path); continue; } DEBUG("Bind mounted \"%s\" onto \"%s\"", tty->name, path); } if (!append_ttyname(&conf->ttys.tty_names, tty->name)) { ERROR("Error setting up container_ttys string"); return -1; } } INFO("Finished setting up %zu /dev/tty device(s)", ttys->max); return 0; } int lxc_allocate_ttys(struct lxc_conf *conf) { size_t i; int ret; struct lxc_tty_info *ttys = &conf->ttys; /* no tty in the configuration */ if (ttys->max == 0) return 0; ttys->tty = malloc(sizeof(*ttys->tty) * ttys->max); if (!ttys->tty) return -ENOMEM; for (i = 0; i < ttys->max; i++) { struct lxc_terminal_info *tty = &ttys->tty[i]; tty->master = -EBADF; tty->slave = -EBADF; ret = openpty(&tty->master, &tty->slave, NULL, NULL, NULL); if (ret < 0) { SYSERROR("Failed to create tty %zu", i); ttys->max = i; lxc_delete_tty(ttys); return -ENOTTY; } ret = ttyname_r(tty->slave, tty->name, sizeof(tty->name)); if (ret < 0) { SYSERROR("Failed to retrieve name of tty %zu slave", i); ttys->max = i; lxc_delete_tty(ttys); return -ENOTTY; } DEBUG("Created tty \"%s\" with master fd %d and slave fd %d", tty->name, tty->master, tty->slave); /* Prevent leaking the file descriptors to the container */ ret = fd_cloexec(tty->master, true); if (ret < 0) SYSWARN("Failed to set FD_CLOEXEC flag on master fd %d of " "tty device \"%s\"", tty->master, tty->name); ret = fd_cloexec(tty->slave, true); if (ret < 0) SYSWARN("Failed to set FD_CLOEXEC flag on slave fd %d of " "tty device \"%s\"", tty->slave, tty->name); tty->busy = 0; } INFO("Finished creating %zu tty devices", ttys->max); return 0; } void lxc_delete_tty(struct lxc_tty_info *ttys) { int i; if (!ttys->tty) return; for (i = 0; i < ttys->max; i++) { struct lxc_terminal_info *tty = &ttys->tty[i]; if (tty->master >= 0) { close(tty->master); tty->master = -EBADF; } if (tty->slave >= 0) { close(tty->slave); tty->slave = -EBADF; } } free(ttys->tty); ttys->tty = NULL; } static int lxc_send_ttys_to_parent(struct lxc_handler *handler) { int i; int ret = -1; struct lxc_conf *conf = handler->conf; struct lxc_tty_info *ttys = &conf->ttys; int sock = handler->data_sock[0]; if (ttys->max == 0) return 0; for (i = 0; i < ttys->max; i++) { int ttyfds[2]; struct lxc_terminal_info *tty = &ttys->tty[i]; ttyfds[0] = tty->master; ttyfds[1] = tty->slave; ret = lxc_abstract_unix_send_fds(sock, ttyfds, 2, NULL, 0); if (ret < 0) break; TRACE("Sent tty \"%s\" with master fd %d and slave fd %d to " "parent", tty->name, tty->master, tty->slave); } if (ret < 0) SYSERROR("Failed to send %zu ttys to parent", ttys->max); else TRACE("Sent %zu ttys to parent", ttys->max); return ret; } static int lxc_create_ttys(struct lxc_handler *handler) { int ret = -1; struct lxc_conf *conf = handler->conf; ret = lxc_allocate_ttys(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->ttys.tty_names) { ret = setenv("container_ttys", conf->ttys.tty_names, 1); if (ret < 0) SYSERROR("Failed to set \"container_ttys=%s\"", conf->ttys.tty_names); } ret = 0; on_error: lxc_delete_tty(&conf->ttys); return ret; } /* Just create a path for /dev under $lxcpath/$name and in rootfs If we hit an * error, log it but don't fail yet. */ static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs, const char *lxcpath) { int ret; size_t clen; char *path; mode_t cur_mask; INFO("Preparing \"/dev\""); /* $(rootfs->mount) + "/dev/pts" + '\0' */ clen = (rootfs->path ? strlen(rootfs->mount) : 0) + 9; path = alloca(clen); ret = snprintf(path, clen, "%s/dev", rootfs->path ? rootfs->mount : ""); if (ret < 0 || (size_t)ret >= clen) return -1; cur_mask = umask(S_IXUSR | S_IXGRP | S_IXOTH); ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"/dev\" directory"); ret = -errno; goto reset_umask; } ret = safe_mount("none", path, "tmpfs", 0, "size=500000,mode=755", rootfs->path ? rootfs->mount : NULL); if (ret < 0) { SYSERROR("Failed to mount tmpfs on \"%s\"", path); goto reset_umask; } TRACE("Mounted tmpfs on \"%s\"", path); ret = snprintf(path, clen, "%s/dev/pts", rootfs->path ? rootfs->mount : ""); if (ret < 0 || (size_t)ret >= clen) { ret = -1; goto reset_umask; } /* If we are running on a devtmpfs mapping, dev/pts may already exist. * If not, then create it and exit if that fails... */ ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", path); ret = -errno; goto reset_umask; } ret = 0; reset_umask: (void)umask(cur_mask); INFO("Prepared \"/dev\""); return ret; } struct lxc_device_node { const char *name; const mode_t mode; const int maj; const int min; }; static const struct lxc_device_node lxc_devices[] = { { "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 }, { "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 }, { "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 }, { "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 }, { "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 }, { "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 }, }; enum { LXC_DEVNODE_BIND, LXC_DEVNODE_MKNOD, LXC_DEVNODE_PARTIAL, LXC_DEVNODE_OPEN, }; static int lxc_fill_autodev(const struct lxc_rootfs *rootfs) { int i, ret; char path[PATH_MAX]; mode_t cmask; int use_mknod = LXC_DEVNODE_MKNOD; ret = snprintf(path, PATH_MAX, "%s/dev", rootfs->path ? rootfs->mount : ""); if (ret < 0 || ret >= PATH_MAX) return -1; /* ignore, just don't try to fill in */ if (!dir_exists(path)) return 0; INFO("Populating \"/dev\""); cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH); for (i = 0; i < sizeof(lxc_devices) / sizeof(lxc_devices[0]); i++) { char hostpath[PATH_MAX]; const struct lxc_device_node *device = &lxc_devices[i]; ret = snprintf(path, PATH_MAX, "%s/dev/%s", rootfs->path ? rootfs->mount : "", device->name); if (ret < 0 || ret >= PATH_MAX) return -1; if (use_mknod >= LXC_DEVNODE_MKNOD) { ret = mknod(path, device->mode, makedev(device->maj, device->min)); if (ret == 0 || (ret < 0 && errno == EEXIST)) { DEBUG("Created device node \"%s\"", path); } else if (ret < 0) { if (errno != EPERM) { SYSERROR("Failed to create device node \"%s\"", path); return -1; } use_mknod = LXC_DEVNODE_BIND; } /* Device nodes are fully useable. */ if (use_mknod == LXC_DEVNODE_OPEN) continue; if (use_mknod == LXC_DEVNODE_MKNOD) { /* See * - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=55956b59df336f6738da916dbb520b6e37df9fbd * - https://lists.linuxfoundation.org/pipermail/containers/2018-June/039176.html */ ret = open(path, O_RDONLY | O_CLOEXEC); if (ret >= 0) { close(ret); /* Device nodes are fully useable. */ use_mknod = LXC_DEVNODE_OPEN; continue; } SYSTRACE("Failed to open \"%s\" device", path); /* Device nodes are only partially useable. */ use_mknod = LXC_DEVNODE_PARTIAL; } } if (use_mknod != LXC_DEVNODE_PARTIAL) { /* If we are dealing with partially functional device * nodes the prio mknod() call will have created the * device node so we can use it as a bind-mount target. */ ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create file \"%s\"", path); return -1; } } /* Fallback to bind-mounting the device from the host. */ ret = snprintf(hostpath, PATH_MAX, "/dev/%s", device->name); if (ret < 0 || ret >= PATH_MAX) return -1; ret = safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL); if (ret < 0) { SYSERROR("Failed to bind mount host device node \"%s\" " "onto \"%s\"", hostpath, path); return -1; } DEBUG("Bind mounted host device node \"%s\" onto \"%s\"", hostpath, path); } (void)umask(cmask); INFO("Populated \"/dev\""); return 0; } static int lxc_mount_rootfs(struct lxc_conf *conf) { int ret; struct lxc_storage *bdev; const struct lxc_rootfs *rootfs = &conf->rootfs; if (!rootfs->path) { ret = mount("", "/", NULL, MS_SLAVE | MS_REC, 0); if (ret < 0) { SYSERROR("Failed to remount \"/\" MS_REC | MS_SLAVE"); return -1; } return 0; } ret = access(rootfs->mount, F_OK); if (ret != 0) { SYSERROR("Failed to access to \"%s\". Check it is present", rootfs->mount); return -1; } bdev = storage_init(conf); if (!bdev) { ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\"", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return -1; } ret = bdev->ops->mount(bdev); storage_put(bdev); if (ret < 0) { ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\"", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return -1; } DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\"", rootfs->path, rootfs->mount, rootfs->options ? rootfs->options : "(null)"); return 0; } int lxc_chroot(const struct lxc_rootfs *rootfs) { int i, ret; char *p, *p2; char buf[LXC_LINELEN]; char *nroot; FILE *f; char *root = rootfs->mount; nroot = realpath(root, NULL); if (!nroot) { SYSERROR("Failed to resolve \"%s\"", root); return -1; } ret = chdir("/"); if (ret < 0) { free(nroot); return -1; } /* We could use here MS_MOVE, but in userns this mount is locked and * can't be moved. */ ret = mount(nroot, "/", NULL, MS_REC | MS_BIND, NULL); if (ret < 0) { SYSERROR("Failed to mount \"%s\" onto \"/\" as MS_REC | MS_BIND", nroot); free(nroot); return -1; } free(nroot); ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL); if (ret < 0) { SYSERROR("Failed to remount \"/\""); return -1; } /* The following code cleans up inherited mounts which are not required * for CT. * * The mountinfo file shows not all mounts, if a few points have been * unmounted between read operations from the mountinfo. So we need to * read mountinfo a few times. * * This loop can be skipped if a container uses userns, because all * inherited mounts are locked and we should live with all this trash. */ for (;;) { int progress = 0; f = fopen("./proc/self/mountinfo", "r"); if (!f) { SYSERROR("Failed to open \"/proc/self/mountinfo\""); return -1; } while (fgets(buf, LXC_LINELEN, f)) { for (p = buf, i=0; p && i < 4; i++) p = strchr(p+1, ' '); if (!p) continue; p2 = strchr(p+1, ' '); if (!p2) continue; *p2 = '\0'; *p = '.'; if (strcmp(p + 1, "/") == 0) continue; if (strcmp(p + 1, "/proc") == 0) continue; ret = umount2(p, MNT_DETACH); if (ret == 0) progress++; } fclose(f); if (!progress) break; } /* This also can be skipped if a container uses userns. */ (void)umount2("./proc", MNT_DETACH); /* It is weird, but chdir("..") moves us in a new root */ ret = chdir(".."); if (ret < 0) { SYSERROR("Failed to chdir(\"..\")"); return -1; } ret = chroot("."); if (ret < 0) { SYSERROR("Failed to chroot(\".\")"); return -1; } return 0; } /* (The following explanation is copied verbatim from the kernel.) * * pivot_root Semantics: * Moves the root file system of the current process to the directory put_old, * makes new_root as the new root file system of the current process, and sets * root/cwd of all processes which had them on the current root to new_root. * * Restrictions: * The new_root and put_old must be directories, and must not be on the * same file system as the current process root. The put_old must be * underneath new_root, i.e. adding a non-zero number of /.. to the string * pointed to by put_old must yield the same directory as new_root. No other * file system may be mounted on put_old. After all, new_root is a mountpoint. * * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem. * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives * in this situation. * * Notes: * - we don't move root/cwd if they are not at the root (reason: if something * cared enough to change them, it's probably wrong to force them elsewhere) * - it's okay to pick a root that isn't the root of a file system, e.g. * /nfs/my_root where /nfs is the mount point. It must be a mountpoint, * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root * first. */ static int lxc_pivot_root(const char *rootfs) { int oldroot; int newroot = -1, ret = -1; oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (oldroot < 0) { SYSERROR("Failed to open old root directory"); return -1; } newroot = open(rootfs, O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (newroot < 0) { SYSERROR("Failed to open new root directory"); goto on_error; } /* change into new root fs */ ret = fchdir(newroot); if (ret < 0) { ret = -1; SYSERROR("Failed to change to new rootfs \"%s\"", rootfs); goto on_error; } /* pivot_root into our new root fs */ ret = pivot_root(".", "."); if (ret < 0) { ret = -1; SYSERROR("Failed to pivot_root()"); goto on_error; } /* At this point the old-root is mounted on top of our new-root. To * unmounted it we must not be chdir'd into it, so escape back to * old-root. */ ret = fchdir(oldroot); if (ret < 0) { ret = -1; SYSERROR("Failed to enter old root directory"); goto on_error; } /* Make oldroot rslave to make sure our umounts don't propagate to the * host. */ ret = mount("", ".", "", MS_SLAVE | MS_REC, NULL); if (ret < 0) { ret = -1; SYSERROR("Failed to make oldroot rslave"); goto on_error; } ret = umount2(".", MNT_DETACH); if (ret < 0) { ret = -1; SYSERROR("Failed to detach old root directory"); goto on_error; } ret = fchdir(newroot); if (ret < 0) { ret = -1; SYSERROR("Failed to re-enter new root directory"); goto on_error; } ret = 0; TRACE("pivot_root(\"%s\") successful", rootfs); on_error: close(oldroot); if (newroot >= 0) close(newroot); return ret; } static int lxc_setup_rootfs_switch_root(const struct lxc_rootfs *rootfs) { if (!rootfs->path) { DEBUG("Container does not have a rootfs"); return 0; } if (detect_ramfs_rootfs()) return lxc_chroot(rootfs); return lxc_pivot_root(rootfs->mount); } static const struct id_map *find_mapped_nsid_entry(struct lxc_conf *conf, unsigned id, enum idtype idtype) { struct lxc_list *it; struct id_map *map; struct id_map *retmap = NULL; /* Shortcut for container's root mappings. */ if (id == 0) { if (idtype == ID_TYPE_UID) return conf->root_nsuid_map; if (idtype == ID_TYPE_GID) return conf->root_nsgid_map; } lxc_list_for_each(it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->nsid && id < map->nsid + map->range) { retmap = map; break; } } return retmap; } static int lxc_setup_devpts(struct lxc_conf *conf) { int ret; char **opts; char devpts_mntopts[256]; char *mntopt_sets[5]; char default_devpts_mntopts[256] = "gid=5,newinstance,ptmxmode=0666,mode=0620"; if (conf->pty_max <= 0) { DEBUG("No new devpts instance will be mounted since no pts " "devices are requested"); return 0; } ret = snprintf(devpts_mntopts, sizeof(devpts_mntopts), "%s,max=%zu", default_devpts_mntopts, conf->pty_max); if (ret < 0 || (size_t)ret >= sizeof(devpts_mntopts)) return -1; ret = umount2("/dev/pts", MNT_DETACH); if (ret < 0) SYSWARN("Failed to unmount old devpts instance"); else DEBUG("Unmounted old devpts instance"); /* Create mountpoint for devpts instance. */ ret = mkdir("/dev/pts", 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"/dev/pts\" directory"); return -1; } /* gid=5 && max= */ mntopt_sets[0] = devpts_mntopts; /* !gid=5 && max= */ mntopt_sets[1] = devpts_mntopts + STRLITERALLEN("gid=5") + 1; /* gid=5 && !max= */ mntopt_sets[2] = default_devpts_mntopts; /* !gid=5 && !max= */ mntopt_sets[3] = default_devpts_mntopts + STRLITERALLEN("gid=5") + 1; /* end */ mntopt_sets[4] = NULL; for (ret = -1, opts = mntopt_sets; opts && *opts; opts++) { /* mount new devpts instance */ ret = mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, *opts); if (ret == 0) break; } if (ret < 0) { SYSERROR("Failed to mount new devpts instance"); return -1; } DEBUG("Mount new devpts instance with options \"%s\"", *opts); /* Remove any pre-existing /dev/ptmx file. */ ret = remove("/dev/ptmx"); if (ret < 0) { if (errno != ENOENT) { SYSERROR("Failed to remove existing \"/dev/ptmx\" file"); return -1; } } else { DEBUG("Removed existing \"/dev/ptmx\" file"); } /* Create dummy /dev/ptmx file as bind mountpoint for /dev/pts/ptmx. */ ret = mknod("/dev/ptmx", S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create dummy \"/dev/ptmx\" file as bind mount target"); return -1; } DEBUG("Created dummy \"/dev/ptmx\" file as bind mount target"); /* Fallback option: create symlink /dev/ptmx -> /dev/pts/ptmx */ ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL); if (!ret) { DEBUG("Bind mounted \"/dev/pts/ptmx\" to \"/dev/ptmx\""); return 0; } else { /* Fallthrough and try to create a symlink. */ ERROR("Failed to bind mount \"/dev/pts/ptmx\" to \"/dev/ptmx\""); } /* Remove the dummy /dev/ptmx file we created above. */ ret = remove("/dev/ptmx"); if (ret < 0) { SYSERROR("Failed to remove existing \"/dev/ptmx\""); return -1; } /* Fallback option: Create symlink /dev/ptmx -> /dev/pts/ptmx. */ ret = symlink("/dev/pts/ptmx", "/dev/ptmx"); if (ret < 0) { SYSERROR("Failed to create symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); return -1; } DEBUG("Created symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); return 0; } static int setup_personality(int persona) { int ret; #if HAVE_SYS_PERSONALITY_H if (persona == -1) return 0; ret = personality(persona); if (ret < 0) { SYSERROR("Failed to set personality to \"0x%x\"", persona); return -1; } INFO("Set personality to \"0x%x\"", persona); #endif return 0; } static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs, const struct lxc_terminal *console) { int ret; char path[PATH_MAX]; char *rootfs_path = rootfs->path ? rootfs->mount : ""; if (console->path && !strcmp(console->path, "none")) return 0; ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs_path); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; /* When we are asked to setup a console we remove any previous * /dev/console bind-mounts. */ if (file_exists(path)) { ret = lxc_unstack_mountpoint(path, false); if (ret < 0) { SYSERROR("Failed to unmount \"%s\"", path); return -ret; } else { DEBUG("Cleared all (%d) mounts from \"%s\"", ret, path); } } /* For unprivileged containers autodev or automounts will already have * taken care of creating /dev/console. */ ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create console"); return -errno; } ret = fchmod(console->slave, S_IXUSR | S_IXGRP); if (ret < 0) { SYSERROR("Failed to set mode \"0%o\" to \"%s\"", S_IXUSR | S_IXGRP, console->name); return -errno; } ret = safe_mount(console->name, path, "none", MS_BIND, 0, rootfs_path); if (ret < 0) { ERROR("Failed to mount \"%s\" on \"%s\"", console->name, path); return -1; } DEBUG("Mounted pts device \"%s\" onto \"%s\"", console->name, path); return 0; } static int lxc_setup_ttydir_console(const struct lxc_rootfs *rootfs, const struct lxc_terminal *console, char *ttydir) { int ret; char path[PATH_MAX], lxcpath[PATH_MAX]; char *rootfs_path = rootfs->path ? rootfs->mount : ""; if (console->path && !strcmp(console->path, "none")) return 0; /* create rootfs/dev/ directory */ ret = snprintf(path, sizeof(path), "%s/dev/%s", rootfs_path, ttydir); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; ret = mkdir(path, 0755); if (ret && errno != EEXIST) { SYSERROR("Failed to create \"%s\"", path); return -errno; } DEBUG("Created directory for console and tty devices at \"%s\"", path); ret = snprintf(lxcpath, sizeof(lxcpath), "%s/dev/%s/console", rootfs_path, ttydir); if (ret < 0 || (size_t)ret >= sizeof(lxcpath)) return -1; ret = mknod(lxcpath, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create \"%s\"", lxcpath); return -errno; } ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs_path); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; if (file_exists(path)) { ret = lxc_unstack_mountpoint(path, false); if (ret < 0) { SYSERROR("Failed to unmount \"%s\"", path); return -ret; } else { DEBUG("Cleared all (%d) mounts from \"%s\"", ret, path); } } ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create console"); return -errno; } ret = fchmod(console->slave, S_IXUSR | S_IXGRP); if (ret < 0) { SYSERROR("Failed to set mode \"0%o\" to \"%s\"", S_IXUSR | S_IXGRP, console->name); return -errno; } /* bind mount console->name to '/dev//console' */ ret = safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs_path); if (ret < 0) { ERROR("Failed to mount \"%s\" on \"%s\"", console->name, lxcpath); return -1; } DEBUG("Mounted \"%s\" onto \"%s\"", console->name, lxcpath); /* bind mount '/dev//console' to '/dev/console' */ ret = safe_mount(lxcpath, path, "none", MS_BIND, 0, rootfs_path); if (ret < 0) { ERROR("Failed to mount \"%s\" on \"%s\"", console->name, lxcpath); return -1; } DEBUG("Mounted \"%s\" onto \"%s\"", console->name, lxcpath); DEBUG("Console has been setup under \"%s\" and mounted to \"%s\"", lxcpath, path); return 0; } static int lxc_setup_console(const struct lxc_rootfs *rootfs, const struct lxc_terminal *console, char *ttydir) { if (!ttydir) return lxc_setup_dev_console(rootfs, console); return lxc_setup_ttydir_console(rootfs, console, ttydir); } static void parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size) { struct mount_opt *mo; /* If opt is found in mount_opt, set or clear flags. * Otherwise append it to data. */ for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) *flags &= ~mo->flag; else *flags |= mo->flag; return; } } if (strlen(*data)) (void)strlcat(*data, ",", size); (void)strlcat(*data, opt, size); } int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata) { char *data, *p, *s; size_t size; *mntdata = NULL; *mntflags = 0L; if (!mntopts) return 0; s = strdup(mntopts); if (!s) return -1; size = strlen(s) + 1; data = malloc(size); if (!data) { free(s); return -1; } *data = 0; lxc_iterate_parts(p, s, ",") parse_mntopt(p, mntflags, &data, size); if (*data) *mntdata = data; else free(data); free(s); return 0; } static void parse_propagationopt(char *opt, unsigned long *flags) { struct mount_opt *mo; /* If opt is found in propagation_opt, set or clear flags. */ for (mo = &propagation_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) != 0) continue; if (mo->clear) *flags &= ~mo->flag; else *flags |= mo->flag; return; } } int parse_propagationopts(const char *mntopts, unsigned long *pflags) { char *p, *s; if (!mntopts) return 0; s = strdup(mntopts); if (!s) { SYSERROR("Failed to allocate memory"); return -ENOMEM; } *pflags = 0L; lxc_iterate_parts(p, s, ",") parse_propagationopt(p, pflags); free(s); return 0; } static void null_endofword(char *word) { while (*word && *word != ' ' && *word != '\t') word++; *word = '\0'; } /* skip @nfields spaces in @src */ static char *get_field(char *src, int nfields) { int i; char *p = src; for (i = 0; i < nfields; i++) { while (*p && *p != ' ' && *p != '\t') p++; if (!*p) break; p++; } return p; } static int mount_entry(const char *fsname, const char *target, const char *fstype, unsigned long mountflags, unsigned long pflags, const char *data, bool optional, bool dev, bool relative, const char *rootfs) { int ret; char srcbuf[PATH_MAX]; const char *srcpath = fsname; #ifdef HAVE_STATVFS struct statvfs sb; #endif if (relative) { ret = snprintf(srcbuf, PATH_MAX, "%s/%s", rootfs ? rootfs : "/", fsname ? fsname : ""); if (ret < 0 || ret >= PATH_MAX) { ERROR("source path is too long"); return -1; } srcpath = srcbuf; } ret = safe_mount(srcpath, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs); if (ret < 0) { if (optional) { SYSINFO("Failed to mount \"%s\" on \"%s\" (optional)", srcpath ? srcpath : "(null)", target); return 0; } SYSERROR("Failed to mount \"%s\" on \"%s\"", srcpath ? srcpath : "(null)", target); return -1; } if ((mountflags & MS_REMOUNT) || (mountflags & MS_BIND)) { unsigned long rqd_flags = 0; DEBUG("Remounting \"%s\" on \"%s\" to respect bind or remount " "options", srcpath ? srcpath : "(none)", target ? target : "(none)"); if (mountflags & MS_RDONLY) rqd_flags |= MS_RDONLY; #ifdef HAVE_STATVFS if (srcpath && statvfs(srcpath, &sb) == 0) { unsigned long required_flags = rqd_flags; if (sb.f_flag & MS_NOSUID) required_flags |= MS_NOSUID; if (sb.f_flag & MS_NODEV && !dev) required_flags |= MS_NODEV; if (sb.f_flag & MS_RDONLY) required_flags |= MS_RDONLY; if (sb.f_flag & MS_NOEXEC) required_flags |= MS_NOEXEC; DEBUG("Flags for \"%s\" were %lu, required extra flags " "are %lu", srcpath, sb.f_flag, required_flags); /* If this was a bind mount request, and required_flags * does not have any flags which are not already in * mountflags, then skip the remount. */ if (!(mountflags & MS_REMOUNT)) { if (!(required_flags & ~mountflags) && rqd_flags == 0) { DEBUG("Mountflags already were %lu, " "skipping remount", mountflags); goto skipremount; } } mountflags |= required_flags; } #endif ret = mount(srcpath, target, fstype, mountflags | MS_REMOUNT, data); if (ret < 0) { if (optional) { SYSINFO("Failed to mount \"%s\" on \"%s\" (optional)", srcpath ? srcpath : "(null)", target); return 0; } SYSERROR("Failed to mount \"%s\" on \"%s\"", srcpath ? srcpath : "(null)", target); return -1; } } if (pflags) { ret = mount(NULL, target, NULL, pflags, NULL); if (ret < 0) { if (optional) { SYSINFO("Failed to change mount propagation " "for \"%s\" (optional)", target); return 0; } else { SYSERROR("Failed to change mount propagation " "for \"%s\" (optional)", target); return -1; } } DEBUG("Changed mount propagation for \"%s\"", target); } #ifdef HAVE_STATVFS skipremount: #endif DEBUG("Mounted \"%s\" on \"%s\" with filesystem type \"%s\"", srcpath ? srcpath : "(null)", target, fstype); return 0; } /* Remove "optional", "create=dir", and "create=file" from mntopt */ static void cull_mntent_opt(struct mntent *mntent) { int i; char *list[] = { "create=dir", "create=file", "optional", "relative", NULL }; for (i = 0; list[i]; i++) { char *p, *p2; p = strstr(mntent->mnt_opts, list[i]); if (!p) continue; p2 = strchr(p, ','); if (!p2) { /* no more mntopts, so just chop it here */ *p = '\0'; continue; } memmove(p, p2 + 1, strlen(p2 + 1) + 1); } } static int mount_entry_create_dir_file(const struct mntent *mntent, const char *path, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int ret; char *p1, *p2; if (strncmp(mntent->mnt_type, "overlay", 7) == 0) { ret = ovl_mkdir(mntent, rootfs, lxc_name, lxc_path); if (ret < 0) return -1; } if (hasmntopt(mntent, "create=dir")) { ret = mkdir_p(path, 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", path); return -1; } } if (!hasmntopt(mntent, "create=file")) return 0; ret = access(path, F_OK); if (ret == 0) return 0; 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; } ret = mknod(path, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) return -errno; return 0; } /* rootfs, lxc_name, and lxc_path can be NULL when the container is created * without a rootfs. */ static inline int mount_entry_on_generic(struct mntent *mntent, const char *path, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int ret; unsigned long mntflags; char *mntdata; bool dev, optional, relative; unsigned long pflags = 0; char *rootfs_path = NULL; optional = hasmntopt(mntent, "optional") != NULL; dev = hasmntopt(mntent, "dev") != NULL; relative = hasmntopt(mntent, "relative") != NULL; if (rootfs && rootfs->path) rootfs_path = rootfs->mount; ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name, lxc_path); if (ret < 0) { if (optional) return 0; return -1; } cull_mntent_opt(mntent); ret = parse_propagationopts(mntent->mnt_opts, &pflags); if (ret < 0) return -1; ret = parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata); if (ret < 0) return -1; ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags, pflags, mntdata, optional, dev, relative, rootfs_path); free(mntdata); return ret; } static inline int mount_entry_on_systemfs(struct mntent *mntent) { int ret; char path[PATH_MAX]; /* For containers created without a rootfs all mounts are treated as * absolute paths starting at / on the host. */ if (mntent->mnt_dir[0] != '/') ret = snprintf(path, sizeof(path), "/%s", mntent->mnt_dir); else ret = snprintf(path, sizeof(path), "%s", mntent->mnt_dir); if (ret < 0 || ret >= sizeof(path)) return -1; return mount_entry_on_generic(mntent, path, NULL, NULL, NULL); } static int mount_entry_on_absolute_rootfs(struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int offset; char *aux; const char *lxcpath; char path[PATH_MAX]; int ret = 0; lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) return -1; /* If rootfs->path is a blockdev path, allow container fstab to use * //rootfs" as the target prefix. */ ret = snprintf(path, PATH_MAX, "%s/%s/rootfs", lxcpath, lxc_name); if (ret < 0 || ret >= PATH_MAX) goto skipvarlib; aux = strstr(mntent->mnt_dir, path); if (aux) { offset = strlen(path); goto skipabs; } skipvarlib: aux = strstr(mntent->mnt_dir, rootfs->path); if (!aux) { WARN("Ignoring mount point \"%s\"", mntent->mnt_dir); return ret; } offset = strlen(rootfs->path); skipabs: ret = snprintf(path, PATH_MAX, "%s/%s", rootfs->mount, aux + offset); if (ret < 0 || ret >= PATH_MAX) return -1; return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } static int mount_entry_on_relative_rootfs(struct mntent *mntent, const struct lxc_rootfs *rootfs, const char *lxc_name, const char *lxc_path) { int ret; char path[PATH_MAX]; /* relative to root mount point */ ret = snprintf(path, sizeof(path), "%s/%s", rootfs->mount, mntent->mnt_dir); if (ret < 0 || (size_t)ret >= sizeof(path)) return -1; return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path); } 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) { char buf[4096]; struct mntent mntent; int ret = -1; while (getmntent_r(file, &mntent, buf, sizeof(buf))) { 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("Finished setting up mounts"); return ret; } static int setup_mount(const struct lxc_conf *conf, const struct lxc_rootfs *rootfs, const char *fstab, const char *lxc_name, const char *lxc_path) { FILE *f; int ret; if (!fstab) return 0; f = setmntent(fstab, "r"); if (!f) { SYSERROR("Failed to open \"%s\"", fstab); return -1; } ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); if (ret < 0) ERROR("Failed to set up mount entries"); endmntent(f); return ret; } FILE *make_anonymous_mount_file(struct lxc_list *mount) { int ret; char *mount_entry; struct lxc_list *iterator; int fd = -1; fd = memfd_create(".lxc_mount_file", MFD_CLOEXEC); if (fd < 0) { char template[] = P_tmpdir "/.lxc_mount_file_XXXXXX"; if (errno != ENOSYS) return NULL; fd = lxc_make_tmpfile(template, true); if (fd < 0) { SYSERROR("Could not create temporary mount file"); return NULL; } TRACE("Created temporary mount file"); } lxc_list_for_each (iterator, mount) { size_t len; mount_entry = iterator->elem; len = strlen(mount_entry); ret = lxc_write_nointr(fd, mount_entry, len); if (ret != len) goto on_error; ret = lxc_write_nointr(fd, "\n", 1); if (ret != 1) goto on_error; } ret = lseek(fd, 0, SEEK_SET); if (ret < 0) goto on_error; return fdopen(fd, "r+"); on_error: SYSERROR("Failed to write mount entry to temporary mount file"); close(fd); return NULL; } static int setup_mount_entries(const struct lxc_conf *conf, const struct lxc_rootfs *rootfs, struct lxc_list *mount, const char *lxc_name, const char *lxc_path) { int ret; FILE *f; f = make_anonymous_mount_file(mount); if (!f) return -1; ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path); fclose(f); return ret; } static int parse_cap(const char *cap) { size_t i; int capid = -1; size_t end = sizeof(caps_opt) / sizeof(caps_opt[0]); char *ptr = NULL; if (strcmp(cap, "none") == 0) return -2; for (i = 0; i < end; i++) { if (strcmp(cap, caps_opt[i].name)) continue; capid = caps_opt[i].value; break; } if (capid < 0) { /* Try to see if it's numeric, so the user may specify * capabilities that the running kernel knows about but we * don't */ errno = 0; capid = strtol(cap, &ptr, 10); if (!ptr || *ptr != '\0' || errno != 0) /* not a valid number */ capid = -1; else if (capid > lxc_caps_last_cap()) /* we have a number but it's not a valid * capability */ capid = -1; } return capid; } int in_caplist(int cap, struct lxc_list *caps) { int capid; struct lxc_list *iterator; lxc_list_for_each (iterator, caps) { capid = parse_cap(iterator->elem); if (capid == cap) return 1; } return 0; } static int setup_caps(struct lxc_list *caps) { int capid; char *drop_entry; struct lxc_list *iterator; lxc_list_for_each (iterator, caps) { int ret; drop_entry = iterator->elem; capid = parse_cap(drop_entry); if (capid < 0) { ERROR("unknown capability %s", drop_entry); return -1; } ret = prctl(PR_CAPBSET_DROP, prctl_arg(capid), prctl_arg(0), prctl_arg(0), prctl_arg(0)); if (ret < 0) { SYSERROR("Failed to remove %s capability", drop_entry); return -1; } DEBUG("Dropped %s (%d) capability", drop_entry, capid); } DEBUG("Capabilities have been setup"); return 0; } static int dropcaps_except(struct lxc_list *caps) { int i, capid, numcaps; char *keep_entry; struct lxc_list *iterator; numcaps = lxc_caps_last_cap() + 1; if (numcaps <= 0 || numcaps > 200) return -1; TRACE("Found %d capabilities", numcaps); /* caplist[i] is 1 if we keep capability i */ int *caplist = alloca(numcaps * sizeof(int)); memset(caplist, 0, numcaps * sizeof(int)); lxc_list_for_each (iterator, caps) { keep_entry = iterator->elem; capid = parse_cap(keep_entry); if (capid == -2) continue; if (capid < 0) { ERROR("Unknown capability %s", keep_entry); return -1; } DEBUG("Keep capability %s (%d)", keep_entry, capid); caplist[capid] = 1; } for (i = 0; i < numcaps; i++) { int ret; if (caplist[i]) continue; ret = prctl(PR_CAPBSET_DROP, prctl_arg(i), prctl_arg(0), prctl_arg(0), prctl_arg(0)); if (ret < 0) { SYSERROR("Failed to remove capability %d", i); return -1; } } DEBUG("Capabilities have been setup"); return 0; } static int parse_resource(const char *res) { int ret; size_t i; int resid = -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; /* Try to see if it's numeric, so the user may specify * resources that the running kernel knows about but * we don't. */ ret = lxc_safe_int(res, &resid); if (ret < 0) return -1; return resid; } int setup_resource_limits(struct lxc_list *limits, pid_t pid) { int resid; struct lxc_list *it; struct lxc_limit *lim; lxc_list_for_each (it, limits) { lim = it->elem; resid = parse_resource(lim->resource); if (resid < 0) { ERROR("Unknown resource %s", lim->resource); return -1; } #if HAVE_PRLIMIT || HAVE_PRLIMIT64 if (prlimit(pid, resid, &lim->limit, NULL) != 0) { SYSERROR("Failed to set limit %s", lim->resource); return -1; } TRACE("Setup \"%s\" limit", lim->resource); #else ERROR("Cannot set limit \"%s\" as prlimit is missing", lim->resource); return -1; #endif } return 0; } int setup_sysctl_parameters(struct lxc_list *sysctls) { struct lxc_list *it; struct lxc_sysctl *elem; int ret = 0; char *tmp = NULL; char filename[PATH_MAX] = {0}; lxc_list_for_each (it, sysctls) { elem = it->elem; tmp = lxc_string_replace(".", "/", elem->key); if (!tmp) { ERROR("Failed to replace key %s", elem->key); return -1; } ret = snprintf(filename, sizeof(filename), "/proc/sys/%s", tmp); free(tmp); if (ret < 0 || (size_t)ret >= sizeof(filename)) { ERROR("Error setting up sysctl parameters path"); return -1; } ret = lxc_write_to_file(filename, elem->value, strlen(elem->value), false, 0666); if (ret < 0) { ERROR("Failed to setup sysctl parameters %s to %s", elem->key, elem->value); return -1; } } return 0; } int setup_proc_filesystem(struct lxc_list *procs, pid_t pid) { struct lxc_list *it; struct lxc_proc *elem; int ret = 0; char *tmp = NULL; char filename[PATH_MAX] = {0}; lxc_list_for_each (it, procs) { elem = it->elem; tmp = lxc_string_replace(".", "/", elem->filename); if (!tmp) { ERROR("Failed to replace key %s", elem->filename); return -1; } ret = snprintf(filename, sizeof(filename), "/proc/%d/%s", pid, tmp); free(tmp); if (ret < 0 || (size_t)ret >= sizeof(filename)) { ERROR("Error setting up proc filesystem path"); return -1; } ret = lxc_write_to_file(filename, elem->value, strlen(elem->value), false, 0666); if (ret < 0) { ERROR("Failed to setup proc filesystem %s to %s", elem->filename, elem->value); return -1; } } return 0; } static char *default_rootfs_mount = LXCROOTFSMOUNT; struct lxc_conf *lxc_conf_init(void) { int i; struct lxc_conf *new; new = malloc(sizeof(*new)); if (!new) return NULL; memset(new, 0, sizeof(*new)); new->loglevel = LXC_LOG_LEVEL_NOTSET; new->personality = -1; new->autodev = 1; new->console.buffer_size = 0; new->console.log_path = NULL; new->console.log_fd = -1; new->console.log_size = 0; new->console.path = NULL; new->console.peer = -1; new->console.proxy.busy = -1; new->console.proxy.master = -1; new->console.proxy.slave = -1; new->console.master = -1; new->console.slave = -1; new->console.name[0] = '\0'; memset(&new->console.ringbuf, 0, sizeof(struct lxc_ringbuf)); new->maincmd_fd = -1; new->nbd_idx = -1; new->rootfs.mount = strdup(default_rootfs_mount); if (!new->rootfs.mount) { free(new); return NULL; } new->logfd = -1; lxc_list_init(&new->cgroup); lxc_list_init(&new->cgroup2); lxc_list_init(&new->network); lxc_list_init(&new->mount_list); lxc_list_init(&new->caps); lxc_list_init(&new->keepcaps); lxc_list_init(&new->id_map); new->root_nsuid_map = NULL; new->root_nsgid_map = NULL; lxc_list_init(&new->includes); lxc_list_init(&new->aliens); lxc_list_init(&new->environment); lxc_list_init(&new->limits); lxc_list_init(&new->sysctls); lxc_list_init(&new->procs); new->hooks_version = 0; for (i = 0; i < NUM_LXC_HOOKS; i++) lxc_list_init(&new->hooks[i]); lxc_list_init(&new->groups); lxc_list_init(&new->state_clients); new->lsm_aa_profile = NULL; new->lsm_se_context = NULL; new->tmp_umount_proc = false; /* if running in a new user namespace, init and COMMAND * default to running as UID/GID 0 when using lxc-execute */ new->init_uid = 0; new->init_gid = 0; memset(&new->cgroup_meta, 0, sizeof(struct lxc_cgroup)); memset(&new->ns_share, 0, sizeof(char *) * LXC_NS_MAX); return new; } int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size) { int fd, ret; char path[PATH_MAX]; if (geteuid() != 0 && idtype == ID_TYPE_GID) { size_t buflen; ret = snprintf(path, PATH_MAX, "/proc/%d/setgroups", pid); if (ret < 0 || ret >= PATH_MAX) return -E2BIG; fd = open(path, O_WRONLY); if (fd < 0 && errno != ENOENT) { SYSERROR("Failed to open \"%s\"", path); return -1; } if (fd >= 0) { buflen = STRLITERALLEN("deny\n"); errno = 0; ret = lxc_write_nointr(fd, "deny\n", buflen); close(fd); if (ret != buflen) { SYSERROR("Failed to write \"deny\" to " "\"/proc/%d/setgroups\"", pid); return -1; } TRACE("Wrote \"deny\" to \"/proc/%d/setgroups\"", pid); } } ret = snprintf(path, PATH_MAX, "/proc/%d/%cid_map", pid, idtype == ID_TYPE_UID ? 'u' : 'g'); if (ret < 0 || ret >= PATH_MAX) return -E2BIG; fd = open(path, O_WRONLY); if (fd < 0) { SYSERROR("Failed to open \"%s\"", path); return -1; } errno = 0; ret = lxc_write_nointr(fd, buf, buf_size); close(fd); if (ret != buf_size) { SYSERROR("Failed to write %cid mapping to \"%s\"", idtype == ID_TYPE_UID ? 'u' : 'g', path); return -1; } return 0; } /* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. * * @return 1 if functional binary was found * @return 0 if binary exists but is lacking privilege * @return -ENOENT if binary does not exist * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID */ static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) { char *path; int ret; struct stat st; int fret = 0; if (cap != CAP_SETUID && cap != CAP_SETGID) return -EINVAL; path = on_path(binary, NULL); if (!path) return -ENOENT; ret = stat(path, &st); if (ret < 0) { fret = -errno; goto cleanup; } /* Check if the binary is setuid. */ if (st.st_mode & S_ISUID) { DEBUG("The binary \"%s\" does have the setuid bit set", path); fret = 1; goto cleanup; } #if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES /* Check if it has the CAP_SETUID capability. */ if ((cap & CAP_SETUID) && lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) && lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) { DEBUG("The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE " "and CAP_PERMITTED sets", path); fret = 1; goto cleanup; } /* Check if it has the CAP_SETGID capability. */ if ((cap & CAP_SETGID) && lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) && lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) { DEBUG("The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE " "and CAP_PERMITTED sets", path); fret = 1; goto cleanup; } #else /* If we cannot check for file capabilities we need to give the benefit * of the doubt. Otherwise we might fail even though all the necessary * file capabilities are set. */ DEBUG("Cannot check for file capabilities as full capability support is " "missing. Manual intervention needed"); fret = 1; #endif cleanup: free(path); return fret; } int lxc_map_ids_exec_wrapper(void *args) { execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL); return -1; } int lxc_map_ids(struct lxc_list *idmap, pid_t pid) { int fill, left; char u_or_g; char *pos; char cmd_output[PATH_MAX]; struct id_map *map; struct lxc_list *iterator; enum idtype type; /* strlen("new@idmap") = 9 * + * strlen(" ") = 1 * + * INTTYPE_TO_STRLEN(uint32_t) * + * strlen(" ") = 1 * * We add some additional space to make sure that we really have * LXC_IDMAPLEN bytes available for our the {g,u]id mapping. */ int ret = 0, gidmap = 0, uidmap = 0; char mapbuf[9 + 1 + INTTYPE_TO_STRLEN(uint32_t) + 1 + LXC_IDMAPLEN] = {0}; bool had_entry = false, use_shadow = false; int hostuid, hostgid; hostuid = geteuid(); hostgid = getegid(); /* If new{g,u}idmap exists, that is, if shadow is handing out subuid * ranges, then insist that root also reserve ranges in subuid. This * will protected it by preventing another user from being handed the * range by shadow. */ uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID); if (uidmap == -ENOENT) WARN("newuidmap binary is missing"); else if (!uidmap) WARN("newuidmap is lacking necessary privileges"); gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID); if (gidmap == -ENOENT) WARN("newgidmap binary is missing"); else if (!gidmap) WARN("newgidmap is lacking necessary privileges"); if (uidmap > 0 && gidmap > 0) { DEBUG("Functional newuidmap and newgidmap binary found"); use_shadow = true; } else { /* In case unprivileged users run application containers via * execute() or a start*() there are valid cases where they may * only want to map their own {g,u}id. Let's not block them from * doing so by requiring geteuid() == 0. */ DEBUG("No newuidmap and newgidmap binary found. Trying to " "write directly with euid %d", hostuid); } /* Check if we really need to use newuidmap and newgidmap. * If the user is only remapping his own {g,u}id, we don't need it. */ if (use_shadow && lxc_list_len(idmap) == 2) { use_shadow = false; lxc_list_for_each(iterator, idmap) { map = iterator->elem; if (map->idtype == ID_TYPE_UID && map->range == 1 && map->nsid == hostuid && map->hostid == hostuid) continue; if (map->idtype == ID_TYPE_GID && map->range == 1 && map->nsid == hostgid && map->hostid == hostgid) continue; use_shadow = true; break; } } for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID; type++, u_or_g = 'g') { pos = mapbuf; if (use_shadow) pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid); lxc_list_for_each(iterator, idmap) { map = iterator->elem; if (map->idtype != type) continue; had_entry = true; left = LXC_IDMAPLEN - (pos - mapbuf); fill = snprintf(pos, left, "%s%lu %lu %lu%s", use_shadow ? " " : "", map->nsid, map->hostid, map->range, use_shadow ? "" : "\n"); if (fill <= 0 || fill >= left) { /* The kernel only takes <= 4k for writes to * /proc//{g,u}id_map */ SYSERROR("Too many %cid mappings defined", u_or_g); return -1; } pos += fill; } if (!had_entry) continue; /* Try to catch the output of new{g,u}idmap to make debugging * easier. */ if (use_shadow) { ret = run_command(cmd_output, sizeof(cmd_output), lxc_map_ids_exec_wrapper, (void *)mapbuf); if (ret < 0) { ERROR("new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf); return -1; } TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf); } else { ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf); if (ret < 0) { ERROR("Failed to write mapping: %s", mapbuf); return -1; } TRACE("Wrote mapping \"%s\"", mapbuf); } memset(mapbuf, 0, sizeof(mapbuf)); } return 0; } /* Return the host uid/gid to which the container root is mapped in val. * Return true if id was found, false otherwise. */ bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype, unsigned long *val) { unsigned nsid; struct id_map *map; struct lxc_list *it; if (idtype == ID_TYPE_UID) nsid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid; else nsid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid; lxc_list_for_each (it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (map->nsid != nsid) continue; *val = map->hostid; return true; } return false; } int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype) { struct id_map *map; struct lxc_list *it; lxc_list_for_each (it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->hostid && id < map->hostid + map->range) return (id - map->hostid) + map->nsid; } return -1; } int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype) { struct id_map *map; struct lxc_list *it; unsigned int freeid = 0; again: lxc_list_for_each (it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (freeid >= map->nsid && freeid < map->nsid + map->range) { freeid = map->nsid + map->range; goto again; } } return freeid; } int chown_mapped_root_exec_wrapper(void *args) { execvp("lxc-usernsexec", args); return -1; } /* chown_mapped_root: for an unprivileged user with uid/gid X to * chown a dir to subuid/subgid Y, he needs to run chown as root * in a userns where nsid 0 is mapped to hostuid/hostgid Y, and * nsid Y is mapped to hostuid/hostgid X. That way, the container * root is privileged with respect to hostuid/hostgid X, allowing * him to do the chown. */ int chown_mapped_root(const char *path, struct lxc_conf *conf) { uid_t rootuid, rootgid; unsigned long val; int hostuid, hostgid, ret; struct stat sb; char map1[100], map2[100], map3[100], map4[100], map5[100]; char ugid[100]; const char *args1[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "-m", map3, "-m", map5, "--", "chown", ugid, path, NULL}; const char *args2[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "-m", map3, "-m", map4, "-m", map5, "--", "chown", ugid, path, NULL}; char cmd_output[PATH_MAX]; hostuid = geteuid(); hostgid = getegid(); if (!get_mapped_rootid(conf, ID_TYPE_UID, &val)) { ERROR("No uid mapping for container root"); return -1; } rootuid = (uid_t)val; if (!get_mapped_rootid(conf, ID_TYPE_GID, &val)) { ERROR("No gid mapping for container root"); return -1; } rootgid = (gid_t)val; if (hostuid == 0) { if (chown(path, rootuid, rootgid) < 0) { ERROR("Error chowning %s", path); return -1; } return 0; } if (rootuid == hostuid) { /* nothing to do */ INFO("Container root is our uid; no need to chown"); return 0; } /* save the current gid of "path" */ if (stat(path, &sb) < 0) { ERROR("Error stat %s", path); return -1; } /* Update the path argument in case this was overlayfs. */ args1[sizeof(args1) / sizeof(args1[0]) - 2] = path; args2[sizeof(args2) / sizeof(args2[0]) - 2] = path; /* * A file has to be group-owned by a gid mapped into the * container, or the container won't be privileged over it. */ DEBUG("trying to chown \"%s\" to %d", path, hostgid); if (sb.st_uid == hostuid && mapped_hostid(sb.st_gid, conf, ID_TYPE_GID) < 0 && chown(path, -1, hostgid) < 0) { ERROR("Failed chgrping %s", path); return -1; } /* "u:0:rootuid:1" */ ret = snprintf(map1, 100, "u:0:%d:1", rootuid); if (ret < 0 || ret >= 100) { ERROR("Error uid printing map string"); return -1; } /* "u:hostuid:hostuid:1" */ ret = snprintf(map2, 100, "u:%d:%d:1", hostuid, hostuid); if (ret < 0 || ret >= 100) { ERROR("Error uid printing map string"); return -1; } /* "g:0:rootgid:1" */ ret = snprintf(map3, 100, "g:0:%d:1", rootgid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } /* "g:pathgid:rootgid+pathgid:1" */ ret = snprintf(map4, 100, "g:%d:%d:1", (gid_t)sb.st_gid, rootgid + (gid_t)sb.st_gid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } /* "g:hostgid:hostgid:1" */ ret = snprintf(map5, 100, "g:%d:%d:1", hostgid, hostgid); if (ret < 0 || ret >= 100) { ERROR("Error gid printing map string"); return -1; } /* "0:pathgid" (chown) */ ret = snprintf(ugid, 100, "0:%d", (gid_t)sb.st_gid); if (ret < 0 || ret >= 100) { ERROR("Error owner printing format string for chown"); return -1; } if (hostgid == sb.st_gid) ret = run_command(cmd_output, sizeof(cmd_output), chown_mapped_root_exec_wrapper, (void *)args1); else ret = run_command(cmd_output, sizeof(cmd_output), chown_mapped_root_exec_wrapper, (void *)args2); if (ret < 0) ERROR("lxc-usernsexec failed: %s", cmd_output); return ret; } /* NOTE: Must not be called from inside the container namespace! */ int lxc_create_tmp_proc_mount(struct lxc_conf *conf) { int mounted; mounted = lxc_mount_proc_if_needed(conf->rootfs.path ? conf->rootfs.mount : ""); if (mounted == -1) { SYSERROR("Failed to mount proc in the container"); /* continue only if there is no rootfs */ if (conf->rootfs.path) return -1; } else if (mounted == 1) { conf->tmp_umount_proc = true; } return 0; } void tmp_proc_unmount(struct lxc_conf *lxc_conf) { if (!lxc_conf->tmp_umount_proc) return; (void)umount2("/proc", MNT_DETACH); lxc_conf->tmp_umount_proc = false; } /* Walk /proc/mounts and change any shared entries to slave. */ void remount_all_slave(void) { int memfd, mntinfo_fd, ret; ssize_t copied; FILE *f; size_t len = 0; char *line = NULL; mntinfo_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); if (mntinfo_fd < 0) { SYSERROR("Failed to open \"/proc/self/mountinfo\""); return; } memfd = memfd_create(".lxc_mountinfo", MFD_CLOEXEC); if (memfd < 0) { char template[] = P_tmpdir "/.lxc_mountinfo_XXXXXX"; if (errno != ENOSYS) { SYSERROR("Failed to create temporary in-memory file"); close(mntinfo_fd); return; } memfd = lxc_make_tmpfile(template, true); if (memfd < 0) { close(mntinfo_fd); WARN("Failed to create temporary file"); return; } } again: copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX); if (copied < 0) { if (errno == EINTR) goto again; SYSERROR("Failed to copy \"/proc/self/mountinfo\""); close(mntinfo_fd); close(memfd); return; } close(mntinfo_fd); /* After a successful fdopen() memfd will be closed when calling * fclose(f). Calling close(memfd) afterwards is undefined. */ ret = lseek(memfd, 0, SEEK_SET); if (ret < 0) { SYSERROR("Failed to reset file descriptor offset"); close(memfd); return; } f = fdopen(memfd, "r"); if (!f) { SYSERROR("Failed to open copy of \"/proc/self/mountinfo\" to mark " "all shared. Continuing"); close(memfd); return; } while (getline(&line, &len, f) != -1) { int ret; char *opts, *target; target = get_field(line, 4); if (!target) continue; opts = get_field(target, 2); if (!opts) continue; null_endofword(opts); if (!strstr(opts, "shared")) continue; null_endofword(target); ret = mount(NULL, target, NULL, MS_SLAVE, NULL); if (ret < 0) { SYSERROR("Failed to make \"%s\" MS_SLAVE", target); ERROR("Continuing..."); continue; } TRACE("Remounted \"%s\" as MS_SLAVE", target); } fclose(f); free(line); TRACE("Remounted all mount table entries as MS_SLAVE"); } static int lxc_execute_bind_init(struct lxc_handler *handler) { int ret; char *p; char path[PATH_MAX], destpath[PATH_MAX]; struct lxc_conf *conf = handler->conf; /* If init exists in the container, don't bind mount a static one */ p = choose_init(conf->rootfs.mount); if (p) { char *old = p; p = strdup(old + strlen(conf->rootfs.mount)); free(old); if (!p) return -ENOMEM; INFO("Found existing init at \"%s\"", p); goto out; } ret = snprintf(path, PATH_MAX, SBINDIR "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) return -1; if (!file_exists(path)) { ERROR("The file \"%s\" does not exist on host", path); return -1; } ret = snprintf(destpath, PATH_MAX, "%s" P_tmpdir "%s", conf->rootfs.mount, "/.lxc-init"); if (ret < 0 || ret >= PATH_MAX) return -1; if (!file_exists(destpath)) { ret = mknod(destpath, S_IFREG | 0000, 0); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create dummy \"%s\" file as bind mount target", destpath); return -1; } } ret = safe_mount(path, destpath, "none", MS_BIND, NULL, conf->rootfs.mount); if (ret < 0) { SYSERROR("Failed to bind mount lxc.init.static into container"); return -1; } p = strdup(destpath + strlen(conf->rootfs.mount)); if (!p) return -ENOMEM; INFO("Bind mounted lxc.init.static into container at \"%s\"", path); out: ((struct execute_args *)handler->data)->init_fd = -1; ((struct execute_args *)handler->data)->init_path = p; return 0; } /* This does the work of remounting / if it is shared, calling the container * pre-mount hooks, and mounting the rootfs. */ int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name, const char *lxcpath) { int ret; if (conf->rootfs_setup) { const char *path = conf->rootfs.mount; /* The rootfs was set up in another namespace. bind-mount it to * give us a mount in our own ns so we can pivot_root to it */ ret = mount(path, path, "rootfs", MS_BIND, NULL); if (ret < 0) { ERROR("Failed to bind mount container / onto itself"); return -1; } TRACE("Bind mounted container / onto itself"); return 0; } remount_all_slave(); ret = run_lxc_hooks(name, "pre-mount", conf, NULL); if (ret < 0) { ERROR("Failed to run pre-mount hooks"); return -1; } ret = lxc_mount_rootfs(conf); if (ret < 0) { ERROR("Failed to setup rootfs for"); return -1; } conf->rootfs_setup = true; return 0; } static bool verify_start_hooks(struct lxc_conf *conf) { char path[PATH_MAX]; struct lxc_list *it; lxc_list_for_each (it, &conf->hooks[LXCHOOK_START]) { int ret; char *hookname = it->elem; ret = snprintf(path, PATH_MAX, "%s%s", conf->rootfs.path ? conf->rootfs.mount : "", hookname); if (ret < 0 || ret >= PATH_MAX) return false; ret = access(path, X_OK); if (ret < 0) { SYSERROR("Start hook \"%s\" not found in container", hookname); return false; } return true; } return true; } static bool execveat_supported(void) { lxc_raw_execveat(-1, "", NULL, NULL, AT_EMPTY_PATH); if (errno == ENOSYS) return false; return true; } int lxc_setup(struct lxc_handler *handler) { int ret; const char *lxcpath = handler->lxcpath, *name = handler->name; struct lxc_conf *lxc_conf = handler->conf; ret = lxc_setup_rootfs_prepare_root(lxc_conf, name, lxcpath); if (ret < 0) { ERROR("Failed to setup rootfs"); return -1; } if (handler->nsfd[LXC_NS_UTS] == -1) { ret = setup_utsname(lxc_conf->utsname); if (ret < 0) { ERROR("Failed to setup the utsname %s", name); return -1; } } ret = lxc_setup_keyring(); if (ret < 0) return -1; ret = lxc_setup_network_in_child_namespaces(lxc_conf, &lxc_conf->network); if (ret < 0) { ERROR("Failed to setup network"); return -1; } ret = lxc_network_send_name_and_ifindex_to_parent(handler); if (ret < 0) { ERROR("Failed to send network device names and ifindices to parent"); return -1; } if (lxc_conf->autodev > 0) { ret = mount_autodev(name, &lxc_conf->rootfs, lxcpath); if (ret < 0) { ERROR("Failed to mount \"/dev\""); return -1; } } /* Do automatic mounts (mainly /proc and /sys), but exclude those that * need to wait until other stuff has finished. */ ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler); if (ret < 0) { ERROR("Failed to setup first automatic mounts"); return -1; } ret = setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath); if (ret < 0) { ERROR("Failed to setup mounts"); return -1; } if (lxc_conf->is_execute) { if (execveat_supported()) { int fd; char path[PATH_MAX]; ret = snprintf(path, PATH_MAX, SBINDIR "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) { ERROR("Path to init.lxc.static too long"); return -1; } fd = open(path, O_PATH | O_CLOEXEC); if (fd < 0) { SYSERROR("Unable to open lxc.init.static"); return -1; } ((struct execute_args *)handler->data)->init_fd = fd; ((struct execute_args *)handler->data)->init_path = NULL; } else { ret = lxc_execute_bind_init(handler); if (ret < 0) { ERROR("Failed to bind-mount the lxc init system"); return -1; } } } /* Now mount only cgroups, if wanted. Before, /sys could not have been * mounted. It is guaranteed to be mounted now either through * automatically or via fstab entries. */ ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler); if (ret < 0) { ERROR("Failed to setup remaining automatic mounts"); return -1; } ret = run_lxc_hooks(name, "mount", lxc_conf, NULL); if (ret < 0) { ERROR("Failed to run mount hooks"); return -1; } if (lxc_conf->autodev > 0) { ret = run_lxc_hooks(name, "autodev", lxc_conf, NULL); if (ret < 0) { ERROR("Failed to run autodev hooks"); return -1; } ret = lxc_fill_autodev(&lxc_conf->rootfs); if (ret < 0) { ERROR("Failed to populate \"/dev\""); return -1; } } if (!lxc_list_empty(&lxc_conf->mount_list)) { ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath); if (ret < 0) { ERROR("Failed to setup mount entries"); return -1; } } /* Make sure any start hooks are in the container */ if (!verify_start_hooks(lxc_conf)) { ERROR("Failed to verify start hooks"); return -1; } ret = lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console, lxc_conf->ttys.dir); if (ret < 0) { ERROR("Failed to setup console"); return -1; } ret = lxc_setup_dev_symlinks(&lxc_conf->rootfs); if (ret < 0) { ERROR("Failed to setup \"/dev\" symlinks"); return -1; } ret = lxc_create_tmp_proc_mount(lxc_conf); if (ret < 0) { ERROR("Failed to \"/proc\" LSMs"); return -1; } ret = lxc_setup_rootfs_switch_root(&lxc_conf->rootfs); if (ret < 0) { ERROR("Failed to pivot root into rootfs"); return -1; } ret = lxc_setup_devpts(lxc_conf); if (ret < 0) { ERROR("Failed to setup new devpts instance"); return -1; } ret = lxc_create_ttys(handler); if (ret < 0) return -1; ret = setup_personality(lxc_conf->personality); if (ret < 0) { ERROR("Failed to set personality"); return -1; } /* Set sysctl value to a path under /proc/sys as determined from the * key. For e.g. net.ipv4.ip_forward translated to * /proc/sys/net/ipv4/ip_forward. */ if (!lxc_list_empty(&lxc_conf->sysctls)) { ret = setup_sysctl_parameters(&lxc_conf->sysctls); if (ret < 0) { ERROR("Failed to setup sysctl parameters"); return -1; } } if (!lxc_list_empty(&lxc_conf->keepcaps)) { if (!lxc_list_empty(&lxc_conf->caps)) { ERROR("Container requests lxc.cap.drop and " "lxc.cap.keep: either use lxc.cap.drop or " "lxc.cap.keep, not both"); return -1; } if (dropcaps_except(&lxc_conf->keepcaps)) { ERROR("Failed to keep capabilities"); return -1; } } else if (setup_caps(&lxc_conf->caps)) { ERROR("Failed to drop capabilities"); return -1; } NOTICE("The container \"%s\" is set up", name); return 0; } int run_lxc_hooks(const char *name, char *hookname, struct lxc_conf *conf, char *argv[]) { struct lxc_list *it; int which = -1; if (strcmp(hookname, "pre-start") == 0) which = LXCHOOK_PRESTART; else if (strcmp(hookname, "start-host") == 0) which = LXCHOOK_START_HOST; else if (strcmp(hookname, "pre-mount") == 0) which = LXCHOOK_PREMOUNT; else if (strcmp(hookname, "mount") == 0) which = LXCHOOK_MOUNT; else if (strcmp(hookname, "autodev") == 0) which = LXCHOOK_AUTODEV; else if (strcmp(hookname, "start") == 0) which = LXCHOOK_START; else if (strcmp(hookname, "stop") == 0) which = LXCHOOK_STOP; else if (strcmp(hookname, "post-stop") == 0) which = LXCHOOK_POSTSTOP; else if (strcmp(hookname, "clone") == 0) which = LXCHOOK_CLONE; else if (strcmp(hookname, "destroy") == 0) which = LXCHOOK_DESTROY; else return -1; lxc_list_for_each (it, &conf->hooks[which]) { int ret; char *hook = it->elem; ret = run_script_argv(name, conf->hooks_version, "lxc", hook, hookname, argv); if (ret < 0) return -1; } return 0; } int lxc_clear_config_caps(struct lxc_conf *c) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &c->caps, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } static int lxc_free_idmap(struct lxc_list *id_map) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, id_map, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_idmaps(struct lxc_conf *c) { return lxc_free_idmap(&c->id_map); } int lxc_clear_config_keepcaps(struct lxc_conf *c) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &c->keepcaps, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version) { char *global_token, *namespaced_token; size_t namespaced_token_len; struct lxc_list *it, *next, *list; const char *k = key; bool all = false; if (version == CGROUP2_SUPER_MAGIC) { global_token = "lxc.cgroup2"; namespaced_token = "lxc.cgroup2."; namespaced_token_len = STRLITERALLEN("lxc.cgroup2."); list = &c->cgroup2; } else if (version == CGROUP_SUPER_MAGIC) { global_token = "lxc.cgroup"; namespaced_token = "lxc.cgroup."; namespaced_token_len = STRLITERALLEN("lxc.cgroup."); list = &c->cgroup; } else { return -EINVAL; } if (strcmp(key, global_token) == 0) all = true; else if (strncmp(key, namespaced_token, namespaced_token_len) == 0) k += namespaced_token_len; else return -EINVAL; lxc_list_for_each_safe (it, list, next) { struct lxc_cgroup *cg = it->elem; if (!all && strcmp(cg->subsystem, k) != 0) continue; lxc_list_del(it); free(cg->subsystem); free(cg->value); free(cg); free(it); } return 0; } int lxc_clear_limits(struct lxc_conf *c, const char *key) { struct lxc_list *it, *next; const char *k = NULL; bool all = false; if (strcmp(key, "lxc.limit") == 0 || strcmp(key, "lxc.prlimit") == 0) all = true; else if (strncmp(key, "lxc.limit.", STRLITERALLEN("lxc.limit.")) == 0) k = key + STRLITERALLEN("lxc.limit."); else if (strncmp(key, "lxc.prlimit.", STRLITERALLEN("lxc.prlimit.")) == 0) k = key + STRLITERALLEN("lxc.prlimit."); 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_sysctls(struct lxc_conf *c, const char *key) { struct lxc_list *it, *next; const char *k = NULL; bool all = false; if (strcmp(key, "lxc.sysctl") == 0) all = true; else if (strncmp(key, "lxc.sysctl.", STRLITERALLEN("lxc.sysctl.")) == 0) k = key + STRLITERALLEN("lxc.sysctl."); else return -1; lxc_list_for_each_safe (it, &c->sysctls, next) { struct lxc_sysctl *elem = it->elem; if (!all && strcmp(elem->key, k) != 0) continue; lxc_list_del(it); free(elem->key); free(elem->value); free(elem); free(it); } return 0; } int lxc_clear_procs(struct lxc_conf *c, const char *key) { struct lxc_list *it, *next; const char *k = NULL; bool all = false; if (strcmp(key, "lxc.proc") == 0) all = true; else if (strncmp(key, "lxc.proc.", STRLITERALLEN("lxc.proc.")) == 0) k = key + STRLITERALLEN("lxc.proc."); else return -1; lxc_list_for_each_safe (it, &c->procs, next) { struct lxc_proc *proc = it->elem; if (!all && strcmp(proc->filename, k) != 0) continue; lxc_list_del(it); free(proc->filename); free(proc->value); free(proc); free(it); } return 0; } int lxc_clear_groups(struct lxc_conf *c) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &c->groups, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_environment(struct lxc_conf *c) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &c->environment, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_mount_entries(struct lxc_conf *c) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &c->mount_list, next) { lxc_list_del(it); free(it->elem); free(it); } return 0; } int lxc_clear_automounts(struct lxc_conf *c) { c->auto_mounts = 0; return 0; } int lxc_clear_hooks(struct lxc_conf *c, const char *key) { int i; struct lxc_list *it, *next; const char *k = NULL; bool all = false, done = false; if (strcmp(key, "lxc.hook") == 0) all = true; else if (strncmp(key, "lxc.hook.", STRLITERALLEN("lxc.hook.")) == 0) k = key + STRLITERALLEN("lxc.hook."); else return -1; for (i = 0; i < NUM_LXC_HOOKS; i++) { if (all || strcmp(k, lxchook_names[i]) == 0) { lxc_list_for_each_safe (it, &c->hooks[i], next) { lxc_list_del(it); free(it->elem); free(it); } done = true; } } if (!done) { ERROR("Invalid hook key: %s", key); return -1; } return 0; } static inline void lxc_clear_aliens(struct lxc_conf *conf) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &conf->aliens, next) { lxc_list_del(it); free(it->elem); free(it); } } void lxc_clear_includes(struct lxc_conf *conf) { struct lxc_list *it, *next; lxc_list_for_each_safe (it, &conf->includes, next) { lxc_list_del(it); free(it->elem); free(it); } } void lxc_conf_free(struct lxc_conf *conf) { if (!conf) return; if (current_config == conf) current_config = NULL; lxc_terminal_conf_free(&conf->console); free(conf->rootfs.mount); free(conf->rootfs.bdev_type); free(conf->rootfs.options); free(conf->rootfs.path); free(conf->logfile); if (conf->logfd != -1) close(conf->logfd); free(conf->utsname); free(conf->ttys.dir); free(conf->ttys.tty_names); free(conf->fstab); free(conf->rcfile); free(conf->execute_cmd); free(conf->init_cmd); free(conf->init_cwd); free(conf->unexpanded_config); free(conf->syslog); lxc_free_networks(&conf->network); free(conf->lsm_aa_profile); free(conf->lsm_se_context); lxc_seccomp_free(conf); lxc_clear_config_caps(conf); lxc_clear_config_keepcaps(conf); lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC); lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC); lxc_clear_hooks(conf, "lxc.hook"); lxc_clear_mount_entries(conf); lxc_clear_idmaps(conf); lxc_clear_groups(conf); lxc_clear_includes(conf); lxc_clear_aliens(conf); lxc_clear_environment(conf); lxc_clear_limits(conf, "lxc.prlimit"); lxc_clear_sysctls(conf, "lxc.sysctl"); lxc_clear_procs(conf, "lxc.proc"); 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]; }; static int run_userns_fn(void *data) { int ret; char c; struct userns_fn_data *d = data; /* Close write end of the pipe. */ close(d->p[1]); /* Wait for parent to finish establishing a new mapping in the user * namespace we are executing in. */ ret = lxc_read_nointr(d->p[0], &c, 1); /* Close read end of the pipe. */ close(d->p[0]); if (ret != 1) return -1; if (d->fn_name) TRACE("Calling function \"%s\"", d->fn_name); /* Call function to run. */ return d->fn(d->arg); } static struct id_map *mapped_nsid_add(struct lxc_conf *conf, unsigned id, enum idtype idtype) { const struct id_map *map; struct id_map *retmap; map = find_mapped_nsid_entry(conf, id, idtype); if (!map) return NULL; retmap = malloc(sizeof(*retmap)); if (!retmap) return NULL; memcpy(retmap, map, sizeof(*retmap)); return retmap; } static struct id_map *find_mapped_hostid_entry(struct lxc_conf *conf, unsigned id, enum idtype idtype) { struct id_map *map; struct lxc_list *it; struct id_map *retmap = NULL; lxc_list_for_each (it, &conf->id_map) { map = it->elem; if (map->idtype != idtype) continue; if (id >= map->hostid && id < map->hostid + map->range) { retmap = map; break; } } return retmap; } /* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already * existing one or establish a new one. */ static struct id_map *mapped_hostid_add(struct lxc_conf *conf, uid_t id, enum idtype type) { int hostid_mapped; struct id_map *entry = NULL, *tmp = NULL; entry = malloc(sizeof(*entry)); if (!entry) return NULL; /* Reuse existing mapping. */ tmp = find_mapped_hostid_entry(conf, id, type); if (tmp) return memcpy(entry, tmp, sizeof(*entry)); /* Find new mapping. */ hostid_mapped = find_unmapped_nsid(conf, type); if (hostid_mapped < 0) { DEBUG("Failed to find free mapping for id %d", id); free(entry); return NULL; } entry->idtype = type; entry->nsid = hostid_mapped; entry->hostid = (unsigned long)id; entry->range = 1; return entry; } struct lxc_list *get_minimal_idmap(struct lxc_conf *conf) { uid_t euid, egid; uid_t nsuid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid; gid_t nsgid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid; struct lxc_list *idmap = NULL, *tmplist = NULL; struct id_map *container_root_uid = NULL, *container_root_gid = NULL, *host_uid_map = NULL, *host_gid_map = NULL; /* Find container root mappings. */ container_root_uid = mapped_nsid_add(conf, nsuid, ID_TYPE_UID); if (!container_root_uid) { DEBUG("Failed to find mapping for namespace uid %d", 0); goto on_error; } euid = geteuid(); if (euid >= container_root_uid->hostid && euid < (container_root_uid->hostid + container_root_uid->range)) host_uid_map = container_root_uid; container_root_gid = mapped_nsid_add(conf, nsgid, ID_TYPE_GID); if (!container_root_gid) { DEBUG("Failed to find mapping for namespace gid %d", 0); goto on_error; } egid = getegid(); if (egid >= container_root_gid->hostid && egid < (container_root_gid->hostid + container_root_gid->range)) host_gid_map = container_root_gid; /* Check whether the {g,u}id of the user has a mapping. */ if (!host_uid_map) host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID); if (!host_uid_map) { DEBUG("Failed to find mapping for uid %d", euid); goto on_error; } if (!host_gid_map) host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID); if (!host_gid_map) { DEBUG("Failed to find mapping for gid %d", egid); goto on_error; } /* Allocate new {g,u}id map list. */ idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; lxc_list_init(idmap); /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, container_root_uid); lxc_list_add_tail(idmap, tmplist); if (host_uid_map && (host_uid_map != container_root_uid)) { /* idmap will now keep track of that memory. */ container_root_uid = NULL; /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_uid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ container_root_uid = NULL; /* idmap will now keep track of that memory. */ host_uid_map = NULL; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, container_root_gid); lxc_list_add_tail(idmap, tmplist); if (host_gid_map && (host_gid_map != container_root_gid)) { /* idmap will now keep track of that memory. */ container_root_gid = NULL; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_gid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ container_root_gid = NULL; /* idmap will now keep track of that memory. */ host_gid_map = NULL; TRACE("Allocated minimal idmapping"); return idmap; on_error: if (idmap) { lxc_free_idmap(idmap); free(idmap); } if (container_root_uid) free(container_root_uid); if (container_root_gid) free(container_root_gid); if (host_uid_map && (host_uid_map != container_root_uid)) free(host_uid_map); if (host_gid_map && (host_gid_map != container_root_gid)) free(host_gid_map); return NULL; } /* Run a function in a new user namespace. * The caller's euid/egid will be mapped if it is not already. * Afaict, userns_exec_1() is only used to operate based on privileges for the * user's own {g,u}id on the host and for the container root's unmapped {g,u}id. * This means we require only to establish a mapping from: * - the container root {g,u}id as seen from the host > user's host {g,u}id * - the container root -> some sub{g,u}id * The former we add, if the user did not specify a mapping. The latter we * retrieve from the container's configured {g,u}id mappings as it must have been * there to start the container in the first place. */ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name) { pid_t pid; int p[2]; struct userns_fn_data d; struct lxc_list *idmap; int ret = -1, status = -1; char c = '1'; if (!conf) return -EINVAL; idmap = get_minimal_idmap(conf); if (!idmap) return -1; ret = pipe2(p, O_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create pipe"); return -1; } d.fn = fn; d.fn_name = fn_name; d.arg = data; d.p[0] = p[0]; d.p[1] = p[1]; /* Clone child in new user namespace. */ pid = lxc_raw_clone_cb(run_userns_fn, &d, CLONE_NEWUSER); if (pid < 0) { ERROR("Failed to clone process in new user namespace"); goto on_error; } close(p[0]); p[0] = -1; if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE || conf->loglevel == LXC_LOG_LEVEL_TRACE) { struct id_map *map; struct lxc_list *it; lxc_list_for_each (it, idmap) { map = it->elem; TRACE("Establishing %cid mapping for \"%d\" in new " "user namespace: nsuid %lu - hostid %lu - range " "%lu", (map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range); } } /* Set up {g,u}id mapping for user namespace of child process. */ ret = lxc_map_ids(idmap, pid); if (ret < 0) { ERROR("Error setting up {g,u}id mappings for child process \"%d\"", pid); goto on_error; } /* Tell child to proceed. */ if (lxc_write_nointr(p[1], &c, 1) != 1) { SYSERROR("Failed telling child process \"%d\" to proceed", pid); goto on_error; } on_error: if (p[0] != -1) close(p[0]); close(p[1]); /* Wait for child to finish. */ if (pid > 0) status = wait_for_pid(pid); if (status < 0) ret = -1; return ret; } int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name) { pid_t pid; uid_t euid, egid; int p[2]; struct id_map *map; struct lxc_list *cur; struct userns_fn_data d; int ret = -1; char c = '1'; struct lxc_list *idmap = NULL, *tmplist = NULL; struct id_map *container_root_uid = NULL, *container_root_gid = NULL, *host_uid_map = NULL, *host_gid_map = NULL; if (!conf) return -EINVAL; ret = pipe2(p, O_CLOEXEC); if (ret < 0) { SYSERROR("opening pipe"); return -1; } d.fn = fn; d.fn_name = fn_name; d.arg = data; d.p[0] = p[0]; d.p[1] = p[1]; /* Clone child in new user namespace. */ pid = lxc_clone(run_userns_fn, &d, CLONE_NEWUSER); if (pid < 0) { ERROR("Failed to clone process in new user namespace"); goto on_error; } close(p[0]); p[0] = -1; euid = geteuid(); egid = getegid(); /* Allocate new {g,u}id map list. */ idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; lxc_list_init(idmap); /* Find container root. */ lxc_list_for_each (cur, &conf->id_map) { struct id_map *tmpmap; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; tmpmap = malloc(sizeof(*tmpmap)); if (!tmpmap) { free(tmplist); goto on_error; } memset(tmpmap, 0, sizeof(*tmpmap)); memcpy(tmpmap, cur->elem, sizeof(*tmpmap)); tmplist->elem = tmpmap; lxc_list_add_tail(idmap, tmplist); map = cur->elem; if (map->idtype == ID_TYPE_UID) if (euid >= map->hostid && euid < map->hostid + map->range) host_uid_map = map; if (map->idtype == ID_TYPE_GID) if (egid >= map->hostid && egid < map->hostid + map->range) host_gid_map = map; if (map->nsid != 0) continue; if (map->idtype == ID_TYPE_UID) if (container_root_uid == NULL) container_root_uid = map; if (map->idtype == ID_TYPE_GID) if (container_root_gid == NULL) container_root_gid = map; } if (!container_root_uid || !container_root_gid) { ERROR("No mapping for container root found"); goto on_error; } /* Check whether the {g,u}id of the user has a mapping. */ if (!host_uid_map) host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID); else host_uid_map = container_root_uid; if (!host_gid_map) host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID); else host_gid_map = container_root_gid; if (!host_uid_map) { DEBUG("Failed to find mapping for uid %d", euid); goto on_error; } if (!host_gid_map) { DEBUG("Failed to find mapping for gid %d", egid); goto on_error; } if (host_uid_map && (host_uid_map != container_root_uid)) { /* Add container root to the map. */ tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_uid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ host_uid_map = NULL; if (host_gid_map && (host_gid_map != container_root_gid)) { tmplist = malloc(sizeof(*tmplist)); if (!tmplist) goto on_error; lxc_list_add_elem(tmplist, host_gid_map); lxc_list_add_tail(idmap, tmplist); } /* idmap will now keep track of that memory. */ host_gid_map = NULL; if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE || conf->loglevel == LXC_LOG_LEVEL_TRACE) { lxc_list_for_each (cur, idmap) { map = cur->elem; TRACE("establishing %cid mapping for \"%d\" in new " "user namespace: nsuid %lu - hostid %lu - range " "%lu", (map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range); } } /* Set up {g,u}id mapping for user namespace of child process. */ ret = lxc_map_ids(idmap, pid); if (ret < 0) { ERROR("error setting up {g,u}id mappings for child process \"%d\"", pid); goto on_error; } /* Tell child to proceed. */ if (lxc_write_nointr(p[1], &c, 1) != 1) { SYSERROR("Failed telling child process \"%d\" to proceed", pid); goto on_error; } on_error: if (p[0] != -1) close(p[0]); close(p[1]); /* Wait for child to finish. */ if (pid > 0) ret = wait_for_pid(pid); if (idmap) { lxc_free_idmap(idmap); free(idmap); } if (host_uid_map && (host_uid_map != container_root_uid)) free(host_uid_map); if (host_gid_map && (host_gid_map != container_root_gid)) free(host_gid_map); return ret; } /* not thread-safe, do not use from api without first forking */ static char *getuname(void) { struct passwd pwent; struct passwd *pwentp = NULL; char *buf; char *username; size_t bufsize; int ret; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getpwuid_r(geteuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) WARN("Could not find matched password record."); ERROR("Failed to get password record - %u", geteuid()); free(buf); return NULL; } username = strdup(pwent.pw_name); free(buf); return username; } /* not thread-safe, do not use from api without first forking */ static char *getgname(void) { struct group grent; struct group *grentp = NULL; char *buf; char *grname; size_t bufsize; int ret; bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getgrgid_r(getegid(), &grent, buf, bufsize, &grentp); if (!grentp) { if (ret == 0) WARN("Could not find matched group record"); ERROR("Failed to get group record - %u", getegid()); free(buf); return NULL; } grname = strdup(grent.gr_name); free(buf); return grname; } /* not thread-safe, do not use from api without first forking */ void suggest_default_idmap(void) { char *uname, *gname; FILE *f; unsigned int uid = 0, urange = 0, gid = 0, grange = 0; size_t len = 0; char *line = NULL; uname = getuname(); if (!uname) return; gname = getgname(); if (!gname) { free(uname); return; } f = fopen(subuidfile, "r"); if (!f) { ERROR("Your system is not configured with subuids"); free(gname); free(uname); return; } while (getline(&line, &len, f) != -1) { char *p, *p2; size_t no_newline = 0; p = strchr(line, ':'); if (*line == '#') continue; if (!p) continue; *p = '\0'; p++; if (strcmp(line, uname)) continue; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; p2++; if (!*p2) continue; no_newline = strcspn(p2, "\n"); p2[no_newline] = '\0'; if (lxc_safe_uint(p, &uid) < 0) WARN("Could not parse UID"); if (lxc_safe_uint(p2, &urange) < 0) WARN("Could not parse UID range"); } fclose(f); f = fopen(subgidfile, "r"); if (!f) { ERROR("Your system is not configured with subgids"); free(gname); free(uname); return; } while (getline(&line, &len, f) != -1) { char *p, *p2; size_t no_newline = 0; p = strchr(line, ':'); if (*line == '#') continue; if (!p) continue; *p = '\0'; p++; if (strcmp(line, uname)) continue; p2 = strchr(p, ':'); if (!p2) continue; *p2 = '\0'; p2++; if (!*p2) continue; no_newline = strcspn(p2, "\n"); p2[no_newline] = '\0'; if (lxc_safe_uint(p, &gid) < 0) WARN("Could not parse GID"); if (lxc_safe_uint(p2, &grange) < 0) WARN("Could not parse GID range"); } fclose(f); free(line); if (!urange || !grange) { ERROR("You do not have subuids or subgids allocated"); ERROR("Unprivileged containers require subuids and subgids"); free(uname); free(gname); return; } ERROR("You must either run as root, or define uid mappings"); ERROR("To pass uid mappings to lxc-create, you could create"); ERROR("~/.config/lxc/default.conf:"); ERROR("lxc.include = %s", LXC_DEFAULT_CONFIG); ERROR("lxc.idmap = u 0 %u %u", uid, urange); ERROR("lxc.idmap = g 0 %u %u", gid, grange); free(gname); free(uname); } static void free_cgroup_settings(struct lxc_list *result) { struct lxc_list *iterator, *next; lxc_list_for_each_safe (iterator, result, next) { lxc_list_del(iterator); free(iterator); } free(result); } /* Return the list of cgroup_settings sorted according to the following rules * 1. Put memory.limit_in_bytes before memory.memsw.limit_in_bytes */ struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings) { struct lxc_list *result; struct lxc_cgroup *cg = NULL; struct lxc_list *it = NULL, *item = NULL, *memsw_limit = NULL; result = malloc(sizeof(*result)); if (!result) return NULL; lxc_list_init(result); /* Iterate over the cgroup settings and copy them to the output list. */ lxc_list_for_each (it, cgroup_settings) { item = malloc(sizeof(*item)); if (!item) { free_cgroup_settings(result); return NULL; } item->elem = it->elem; cg = it->elem; if (strcmp(cg->subsystem, "memory.memsw.limit_in_bytes") == 0) { /* Store the memsw_limit location */ memsw_limit = item; } else if (strcmp(cg->subsystem, "memory.limit_in_bytes") == 0 && memsw_limit != NULL) { /* lxc.cgroup.memory.memsw.limit_in_bytes is found * before lxc.cgroup.memory.limit_in_bytes, swap these * two items */ item->elem = memsw_limit->elem; memsw_limit->elem = it->elem; } lxc_list_add_tail(result, item); } return result; } lxc-3.0.3/src/lxc/error.c0000644061062106075000000000323413375633353012123 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "error.h" #include "log.h" lxc_log_define(error, lxc); /*---------------------------------------------------------------------------*/ /* lxc_error_set_and_log * function is here to convert * the reported status to an exit code as detailed here: * * 0-126 exit code of the application * 128+n signal n received by the application * 255 lxc error */ extern int lxc_error_set_and_log(int pid, int status) { int ret = 0; if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret) INFO("Child <%d> ended on error (%d)", pid, ret); } if (WIFSIGNALED(status)) { int signal = WTERMSIG(status); INFO("Child <%d> ended on signal (%d)", pid, signal); ret = 128 + signal; } return ret; } lxc-3.0.3/src/lxc/attach_options.h0000644061062106075000000001414613375633353014022 00000000000000/*! \file * * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ATTACH_OPTIONS_H #define __LXC_ATTACH_OPTIONS_H #include #ifdef __cplusplus extern "C" { #endif /*! * LXC environment policy. */ typedef enum lxc_attach_env_policy_t { LXC_ATTACH_KEEP_ENV, /*!< Retain the environment */ LXC_ATTACH_CLEAR_ENV /*!< Clear the environment */ } lxc_attach_env_policy_t; enum { /* The following are on by default: */ LXC_ATTACH_MOVE_TO_CGROUP = 0x00000001, /*!< Move to cgroup */ LXC_ATTACH_DROP_CAPABILITIES = 0x00000002, /*!< Drop capabilities */ LXC_ATTACH_SET_PERSONALITY = 0x00000004, /*!< Set personality */ LXC_ATTACH_LSM_EXEC = 0x00000008, /*!< Execute under a Linux Security Module */ /* The following are off by default: */ LXC_ATTACH_REMOUNT_PROC_SYS = 0x00010000, /*!< Remount /proc filesystem */ LXC_ATTACH_LSM_NOW = 0x00020000, /*!< FIXME: unknown */ /* Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges. */ LXC_ATTACH_NO_NEW_PRIVS = 0x00040000, /*!< PR_SET_NO_NEW_PRIVS */ LXC_ATTACH_TERMINAL = 0x00080000, /*!< Allocate new terminal for attached process. */ /* We have 16 bits for things that are on by default and 16 bits that * are off by default, that should be sufficient to keep binary * compatibility for a while */ LXC_ATTACH_DEFAULT = 0x0000FFFF /*!< Mask of flags to apply by default */ }; /*! All Linux Security Module flags */ #define LXC_ATTACH_LSM (LXC_ATTACH_LSM_EXEC | LXC_ATTACH_LSM_NOW) /*! LXC attach function type. * * Function to run in container. * * \param payload \ref lxc_attach_command_t to run. * * \return Function should return \c 0 on success, and any other value to denote failure. */ typedef int (*lxc_attach_exec_t)(void* payload); /*! * LXC attach options for \ref lxc_container \c attach(). */ typedef struct lxc_attach_options_t { /*! Any combination of LXC_ATTACH_* flags */ int attach_flags; /*! The namespaces to attach to (CLONE_NEW... flags) */ int namespaces; /*! Initial personality (\c -1 to autodetect). * \warning This may be ignored if lxc is compiled without personality * support) */ long personality; /*! Initial current directory, use \c NULL to use cwd. * If the current directory does not exist in the container, the root * directory will be used instead because of kernel defaults. */ char* initial_cwd; /*! The user-id to run as. * * \note Set to \c -1 for default behaviour (init uid for userns * containers or \c 0 (super-user) if detection fails). */ uid_t uid; /*! The group-id to run as. * * \note Set to \c -1 for default behaviour (init gid for userns * containers or \c 0 (super-user) if detection fails). */ gid_t gid; /*! Environment policy */ lxc_attach_env_policy_t env_policy; /*! Extra environment variables to set in the container environment */ char** extra_env_vars; /*! Names of environment variables in existing environment to retain * in container environment. */ char** extra_keep_env; /**@{*/ /*! File descriptors for stdin, stdout and stderr, * \c dup2() will be used before calling exec_function, * (assuming not \c 0, \c 1 and \c 2 are specified) and the * original fds are closed before passing control * over. Any \c O_CLOEXEC flag will be removed after * that. */ int stdin_fd; /*!< stdin file descriptor */ int stdout_fd; /*!< stdout file descriptor */ int stderr_fd; /*!< stderr file descriptor */ /**@}*/ /*! File descriptor to log output. */ int log_fd; } lxc_attach_options_t; /*! Default attach options to use */ #define LXC_ATTACH_OPTIONS_DEFAULT \ { \ /* .attach_flags = */ LXC_ATTACH_DEFAULT, \ /* .namespaces = */ -1, \ /* .personality = */ -1, \ /* .initial_cwd = */ NULL, \ /* .uid = */ (uid_t)-1, \ /* .gid = */ (gid_t)-1, \ /* .env_policy = */ LXC_ATTACH_KEEP_ENV, \ /* .extra_env_vars = */ NULL, \ /* .extra_keep_env = */ NULL, \ /* .stdin_fd = */ 0, \ /* .stdout_fd = */ 1, \ /* .stderr_fd = */ 2, \ /* .log_fd = */ -EBADF, \ } /*! * Representation of a command to run in a container. */ typedef struct lxc_attach_command_t { char* program; /*!< The program to run (passed to execvp) */ char** argv; /*!< The argv pointer of that program, including the program itself in argv[0] */ } lxc_attach_command_t; /*! * \brief Run a command in the container. * * \param payload \ref lxc_attach_command_t to run. * * \return \c -1 on error, exit code of lxc_attach_command_t program on success. */ extern int lxc_attach_run_command(void* payload); /*! * \brief Run a shell command in the container. * * \param payload Not used. * * \return Exit code of shell. */ extern int lxc_attach_run_shell(void* payload); #ifdef __cplusplus } #endif #endif lxc-3.0.3/src/lxc/parse.c0000644061062106075000000001101513375633353012100 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include "config.h" #include "file_utils.h" #include "log.h" #include "macro.h" #include "parse.h" #include "syscall_wrappers.h" #include "utils.h" lxc_log_define(parse, lxc); void *lxc_strmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { void *tmp = NULL, *overlap = NULL; /* We establish an anonymous mapping that is one byte larger than the * underlying file. The pages handed to us are zero filled. */ tmp = mmap(addr, length + 1, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (tmp == MAP_FAILED) return tmp; /* Now we establish a fixed-address mapping starting at the address we * received from our anonymous mapping and replace all bytes excluding * the additional \0-byte with the file. This allows us to use normal * string-handling functions. */ overlap = mmap(tmp, length, prot, MAP_FIXED | flags, fd, offset); if (overlap == MAP_FAILED) munmap(tmp, length + 1); return overlap; } int lxc_strmunmap(void *addr, size_t length) { return munmap(addr, length + 1); } int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data) { int saved_errno; ssize_t ret = -1, bytes_sent; char *line; int fd = -1, memfd = -1; char *buf = NULL; memfd = memfd_create(".lxc_config_file", MFD_CLOEXEC); if (memfd < 0) { char template[] = P_tmpdir "/.lxc_config_file_XXXXXX"; if (errno != ENOSYS) { SYSERROR("Failed to create memory file"); goto on_error; } TRACE("Failed to create in-memory file. Falling back to " "temporary file"); memfd = lxc_make_tmpfile(template, true); if (memfd < 0) { SYSERROR("Failed to create temporary file \"%s\"", template); goto on_error; } } fd = open(file, O_RDONLY | O_CLOEXEC); if (fd < 0) { SYSERROR("Failed to open file \"%s\"", file); return -1; } /* sendfile() handles up to 2GB. No config file should be that big. */ bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX); if (bytes_sent < 0) { SYSERROR("Failed to sendfile \"%s\"", file); goto on_error; } ret = lxc_write_nointr(memfd, "\0", 1); if (ret < 0) { SYSERROR("Failed to append zero byte"); goto on_error; } bytes_sent++; ret = lseek(memfd, 0, SEEK_SET); if (ret < 0) { SYSERROR("Failed to lseek"); goto on_error; } ret = -1; buf = mmap(NULL, bytes_sent, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, memfd, 0); if (buf == MAP_FAILED) { buf = NULL; SYSERROR("Failed to mmap"); goto on_error; } ret = 0; lxc_iterate_parts(line, buf, "\n\0") { ret = callback(line, data); if (ret) { /* Callback rv > 0 means stop here callback rv < 0 means * error. */ if (ret < 0) ERROR("Failed to parse config file \"%s\" at " "line \"%s\"", file, line); break; } } on_error: saved_errno = errno; if (fd >= 0) close(fd); if (memfd >= 0) close(memfd); if (buf && munmap(buf, bytes_sent)) { SYSERROR("Failed to unmap"); if (ret == 0) ret = -1; } errno = saved_errno; return ret; } int lxc_file_for_each_line(const char *file, lxc_file_cb callback, void *data) { FILE *f; int err = 0; char *line = NULL; size_t len = 0; f = fopen(file, "r"); if (!f) { SYSERROR("Failed to open \"%s\"", file); return -1; } while (getline(&line, &len, f) != -1) { err = callback(line, data); if (err) { /* Callback rv > 0 means stop here callback rv < 0 means * error. */ if (err < 0) ERROR("Failed to parse config: \"%s\"", line); break; } } free(line); fclose(f); return err; } lxc-3.0.3/src/lxc/syscall_wrappers.h0000644061062106075000000001331213375633353014372 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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_SYSCALL_WRAPPER_H #define __LXC_SYSCALL_WRAPPER_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "config.h" #ifdef HAVE_LINUX_MEMFD_H #include #endif #ifdef HAVE_SYS_SIGNALFD_H #include #endif typedef int32_t key_serial_t; #if !HAVE_KEYCTL static inline long __keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { #ifdef __NR_keyctl return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); #else errno = ENOSYS; return -1; #endif } #define keyctl __keyctl #endif #ifndef HAVE_MEMFD_CREATE static inline int memfd_create(const char *name, unsigned int flags) { #ifndef __NR_memfd_create #if defined __i386__ #define __NR_memfd_create 356 #elif defined __x86_64__ #define __NR_memfd_create 319 #elif defined __arm__ #define __NR_memfd_create 385 #elif defined __aarch64__ #define __NR_memfd_create 279 #elif defined __s390__ #define __NR_memfd_create 350 #elif defined __powerpc__ #define __NR_memfd_create 360 #elif defined __sparc__ #define __NR_memfd_create 348 #elif defined __blackfin__ #define __NR_memfd_create 390 #elif defined __ia64__ #define __NR_memfd_create 1340 #elif defined _MIPS_SIM #if _MIPS_SIM == _MIPS_SIM_ABI32 #define __NR_memfd_create 4354 #endif #if _MIPS_SIM == _MIPS_SIM_NABI32 #define __NR_memfd_create 6318 #endif #if _MIPS_SIM == _MIPS_SIM_ABI64 #define __NR_memfd_create 5314 #endif #endif #endif #ifdef __NR_memfd_create return syscall(__NR_memfd_create, name, flags); #else errno = ENOSYS; return -1; #endif } #else extern int memfd_create(const char *name, unsigned int flags); #endif #if !HAVE_PIVOT_ROOT static int pivot_root(const char *new_root, const char *put_old) { #ifdef __NR_pivot_root return syscall(__NR_pivot_root, new_root, put_old); #else errno = ENOSYS; return -1; #endif } #else extern int pivot_root(const char *new_root, const char *put_old); #endif #if !defined(__NR_setns) && !defined(__NR_set_ns) #if defined(__x86_64__) #define __NR_setns 308 #elif defined(__i386__) #define __NR_setns 346 #elif defined(__arm__) #define __NR_setns 375 #elif defined(__aarch64__) #define __NR_setns 375 #elif defined(__powerpc__) #define __NR_setns 350 #elif defined(__s390__) #define __NR_setns 339 #endif #endif /* Define sethostname() if missing from the C library */ #ifndef HAVE_SETHOSTNAME static inline int sethostname(const char *name, size_t len) { #ifdef __NR_sethostname return syscall(__NR_sethostname, name, len); #else errno = ENOSYS; return -1; #endif } #endif /* Define setns() if missing from the C library */ #ifndef HAVE_SETNS static inline int setns(int fd, int nstype) { #ifdef __NR_setns return syscall(__NR_setns, fd, nstype); #elif defined(__NR_set_ns) return syscall(__NR_set_ns, fd, nstype); #else errno = ENOSYS; return -1; #endif } #endif #ifndef HAVE_SYS_SIGNALFD_H struct signalfd_siginfo { uint32_t ssi_signo; int32_t ssi_errno; int32_t ssi_code; uint32_t ssi_pid; uint32_t ssi_uid; int32_t ssi_fd; uint32_t ssi_tid; uint32_t ssi_band; uint32_t ssi_overrun; uint32_t ssi_trapno; int32_t ssi_status; int32_t ssi_int; uint64_t ssi_ptr; uint64_t ssi_utime; uint64_t ssi_stime; uint64_t ssi_addr; uint8_t __pad[48]; }; #ifndef __NR_signalfd4 /* assume kernel headers are too old */ #if __i386__ #define __NR_signalfd4 327 #elif __x86_64__ #define __NR_signalfd4 289 #elif __powerpc__ #define __NR_signalfd4 313 #elif __s390x__ #define __NR_signalfd4 322 #elif __arm__ #define __NR_signalfd4 355 #elif __mips__ && _MIPS_SIM == _ABIO32 #define __NR_signalfd4 4324 #elif __mips__ && _MIPS_SIM == _ABI64 #define __NR_signalfd4 5283 #elif __mips__ && _MIPS_SIM == _ABIN32 #define __NR_signalfd4 6287 #endif #endif #ifndef __NR_signalfd /* assume kernel headers are too old */ #if __i386__ #define __NR_signalfd 321 #elif __x86_64__ #define __NR_signalfd 282 #elif __powerpc__ #define __NR_signalfd 305 #elif __s390x__ #define __NR_signalfd 316 #elif __arm__ #define __NR_signalfd 349 #elif __mips__ && _MIPS_SIM == _ABIO32 #define __NR_signalfd 4317 #elif __mips__ && _MIPS_SIM == _ABI64 #define __NR_signalfd 5276 #elif __mips__ && _MIPS_SIM == _ABIN32 #define __NR_signalfd 6280 #endif #endif static inline int signalfd(int fd, const sigset_t *mask, int flags) { int retval; retval = syscall(__NR_signalfd4, fd, mask, _NSIG / 8, flags); if (errno == ENOSYS && flags == 0) retval = syscall(__NR_signalfd, fd, mask, _NSIG / 8); return retval; } #endif /* Define unshare() if missing from the C library */ #ifndef HAVE_UNSHARE static inline int unshare(int flags) { #ifdef __NR_unshare return syscall(__NR_unshare, flags); #else errno = ENOSYS; return -1; #endif } #else extern int unshare(int); #endif #endif /* __LXC_SYSCALL_WRAPPER_H */ lxc-3.0.3/src/lxc/Makefile.in0000644061062106075000000060604613375633363012706 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @IS_BIONIC_TRUE@am__append_1 = ../include/lxcmntent.h \ @IS_BIONIC_TRUE@ ../include/openpty.h @HAVE_PRLIMIT64_TRUE@@HAVE_PRLIMIT_FALSE@am__append_2 = ../include/prlimit.h @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_3 = ../include/getline.h @HAVE_GETSUBOPT_FALSE@am__append_4 = tools/include/getsubopt.h @HAVE_GETGRGID_R_FALSE@am__append_5 = ../include/getgrgid_r.h @ENABLE_APPARMOR_TRUE@am__append_6 = lsm/apparmor.c @ENABLE_SELINUX_TRUE@am__append_7 = lsm/selinux.c @IS_BIONIC_TRUE@am__append_8 = ../include/lxcmntent.c ../include/lxcmntent.h \ @IS_BIONIC_TRUE@ ../include/openpty.c ../include/openpty.h @HAVE_GETGRGID_R_FALSE@am__append_9 = ../include/getgrgid_r.c ../include/getgrgid_r.h @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__append_10 = ../include/getline.c ../include/getline.h @HAVE_PRLIMIT64_TRUE@@HAVE_PRLIMIT_FALSE@am__append_11 = ../include/prlimit.c ../include/prlimit.h @ENABLE_SECCOMP_TRUE@am__append_12 = seccomp.c @HAVE_STRLCPY_FALSE@am__append_13 = ../include/strlcpy.c ../include/strlcpy.h @HAVE_STRLCAT_FALSE@am__append_14 = ../include/strlcat.c ../include/strlcat.h @ENABLE_APPARMOR_TRUE@am__append_15 = -DHAVE_APPARMOR @ENABLE_GNUTLS_TRUE@am__append_16 = -DHAVE_LIBGNUTLS @ENABLE_SECCOMP_TRUE@am__append_17 = -DHAVE_SECCOMP \ @ENABLE_SECCOMP_TRUE@ $(SECCOMP_CFLAGS) @ENABLE_SELINUX_TRUE@am__append_18 = -DHAVE_SELINUX @USE_CONFIGPATH_LOGS_TRUE@am__append_19 = -DUSE_CONFIGPATH_LOGS @ENABLE_COMMANDS_TRUE@am__append_20 = cmd/lxc-checkconfig \ @ENABLE_COMMANDS_TRUE@ cmd/lxc-update-config @ENABLE_COMMANDS_TRUE@@ENABLE_TOOLS_FALSE@bin_PROGRAMS = lxc-usernsexec$(EXEEXT) @ENABLE_TOOLS_TRUE@bin_PROGRAMS = lxc-attach$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-autostart$(EXEEXT) lxc-cgroup$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-checkpoint$(EXEEXT) lxc-copy$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-config$(EXEEXT) lxc-console$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-create$(EXEEXT) lxc-destroy$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-device$(EXEEXT) lxc-execute$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-freeze$(EXEEXT) lxc-info$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-ls$(EXEEXT) lxc-monitor$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-snapshot$(EXEEXT) lxc-start$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-stop$(EXEEXT) lxc-top$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-unfreeze$(EXEEXT) lxc-unshare$(EXEEXT) \ @ENABLE_TOOLS_TRUE@ lxc-wait$(EXEEXT) $(am__EXEEXT_1) @ENABLE_COMMANDS_TRUE@@ENABLE_TOOLS_TRUE@am__append_21 = lxc-usernsexec @ENABLE_COMMANDS_TRUE@sbin_PROGRAMS = init.lxc$(EXEEXT) \ @ENABLE_COMMANDS_TRUE@ $(am__EXEEXT_2) @ENABLE_COMMANDS_TRUE@pkglibexec_PROGRAMS = lxc-monitord$(EXEEXT) \ @ENABLE_COMMANDS_TRUE@ lxc-user-nic$(EXEEXT) @ENABLE_RPATH_TRUE@am__append_22 = -Wl,-rpath -Wl,$(libdir) @ENABLE_TOOLS_TRUE@@HAVE_GETSUBOPT_FALSE@am__append_23 = tools/include/getsubopt.c tools/include/getsubopt.h @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@am__append_24 = init.lxc.static @ENABLE_COMMANDS_TRUE@@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__append_25 = ../include/getline.c ../include/getline.h @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCPY_FALSE@am__append_26 = ../include/strlcpy.c ../include/strlcpy.h @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCAT_FALSE@am__append_27 = ../include/strlcat.c ../include/strlcat.h @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@@HAVE_STRLCAT_FALSE@am__append_28 = ../include/strlcat.c ../include/strlcat.h @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@@HAVE_STRLCPY_FALSE@am__append_29 = ../include/strlcpy.c ../include/strlcpy.h subdir = src/lxc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ $(top_srcdir)/config/ax_check_compile_flag.m4 \ $(top_srcdir)/config/ax_check_link_flag.m4 \ $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/libtool.m4 \ $(top_srcdir)/config/ltoptions.m4 \ $(top_srcdir)/config/ltsugar.m4 \ $(top_srcdir)/config/ltversion.m4 \ $(top_srcdir)/config/lt~obsolete.m4 \ $(top_srcdir)/config/tls.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__noinst_HEADERS_DIST) \ $(pkginclude_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = lxc.functions version.h CONFIG_CLEAN_VPATH_FILES = @ENABLE_COMMANDS_TRUE@@ENABLE_TOOLS_TRUE@am__EXEEXT_1 = lxc-usernsexec$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(pamdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(pkgincludedir)" @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@am__EXEEXT_2 = init.lxc.static$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(pkglibexec_PROGRAMS) $(sbin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) $(pam_LTLIBRARIES) am__DEPENDENCIES_1 = liblxc_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__liblxc_la_SOURCES_DIST = af_unix.c af_unix.h attach.c attach.h \ caps.c caps.h cgroups/cgfsng.c cgroups/cgroup.c \ cgroups/cgroup.h cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ compiler.h commands.c commands.h commands_utils.c \ commands_utils.h conf.c conf.h confile.c confile.h \ confile_utils.c confile_utils.h criu.c criu.h error.c error.h \ execute.c freezer.c file_utils.c file_utils.h \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ initutils.c initutils.h list.h log.c log.h lxc.h \ lxccontainer.c lxccontainer.h lxclock.c lxclock.h lxcseccomp.h \ macro.h mainloop.c mainloop.h namespace.c namespace.h nl.c \ nl.h network.c network.h monitor.c monitor.h parse.c parse.h \ raw_syscalls.c raw_syscalls.h ringbuf.c ringbuf.h rtnl.c \ rtnl.h state.c state.h start.c start.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/storage.c storage/storage.h storage/storage_utils.c \ storage/storage_utils.h storage/zfs.c storage/zfs.h \ string_utils.c string_utils.h sync.c sync.h syscall_wrappers.h \ terminal.c utils.c utils.h version.h lsm/lsm.c lsm/lsm.h \ lsm/nop.c lsm/apparmor.c lsm/selinux.c ../include/lxcmntent.c \ ../include/lxcmntent.h ../include/openpty.c \ ../include/openpty.h ../include/getgrgid_r.c \ ../include/getgrgid_r.h ../include/getline.c \ ../include/getline.h ../include/prlimit.c ../include/prlimit.h \ seccomp.c ../include/strlcpy.c ../include/strlcpy.h \ ../include/strlcat.c ../include/strlcat.h am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_APPARMOR_TRUE@am__objects_1 = lsm/liblxc_la-apparmor.lo @ENABLE_SELINUX_TRUE@am__objects_2 = lsm/liblxc_la-selinux.lo am__objects_3 = lsm/liblxc_la-lsm.lo lsm/liblxc_la-nop.lo \ $(am__objects_1) $(am__objects_2) @IS_BIONIC_TRUE@am__objects_4 = ../include/liblxc_la-lxcmntent.lo \ @IS_BIONIC_TRUE@ ../include/liblxc_la-openpty.lo @HAVE_GETGRGID_R_FALSE@am__objects_5 = \ @HAVE_GETGRGID_R_FALSE@ ../include/liblxc_la-getgrgid_r.lo @HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@am__objects_6 = ../include/liblxc_la-getline.lo @HAVE_PRLIMIT64_TRUE@@HAVE_PRLIMIT_FALSE@am__objects_7 = ../include/liblxc_la-prlimit.lo @ENABLE_SECCOMP_TRUE@am__objects_8 = liblxc_la-seccomp.lo @HAVE_STRLCPY_FALSE@am__objects_9 = ../include/liblxc_la-strlcpy.lo @HAVE_STRLCAT_FALSE@am__objects_10 = ../include/liblxc_la-strlcat.lo am_liblxc_la_OBJECTS = liblxc_la-af_unix.lo liblxc_la-attach.lo \ liblxc_la-caps.lo cgroups/liblxc_la-cgfsng.lo \ cgroups/liblxc_la-cgroup.lo cgroups/liblxc_la-cgroup_utils.lo \ liblxc_la-commands.lo liblxc_la-commands_utils.lo \ liblxc_la-conf.lo liblxc_la-confile.lo \ liblxc_la-confile_utils.lo liblxc_la-criu.lo \ liblxc_la-error.lo liblxc_la-execute.lo liblxc_la-freezer.lo \ liblxc_la-file_utils.lo ../include/liblxc_la-netns_ifaddrs.lo \ liblxc_la-initutils.lo liblxc_la-log.lo \ liblxc_la-lxccontainer.lo liblxc_la-lxclock.lo \ liblxc_la-mainloop.lo liblxc_la-namespace.lo liblxc_la-nl.lo \ liblxc_la-network.lo liblxc_la-monitor.lo liblxc_la-parse.lo \ liblxc_la-raw_syscalls.lo liblxc_la-ringbuf.lo \ liblxc_la-rtnl.lo liblxc_la-state.lo liblxc_la-start.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-storage.lo \ storage/liblxc_la-storage_utils.lo storage/liblxc_la-zfs.lo \ liblxc_la-string_utils.lo liblxc_la-sync.lo \ liblxc_la-terminal.lo liblxc_la-utils.lo $(am__objects_3) \ $(am__objects_4) $(am__objects_5) $(am__objects_6) \ $(am__objects_7) $(am__objects_8) $(am__objects_9) \ $(am__objects_10) liblxc_la_OBJECTS = $(am_liblxc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = liblxc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(liblxc_la_CFLAGS) \ $(CFLAGS) $(liblxc_la_LDFLAGS) $(LDFLAGS) -o $@ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_cgfs_la_DEPENDENCIES = \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(am__DEPENDENCIES_1) am__pam_cgfs_la_SOURCES_DIST = pam/pam_cgfs.c file_utils.c \ file_utils.h macro.h string_utils.c string_utils.h \ ../include/strlcat.c ../include/strlcat.h ../include/strlcpy.c \ ../include/strlcpy.h @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@@HAVE_STRLCAT_FALSE@am__objects_11 = ../include/pam_cgfs_la-strlcat.lo @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@@HAVE_STRLCPY_FALSE@am__objects_12 = ../include/pam_cgfs_la-strlcpy.lo @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@am_pam_cgfs_la_OBJECTS = \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ pam/cgfs_la-pam_cgfs.lo \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ pam_cgfs_la-file_utils.lo \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ pam_cgfs_la-string_utils.lo \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(am__objects_11) \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(am__objects_12) pam_cgfs_la_OBJECTS = $(am_pam_cgfs_la_OBJECTS) pam_cgfs_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(pam_cgfs_la_CFLAGS) \ $(CFLAGS) $(pam_cgfs_la_LDFLAGS) $(LDFLAGS) -o $@ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@am_pam_cgfs_la_rpath = -rpath \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(pamdir) am__init_lxc_SOURCES_DIST = cmd/lxc_init.c compiler.h error.h \ initutils.c initutils.h parse.c parse.h raw_syscalls.c \ raw_syscalls.h string_utils.c string_utils.h @ENABLE_COMMANDS_TRUE@am_init_lxc_OBJECTS = cmd/lxc_init.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ initutils.$(OBJEXT) parse.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ raw_syscalls.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ string_utils.$(OBJEXT) init_lxc_OBJECTS = $(am_init_lxc_OBJECTS) init_lxc_LDADD = $(LDADD) init_lxc_DEPENDENCIES = liblxc.la am__init_lxc_static_SOURCES_DIST = cmd/lxc_init.c caps.c caps.h \ error.c error.h initutils.c initutils.h file_utils.c \ file_utils.h log.c log.h macro.h namespace.c namespace.h \ string_utils.c string_utils.h ../include/getline.c \ ../include/getline.h ../include/strlcpy.c ../include/strlcpy.h \ ../include/strlcat.c ../include/strlcat.h @ENABLE_COMMANDS_TRUE@@HAVE_FGETLN_TRUE@@HAVE_GETLINE_FALSE@@HAVE_STATIC_LIBCAP_TRUE@am__objects_13 = ../include/init_lxc_static-getline.$(OBJEXT) @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCPY_FALSE@am__objects_14 = ../include/init_lxc_static-strlcpy.$(OBJEXT) @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@@HAVE_STRLCAT_FALSE@am__objects_15 = ../include/init_lxc_static-strlcat.$(OBJEXT) @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@am_init_lxc_static_OBJECTS = cmd/init_lxc_static-lxc_init.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-caps.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-error.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-initutils.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-file_utils.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-log.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-namespace.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ init_lxc_static-string_utils.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_13) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_14) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__objects_15) init_lxc_static_OBJECTS = $(am_init_lxc_static_OBJECTS) init_lxc_static_DEPENDENCIES = init_lxc_static_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(init_lxc_static_CFLAGS) $(CFLAGS) $(init_lxc_static_LDFLAGS) \ $(LDFLAGS) -o $@ am__lxc_attach_SOURCES_DIST = tools/lxc_attach.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_attach_OBJECTS = tools/lxc_attach.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_attach_OBJECTS = $(am_lxc_attach_OBJECTS) lxc_attach_LDADD = $(LDADD) lxc_attach_DEPENDENCIES = liblxc.la am__lxc_autostart_SOURCES_DIST = tools/lxc_autostart.c \ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_autostart_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_autostart.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_autostart_OBJECTS = $(am_lxc_autostart_OBJECTS) lxc_autostart_LDADD = $(LDADD) lxc_autostart_DEPENDENCIES = liblxc.la am__lxc_cgroup_SOURCES_DIST = tools/lxc_cgroup.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_cgroup_OBJECTS = tools/lxc_cgroup.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_cgroup_OBJECTS = $(am_lxc_cgroup_OBJECTS) lxc_cgroup_LDADD = $(LDADD) lxc_cgroup_DEPENDENCIES = liblxc.la am__lxc_checkpoint_SOURCES_DIST = tools/lxc_checkpoint.c \ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_checkpoint_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_checkpoint.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_checkpoint_OBJECTS = $(am_lxc_checkpoint_OBJECTS) lxc_checkpoint_LDADD = $(LDADD) lxc_checkpoint_DEPENDENCIES = liblxc.la am__lxc_config_SOURCES_DIST = tools/lxc_config.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_config_OBJECTS = tools/lxc_config.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_config_OBJECTS = $(am_lxc_config_OBJECTS) lxc_config_LDADD = $(LDADD) lxc_config_DEPENDENCIES = liblxc.la am__lxc_console_SOURCES_DIST = tools/lxc_console.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_console_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_console.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_console_OBJECTS = $(am_lxc_console_OBJECTS) lxc_console_LDADD = $(LDADD) lxc_console_DEPENDENCIES = liblxc.la am__lxc_copy_SOURCES_DIST = tools/lxc_copy.c tools/arguments.c \ tools/arguments.h storage/storage_utils.c \ storage/storage_utils.h tools/include/getsubopt.c \ tools/include/getsubopt.h @ENABLE_TOOLS_TRUE@@HAVE_GETSUBOPT_FALSE@am__objects_16 = tools/include/getsubopt.$(OBJEXT) @ENABLE_TOOLS_TRUE@am_lxc_copy_OBJECTS = tools/lxc_copy.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ storage/storage_utils.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ $(am__objects_16) lxc_copy_OBJECTS = $(am_lxc_copy_OBJECTS) lxc_copy_LDADD = $(LDADD) lxc_copy_DEPENDENCIES = liblxc.la am__lxc_create_SOURCES_DIST = tools/lxc_create.c tools/arguments.c \ tools/arguments.h storage/storage_utils.c \ storage/storage_utils.h @ENABLE_TOOLS_TRUE@am_lxc_create_OBJECTS = tools/lxc_create.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ storage/storage_utils.$(OBJEXT) lxc_create_OBJECTS = $(am_lxc_create_OBJECTS) lxc_create_LDADD = $(LDADD) lxc_create_DEPENDENCIES = liblxc.la am__lxc_destroy_SOURCES_DIST = tools/lxc_destroy.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_destroy_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_destroy.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_destroy_OBJECTS = $(am_lxc_destroy_OBJECTS) lxc_destroy_LDADD = $(LDADD) lxc_destroy_DEPENDENCIES = liblxc.la am__lxc_device_SOURCES_DIST = tools/lxc_device.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_device_OBJECTS = tools/lxc_device.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_device_OBJECTS = $(am_lxc_device_OBJECTS) lxc_device_LDADD = $(LDADD) lxc_device_DEPENDENCIES = liblxc.la am__lxc_execute_SOURCES_DIST = tools/lxc_execute.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_execute_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_execute.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_execute_OBJECTS = $(am_lxc_execute_OBJECTS) lxc_execute_LDADD = $(LDADD) lxc_execute_DEPENDENCIES = liblxc.la am__lxc_freeze_SOURCES_DIST = tools/lxc_freeze.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_freeze_OBJECTS = tools/lxc_freeze.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_freeze_OBJECTS = $(am_lxc_freeze_OBJECTS) lxc_freeze_LDADD = $(LDADD) lxc_freeze_DEPENDENCIES = liblxc.la am__lxc_info_SOURCES_DIST = tools/lxc_info.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_info_OBJECTS = tools/lxc_info.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_info_OBJECTS = $(am_lxc_info_OBJECTS) lxc_info_LDADD = $(LDADD) lxc_info_DEPENDENCIES = liblxc.la am__lxc_ls_SOURCES_DIST = tools/lxc_ls.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_ls_OBJECTS = tools/lxc_ls.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_ls_OBJECTS = $(am_lxc_ls_OBJECTS) lxc_ls_LDADD = $(LDADD) lxc_ls_DEPENDENCIES = liblxc.la am__lxc_monitor_SOURCES_DIST = tools/lxc_monitor.c macro.h \ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_monitor_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_monitor.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_monitor_OBJECTS = $(am_lxc_monitor_OBJECTS) lxc_monitor_LDADD = $(LDADD) lxc_monitor_DEPENDENCIES = liblxc.la am__lxc_monitord_SOURCES_DIST = cmd/lxc_monitord.c af_unix.c af_unix.h \ log.c log.h mainloop.c mainloop.h monitor.c monitor.h \ raw_syscalls.c raw_syscalls.h utils.c utils.h @ENABLE_COMMANDS_TRUE@am_lxc_monitord_OBJECTS = \ @ENABLE_COMMANDS_TRUE@ cmd/lxc_monitord.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ af_unix.$(OBJEXT) log.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ mainloop.$(OBJEXT) monitor.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ raw_syscalls.$(OBJEXT) utils.$(OBJEXT) lxc_monitord_OBJECTS = $(am_lxc_monitord_OBJECTS) lxc_monitord_LDADD = $(LDADD) lxc_monitord_DEPENDENCIES = liblxc.la am__lxc_snapshot_SOURCES_DIST = tools/lxc_snapshot.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_snapshot_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_snapshot.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_snapshot_OBJECTS = $(am_lxc_snapshot_OBJECTS) lxc_snapshot_LDADD = $(LDADD) lxc_snapshot_DEPENDENCIES = liblxc.la am__lxc_start_SOURCES_DIST = tools/lxc_start.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_start_OBJECTS = tools/lxc_start.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_start_OBJECTS = $(am_lxc_start_OBJECTS) lxc_start_LDADD = $(LDADD) lxc_start_DEPENDENCIES = liblxc.la am__lxc_stop_SOURCES_DIST = tools/lxc_stop.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_stop_OBJECTS = tools/lxc_stop.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_stop_OBJECTS = $(am_lxc_stop_OBJECTS) lxc_stop_LDADD = $(LDADD) lxc_stop_DEPENDENCIES = liblxc.la am__lxc_top_SOURCES_DIST = tools/lxc_top.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_top_OBJECTS = tools/lxc_top.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_top_OBJECTS = $(am_lxc_top_OBJECTS) lxc_top_LDADD = $(LDADD) lxc_top_DEPENDENCIES = liblxc.la am__lxc_unfreeze_SOURCES_DIST = tools/lxc_unfreeze.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_unfreeze_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_unfreeze.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_unfreeze_OBJECTS = $(am_lxc_unfreeze_OBJECTS) lxc_unfreeze_LDADD = $(LDADD) lxc_unfreeze_DEPENDENCIES = liblxc.la am__lxc_unshare_SOURCES_DIST = tools/lxc_unshare.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_unshare_OBJECTS = \ @ENABLE_TOOLS_TRUE@ tools/lxc_unshare.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_unshare_OBJECTS = $(am_lxc_unshare_OBJECTS) lxc_unshare_LDADD = $(LDADD) lxc_unshare_DEPENDENCIES = liblxc.la am__lxc_user_nic_SOURCES_DIST = cmd/lxc_user_nic.c \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h log.c \ log.h network.c network.h parse.c parse.h raw_syscalls.c \ raw_syscalls.h syscall_wrappers.h @ENABLE_COMMANDS_TRUE@am_lxc_user_nic_OBJECTS = \ @ENABLE_COMMANDS_TRUE@ cmd/lxc_user_nic.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ ../include/netns_ifaddrs.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ log.$(OBJEXT) network.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ parse.$(OBJEXT) raw_syscalls.$(OBJEXT) lxc_user_nic_OBJECTS = $(am_lxc_user_nic_OBJECTS) lxc_user_nic_LDADD = $(LDADD) lxc_user_nic_DEPENDENCIES = liblxc.la am__lxc_usernsexec_SOURCES_DIST = cmd/lxc_usernsexec.c conf.c conf.h \ list.h log.c log.h macro.h file_utils.c file_utils.h \ string_utils.c string_utils.h syscall_wrappers.h utils.c \ utils.h @ENABLE_COMMANDS_TRUE@am_lxc_usernsexec_OBJECTS = \ @ENABLE_COMMANDS_TRUE@ cmd/lxc_usernsexec.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ conf.$(OBJEXT) log.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ file_utils.$(OBJEXT) \ @ENABLE_COMMANDS_TRUE@ string_utils.$(OBJEXT) utils.$(OBJEXT) lxc_usernsexec_OBJECTS = $(am_lxc_usernsexec_OBJECTS) lxc_usernsexec_LDADD = $(LDADD) lxc_usernsexec_DEPENDENCIES = liblxc.la am__lxc_wait_SOURCES_DIST = tools/lxc_wait.c tools/arguments.c \ tools/arguments.h @ENABLE_TOOLS_TRUE@am_lxc_wait_OBJECTS = tools/lxc_wait.$(OBJEXT) \ @ENABLE_TOOLS_TRUE@ tools/arguments.$(OBJEXT) lxc_wait_OBJECTS = $(am_lxc_wait_OBJECTS) lxc_wait_LDADD = $(LDADD) lxc_wait_DEPENDENCIES = liblxc.la SCRIPTS = $(bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ../include/$(DEPDIR)/init_lxc_static-getline.Po \ ../include/$(DEPDIR)/init_lxc_static-strlcat.Po \ ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po \ ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo \ ../include/$(DEPDIR)/liblxc_la-getline.Plo \ ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo \ ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Plo \ ../include/$(DEPDIR)/liblxc_la-openpty.Plo \ ../include/$(DEPDIR)/liblxc_la-prlimit.Plo \ ../include/$(DEPDIR)/liblxc_la-strlcat.Plo \ ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo \ ../include/$(DEPDIR)/netns_ifaddrs.Po \ ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Plo \ ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Plo \ ./$(DEPDIR)/af_unix.Po ./$(DEPDIR)/conf.Po \ ./$(DEPDIR)/file_utils.Po ./$(DEPDIR)/init_lxc_static-caps.Po \ ./$(DEPDIR)/init_lxc_static-error.Po \ ./$(DEPDIR)/init_lxc_static-file_utils.Po \ ./$(DEPDIR)/init_lxc_static-initutils.Po \ ./$(DEPDIR)/init_lxc_static-log.Po \ ./$(DEPDIR)/init_lxc_static-namespace.Po \ ./$(DEPDIR)/init_lxc_static-string_utils.Po \ ./$(DEPDIR)/initutils.Po ./$(DEPDIR)/liblxc_la-af_unix.Plo \ ./$(DEPDIR)/liblxc_la-attach.Plo \ ./$(DEPDIR)/liblxc_la-caps.Plo \ ./$(DEPDIR)/liblxc_la-commands.Plo \ ./$(DEPDIR)/liblxc_la-commands_utils.Plo \ ./$(DEPDIR)/liblxc_la-conf.Plo \ ./$(DEPDIR)/liblxc_la-confile.Plo \ ./$(DEPDIR)/liblxc_la-confile_utils.Plo \ ./$(DEPDIR)/liblxc_la-criu.Plo ./$(DEPDIR)/liblxc_la-error.Plo \ ./$(DEPDIR)/liblxc_la-execute.Plo \ ./$(DEPDIR)/liblxc_la-file_utils.Plo \ ./$(DEPDIR)/liblxc_la-freezer.Plo \ ./$(DEPDIR)/liblxc_la-initutils.Plo \ ./$(DEPDIR)/liblxc_la-log.Plo \ ./$(DEPDIR)/liblxc_la-lxccontainer.Plo \ ./$(DEPDIR)/liblxc_la-lxclock.Plo \ ./$(DEPDIR)/liblxc_la-mainloop.Plo \ ./$(DEPDIR)/liblxc_la-monitor.Plo \ ./$(DEPDIR)/liblxc_la-namespace.Plo \ ./$(DEPDIR)/liblxc_la-network.Plo ./$(DEPDIR)/liblxc_la-nl.Plo \ ./$(DEPDIR)/liblxc_la-parse.Plo \ ./$(DEPDIR)/liblxc_la-raw_syscalls.Plo \ ./$(DEPDIR)/liblxc_la-ringbuf.Plo \ ./$(DEPDIR)/liblxc_la-rtnl.Plo \ ./$(DEPDIR)/liblxc_la-seccomp.Plo \ ./$(DEPDIR)/liblxc_la-start.Plo \ ./$(DEPDIR)/liblxc_la-state.Plo \ ./$(DEPDIR)/liblxc_la-string_utils.Plo \ ./$(DEPDIR)/liblxc_la-sync.Plo \ ./$(DEPDIR)/liblxc_la-terminal.Plo \ ./$(DEPDIR)/liblxc_la-utils.Plo ./$(DEPDIR)/log.Po \ ./$(DEPDIR)/mainloop.Po ./$(DEPDIR)/monitor.Po \ ./$(DEPDIR)/network.Po ./$(DEPDIR)/pam_cgfs_la-file_utils.Plo \ ./$(DEPDIR)/pam_cgfs_la-string_utils.Plo ./$(DEPDIR)/parse.Po \ ./$(DEPDIR)/raw_syscalls.Po ./$(DEPDIR)/string_utils.Po \ ./$(DEPDIR)/utils.Po cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo \ cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo \ cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po \ cmd/$(DEPDIR)/lxc_init.Po cmd/$(DEPDIR)/lxc_monitord.Po \ cmd/$(DEPDIR)/lxc_user_nic.Po cmd/$(DEPDIR)/lxc_usernsexec.Po \ lsm/$(DEPDIR)/liblxc_la-apparmor.Plo \ lsm/$(DEPDIR)/liblxc_la-lsm.Plo \ lsm/$(DEPDIR)/liblxc_la-nop.Plo \ lsm/$(DEPDIR)/liblxc_la-selinux.Plo \ pam/$(DEPDIR)/cgfs_la-pam_cgfs.Plo \ storage/$(DEPDIR)/liblxc_la-btrfs.Plo \ storage/$(DEPDIR)/liblxc_la-dir.Plo \ storage/$(DEPDIR)/liblxc_la-loop.Plo \ storage/$(DEPDIR)/liblxc_la-lvm.Plo \ storage/$(DEPDIR)/liblxc_la-nbd.Plo \ storage/$(DEPDIR)/liblxc_la-overlay.Plo \ storage/$(DEPDIR)/liblxc_la-rbd.Plo \ storage/$(DEPDIR)/liblxc_la-rsync.Plo \ storage/$(DEPDIR)/liblxc_la-storage.Plo \ storage/$(DEPDIR)/liblxc_la-storage_utils.Plo \ storage/$(DEPDIR)/liblxc_la-zfs.Plo \ storage/$(DEPDIR)/storage_utils.Po \ tools/$(DEPDIR)/arguments.Po tools/$(DEPDIR)/lxc_attach.Po \ tools/$(DEPDIR)/lxc_autostart.Po tools/$(DEPDIR)/lxc_cgroup.Po \ tools/$(DEPDIR)/lxc_checkpoint.Po \ tools/$(DEPDIR)/lxc_config.Po tools/$(DEPDIR)/lxc_console.Po \ tools/$(DEPDIR)/lxc_copy.Po tools/$(DEPDIR)/lxc_create.Po \ tools/$(DEPDIR)/lxc_destroy.Po tools/$(DEPDIR)/lxc_device.Po \ tools/$(DEPDIR)/lxc_execute.Po tools/$(DEPDIR)/lxc_freeze.Po \ tools/$(DEPDIR)/lxc_info.Po tools/$(DEPDIR)/lxc_ls.Po \ tools/$(DEPDIR)/lxc_monitor.Po tools/$(DEPDIR)/lxc_snapshot.Po \ tools/$(DEPDIR)/lxc_start.Po tools/$(DEPDIR)/lxc_stop.Po \ tools/$(DEPDIR)/lxc_top.Po tools/$(DEPDIR)/lxc_unfreeze.Po \ tools/$(DEPDIR)/lxc_unshare.Po tools/$(DEPDIR)/lxc_wait.Po \ tools/include/$(DEPDIR)/getsubopt.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(liblxc_la_SOURCES) $(pam_cgfs_la_SOURCES) \ $(init_lxc_SOURCES) $(init_lxc_static_SOURCES) \ $(lxc_attach_SOURCES) $(lxc_autostart_SOURCES) \ $(lxc_cgroup_SOURCES) $(lxc_checkpoint_SOURCES) \ $(lxc_config_SOURCES) $(lxc_console_SOURCES) \ $(lxc_copy_SOURCES) $(lxc_create_SOURCES) \ $(lxc_destroy_SOURCES) $(lxc_device_SOURCES) \ $(lxc_execute_SOURCES) $(lxc_freeze_SOURCES) \ $(lxc_info_SOURCES) $(lxc_ls_SOURCES) $(lxc_monitor_SOURCES) \ $(lxc_monitord_SOURCES) $(lxc_snapshot_SOURCES) \ $(lxc_start_SOURCES) $(lxc_stop_SOURCES) $(lxc_top_SOURCES) \ $(lxc_unfreeze_SOURCES) $(lxc_unshare_SOURCES) \ $(lxc_user_nic_SOURCES) $(lxc_usernsexec_SOURCES) \ $(lxc_wait_SOURCES) DIST_SOURCES = $(am__liblxc_la_SOURCES_DIST) \ $(am__pam_cgfs_la_SOURCES_DIST) $(am__init_lxc_SOURCES_DIST) \ $(am__init_lxc_static_SOURCES_DIST) \ $(am__lxc_attach_SOURCES_DIST) \ $(am__lxc_autostart_SOURCES_DIST) \ $(am__lxc_cgroup_SOURCES_DIST) \ $(am__lxc_checkpoint_SOURCES_DIST) \ $(am__lxc_config_SOURCES_DIST) $(am__lxc_console_SOURCES_DIST) \ $(am__lxc_copy_SOURCES_DIST) $(am__lxc_create_SOURCES_DIST) \ $(am__lxc_destroy_SOURCES_DIST) $(am__lxc_device_SOURCES_DIST) \ $(am__lxc_execute_SOURCES_DIST) $(am__lxc_freeze_SOURCES_DIST) \ $(am__lxc_info_SOURCES_DIST) $(am__lxc_ls_SOURCES_DIST) \ $(am__lxc_monitor_SOURCES_DIST) \ $(am__lxc_monitord_SOURCES_DIST) \ $(am__lxc_snapshot_SOURCES_DIST) $(am__lxc_start_SOURCES_DIST) \ $(am__lxc_stop_SOURCES_DIST) $(am__lxc_top_SOURCES_DIST) \ $(am__lxc_unfreeze_SOURCES_DIST) \ $(am__lxc_unshare_SOURCES_DIST) \ $(am__lxc_user_nic_SOURCES_DIST) \ $(am__lxc_usernsexec_SOURCES_DIST) \ $(am__lxc_wait_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__noinst_HEADERS_DIST = attach.h caps.h cgroups/cgroup.h \ cgroups/cgroup_utils.h compiler.h conf.h confile.h \ confile_utils.h criu.h error.h file_utils.h \ ../include/netns_ifaddrs.h initutils.h list.h log.h lxc.h \ lxclock.h macro.h monitor.h namespace.h raw_syscalls.h start.h \ state.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/storage.h storage/storage_utils.h \ storage/zfs.h string_utils.h syscall_wrappers.h terminal.h \ ../tests/lxctest.h tools/arguments.h utils.h \ ../include/lxcmntent.h ../include/openpty.h \ ../include/prlimit.h ../include/getline.h \ tools/include/getsubopt.h ../include/getgrgid_r.h HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/lxc.functions.in \ $(srcdir)/version.h.in $(top_srcdir)/config/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CAP_LIBS = @CAP_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIR = @DATADIR@ DEFAULT_CGROUP_PATTERN = @DEFAULT_CGROUP_PATTERN@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOCDIR = @DOCDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ HAVE_DOXYGEN = @HAVE_DOXYGEN@ INCLUDEDIR = @INCLUDEDIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LXCBINHOOKDIR = @LXCBINHOOKDIR@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_ABI = @LXC_ABI@ LXC_ABI_MAJOR = @LXC_ABI_MAJOR@ LXC_ABI_MICRO = @LXC_ABI_MICRO@ LXC_ABI_MINOR = @LXC_ABI_MINOR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ LXC_DEVEL = @LXC_DEVEL@ LXC_DISTRO_SYSCONF = @LXC_DISTRO_SYSCONF@ LXC_GENERATE_DATE = @LXC_GENERATE_DATE@ LXC_GLOBAL_CONF = @LXC_GLOBAL_CONF@ LXC_USERNIC_CONF = @LXC_USERNIC_CONF@ LXC_USERNIC_DB = @LXC_USERNIC_DB@ LXC_VERSION = @LXC_VERSION@ LXC_VERSION_BASE = @LXC_VERSION_BASE@ LXC_VERSION_BETA = @LXC_VERSION_BETA@ LXC_VERSION_MAJOR = @LXC_VERSION_MAJOR@ LXC_VERSION_MICRO = @LXC_VERSION_MICRO@ LXC_VERSION_MINOR = @LXC_VERSION_MINOR@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PAM_CFLAGS = @PAM_CFLAGS@ PAM_LIBS = @PAM_LIBS@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PREFIX = @PREFIX@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RUNTIME_PATH = @RUNTIME_PATH@ SBINDIR = @SBINDIR@ SECCOMP_CFLAGS = @SECCOMP_CFLAGS@ SECCOMP_LIBS = @SECCOMP_LIBS@ SED = @SED@ SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSCONFDIR = @SYSCONFDIR@ SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bashcompdir = @bashcompdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ db2xman = @db2xman@ docdir = @docdir@ docdtd = @docdtd@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pamdir = @pamdir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkginclude_HEADERS = attach_options.h \ lxccontainer.h \ version.h noinst_HEADERS = attach.h caps.h cgroups/cgroup.h \ cgroups/cgroup_utils.h compiler.h conf.h confile.h \ confile_utils.h criu.h error.h file_utils.h \ ../include/netns_ifaddrs.h initutils.h list.h log.h lxc.h \ lxclock.h macro.h monitor.h namespace.h raw_syscalls.h start.h \ state.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/storage.h storage/storage_utils.h \ storage/zfs.h string_utils.h syscall_wrappers.h terminal.h \ ../tests/lxctest.h tools/arguments.h storage/storage_utils.h \ utils.h $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) $(am__append_5) sodir = $(libdir) LSM_SOURCES = lsm/lsm.c lsm/lsm.h lsm/nop.c $(am__append_6) \ $(am__append_7) lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = af_unix.c af_unix.h attach.c attach.h caps.c \ caps.h cgroups/cgfsng.c cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup_utils.c cgroups/cgroup_utils.h compiler.h \ commands.c commands.h commands_utils.c commands_utils.h conf.c \ conf.h confile.c confile.h confile_utils.c confile_utils.h \ criu.c criu.h error.c error.h execute.c freezer.c file_utils.c \ file_utils.h ../include/netns_ifaddrs.c \ ../include/netns_ifaddrs.h initutils.c initutils.h list.h \ log.c log.h lxc.h lxccontainer.c lxccontainer.h lxclock.c \ lxclock.h lxcseccomp.h macro.h mainloop.c mainloop.h \ namespace.c namespace.h nl.c nl.h network.c network.h \ monitor.c monitor.h parse.c parse.h raw_syscalls.c \ raw_syscalls.h ringbuf.c ringbuf.h rtnl.c rtnl.h state.c \ state.h start.c start.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/storage.c storage/storage.h storage/storage_utils.c \ storage/storage_utils.h storage/zfs.c storage/zfs.h \ string_utils.c string_utils.h sync.c sync.h syscall_wrappers.h \ terminal.c utils.c utils.h version.h $(LSM_SOURCES) \ $(am__append_8) $(am__append_9) $(am__append_10) \ $(am__append_11) $(am__append_12) $(am__append_13) \ $(am__append_14) AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ -DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" -DSBINDIR=\"$(SBINDIR)\" -I \ $(top_srcdir)/src -I $(top_srcdir)/src/lxc -I \ $(top_srcdir)/src/lxc/storage -I $(top_srcdir)/src/lxc/cgroups \ $(am__append_15) $(am__append_16) $(am__append_17) \ $(am__append_18) $(am__append_19) # build the shared library liblxc_la_CFLAGS = -fPIC \ -DPIC \ $(AM_CFLAGS) \ -pthread liblxc_la_LDFLAGS = -pthread \ -Wl,-no-undefined \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) \ $(GNUTLS_LIBS) \ $(SELINUX_LIBS) \ $(SECCOMP_LIBS) bin_SCRIPTS = $(am__append_20) AM_LDFLAGS = -Wl,-E $(am__append_22) LDADD = liblxc.la \ @CAP_LIBS@ \ @GNUTLS_LIBS@ \ @SECCOMP_LIBS@ \ @SELINUX_LIBS@ @ENABLE_TOOLS_TRUE@lxc_attach_SOURCES = tools/lxc_attach.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_autostart_SOURCES = tools/lxc_autostart.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_cgroup_SOURCES = tools/lxc_cgroup.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_config_SOURCES = tools/lxc_config.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_console_SOURCES = tools/lxc_console.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_destroy_SOURCES = tools/lxc_destroy.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_device_SOURCES = tools/lxc_device.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_execute_SOURCES = tools/lxc_execute.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_freeze_SOURCES = tools/lxc_freeze.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_info_SOURCES = tools/lxc_info.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_monitor_SOURCES = tools/lxc_monitor.c \ @ENABLE_TOOLS_TRUE@ macro.h \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_ls_SOURCES = tools/lxc_ls.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_copy_SOURCES = tools/lxc_copy.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h \ @ENABLE_TOOLS_TRUE@ storage/storage_utils.c \ @ENABLE_TOOLS_TRUE@ storage/storage_utils.h $(am__append_23) @ENABLE_TOOLS_TRUE@lxc_start_SOURCES = tools/lxc_start.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_stop_SOURCES = tools/lxc_stop.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_top_SOURCES = tools/lxc_top.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_unshare_SOURCES = tools/lxc_unshare.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_wait_SOURCES = tools/lxc_wait.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_create_SOURCES = tools/lxc_create.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h \ @ENABLE_TOOLS_TRUE@ storage/storage_utils.c storage/storage_utils.h @ENABLE_TOOLS_TRUE@lxc_snapshot_SOURCES = tools/lxc_snapshot.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h @ENABLE_TOOLS_TRUE@lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c \ @ENABLE_TOOLS_TRUE@ tools/arguments.c tools/arguments.h # Binaries shipping with liblxc @ENABLE_COMMANDS_TRUE@init_lxc_SOURCES = cmd/lxc_init.c \ @ENABLE_COMMANDS_TRUE@ compiler.h \ @ENABLE_COMMANDS_TRUE@ error.h \ @ENABLE_COMMANDS_TRUE@ initutils.c initutils.h \ @ENABLE_COMMANDS_TRUE@ parse.c parse.h \ @ENABLE_COMMANDS_TRUE@ raw_syscalls.c raw_syscalls.h \ @ENABLE_COMMANDS_TRUE@ string_utils.c string_utils.h @ENABLE_COMMANDS_TRUE@lxc_monitord_SOURCES = cmd/lxc_monitord.c \ @ENABLE_COMMANDS_TRUE@ af_unix.c af_unix.h \ @ENABLE_COMMANDS_TRUE@ log.c log.h \ @ENABLE_COMMANDS_TRUE@ mainloop.c mainloop.h \ @ENABLE_COMMANDS_TRUE@ monitor.c monitor.h \ @ENABLE_COMMANDS_TRUE@ raw_syscalls.c raw_syscalls.h \ @ENABLE_COMMANDS_TRUE@ utils.c utils.h @ENABLE_COMMANDS_TRUE@lxc_user_nic_SOURCES = cmd/lxc_user_nic.c \ @ENABLE_COMMANDS_TRUE@ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ @ENABLE_COMMANDS_TRUE@ log.c log.h \ @ENABLE_COMMANDS_TRUE@ network.c network.h \ @ENABLE_COMMANDS_TRUE@ parse.c parse.h \ @ENABLE_COMMANDS_TRUE@ raw_syscalls.c raw_syscalls.h \ @ENABLE_COMMANDS_TRUE@ syscall_wrappers.h @ENABLE_COMMANDS_TRUE@lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \ @ENABLE_COMMANDS_TRUE@ conf.c conf.h \ @ENABLE_COMMANDS_TRUE@ list.h \ @ENABLE_COMMANDS_TRUE@ log.c log.h \ @ENABLE_COMMANDS_TRUE@ macro.h \ @ENABLE_COMMANDS_TRUE@ file_utils.c file_utils.h \ @ENABLE_COMMANDS_TRUE@ string_utils.c string_utils.h \ @ENABLE_COMMANDS_TRUE@ syscall_wrappers.h \ @ENABLE_COMMANDS_TRUE@ utils.c utils.h @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_SOURCES = \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ cmd/lxc_init.c \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ caps.c caps.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ error.c error.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ initutils.c \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ initutils.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ file_utils.c \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ file_utils.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ log.c log.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ macro.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ namespace.c \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ namespace.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ string_utils.c \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ string_utils.h \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__append_25) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__append_26) \ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@ $(am__append_27) @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDFLAGS = -all-static @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_LDADD = @CAP_LIBS@ @ENABLE_COMMANDS_TRUE@@HAVE_STATIC_LIBCAP_TRUE@init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_LTLIBRARIES = pam_cgfs.la @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_cgfs_la_SOURCES = pam/pam_cgfs.c \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ file_utils.c file_utils.h \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ macro.h string_utils.c \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ string_utils.h \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(am__append_28) \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(am__append_29) @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_cgfs_la_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_cgfs_la_LIBADD = $(AM_LIBS) \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(PAM_LIBS) \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ -L$(top_srcdir) @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@pam_cgfs_la_LDFLAGS = $(AM_LDFLAGS) \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ -avoid-version \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ -module \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ -shared \ @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ -Wl,-no-undefined all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/lxc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/lxc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): lxc.functions: $(top_builddir)/config.status $(srcdir)/lxc.functions.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ version.h: $(top_builddir)/config.status $(srcdir)/version.h.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files clean-pkglibexecPROGRAMS: @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-pamLTLIBRARIES: $(pam_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pam_LTLIBRARIES)'; test -n "$(pamdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pamdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pamdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pamdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pamdir)"; \ } uninstall-pamLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(pam_LTLIBRARIES)'; test -n "$(pamdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pamdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pamdir)/$$f"; \ done clean-pamLTLIBRARIES: -test -z "$(pam_LTLIBRARIES)" || rm -f $(pam_LTLIBRARIES) @list='$(pam_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } cgroups/$(am__dirstamp): @$(MKDIR_P) cgroups @: > cgroups/$(am__dirstamp) cgroups/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) cgroups/$(DEPDIR) @: > cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgfsng.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgroup.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) cgroups/liblxc_la-cgroup_utils.lo: cgroups/$(am__dirstamp) \ cgroups/$(DEPDIR)/$(am__dirstamp) ../include/$(am__dirstamp): @$(MKDIR_P) ../include @: > ../include/$(am__dirstamp) ../include/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../include/$(DEPDIR) @: > ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-netns_ifaddrs.lo: ../include/$(am__dirstamp) \ ../include/$(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-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-storage.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-storage_utils.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) storage/liblxc_la-zfs.lo: storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) lsm/$(am__dirstamp): @$(MKDIR_P) lsm @: > lsm/$(am__dirstamp) lsm/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lsm/$(DEPDIR) @: > lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-lsm.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-nop.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-apparmor.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) lsm/liblxc_la-selinux.lo: lsm/$(am__dirstamp) \ lsm/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-lxcmntent.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-openpty.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-getgrgid_r.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-getline.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-prlimit.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-strlcpy.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/liblxc_la-strlcat.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) liblxc.la: $(liblxc_la_OBJECTS) $(liblxc_la_DEPENDENCIES) $(EXTRA_liblxc_la_DEPENDENCIES) $(AM_V_CCLD)$(liblxc_la_LINK) -rpath $(libdir) $(liblxc_la_OBJECTS) $(liblxc_la_LIBADD) $(LIBS) pam/$(am__dirstamp): @$(MKDIR_P) pam @: > pam/$(am__dirstamp) pam/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) pam/$(DEPDIR) @: > pam/$(DEPDIR)/$(am__dirstamp) pam/cgfs_la-pam_cgfs.lo: pam/$(am__dirstamp) \ pam/$(DEPDIR)/$(am__dirstamp) ../include/pam_cgfs_la-strlcat.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/pam_cgfs_la-strlcpy.lo: ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) pam_cgfs.la: $(pam_cgfs_la_OBJECTS) $(pam_cgfs_la_DEPENDENCIES) $(EXTRA_pam_cgfs_la_DEPENDENCIES) $(AM_V_CCLD)$(pam_cgfs_la_LINK) $(am_pam_cgfs_la_rpath) $(pam_cgfs_la_OBJECTS) $(pam_cgfs_la_LIBADD) $(LIBS) cmd/$(am__dirstamp): @$(MKDIR_P) cmd @: > cmd/$(am__dirstamp) cmd/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) cmd/$(DEPDIR) @: > cmd/$(DEPDIR)/$(am__dirstamp) cmd/lxc_init.$(OBJEXT): cmd/$(am__dirstamp) \ cmd/$(DEPDIR)/$(am__dirstamp) init.lxc$(EXEEXT): $(init_lxc_OBJECTS) $(init_lxc_DEPENDENCIES) $(EXTRA_init_lxc_DEPENDENCIES) @rm -f init.lxc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(init_lxc_OBJECTS) $(init_lxc_LDADD) $(LIBS) cmd/init_lxc_static-lxc_init.$(OBJEXT): cmd/$(am__dirstamp) \ cmd/$(DEPDIR)/$(am__dirstamp) ../include/init_lxc_static-getline.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/init_lxc_static-strlcpy.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) ../include/init_lxc_static-strlcat.$(OBJEXT): \ ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) init.lxc.static$(EXEEXT): $(init_lxc_static_OBJECTS) $(init_lxc_static_DEPENDENCIES) $(EXTRA_init_lxc_static_DEPENDENCIES) @rm -f init.lxc.static$(EXEEXT) $(AM_V_CCLD)$(init_lxc_static_LINK) $(init_lxc_static_OBJECTS) $(init_lxc_static_LDADD) $(LIBS) tools/$(am__dirstamp): @$(MKDIR_P) tools @: > tools/$(am__dirstamp) tools/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/$(DEPDIR) @: > tools/$(DEPDIR)/$(am__dirstamp) tools/lxc_attach.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/arguments.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-attach$(EXEEXT): $(lxc_attach_OBJECTS) $(lxc_attach_DEPENDENCIES) $(EXTRA_lxc_attach_DEPENDENCIES) @rm -f lxc-attach$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_attach_OBJECTS) $(lxc_attach_LDADD) $(LIBS) tools/lxc_autostart.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-autostart$(EXEEXT): $(lxc_autostart_OBJECTS) $(lxc_autostart_DEPENDENCIES) $(EXTRA_lxc_autostart_DEPENDENCIES) @rm -f lxc-autostart$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_autostart_OBJECTS) $(lxc_autostart_LDADD) $(LIBS) tools/lxc_cgroup.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-cgroup$(EXEEXT): $(lxc_cgroup_OBJECTS) $(lxc_cgroup_DEPENDENCIES) $(EXTRA_lxc_cgroup_DEPENDENCIES) @rm -f lxc-cgroup$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_cgroup_OBJECTS) $(lxc_cgroup_LDADD) $(LIBS) tools/lxc_checkpoint.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-checkpoint$(EXEEXT): $(lxc_checkpoint_OBJECTS) $(lxc_checkpoint_DEPENDENCIES) $(EXTRA_lxc_checkpoint_DEPENDENCIES) @rm -f lxc-checkpoint$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_checkpoint_OBJECTS) $(lxc_checkpoint_LDADD) $(LIBS) tools/lxc_config.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-config$(EXEEXT): $(lxc_config_OBJECTS) $(lxc_config_DEPENDENCIES) $(EXTRA_lxc_config_DEPENDENCIES) @rm -f lxc-config$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_config_OBJECTS) $(lxc_config_LDADD) $(LIBS) tools/lxc_console.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-console$(EXEEXT): $(lxc_console_OBJECTS) $(lxc_console_DEPENDENCIES) $(EXTRA_lxc_console_DEPENDENCIES) @rm -f lxc-console$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_console_OBJECTS) $(lxc_console_LDADD) $(LIBS) tools/lxc_copy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) storage/storage_utils.$(OBJEXT): storage/$(am__dirstamp) \ storage/$(DEPDIR)/$(am__dirstamp) tools/include/$(am__dirstamp): @$(MKDIR_P) tools/include @: > tools/include/$(am__dirstamp) tools/include/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/include/$(DEPDIR) @: > tools/include/$(DEPDIR)/$(am__dirstamp) tools/include/getsubopt.$(OBJEXT): tools/include/$(am__dirstamp) \ tools/include/$(DEPDIR)/$(am__dirstamp) lxc-copy$(EXEEXT): $(lxc_copy_OBJECTS) $(lxc_copy_DEPENDENCIES) $(EXTRA_lxc_copy_DEPENDENCIES) @rm -f lxc-copy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_copy_OBJECTS) $(lxc_copy_LDADD) $(LIBS) tools/lxc_create.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-create$(EXEEXT): $(lxc_create_OBJECTS) $(lxc_create_DEPENDENCIES) $(EXTRA_lxc_create_DEPENDENCIES) @rm -f lxc-create$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_create_OBJECTS) $(lxc_create_LDADD) $(LIBS) tools/lxc_destroy.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-destroy$(EXEEXT): $(lxc_destroy_OBJECTS) $(lxc_destroy_DEPENDENCIES) $(EXTRA_lxc_destroy_DEPENDENCIES) @rm -f lxc-destroy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_destroy_OBJECTS) $(lxc_destroy_LDADD) $(LIBS) tools/lxc_device.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-device$(EXEEXT): $(lxc_device_OBJECTS) $(lxc_device_DEPENDENCIES) $(EXTRA_lxc_device_DEPENDENCIES) @rm -f lxc-device$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_device_OBJECTS) $(lxc_device_LDADD) $(LIBS) tools/lxc_execute.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-execute$(EXEEXT): $(lxc_execute_OBJECTS) $(lxc_execute_DEPENDENCIES) $(EXTRA_lxc_execute_DEPENDENCIES) @rm -f lxc-execute$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_execute_OBJECTS) $(lxc_execute_LDADD) $(LIBS) tools/lxc_freeze.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-freeze$(EXEEXT): $(lxc_freeze_OBJECTS) $(lxc_freeze_DEPENDENCIES) $(EXTRA_lxc_freeze_DEPENDENCIES) @rm -f lxc-freeze$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_freeze_OBJECTS) $(lxc_freeze_LDADD) $(LIBS) tools/lxc_info.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-info$(EXEEXT): $(lxc_info_OBJECTS) $(lxc_info_DEPENDENCIES) $(EXTRA_lxc_info_DEPENDENCIES) @rm -f lxc-info$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_info_OBJECTS) $(lxc_info_LDADD) $(LIBS) tools/lxc_ls.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-ls$(EXEEXT): $(lxc_ls_OBJECTS) $(lxc_ls_DEPENDENCIES) $(EXTRA_lxc_ls_DEPENDENCIES) @rm -f lxc-ls$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_ls_OBJECTS) $(lxc_ls_LDADD) $(LIBS) tools/lxc_monitor.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-monitor$(EXEEXT): $(lxc_monitor_OBJECTS) $(lxc_monitor_DEPENDENCIES) $(EXTRA_lxc_monitor_DEPENDENCIES) @rm -f lxc-monitor$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_monitor_OBJECTS) $(lxc_monitor_LDADD) $(LIBS) cmd/lxc_monitord.$(OBJEXT): cmd/$(am__dirstamp) \ cmd/$(DEPDIR)/$(am__dirstamp) lxc-monitord$(EXEEXT): $(lxc_monitord_OBJECTS) $(lxc_monitord_DEPENDENCIES) $(EXTRA_lxc_monitord_DEPENDENCIES) @rm -f lxc-monitord$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_monitord_OBJECTS) $(lxc_monitord_LDADD) $(LIBS) tools/lxc_snapshot.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-snapshot$(EXEEXT): $(lxc_snapshot_OBJECTS) $(lxc_snapshot_DEPENDENCIES) $(EXTRA_lxc_snapshot_DEPENDENCIES) @rm -f lxc-snapshot$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_snapshot_OBJECTS) $(lxc_snapshot_LDADD) $(LIBS) tools/lxc_start.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-start$(EXEEXT): $(lxc_start_OBJECTS) $(lxc_start_DEPENDENCIES) $(EXTRA_lxc_start_DEPENDENCIES) @rm -f lxc-start$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_start_OBJECTS) $(lxc_start_LDADD) $(LIBS) tools/lxc_stop.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-stop$(EXEEXT): $(lxc_stop_OBJECTS) $(lxc_stop_DEPENDENCIES) $(EXTRA_lxc_stop_DEPENDENCIES) @rm -f lxc-stop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_stop_OBJECTS) $(lxc_stop_LDADD) $(LIBS) tools/lxc_top.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-top$(EXEEXT): $(lxc_top_OBJECTS) $(lxc_top_DEPENDENCIES) $(EXTRA_lxc_top_DEPENDENCIES) @rm -f lxc-top$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_top_OBJECTS) $(lxc_top_LDADD) $(LIBS) tools/lxc_unfreeze.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-unfreeze$(EXEEXT): $(lxc_unfreeze_OBJECTS) $(lxc_unfreeze_DEPENDENCIES) $(EXTRA_lxc_unfreeze_DEPENDENCIES) @rm -f lxc-unfreeze$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_unfreeze_OBJECTS) $(lxc_unfreeze_LDADD) $(LIBS) tools/lxc_unshare.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-unshare$(EXEEXT): $(lxc_unshare_OBJECTS) $(lxc_unshare_DEPENDENCIES) $(EXTRA_lxc_unshare_DEPENDENCIES) @rm -f lxc-unshare$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_unshare_OBJECTS) $(lxc_unshare_LDADD) $(LIBS) cmd/lxc_user_nic.$(OBJEXT): cmd/$(am__dirstamp) \ cmd/$(DEPDIR)/$(am__dirstamp) ../include/netns_ifaddrs.$(OBJEXT): ../include/$(am__dirstamp) \ ../include/$(DEPDIR)/$(am__dirstamp) lxc-user-nic$(EXEEXT): $(lxc_user_nic_OBJECTS) $(lxc_user_nic_DEPENDENCIES) $(EXTRA_lxc_user_nic_DEPENDENCIES) @rm -f lxc-user-nic$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_user_nic_OBJECTS) $(lxc_user_nic_LDADD) $(LIBS) cmd/lxc_usernsexec.$(OBJEXT): cmd/$(am__dirstamp) \ cmd/$(DEPDIR)/$(am__dirstamp) lxc-usernsexec$(EXEEXT): $(lxc_usernsexec_OBJECTS) $(lxc_usernsexec_DEPENDENCIES) $(EXTRA_lxc_usernsexec_DEPENDENCIES) @rm -f lxc-usernsexec$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_usernsexec_OBJECTS) $(lxc_usernsexec_LDADD) $(LIBS) tools/lxc_wait.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) lxc-wait$(EXEEXT): $(lxc_wait_OBJECTS) $(lxc_wait_DEPENDENCIES) $(EXTRA_lxc_wait_DEPENDENCIES) @rm -f lxc-wait$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lxc_wait_OBJECTS) $(lxc_wait_LDADD) $(LIBS) install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../include/*.$(OBJEXT) -rm -f ../include/*.lo -rm -f cgroups/*.$(OBJEXT) -rm -f cgroups/*.lo -rm -f cmd/*.$(OBJEXT) -rm -f lsm/*.$(OBJEXT) -rm -f lsm/*.lo -rm -f pam/*.$(OBJEXT) -rm -f pam/*.lo -rm -f storage/*.$(OBJEXT) -rm -f storage/*.lo -rm -f tools/*.$(OBJEXT) -rm -f tools/include/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-getline.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-strlcat.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/init_lxc_static-strlcpy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-getline.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-openpty.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-prlimit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-strlcat.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/liblxc_la-strlcpy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/netns_ifaddrs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/pam_cgfs_la-strlcat.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/af_unix.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-caps.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-error.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-file_utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-initutils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-namespace.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_lxc_static-string_utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initutils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-af_unix.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-attach.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-caps.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-commands_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-conf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-confile_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-criu.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-error.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-execute.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-file_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-freezer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-initutils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxccontainer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-lxclock.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-mainloop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-monitor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-namespace.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-network.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-nl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-parse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-raw_syscalls.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-ringbuf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-rtnl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-seccomp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-start.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-state.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-string_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-sync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-terminal.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblxc_la-utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainloop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_cgfs_la-file_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_cgfs_la-string_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw_syscalls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string_utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cmd/$(DEPDIR)/lxc_init.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cmd/$(DEPDIR)/lxc_monitord.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cmd/$(DEPDIR)/lxc_user_nic.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@cmd/$(DEPDIR)/lxc_usernsexec.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-apparmor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-lsm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-nop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@lsm/$(DEPDIR)/liblxc_la-selinux.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@pam/$(DEPDIR)/cgfs_la-pam_cgfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-btrfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-dir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-loop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-lvm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-nbd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-overlay.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rbd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-rsync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-storage_utils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/liblxc_la-zfs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@storage/$(DEPDIR)/storage_utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/arguments.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_attach.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_autostart.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_cgroup.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_checkpoint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_console.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_copy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_create.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_destroy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_device.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_execute.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_freeze.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_info.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_ls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_snapshot.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_start.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_stop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_top.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_unfreeze.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_unshare.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lxc_wait.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tools/include/$(DEPDIR)/getsubopt.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< liblxc_la-af_unix.lo: af_unix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-af_unix.lo -MD -MP -MF $(DEPDIR)/liblxc_la-af_unix.Tpo -c -o liblxc_la-af_unix.lo `test -f 'af_unix.c' || echo '$(srcdir)/'`af_unix.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-af_unix.Tpo $(DEPDIR)/liblxc_la-af_unix.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='af_unix.c' object='liblxc_la-af_unix.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-af_unix.lo `test -f 'af_unix.c' || echo '$(srcdir)/'`af_unix.c liblxc_la-attach.lo: attach.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-attach.lo -MD -MP -MF $(DEPDIR)/liblxc_la-attach.Tpo -c -o liblxc_la-attach.lo `test -f 'attach.c' || echo '$(srcdir)/'`attach.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-attach.Tpo $(DEPDIR)/liblxc_la-attach.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attach.c' object='liblxc_la-attach.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-attach.lo `test -f 'attach.c' || echo '$(srcdir)/'`attach.c liblxc_la-caps.lo: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-caps.lo -MD -MP -MF $(DEPDIR)/liblxc_la-caps.Tpo -c -o liblxc_la-caps.lo `test -f 'caps.c' || echo '$(srcdir)/'`caps.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-caps.Tpo $(DEPDIR)/liblxc_la-caps.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='liblxc_la-caps.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-caps.lo `test -f 'caps.c' || echo '$(srcdir)/'`caps.c cgroups/liblxc_la-cgfsng.lo: cgroups/cgfsng.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgfsng.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgfsng.Tpo -c -o cgroups/liblxc_la-cgfsng.lo `test -f 'cgroups/cgfsng.c' || echo '$(srcdir)/'`cgroups/cgfsng.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgfsng.Tpo cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgfsng.c' object='cgroups/liblxc_la-cgfsng.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgfsng.lo `test -f 'cgroups/cgfsng.c' || echo '$(srcdir)/'`cgroups/cgfsng.c cgroups/liblxc_la-cgroup.lo: cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT cgroups/liblxc_la-cgroup.lo -MD -MP -MF cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo -c -o cgroups/liblxc_la-cgroup.lo `test -f 'cgroups/cgroup.c' || echo '$(srcdir)/'`cgroups/cgroup.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cgroups/$(DEPDIR)/liblxc_la-cgroup.Tpo cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cgroups/cgroup.c' object='cgroups/liblxc_la-cgroup.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o cgroups/liblxc_la-cgroup.lo `test -f 'cgroups/cgroup.c' || echo '$(srcdir)/'`cgroups/cgroup.c 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 liblxc_la-commands.lo: commands.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-commands.lo -MD -MP -MF $(DEPDIR)/liblxc_la-commands.Tpo -c -o liblxc_la-commands.lo `test -f 'commands.c' || echo '$(srcdir)/'`commands.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-commands.Tpo $(DEPDIR)/liblxc_la-commands.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='liblxc_la-commands.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands.lo `test -f 'commands.c' || echo '$(srcdir)/'`commands.c liblxc_la-commands_utils.lo: commands_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-commands_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-commands_utils.Tpo -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-commands_utils.Tpo $(DEPDIR)/liblxc_la-commands_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands_utils.c' object='liblxc_la-commands_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-commands_utils.lo `test -f 'commands_utils.c' || echo '$(srcdir)/'`commands_utils.c liblxc_la-conf.lo: conf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-conf.lo -MD -MP -MF $(DEPDIR)/liblxc_la-conf.Tpo -c -o liblxc_la-conf.lo `test -f 'conf.c' || echo '$(srcdir)/'`conf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-conf.Tpo $(DEPDIR)/liblxc_la-conf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='liblxc_la-conf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-conf.lo `test -f 'conf.c' || echo '$(srcdir)/'`conf.c liblxc_la-confile.lo: confile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile.Tpo -c -o liblxc_la-confile.lo `test -f 'confile.c' || echo '$(srcdir)/'`confile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile.Tpo $(DEPDIR)/liblxc_la-confile.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile.c' object='liblxc_la-confile.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile.lo `test -f 'confile.c' || echo '$(srcdir)/'`confile.c liblxc_la-confile_utils.lo: confile_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-confile_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-confile_utils.Tpo -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-confile_utils.Tpo $(DEPDIR)/liblxc_la-confile_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='confile_utils.c' object='liblxc_la-confile_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-confile_utils.lo `test -f 'confile_utils.c' || echo '$(srcdir)/'`confile_utils.c liblxc_la-criu.lo: criu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-criu.lo -MD -MP -MF $(DEPDIR)/liblxc_la-criu.Tpo -c -o liblxc_la-criu.lo `test -f 'criu.c' || echo '$(srcdir)/'`criu.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-criu.Tpo $(DEPDIR)/liblxc_la-criu.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='criu.c' object='liblxc_la-criu.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-criu.lo `test -f 'criu.c' || echo '$(srcdir)/'`criu.c liblxc_la-error.lo: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-error.lo -MD -MP -MF $(DEPDIR)/liblxc_la-error.Tpo -c -o liblxc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-error.Tpo $(DEPDIR)/liblxc_la-error.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='liblxc_la-error.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c liblxc_la-execute.lo: execute.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-execute.lo -MD -MP -MF $(DEPDIR)/liblxc_la-execute.Tpo -c -o liblxc_la-execute.lo `test -f 'execute.c' || echo '$(srcdir)/'`execute.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-execute.Tpo $(DEPDIR)/liblxc_la-execute.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='execute.c' object='liblxc_la-execute.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-execute.lo `test -f 'execute.c' || echo '$(srcdir)/'`execute.c liblxc_la-freezer.lo: freezer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-freezer.lo -MD -MP -MF $(DEPDIR)/liblxc_la-freezer.Tpo -c -o liblxc_la-freezer.lo `test -f 'freezer.c' || echo '$(srcdir)/'`freezer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-freezer.Tpo $(DEPDIR)/liblxc_la-freezer.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='freezer.c' object='liblxc_la-freezer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-freezer.lo `test -f 'freezer.c' || echo '$(srcdir)/'`freezer.c liblxc_la-file_utils.lo: file_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-file_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-file_utils.Tpo -c -o liblxc_la-file_utils.lo `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-file_utils.Tpo $(DEPDIR)/liblxc_la-file_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file_utils.c' object='liblxc_la-file_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-file_utils.lo `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c ../include/liblxc_la-netns_ifaddrs.lo: ../include/netns_ifaddrs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-netns_ifaddrs.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Tpo -c -o ../include/liblxc_la-netns_ifaddrs.lo `test -f '../include/netns_ifaddrs.c' || echo '$(srcdir)/'`../include/netns_ifaddrs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Tpo ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/netns_ifaddrs.c' object='../include/liblxc_la-netns_ifaddrs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-netns_ifaddrs.lo `test -f '../include/netns_ifaddrs.c' || echo '$(srcdir)/'`../include/netns_ifaddrs.c liblxc_la-initutils.lo: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-initutils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-initutils.Tpo -c -o liblxc_la-initutils.lo `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-initutils.Tpo $(DEPDIR)/liblxc_la-initutils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='liblxc_la-initutils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-initutils.lo `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c liblxc_la-log.lo: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-log.lo -MD -MP -MF $(DEPDIR)/liblxc_la-log.Tpo -c -o liblxc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-log.Tpo $(DEPDIR)/liblxc_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='liblxc_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c liblxc_la-lxccontainer.lo: lxccontainer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxccontainer.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxccontainer.Tpo -c -o liblxc_la-lxccontainer.lo `test -f 'lxccontainer.c' || echo '$(srcdir)/'`lxccontainer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxccontainer.Tpo $(DEPDIR)/liblxc_la-lxccontainer.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxccontainer.c' object='liblxc_la-lxccontainer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxccontainer.lo `test -f 'lxccontainer.c' || echo '$(srcdir)/'`lxccontainer.c liblxc_la-lxclock.lo: lxclock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-lxclock.lo -MD -MP -MF $(DEPDIR)/liblxc_la-lxclock.Tpo -c -o liblxc_la-lxclock.lo `test -f 'lxclock.c' || echo '$(srcdir)/'`lxclock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-lxclock.Tpo $(DEPDIR)/liblxc_la-lxclock.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lxclock.c' object='liblxc_la-lxclock.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-lxclock.lo `test -f 'lxclock.c' || echo '$(srcdir)/'`lxclock.c liblxc_la-mainloop.lo: mainloop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-mainloop.lo -MD -MP -MF $(DEPDIR)/liblxc_la-mainloop.Tpo -c -o liblxc_la-mainloop.lo `test -f 'mainloop.c' || echo '$(srcdir)/'`mainloop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-mainloop.Tpo $(DEPDIR)/liblxc_la-mainloop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mainloop.c' object='liblxc_la-mainloop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-mainloop.lo `test -f 'mainloop.c' || echo '$(srcdir)/'`mainloop.c liblxc_la-namespace.lo: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-namespace.lo -MD -MP -MF $(DEPDIR)/liblxc_la-namespace.Tpo -c -o liblxc_la-namespace.lo `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-namespace.Tpo $(DEPDIR)/liblxc_la-namespace.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='liblxc_la-namespace.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-namespace.lo `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c liblxc_la-nl.lo: nl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-nl.lo -MD -MP -MF $(DEPDIR)/liblxc_la-nl.Tpo -c -o liblxc_la-nl.lo `test -f 'nl.c' || echo '$(srcdir)/'`nl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-nl.Tpo $(DEPDIR)/liblxc_la-nl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nl.c' object='liblxc_la-nl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-nl.lo `test -f 'nl.c' || echo '$(srcdir)/'`nl.c liblxc_la-network.lo: network.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-network.lo -MD -MP -MF $(DEPDIR)/liblxc_la-network.Tpo -c -o liblxc_la-network.lo `test -f 'network.c' || echo '$(srcdir)/'`network.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-network.Tpo $(DEPDIR)/liblxc_la-network.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network.c' object='liblxc_la-network.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-network.lo `test -f 'network.c' || echo '$(srcdir)/'`network.c liblxc_la-monitor.lo: monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-monitor.lo -MD -MP -MF $(DEPDIR)/liblxc_la-monitor.Tpo -c -o liblxc_la-monitor.lo `test -f 'monitor.c' || echo '$(srcdir)/'`monitor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-monitor.Tpo $(DEPDIR)/liblxc_la-monitor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='monitor.c' object='liblxc_la-monitor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-monitor.lo `test -f 'monitor.c' || echo '$(srcdir)/'`monitor.c liblxc_la-parse.lo: parse.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-parse.lo -MD -MP -MF $(DEPDIR)/liblxc_la-parse.Tpo -c -o liblxc_la-parse.lo `test -f 'parse.c' || echo '$(srcdir)/'`parse.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-parse.Tpo $(DEPDIR)/liblxc_la-parse.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse.c' object='liblxc_la-parse.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-parse.lo `test -f 'parse.c' || echo '$(srcdir)/'`parse.c liblxc_la-raw_syscalls.lo: raw_syscalls.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-raw_syscalls.lo -MD -MP -MF $(DEPDIR)/liblxc_la-raw_syscalls.Tpo -c -o liblxc_la-raw_syscalls.lo `test -f 'raw_syscalls.c' || echo '$(srcdir)/'`raw_syscalls.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-raw_syscalls.Tpo $(DEPDIR)/liblxc_la-raw_syscalls.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='raw_syscalls.c' object='liblxc_la-raw_syscalls.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-raw_syscalls.lo `test -f 'raw_syscalls.c' || echo '$(srcdir)/'`raw_syscalls.c liblxc_la-ringbuf.lo: ringbuf.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-ringbuf.lo -MD -MP -MF $(DEPDIR)/liblxc_la-ringbuf.Tpo -c -o liblxc_la-ringbuf.lo `test -f 'ringbuf.c' || echo '$(srcdir)/'`ringbuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-ringbuf.Tpo $(DEPDIR)/liblxc_la-ringbuf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ringbuf.c' object='liblxc_la-ringbuf.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-ringbuf.lo `test -f 'ringbuf.c' || echo '$(srcdir)/'`ringbuf.c liblxc_la-rtnl.lo: rtnl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-rtnl.lo -MD -MP -MF $(DEPDIR)/liblxc_la-rtnl.Tpo -c -o liblxc_la-rtnl.lo `test -f 'rtnl.c' || echo '$(srcdir)/'`rtnl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-rtnl.Tpo $(DEPDIR)/liblxc_la-rtnl.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtnl.c' object='liblxc_la-rtnl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-rtnl.lo `test -f 'rtnl.c' || echo '$(srcdir)/'`rtnl.c liblxc_la-state.lo: state.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-state.lo -MD -MP -MF $(DEPDIR)/liblxc_la-state.Tpo -c -o liblxc_la-state.lo `test -f 'state.c' || echo '$(srcdir)/'`state.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-state.Tpo $(DEPDIR)/liblxc_la-state.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='state.c' object='liblxc_la-state.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-state.lo `test -f 'state.c' || echo '$(srcdir)/'`state.c liblxc_la-start.lo: start.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-start.lo -MD -MP -MF $(DEPDIR)/liblxc_la-start.Tpo -c -o liblxc_la-start.lo `test -f 'start.c' || echo '$(srcdir)/'`start.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-start.Tpo $(DEPDIR)/liblxc_la-start.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='start.c' object='liblxc_la-start.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-start.lo `test -f 'start.c' || echo '$(srcdir)/'`start.c storage/liblxc_la-btrfs.lo: storage/btrfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-btrfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-btrfs.Tpo -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-btrfs.Tpo storage/$(DEPDIR)/liblxc_la-btrfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/btrfs.c' object='storage/liblxc_la-btrfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-btrfs.lo `test -f 'storage/btrfs.c' || echo '$(srcdir)/'`storage/btrfs.c storage/liblxc_la-dir.lo: storage/dir.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-dir.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-dir.Tpo -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-dir.Tpo storage/$(DEPDIR)/liblxc_la-dir.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/dir.c' object='storage/liblxc_la-dir.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-dir.lo `test -f 'storage/dir.c' || echo '$(srcdir)/'`storage/dir.c storage/liblxc_la-loop.lo: storage/loop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-loop.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-loop.Tpo -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-loop.Tpo storage/$(DEPDIR)/liblxc_la-loop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/loop.c' object='storage/liblxc_la-loop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-loop.lo `test -f 'storage/loop.c' || echo '$(srcdir)/'`storage/loop.c storage/liblxc_la-lvm.lo: storage/lvm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-lvm.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-lvm.Tpo -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-lvm.Tpo storage/$(DEPDIR)/liblxc_la-lvm.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/lvm.c' object='storage/liblxc_la-lvm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-lvm.lo `test -f 'storage/lvm.c' || echo '$(srcdir)/'`storage/lvm.c storage/liblxc_la-nbd.lo: storage/nbd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-nbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-nbd.Tpo -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-nbd.Tpo storage/$(DEPDIR)/liblxc_la-nbd.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/nbd.c' object='storage/liblxc_la-nbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-nbd.lo `test -f 'storage/nbd.c' || echo '$(srcdir)/'`storage/nbd.c storage/liblxc_la-overlay.lo: storage/overlay.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-overlay.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-overlay.Tpo -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-overlay.Tpo storage/$(DEPDIR)/liblxc_la-overlay.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/overlay.c' object='storage/liblxc_la-overlay.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-overlay.lo `test -f 'storage/overlay.c' || echo '$(srcdir)/'`storage/overlay.c storage/liblxc_la-rbd.lo: storage/rbd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rbd.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rbd.Tpo -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rbd.Tpo storage/$(DEPDIR)/liblxc_la-rbd.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rbd.c' object='storage/liblxc_la-rbd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rbd.lo `test -f 'storage/rbd.c' || echo '$(srcdir)/'`storage/rbd.c storage/liblxc_la-rsync.lo: storage/rsync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-rsync.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-rsync.Tpo -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-rsync.Tpo storage/$(DEPDIR)/liblxc_la-rsync.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/rsync.c' object='storage/liblxc_la-rsync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-rsync.lo `test -f 'storage/rsync.c' || echo '$(srcdir)/'`storage/rsync.c storage/liblxc_la-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-storage_utils.lo: storage/storage_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-storage_utils.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-storage_utils.Tpo storage/$(DEPDIR)/liblxc_la-storage_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/storage_utils.c' object='storage/liblxc_la-storage_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-storage_utils.lo `test -f 'storage/storage_utils.c' || echo '$(srcdir)/'`storage/storage_utils.c storage/liblxc_la-zfs.lo: storage/zfs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT storage/liblxc_la-zfs.lo -MD -MP -MF storage/$(DEPDIR)/liblxc_la-zfs.Tpo -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) storage/$(DEPDIR)/liblxc_la-zfs.Tpo storage/$(DEPDIR)/liblxc_la-zfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='storage/zfs.c' object='storage/liblxc_la-zfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o storage/liblxc_la-zfs.lo `test -f 'storage/zfs.c' || echo '$(srcdir)/'`storage/zfs.c liblxc_la-string_utils.lo: string_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-string_utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-string_utils.Tpo -c -o liblxc_la-string_utils.lo `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-string_utils.Tpo $(DEPDIR)/liblxc_la-string_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='string_utils.c' object='liblxc_la-string_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-string_utils.lo `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c liblxc_la-sync.lo: sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-sync.lo -MD -MP -MF $(DEPDIR)/liblxc_la-sync.Tpo -c -o liblxc_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-sync.Tpo $(DEPDIR)/liblxc_la-sync.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sync.c' object='liblxc_la-sync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c liblxc_la-terminal.lo: terminal.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-terminal.lo -MD -MP -MF $(DEPDIR)/liblxc_la-terminal.Tpo -c -o liblxc_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-terminal.Tpo $(DEPDIR)/liblxc_la-terminal.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='terminal.c' object='liblxc_la-terminal.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-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c liblxc_la-utils.lo: utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-utils.lo -MD -MP -MF $(DEPDIR)/liblxc_la-utils.Tpo -c -o liblxc_la-utils.lo `test -f 'utils.c' || echo '$(srcdir)/'`utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-utils.Tpo $(DEPDIR)/liblxc_la-utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='liblxc_la-utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-utils.lo `test -f 'utils.c' || echo '$(srcdir)/'`utils.c lsm/liblxc_la-lsm.lo: lsm/lsm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-lsm.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-lsm.Tpo -c -o lsm/liblxc_la-lsm.lo `test -f 'lsm/lsm.c' || echo '$(srcdir)/'`lsm/lsm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-lsm.Tpo lsm/$(DEPDIR)/liblxc_la-lsm.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/lsm.c' object='lsm/liblxc_la-lsm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-lsm.lo `test -f 'lsm/lsm.c' || echo '$(srcdir)/'`lsm/lsm.c lsm/liblxc_la-nop.lo: lsm/nop.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-nop.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-nop.Tpo -c -o lsm/liblxc_la-nop.lo `test -f 'lsm/nop.c' || echo '$(srcdir)/'`lsm/nop.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-nop.Tpo lsm/$(DEPDIR)/liblxc_la-nop.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/nop.c' object='lsm/liblxc_la-nop.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-nop.lo `test -f 'lsm/nop.c' || echo '$(srcdir)/'`lsm/nop.c lsm/liblxc_la-apparmor.lo: lsm/apparmor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-apparmor.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-apparmor.Tpo -c -o lsm/liblxc_la-apparmor.lo `test -f 'lsm/apparmor.c' || echo '$(srcdir)/'`lsm/apparmor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-apparmor.Tpo lsm/$(DEPDIR)/liblxc_la-apparmor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/apparmor.c' object='lsm/liblxc_la-apparmor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-apparmor.lo `test -f 'lsm/apparmor.c' || echo '$(srcdir)/'`lsm/apparmor.c lsm/liblxc_la-selinux.lo: lsm/selinux.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT lsm/liblxc_la-selinux.lo -MD -MP -MF lsm/$(DEPDIR)/liblxc_la-selinux.Tpo -c -o lsm/liblxc_la-selinux.lo `test -f 'lsm/selinux.c' || echo '$(srcdir)/'`lsm/selinux.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lsm/$(DEPDIR)/liblxc_la-selinux.Tpo lsm/$(DEPDIR)/liblxc_la-selinux.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lsm/selinux.c' object='lsm/liblxc_la-selinux.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o lsm/liblxc_la-selinux.lo `test -f 'lsm/selinux.c' || echo '$(srcdir)/'`lsm/selinux.c ../include/liblxc_la-lxcmntent.lo: ../include/lxcmntent.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-lxcmntent.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-lxcmntent.Tpo -c -o ../include/liblxc_la-lxcmntent.lo `test -f '../include/lxcmntent.c' || echo '$(srcdir)/'`../include/lxcmntent.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-lxcmntent.Tpo ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/lxcmntent.c' object='../include/liblxc_la-lxcmntent.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-lxcmntent.lo `test -f '../include/lxcmntent.c' || echo '$(srcdir)/'`../include/lxcmntent.c ../include/liblxc_la-openpty.lo: ../include/openpty.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-openpty.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-openpty.Tpo -c -o ../include/liblxc_la-openpty.lo `test -f '../include/openpty.c' || echo '$(srcdir)/'`../include/openpty.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-openpty.Tpo ../include/$(DEPDIR)/liblxc_la-openpty.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/openpty.c' object='../include/liblxc_la-openpty.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-openpty.lo `test -f '../include/openpty.c' || echo '$(srcdir)/'`../include/openpty.c ../include/liblxc_la-getgrgid_r.lo: ../include/getgrgid_r.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-getgrgid_r.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Tpo -c -o ../include/liblxc_la-getgrgid_r.lo `test -f '../include/getgrgid_r.c' || echo '$(srcdir)/'`../include/getgrgid_r.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Tpo ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getgrgid_r.c' object='../include/liblxc_la-getgrgid_r.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-getgrgid_r.lo `test -f '../include/getgrgid_r.c' || echo '$(srcdir)/'`../include/getgrgid_r.c ../include/liblxc_la-getline.lo: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-getline.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-getline.Tpo -c -o ../include/liblxc_la-getline.lo `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-getline.Tpo ../include/$(DEPDIR)/liblxc_la-getline.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/liblxc_la-getline.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-getline.lo `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c ../include/liblxc_la-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 liblxc_la-seccomp.lo: seccomp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT liblxc_la-seccomp.lo -MD -MP -MF $(DEPDIR)/liblxc_la-seccomp.Tpo -c -o liblxc_la-seccomp.lo `test -f 'seccomp.c' || echo '$(srcdir)/'`seccomp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblxc_la-seccomp.Tpo $(DEPDIR)/liblxc_la-seccomp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='seccomp.c' object='liblxc_la-seccomp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o liblxc_la-seccomp.lo `test -f 'seccomp.c' || echo '$(srcdir)/'`seccomp.c ../include/liblxc_la-strlcpy.lo: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-strlcpy.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-strlcpy.Tpo -c -o ../include/liblxc_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-strlcpy.Tpo ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/liblxc_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c ../include/liblxc_la-strlcat.lo: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -MT ../include/liblxc_la-strlcat.lo -MD -MP -MF ../include/$(DEPDIR)/liblxc_la-strlcat.Tpo -c -o ../include/liblxc_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/liblxc_la-strlcat.Tpo ../include/$(DEPDIR)/liblxc_la-strlcat.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/liblxc_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblxc_la_CFLAGS) $(CFLAGS) -c -o ../include/liblxc_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c pam/cgfs_la-pam_cgfs.lo: pam/pam_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) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -MT pam/cgfs_la-pam_cgfs.lo -MD -MP -MF pam/$(DEPDIR)/cgfs_la-pam_cgfs.Tpo -c -o pam/cgfs_la-pam_cgfs.lo `test -f 'pam/pam_cgfs.c' || echo '$(srcdir)/'`pam/pam_cgfs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pam/$(DEPDIR)/cgfs_la-pam_cgfs.Tpo pam/$(DEPDIR)/cgfs_la-pam_cgfs.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pam/pam_cgfs.c' object='pam/cgfs_la-pam_cgfs.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -c -o pam/cgfs_la-pam_cgfs.lo `test -f 'pam/pam_cgfs.c' || echo '$(srcdir)/'`pam/pam_cgfs.c pam_cgfs_la-file_utils.lo: file_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) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -MT pam_cgfs_la-file_utils.lo -MD -MP -MF $(DEPDIR)/pam_cgfs_la-file_utils.Tpo -c -o pam_cgfs_la-file_utils.lo `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/pam_cgfs_la-file_utils.Tpo $(DEPDIR)/pam_cgfs_la-file_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file_utils.c' object='pam_cgfs_la-file_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) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -c -o pam_cgfs_la-file_utils.lo `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c pam_cgfs_la-string_utils.lo: string_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) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -MT pam_cgfs_la-string_utils.lo -MD -MP -MF $(DEPDIR)/pam_cgfs_la-string_utils.Tpo -c -o pam_cgfs_la-string_utils.lo `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/pam_cgfs_la-string_utils.Tpo $(DEPDIR)/pam_cgfs_la-string_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='string_utils.c' object='pam_cgfs_la-string_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) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -c -o pam_cgfs_la-string_utils.lo `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c ../include/pam_cgfs_la-strlcat.lo: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -MT ../include/pam_cgfs_la-strlcat.lo -MD -MP -MF ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Tpo -c -o ../include/pam_cgfs_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Tpo ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/pam_cgfs_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -c -o ../include/pam_cgfs_la-strlcat.lo `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c ../include/pam_cgfs_la-strlcpy.lo: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -MT ../include/pam_cgfs_la-strlcpy.lo -MD -MP -MF ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Tpo -c -o ../include/pam_cgfs_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Tpo ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/pam_cgfs_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pam_cgfs_la_CFLAGS) $(CFLAGS) -c -o ../include/pam_cgfs_la-strlcpy.lo `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c cmd/init_lxc_static-lxc_init.o: cmd/lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT cmd/init_lxc_static-lxc_init.o -MD -MP -MF cmd/$(DEPDIR)/init_lxc_static-lxc_init.Tpo -c -o cmd/init_lxc_static-lxc_init.o `test -f 'cmd/lxc_init.c' || echo '$(srcdir)/'`cmd/lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cmd/$(DEPDIR)/init_lxc_static-lxc_init.Tpo cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd/lxc_init.c' object='cmd/init_lxc_static-lxc_init.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o cmd/init_lxc_static-lxc_init.o `test -f 'cmd/lxc_init.c' || echo '$(srcdir)/'`cmd/lxc_init.c cmd/init_lxc_static-lxc_init.obj: cmd/lxc_init.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT cmd/init_lxc_static-lxc_init.obj -MD -MP -MF cmd/$(DEPDIR)/init_lxc_static-lxc_init.Tpo -c -o cmd/init_lxc_static-lxc_init.obj `if test -f 'cmd/lxc_init.c'; then $(CYGPATH_W) 'cmd/lxc_init.c'; else $(CYGPATH_W) '$(srcdir)/cmd/lxc_init.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) cmd/$(DEPDIR)/init_lxc_static-lxc_init.Tpo cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd/lxc_init.c' object='cmd/init_lxc_static-lxc_init.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o cmd/init_lxc_static-lxc_init.obj `if test -f 'cmd/lxc_init.c'; then $(CYGPATH_W) 'cmd/lxc_init.c'; else $(CYGPATH_W) '$(srcdir)/cmd/lxc_init.c'; fi` init_lxc_static-caps.o: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-caps.o -MD -MP -MF $(DEPDIR)/init_lxc_static-caps.Tpo -c -o init_lxc_static-caps.o `test -f 'caps.c' || echo '$(srcdir)/'`caps.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-caps.Tpo $(DEPDIR)/init_lxc_static-caps.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='init_lxc_static-caps.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-caps.o `test -f 'caps.c' || echo '$(srcdir)/'`caps.c init_lxc_static-caps.obj: caps.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-caps.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-caps.Tpo -c -o init_lxc_static-caps.obj `if test -f 'caps.c'; then $(CYGPATH_W) 'caps.c'; else $(CYGPATH_W) '$(srcdir)/caps.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-caps.Tpo $(DEPDIR)/init_lxc_static-caps.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='caps.c' object='init_lxc_static-caps.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-caps.obj `if test -f 'caps.c'; then $(CYGPATH_W) 'caps.c'; else $(CYGPATH_W) '$(srcdir)/caps.c'; fi` init_lxc_static-error.o: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-error.o -MD -MP -MF $(DEPDIR)/init_lxc_static-error.Tpo -c -o init_lxc_static-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-error.Tpo $(DEPDIR)/init_lxc_static-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='init_lxc_static-error.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c init_lxc_static-error.obj: error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-error.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-error.Tpo -c -o init_lxc_static-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-error.Tpo $(DEPDIR)/init_lxc_static-error.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='init_lxc_static-error.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` init_lxc_static-initutils.o: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-initutils.o -MD -MP -MF $(DEPDIR)/init_lxc_static-initutils.Tpo -c -o init_lxc_static-initutils.o `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-initutils.Tpo $(DEPDIR)/init_lxc_static-initutils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='init_lxc_static-initutils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-initutils.o `test -f 'initutils.c' || echo '$(srcdir)/'`initutils.c init_lxc_static-initutils.obj: initutils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-initutils.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-initutils.Tpo -c -o init_lxc_static-initutils.obj `if test -f 'initutils.c'; then $(CYGPATH_W) 'initutils.c'; else $(CYGPATH_W) '$(srcdir)/initutils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-initutils.Tpo $(DEPDIR)/init_lxc_static-initutils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='initutils.c' object='init_lxc_static-initutils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-initutils.obj `if test -f 'initutils.c'; then $(CYGPATH_W) 'initutils.c'; else $(CYGPATH_W) '$(srcdir)/initutils.c'; fi` init_lxc_static-file_utils.o: file_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-file_utils.o -MD -MP -MF $(DEPDIR)/init_lxc_static-file_utils.Tpo -c -o init_lxc_static-file_utils.o `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-file_utils.Tpo $(DEPDIR)/init_lxc_static-file_utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file_utils.c' object='init_lxc_static-file_utils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-file_utils.o `test -f 'file_utils.c' || echo '$(srcdir)/'`file_utils.c init_lxc_static-file_utils.obj: file_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-file_utils.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-file_utils.Tpo -c -o init_lxc_static-file_utils.obj `if test -f 'file_utils.c'; then $(CYGPATH_W) 'file_utils.c'; else $(CYGPATH_W) '$(srcdir)/file_utils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-file_utils.Tpo $(DEPDIR)/init_lxc_static-file_utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file_utils.c' object='init_lxc_static-file_utils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-file_utils.obj `if test -f 'file_utils.c'; then $(CYGPATH_W) 'file_utils.c'; else $(CYGPATH_W) '$(srcdir)/file_utils.c'; fi` init_lxc_static-log.o: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-log.o -MD -MP -MF $(DEPDIR)/init_lxc_static-log.Tpo -c -o init_lxc_static-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-log.Tpo $(DEPDIR)/init_lxc_static-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='init_lxc_static-log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c init_lxc_static-log.obj: log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-log.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-log.Tpo -c -o init_lxc_static-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-log.Tpo $(DEPDIR)/init_lxc_static-log.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='init_lxc_static-log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` init_lxc_static-namespace.o: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-namespace.o -MD -MP -MF $(DEPDIR)/init_lxc_static-namespace.Tpo -c -o init_lxc_static-namespace.o `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-namespace.Tpo $(DEPDIR)/init_lxc_static-namespace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='init_lxc_static-namespace.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-namespace.o `test -f 'namespace.c' || echo '$(srcdir)/'`namespace.c init_lxc_static-namespace.obj: namespace.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-namespace.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-namespace.Tpo -c -o init_lxc_static-namespace.obj `if test -f 'namespace.c'; then $(CYGPATH_W) 'namespace.c'; else $(CYGPATH_W) '$(srcdir)/namespace.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-namespace.Tpo $(DEPDIR)/init_lxc_static-namespace.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namespace.c' object='init_lxc_static-namespace.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-namespace.obj `if test -f 'namespace.c'; then $(CYGPATH_W) 'namespace.c'; else $(CYGPATH_W) '$(srcdir)/namespace.c'; fi` init_lxc_static-string_utils.o: string_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-string_utils.o -MD -MP -MF $(DEPDIR)/init_lxc_static-string_utils.Tpo -c -o init_lxc_static-string_utils.o `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-string_utils.Tpo $(DEPDIR)/init_lxc_static-string_utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='string_utils.c' object='init_lxc_static-string_utils.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-string_utils.o `test -f 'string_utils.c' || echo '$(srcdir)/'`string_utils.c init_lxc_static-string_utils.obj: string_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT init_lxc_static-string_utils.obj -MD -MP -MF $(DEPDIR)/init_lxc_static-string_utils.Tpo -c -o init_lxc_static-string_utils.obj `if test -f 'string_utils.c'; then $(CYGPATH_W) 'string_utils.c'; else $(CYGPATH_W) '$(srcdir)/string_utils.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/init_lxc_static-string_utils.Tpo $(DEPDIR)/init_lxc_static-string_utils.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='string_utils.c' object='init_lxc_static-string_utils.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o init_lxc_static-string_utils.obj `if test -f 'string_utils.c'; then $(CYGPATH_W) 'string_utils.c'; else $(CYGPATH_W) '$(srcdir)/string_utils.c'; fi` ../include/init_lxc_static-getline.o: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-getline.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-getline.Tpo -c -o ../include/init_lxc_static-getline.o `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-getline.Tpo ../include/$(DEPDIR)/init_lxc_static-getline.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/init_lxc_static-getline.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-getline.o `test -f '../include/getline.c' || echo '$(srcdir)/'`../include/getline.c ../include/init_lxc_static-getline.obj: ../include/getline.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-getline.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-getline.Tpo -c -o ../include/init_lxc_static-getline.obj `if test -f '../include/getline.c'; then $(CYGPATH_W) '../include/getline.c'; else $(CYGPATH_W) '$(srcdir)/../include/getline.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-getline.Tpo ../include/$(DEPDIR)/init_lxc_static-getline.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/getline.c' object='../include/init_lxc_static-getline.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-getline.obj `if test -f '../include/getline.c'; then $(CYGPATH_W) '../include/getline.c'; else $(CYGPATH_W) '$(srcdir)/../include/getline.c'; fi` ../include/init_lxc_static-strlcpy.o: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcpy.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo -c -o ../include/init_lxc_static-strlcpy.o `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/init_lxc_static-strlcpy.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcpy.o `test -f '../include/strlcpy.c' || echo '$(srcdir)/'`../include/strlcpy.c ../include/init_lxc_static-strlcpy.obj: ../include/strlcpy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcpy.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo -c -o ../include/init_lxc_static-strlcpy.obj `if test -f '../include/strlcpy.c'; then $(CYGPATH_W) '../include/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcpy.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcpy.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcpy.c' object='../include/init_lxc_static-strlcpy.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcpy.obj `if test -f '../include/strlcpy.c'; then $(CYGPATH_W) '../include/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcpy.c'; fi` ../include/init_lxc_static-strlcat.o: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcat.o -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo -c -o ../include/init_lxc_static-strlcat.o `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/init_lxc_static-strlcat.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcat.o `test -f '../include/strlcat.c' || echo '$(srcdir)/'`../include/strlcat.c ../include/init_lxc_static-strlcat.obj: ../include/strlcat.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -MT ../include/init_lxc_static-strlcat.obj -MD -MP -MF ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo -c -o ../include/init_lxc_static-strlcat.obj `if test -f '../include/strlcat.c'; then $(CYGPATH_W) '../include/strlcat.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcat.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../include/$(DEPDIR)/init_lxc_static-strlcat.Tpo ../include/$(DEPDIR)/init_lxc_static-strlcat.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../include/strlcat.c' object='../include/init_lxc_static-strlcat.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(init_lxc_static_CFLAGS) $(CFLAGS) -c -o ../include/init_lxc_static-strlcat.obj `if test -f '../include/strlcat.c'; then $(CYGPATH_W) '../include/strlcat.c'; else $(CYGPATH_W) '$(srcdir)/../include/strlcat.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf ../include/.libs ../include/_libs -rm -rf cgroups/.libs cgroups/_libs -rm -rf lsm/.libs lsm/_libs -rm -rf pam/.libs pam/_libs -rm -rf storage/.libs storage/_libs install-pkgincludeHEADERS: $(pkginclude_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ done uninstall-pkgincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pamdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../include/$(DEPDIR)/$(am__dirstamp) -rm -f ../include/$(am__dirstamp) -rm -f cgroups/$(DEPDIR)/$(am__dirstamp) -rm -f cgroups/$(am__dirstamp) -rm -f cmd/$(DEPDIR)/$(am__dirstamp) -rm -f cmd/$(am__dirstamp) -rm -f lsm/$(DEPDIR)/$(am__dirstamp) -rm -f lsm/$(am__dirstamp) -rm -f pam/$(DEPDIR)/$(am__dirstamp) -rm -f pam/$(am__dirstamp) -rm -f storage/$(DEPDIR)/$(am__dirstamp) -rm -f storage/$(am__dirstamp) -rm -f tools/$(DEPDIR)/$(am__dirstamp) -rm -f tools/$(am__dirstamp) -rm -f tools/include/$(DEPDIR)/$(am__dirstamp) -rm -f tools/include/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @ENABLE_PAM_FALSE@install-data-hook: @HAVE_PAM_FALSE@install-data-hook: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-pamLTLIBRARIES clean-pkglibexecPROGRAMS \ clean-sbinPROGRAMS mostlyclean-am distclean: distclean-am -rm -f ../include/$(DEPDIR)/init_lxc_static-getline.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcat.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po -rm -f ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getline.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-openpty.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-prlimit.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo -rm -f ../include/$(DEPDIR)/netns_ifaddrs.Po -rm -f ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Plo -rm -f ./$(DEPDIR)/af_unix.Po -rm -f ./$(DEPDIR)/conf.Po -rm -f ./$(DEPDIR)/file_utils.Po -rm -f ./$(DEPDIR)/init_lxc_static-caps.Po -rm -f ./$(DEPDIR)/init_lxc_static-error.Po -rm -f ./$(DEPDIR)/init_lxc_static-file_utils.Po -rm -f ./$(DEPDIR)/init_lxc_static-initutils.Po -rm -f ./$(DEPDIR)/init_lxc_static-log.Po -rm -f ./$(DEPDIR)/init_lxc_static-namespace.Po -rm -f ./$(DEPDIR)/init_lxc_static-string_utils.Po -rm -f ./$(DEPDIR)/initutils.Po -rm -f ./$(DEPDIR)/liblxc_la-af_unix.Plo -rm -f ./$(DEPDIR)/liblxc_la-attach.Plo -rm -f ./$(DEPDIR)/liblxc_la-caps.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-conf.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-criu.Plo -rm -f ./$(DEPDIR)/liblxc_la-error.Plo -rm -f ./$(DEPDIR)/liblxc_la-execute.Plo -rm -f ./$(DEPDIR)/liblxc_la-file_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-freezer.Plo -rm -f ./$(DEPDIR)/liblxc_la-initutils.Plo -rm -f ./$(DEPDIR)/liblxc_la-log.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxccontainer.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxclock.Plo -rm -f ./$(DEPDIR)/liblxc_la-mainloop.Plo -rm -f ./$(DEPDIR)/liblxc_la-monitor.Plo -rm -f ./$(DEPDIR)/liblxc_la-namespace.Plo -rm -f ./$(DEPDIR)/liblxc_la-network.Plo -rm -f ./$(DEPDIR)/liblxc_la-nl.Plo -rm -f ./$(DEPDIR)/liblxc_la-parse.Plo -rm -f ./$(DEPDIR)/liblxc_la-raw_syscalls.Plo -rm -f ./$(DEPDIR)/liblxc_la-ringbuf.Plo -rm -f ./$(DEPDIR)/liblxc_la-rtnl.Plo -rm -f ./$(DEPDIR)/liblxc_la-seccomp.Plo -rm -f ./$(DEPDIR)/liblxc_la-start.Plo -rm -f ./$(DEPDIR)/liblxc_la-state.Plo -rm -f ./$(DEPDIR)/liblxc_la-string_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-sync.Plo -rm -f ./$(DEPDIR)/liblxc_la-terminal.Plo -rm -f ./$(DEPDIR)/liblxc_la-utils.Plo -rm -f ./$(DEPDIR)/log.Po -rm -f ./$(DEPDIR)/mainloop.Po -rm -f ./$(DEPDIR)/monitor.Po -rm -f ./$(DEPDIR)/network.Po -rm -f ./$(DEPDIR)/pam_cgfs_la-file_utils.Plo -rm -f ./$(DEPDIR)/pam_cgfs_la-string_utils.Plo -rm -f ./$(DEPDIR)/parse.Po -rm -f ./$(DEPDIR)/raw_syscalls.Po -rm -f ./$(DEPDIR)/string_utils.Po -rm -f ./$(DEPDIR)/utils.Po -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo -rm -f cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po -rm -f cmd/$(DEPDIR)/lxc_init.Po -rm -f cmd/$(DEPDIR)/lxc_monitord.Po -rm -f cmd/$(DEPDIR)/lxc_user_nic.Po -rm -f cmd/$(DEPDIR)/lxc_usernsexec.Po -rm -f lsm/$(DEPDIR)/liblxc_la-apparmor.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-lsm.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-nop.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-selinux.Plo -rm -f pam/$(DEPDIR)/cgfs_la-pam_cgfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-btrfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-dir.Plo -rm -f storage/$(DEPDIR)/liblxc_la-loop.Plo -rm -f storage/$(DEPDIR)/liblxc_la-lvm.Plo -rm -f storage/$(DEPDIR)/liblxc_la-nbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-overlay.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rsync.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage_utils.Plo -rm -f storage/$(DEPDIR)/liblxc_la-zfs.Plo -rm -f storage/$(DEPDIR)/storage_utils.Po -rm -f tools/$(DEPDIR)/arguments.Po -rm -f tools/$(DEPDIR)/lxc_attach.Po -rm -f tools/$(DEPDIR)/lxc_autostart.Po -rm -f tools/$(DEPDIR)/lxc_cgroup.Po -rm -f tools/$(DEPDIR)/lxc_checkpoint.Po -rm -f tools/$(DEPDIR)/lxc_config.Po -rm -f tools/$(DEPDIR)/lxc_console.Po -rm -f tools/$(DEPDIR)/lxc_copy.Po -rm -f tools/$(DEPDIR)/lxc_create.Po -rm -f tools/$(DEPDIR)/lxc_destroy.Po -rm -f tools/$(DEPDIR)/lxc_device.Po -rm -f tools/$(DEPDIR)/lxc_execute.Po -rm -f tools/$(DEPDIR)/lxc_freeze.Po -rm -f tools/$(DEPDIR)/lxc_info.Po -rm -f tools/$(DEPDIR)/lxc_ls.Po -rm -f tools/$(DEPDIR)/lxc_monitor.Po -rm -f tools/$(DEPDIR)/lxc_snapshot.Po -rm -f tools/$(DEPDIR)/lxc_start.Po -rm -f tools/$(DEPDIR)/lxc_stop.Po -rm -f tools/$(DEPDIR)/lxc_top.Po -rm -f tools/$(DEPDIR)/lxc_unfreeze.Po -rm -f tools/$(DEPDIR)/lxc_unshare.Po -rm -f tools/$(DEPDIR)/lxc_wait.Po -rm -f tools/include/$(DEPDIR)/getsubopt.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pamLTLIBRARIES install-pkgincludeHEADERS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-binSCRIPTS \ install-exec-local install-libLTLIBRARIES \ install-pkglibexecPROGRAMS install-sbinPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ../include/$(DEPDIR)/init_lxc_static-getline.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcat.Po -rm -f ../include/$(DEPDIR)/init_lxc_static-strlcpy.Po -rm -f ../include/$(DEPDIR)/liblxc_la-getgrgid_r.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-getline.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-lxcmntent.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-netns_ifaddrs.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-openpty.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-prlimit.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/liblxc_la-strlcpy.Plo -rm -f ../include/$(DEPDIR)/netns_ifaddrs.Po -rm -f ../include/$(DEPDIR)/pam_cgfs_la-strlcat.Plo -rm -f ../include/$(DEPDIR)/pam_cgfs_la-strlcpy.Plo -rm -f ./$(DEPDIR)/af_unix.Po -rm -f ./$(DEPDIR)/conf.Po -rm -f ./$(DEPDIR)/file_utils.Po -rm -f ./$(DEPDIR)/init_lxc_static-caps.Po -rm -f ./$(DEPDIR)/init_lxc_static-error.Po -rm -f ./$(DEPDIR)/init_lxc_static-file_utils.Po -rm -f ./$(DEPDIR)/init_lxc_static-initutils.Po -rm -f ./$(DEPDIR)/init_lxc_static-log.Po -rm -f ./$(DEPDIR)/init_lxc_static-namespace.Po -rm -f ./$(DEPDIR)/init_lxc_static-string_utils.Po -rm -f ./$(DEPDIR)/initutils.Po -rm -f ./$(DEPDIR)/liblxc_la-af_unix.Plo -rm -f ./$(DEPDIR)/liblxc_la-attach.Plo -rm -f ./$(DEPDIR)/liblxc_la-caps.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands.Plo -rm -f ./$(DEPDIR)/liblxc_la-commands_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-conf.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile.Plo -rm -f ./$(DEPDIR)/liblxc_la-confile_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-criu.Plo -rm -f ./$(DEPDIR)/liblxc_la-error.Plo -rm -f ./$(DEPDIR)/liblxc_la-execute.Plo -rm -f ./$(DEPDIR)/liblxc_la-file_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-freezer.Plo -rm -f ./$(DEPDIR)/liblxc_la-initutils.Plo -rm -f ./$(DEPDIR)/liblxc_la-log.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxccontainer.Plo -rm -f ./$(DEPDIR)/liblxc_la-lxclock.Plo -rm -f ./$(DEPDIR)/liblxc_la-mainloop.Plo -rm -f ./$(DEPDIR)/liblxc_la-monitor.Plo -rm -f ./$(DEPDIR)/liblxc_la-namespace.Plo -rm -f ./$(DEPDIR)/liblxc_la-network.Plo -rm -f ./$(DEPDIR)/liblxc_la-nl.Plo -rm -f ./$(DEPDIR)/liblxc_la-parse.Plo -rm -f ./$(DEPDIR)/liblxc_la-raw_syscalls.Plo -rm -f ./$(DEPDIR)/liblxc_la-ringbuf.Plo -rm -f ./$(DEPDIR)/liblxc_la-rtnl.Plo -rm -f ./$(DEPDIR)/liblxc_la-seccomp.Plo -rm -f ./$(DEPDIR)/liblxc_la-start.Plo -rm -f ./$(DEPDIR)/liblxc_la-state.Plo -rm -f ./$(DEPDIR)/liblxc_la-string_utils.Plo -rm -f ./$(DEPDIR)/liblxc_la-sync.Plo -rm -f ./$(DEPDIR)/liblxc_la-terminal.Plo -rm -f ./$(DEPDIR)/liblxc_la-utils.Plo -rm -f ./$(DEPDIR)/log.Po -rm -f ./$(DEPDIR)/mainloop.Po -rm -f ./$(DEPDIR)/monitor.Po -rm -f ./$(DEPDIR)/network.Po -rm -f ./$(DEPDIR)/pam_cgfs_la-file_utils.Plo -rm -f ./$(DEPDIR)/pam_cgfs_la-string_utils.Plo -rm -f ./$(DEPDIR)/parse.Po -rm -f ./$(DEPDIR)/raw_syscalls.Po -rm -f ./$(DEPDIR)/string_utils.Po -rm -f ./$(DEPDIR)/utils.Po -rm -f cgroups/$(DEPDIR)/liblxc_la-cgfsng.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup.Plo -rm -f cgroups/$(DEPDIR)/liblxc_la-cgroup_utils.Plo -rm -f cmd/$(DEPDIR)/init_lxc_static-lxc_init.Po -rm -f cmd/$(DEPDIR)/lxc_init.Po -rm -f cmd/$(DEPDIR)/lxc_monitord.Po -rm -f cmd/$(DEPDIR)/lxc_user_nic.Po -rm -f cmd/$(DEPDIR)/lxc_usernsexec.Po -rm -f lsm/$(DEPDIR)/liblxc_la-apparmor.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-lsm.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-nop.Plo -rm -f lsm/$(DEPDIR)/liblxc_la-selinux.Plo -rm -f pam/$(DEPDIR)/cgfs_la-pam_cgfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-btrfs.Plo -rm -f storage/$(DEPDIR)/liblxc_la-dir.Plo -rm -f storage/$(DEPDIR)/liblxc_la-loop.Plo -rm -f storage/$(DEPDIR)/liblxc_la-lvm.Plo -rm -f storage/$(DEPDIR)/liblxc_la-nbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-overlay.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rbd.Plo -rm -f storage/$(DEPDIR)/liblxc_la-rsync.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage.Plo -rm -f storage/$(DEPDIR)/liblxc_la-storage_utils.Plo -rm -f storage/$(DEPDIR)/liblxc_la-zfs.Plo -rm -f storage/$(DEPDIR)/storage_utils.Po -rm -f tools/$(DEPDIR)/arguments.Po -rm -f tools/$(DEPDIR)/lxc_attach.Po -rm -f tools/$(DEPDIR)/lxc_autostart.Po -rm -f tools/$(DEPDIR)/lxc_cgroup.Po -rm -f tools/$(DEPDIR)/lxc_checkpoint.Po -rm -f tools/$(DEPDIR)/lxc_config.Po -rm -f tools/$(DEPDIR)/lxc_console.Po -rm -f tools/$(DEPDIR)/lxc_copy.Po -rm -f tools/$(DEPDIR)/lxc_create.Po -rm -f tools/$(DEPDIR)/lxc_destroy.Po -rm -f tools/$(DEPDIR)/lxc_device.Po -rm -f tools/$(DEPDIR)/lxc_execute.Po -rm -f tools/$(DEPDIR)/lxc_freeze.Po -rm -f tools/$(DEPDIR)/lxc_info.Po -rm -f tools/$(DEPDIR)/lxc_ls.Po -rm -f tools/$(DEPDIR)/lxc_monitor.Po -rm -f tools/$(DEPDIR)/lxc_snapshot.Po -rm -f tools/$(DEPDIR)/lxc_start.Po -rm -f tools/$(DEPDIR)/lxc_stop.Po -rm -f tools/$(DEPDIR)/lxc_top.Po -rm -f tools/$(DEPDIR)/lxc_unfreeze.Po -rm -f tools/$(DEPDIR)/lxc_unshare.Po -rm -f tools/$(DEPDIR)/lxc_wait.Po -rm -f tools/include/$(DEPDIR)/getsubopt.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-libLTLIBRARIES uninstall-local \ uninstall-pamLTLIBRARIES uninstall-pkgincludeHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-sbinPROGRAMS .MAKE: install-am install-data-am install-exec-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-pamLTLIBRARIES clean-pkglibexecPROGRAMS \ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-binSCRIPTS \ install-data install-data-am install-data-hook install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-exec-local install-html install-html-am install-info \ install-info-am install-libLTLIBRARIES install-man \ install-pamLTLIBRARIES install-pdf install-pdf-am \ install-pkgincludeHEADERS install-pkglibexecPROGRAMS \ install-ps install-ps-am install-sbinPROGRAMS install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-binSCRIPTS uninstall-libLTLIBRARIES uninstall-local \ uninstall-pamLTLIBRARIES uninstall-pkgincludeHEADERS \ uninstall-pkglibexecPROGRAMS uninstall-sbinPROGRAMS .PRECIOUS: Makefile install-exec-local: install-libLTLIBRARIES mkdir -p $(DESTDIR)$(datadir)/lxc install -c -m 644 lxc.functions $(DESTDIR)$(datadir)/lxc mv $(shell readlink -f $(DESTDIR)$(libdir)/liblxc.so) $(DESTDIR)$(libdir)/liblxc.so.@LXC_ABI@ rm -f $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.1 cd $(DESTDIR)$(libdir); \ ln -sf liblxc.so.@LXC_ABI@ liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)); \ ln -sf liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) liblxc.so install-exec-hook: chmod u+s $(DESTDIR)$(libexecdir)/lxc/lxc-user-nic uninstall-local: $(RM) $(DESTDIR)$(libdir)/liblxc.so* $(RM) $(DESTDIR)$(libdir)/liblxc.a @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(RM) $(DESTDIR)$(pamdir)/pam_cgfs.so* @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@install-data-hook: install-pamLTLIBRARIES @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.la" @ENABLE_PAM_TRUE@@HAVE_PAM_TRUE@ $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.a" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: lxc-3.0.3/src/lxc/initutils.c0000644061062106075000000001746713375633353013033 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include "compiler.h" #include "config.h" #include "file_utils.h" #include "initutils.h" #include "log.h" #include "macro.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(initutils, lxc); static char *copy_global_config_value(char *p) { int len = strlen(p); char *retbuf; if (len < 1) return NULL; if (p[len-1] == '\n') { p[len-1] = '\0'; len--; } retbuf = malloc(len + 1); if (!retbuf) return NULL; (void)strlcpy(retbuf, p, len + 1); return retbuf; } const char *lxc_global_config_value(const char *option_name) { static const char * const options[][2] = { { "lxc.bdev.lvm.vg", DEFAULT_VG }, { "lxc.bdev.lvm.thin_pool", DEFAULT_THIN_POOL }, { "lxc.bdev.zfs.root", DEFAULT_ZFSROOT }, { "lxc.bdev.rbd.rbdpool", DEFAULT_RBDPOOL }, { "lxc.lxcpath", NULL }, { "lxc.default_config", NULL }, { "lxc.cgroup.pattern", NULL }, { "lxc.cgroup.use", NULL }, { NULL, NULL }, }; /* placed in the thread local storage pool for non-bionic targets */ #ifdef HAVE_TLS static thread_local const char *values[sizeof(options) / sizeof(options[0])] = {0}; #else static const char *values[sizeof(options) / sizeof(options[0])] = {0}; #endif /* user_config_path is freed as soon as it is used */ char *user_config_path = NULL; /* * The following variables are freed at bottom unconditionally. * So NULL the value if it is to be returned to the caller */ char *user_default_config_path = NULL; char *user_lxc_path = NULL; char *user_cgroup_pattern = NULL; if (geteuid() > 0) { const char *user_home = getenv("HOME"); if (!user_home) user_home = "/"; user_config_path = malloc(sizeof(char) * (22 + strlen(user_home))); user_default_config_path = malloc(sizeof(char) * (26 + strlen(user_home))); user_lxc_path = malloc(sizeof(char) * (19 + strlen(user_home))); sprintf(user_config_path, "%s/.config/lxc/lxc.conf", user_home); sprintf(user_default_config_path, "%s/.config/lxc/default.conf", user_home); sprintf(user_lxc_path, "%s/.local/share/lxc/", user_home); user_cgroup_pattern = strdup("lxc/%n"); } else { user_config_path = strdup(LXC_GLOBAL_CONF); user_default_config_path = strdup(LXC_DEFAULT_CONFIG); user_lxc_path = strdup(LXCPATH); user_cgroup_pattern = strdup(DEFAULT_CGROUP_PATTERN); } const char * const (*ptr)[2]; size_t i; char buf[1024], *p, *p2; FILE *fin = NULL; for (i = 0, ptr = options; (*ptr)[0]; ptr++, i++) { if (!strcmp(option_name, (*ptr)[0])) break; } if (!(*ptr)[0]) { free(user_config_path); free(user_default_config_path); free(user_lxc_path); free(user_cgroup_pattern); errno = EINVAL; return NULL; } if (values[i]) { free(user_config_path); free(user_default_config_path); free(user_lxc_path); free(user_cgroup_pattern); return values[i]; } fin = fopen_cloexec(user_config_path, "r"); free(user_config_path); if (fin) { while (fgets(buf, 1024, fin)) { if (buf[0] == '#') continue; p = strstr(buf, option_name); if (!p) continue; /* see if there was just white space in front * of the option name */ for (p2 = buf; p2 < p; p2++) { if (*p2 != ' ' && *p2 != '\t') break; } if (p2 < p) continue; p = strchr(p, '='); if (!p) continue; /* see if there was just white space after * the option name */ for (p2 += strlen(option_name); p2 < p; p2++) { if (*p2 != ' ' && *p2 != '\t') break; } if (p2 < p) continue; p++; while (*p && (*p == ' ' || *p == '\t')) p++; if (!*p) continue; if (strcmp(option_name, "lxc.lxcpath") == 0) { free(user_lxc_path); user_lxc_path = copy_global_config_value(p); remove_trailing_slashes(user_lxc_path); values[i] = user_lxc_path; user_lxc_path = NULL; goto out; } values[i] = copy_global_config_value(p); goto out; } } /* could not find value, use default */ if (strcmp(option_name, "lxc.lxcpath") == 0) { remove_trailing_slashes(user_lxc_path); values[i] = user_lxc_path; user_lxc_path = NULL; } else if (strcmp(option_name, "lxc.default_config") == 0) { values[i] = user_default_config_path; user_default_config_path = NULL; } else if (strcmp(option_name, "lxc.cgroup.pattern") == 0) { values[i] = user_cgroup_pattern; user_cgroup_pattern = NULL; } else values[i] = (*ptr)[1]; /* special case: if default value is NULL, * and there is no config, don't view that * as an error... */ if (!values[i]) errno = 0; out: if (fin) fclose(fin); free(user_cgroup_pattern); free(user_default_config_path); free(user_lxc_path); return values[i]; } /* * Sets the process title to the specified title. Note that this may fail if * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). */ int setproctitle(char *title) { static char *proctitle = NULL; char buf[2048], *tmp; FILE *f; int i, len, ret = 0; /* We don't really need to know all of this stuff, but unfortunately * PR_SET_MM_MAP requires us to set it all at once, so we have to * figure it out anyway. */ unsigned long start_data, end_data, start_brk, start_code, end_code, start_stack, arg_start, arg_end, env_start, env_end, brk_val; struct prctl_mm_map prctl_map; f = fopen_cloexec("/proc/self/stat", "r"); if (!f) { return -1; } tmp = fgets(buf, sizeof(buf), f); fclose(f); if (!tmp) { return -1; } /* Skip the first 25 fields, column 26-28 are start_code, end_code, * and start_stack */ tmp = strchr(buf, ' '); for (i = 0; i < 24; i++) { if (!tmp) return -1; tmp = strchr(tmp+1, ' '); } if (!tmp) return -1; i = sscanf(tmp, "%lu %lu %lu", &start_code, &end_code, &start_stack); if (i != 3) return -1; /* Skip the next 19 fields, column 45-51 are start_data to arg_end */ for (i = 0; i < 19; i++) { if (!tmp) return -1; tmp = strchr(tmp+1, ' '); } if (!tmp) return -1; i = sscanf(tmp, "%lu %lu %lu %*u %*u %lu %lu", &start_data, &end_data, &start_brk, &env_start, &env_end); if (i != 5) return -1; /* Include the null byte here, because in the calculations below we * want to have room for it. */ len = strlen(title) + 1; proctitle = realloc(proctitle, len); if (!proctitle) return -1; arg_start = (unsigned long) proctitle; arg_end = arg_start + len; brk_val = syscall(__NR_brk, 0); prctl_map = (struct prctl_mm_map) { .start_code = start_code, .end_code = end_code, .start_stack = start_stack, .start_data = start_data, .end_data = end_data, .start_brk = start_brk, .brk = brk_val, .arg_start = arg_start, .arg_end = arg_end, .env_start = env_start, .env_end = env_end, .auxv = NULL, .auxv_size = 0, .exe_fd = -1, }; ret = prctl(PR_SET_MM, prctl_arg(PR_SET_MM_MAP), prctl_arg(&prctl_map), prctl_arg(sizeof(prctl_map)), prctl_arg(0)); if (ret == 0) (void)strlcpy((char*)arg_start, title, len); else SYSWARN("Failed to set cmdline"); return ret; } lxc-3.0.3/src/lxc/file_utils.h0000644061062106075000000000457413375633353013146 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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_FILE_UTILS_H #define __LXC_FILE_UTILS_H #include #include #include #include #include #include #include /* read and write whole files */ extern int lxc_write_to_file(const char *filename, const void *buf, size_t count, bool add_newline, mode_t mode); extern int lxc_read_from_file(const char *filename, void *buf, size_t count); /* send and receive buffers completely */ extern ssize_t lxc_write_nointr(int fd, const void *buf, size_t count); extern ssize_t lxc_send_nointr(int sockfd, void *buf, size_t len, int flags); 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_recv_nointr(int sockfd, void *buf, size_t len, int flags); extern bool file_exists(const char *f); extern int print_to_file(const char *file, const char *content); extern int is_dir(const char *path); extern int lxc_count_file_lines(const char *fn); extern int lxc_make_tmpfile(char *template, bool rm); /* __typeof__ should be safe to use with all compilers. */ typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic; extern bool has_fs_type(const char *path, fs_type_magic magic_val); extern bool fhas_fs_type(int fd, fs_type_magic magic_val); extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val); extern FILE *fopen_cloexec(const char *path, const char *mode); extern ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count); #endif /* __LXC_FILE_UTILS_H */ lxc-3.0.3/src/lxc/confile.c0000644061062106075000000036114313375633353012417 00000000000000/* * lxc: linux Container library * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * Serge Hallyn * Christian Brauner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "../include/netns_ifaddrs.h" #include "log.h" #include "lxcseccomp.h" #include "network.h" #include "parse.h" #include "storage.h" #include "utils.h" #if HAVE_SYS_PERSONALITY_H #include #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef HAVE_STRLCAT #include "include/strlcat.h" #endif lxc_log_define(confile, lxc); #define lxc_config_define(name) \ __hot static int set_config_##name(const char *, const char *, \ struct lxc_conf *, void *); \ __hot static int get_config_##name(const char *, char *, int, \ struct lxc_conf *, void *); \ __hot static int clr_config_##name(const char *, struct lxc_conf *, \ void *); lxc_config_define(autodev); lxc_config_define(apparmor_allow_incomplete); lxc_config_define(apparmor_profile); lxc_config_define(cap_drop); lxc_config_define(cap_keep); lxc_config_define(cgroup_controller); lxc_config_define(cgroup2_controller); lxc_config_define(cgroup_dir); lxc_config_define(console_buffer_size); lxc_config_define(console_logfile); lxc_config_define(console_path); lxc_config_define(console_rotate); lxc_config_define(console_size); lxc_config_define(environment); lxc_config_define(ephemeral); lxc_config_define(execute_cmd); lxc_config_define(group); lxc_config_define(hooks); lxc_config_define(hooks_version); lxc_config_define(idmaps); lxc_config_define(includefiles); lxc_config_define(init_cmd); lxc_config_define(init_cwd); lxc_config_define(init_gid); lxc_config_define(init_uid); lxc_config_define(log_file); lxc_config_define(log_level); lxc_config_define(log_syslog); lxc_config_define(monitor); lxc_config_define(mount); lxc_config_define(mount_auto); lxc_config_define(mount_fstab); lxc_config_define(namespace_clone); lxc_config_define(namespace_keep); lxc_config_define(namespace_share); lxc_config_define(net); lxc_config_define(net_flags); lxc_config_define(net_hwaddr); lxc_config_define(net_ipv4_address); lxc_config_define(net_ipv4_gateway); lxc_config_define(net_ipv6_address); lxc_config_define(net_ipv6_gateway); lxc_config_define(net_link); lxc_config_define(net_macvlan_mode); lxc_config_define(net_mtu); lxc_config_define(net_name); lxc_config_define(net_nic); lxc_config_define(net_script_down); lxc_config_define(net_script_up); lxc_config_define(net_type); lxc_config_define(net_veth_pair); lxc_config_define(net_vlan_id); lxc_config_define(no_new_privs); lxc_config_define(personality); lxc_config_define(prlimit); lxc_config_define(pty_max); lxc_config_define(rootfs_mount); lxc_config_define(rootfs_options); lxc_config_define(rootfs_path); lxc_config_define(seccomp_profile); lxc_config_define(selinux_context); lxc_config_define(signal_halt); lxc_config_define(signal_reboot); lxc_config_define(signal_stop); lxc_config_define(start); lxc_config_define(tty_max); lxc_config_define(tty_dir); lxc_config_define(uts_name); lxc_config_define(sysctl); lxc_config_define(proc); static struct lxc_config_t config_jump_table[] = { { "lxc.arch", set_config_personality, get_config_personality, clr_config_personality, }, { "lxc.apparmor.profile", set_config_apparmor_profile, get_config_apparmor_profile, clr_config_apparmor_profile, }, { "lxc.apparmor.allow_incomplete", set_config_apparmor_allow_incomplete, get_config_apparmor_allow_incomplete, clr_config_apparmor_allow_incomplete, }, { "lxc.autodev", set_config_autodev, get_config_autodev, clr_config_autodev, }, { "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, }, { "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, }, { "lxc.cgroup2", set_config_cgroup2_controller, get_config_cgroup2_controller, clr_config_cgroup2_controller, }, { "lxc.cgroup.dir", set_config_cgroup_dir, get_config_cgroup_dir, clr_config_cgroup_dir, }, { "lxc.cgroup", set_config_cgroup_controller, get_config_cgroup_controller, clr_config_cgroup_controller, }, { "lxc.console.buffer.size", set_config_console_buffer_size, get_config_console_buffer_size, clr_config_console_buffer_size, }, { "lxc.console.logfile", set_config_console_logfile, get_config_console_logfile, clr_config_console_logfile, }, { "lxc.console.path", set_config_console_path, get_config_console_path, clr_config_console_path, }, { "lxc.console.rotate", set_config_console_rotate, get_config_console_rotate, clr_config_console_rotate, }, { "lxc.console.size", set_config_console_size, get_config_console_size, clr_config_console_size, }, { "lxc.environment", set_config_environment, get_config_environment, clr_config_environment, }, { "lxc.ephemeral", set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, }, { "lxc.execute.cmd", set_config_execute_cmd, get_config_execute_cmd, clr_config_execute_cmd, }, { "lxc.group", set_config_group, get_config_group, clr_config_group, }, { "lxc.hook.autodev", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.clone", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.destroy", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.mount", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.post-stop", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.pre-mount", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.pre-start", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.start", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.start-host", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.stop", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.version", set_config_hooks_version, get_config_hooks_version, clr_config_hooks_version, }, { "lxc.hook", set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.idmap", set_config_idmaps, get_config_idmaps, clr_config_idmaps, }, { "lxc.include", set_config_includefiles, get_config_includefiles, clr_config_includefiles, }, { "lxc.init.cmd", set_config_init_cmd, get_config_init_cmd, clr_config_init_cmd, }, { "lxc.init.gid", set_config_init_gid, get_config_init_gid, clr_config_init_gid, }, { "lxc.init.uid", set_config_init_uid, get_config_init_uid, clr_config_init_uid, }, { "lxc.init.cwd", set_config_init_cwd, get_config_init_cwd, clr_config_init_cwd, }, { "lxc.log.file", set_config_log_file, get_config_log_file, clr_config_log_file, }, { "lxc.log.level", set_config_log_level, get_config_log_level, clr_config_log_level, }, { "lxc.log.syslog", set_config_log_syslog, get_config_log_syslog, clr_config_log_syslog, }, { "lxc.monitor.unshare", set_config_monitor, get_config_monitor, clr_config_monitor, }, { "lxc.mount.auto", set_config_mount_auto, get_config_mount_auto, clr_config_mount_auto, }, { "lxc.mount.entry", set_config_mount, get_config_mount, clr_config_mount, }, { "lxc.mount.fstab", set_config_mount_fstab, get_config_mount_fstab, clr_config_mount_fstab, }, { "lxc.namespace.clone", set_config_namespace_clone, get_config_namespace_clone, clr_config_namespace_clone, }, { "lxc.namespace.keep", set_config_namespace_keep, get_config_namespace_keep, clr_config_namespace_keep, }, { "lxc.namespace.share", set_config_namespace_share, get_config_namespace_share, clr_config_namespace_share, }, { "lxc.net.flags", set_config_net_flags, get_config_net_flags, clr_config_net_flags, }, { "lxc.net.hwaddr", set_config_net_hwaddr, get_config_net_hwaddr, clr_config_net_hwaddr, }, { "lxc.net.ipv4.address", set_config_net_ipv4_address, get_config_net_ipv4_address, clr_config_net_ipv4_address, }, { "lxc.net.ipv4.gateway", set_config_net_ipv4_gateway, get_config_net_ipv4_gateway, clr_config_net_ipv4_gateway, }, { "lxc.net.ipv6.address", set_config_net_ipv6_address, get_config_net_ipv6_address, clr_config_net_ipv6_address, }, { "lxc.net.ipv6.gateway", set_config_net_ipv6_gateway, get_config_net_ipv6_gateway, clr_config_net_ipv6_gateway, }, { "lxc.net.link", set_config_net_link, get_config_net_link, clr_config_net_link, }, { "lxc.net.macvlan.mode", set_config_net_macvlan_mode, get_config_net_macvlan_mode, clr_config_net_macvlan_mode, }, { "lxc.net.mtu", set_config_net_mtu, get_config_net_mtu, clr_config_net_mtu, }, { "lxc.net.name", set_config_net_name, get_config_net_name, clr_config_net_name, }, { "lxc.net.script.down", set_config_net_script_down, get_config_net_script_down, clr_config_net_script_down, }, { "lxc.net.script.up", set_config_net_script_up, get_config_net_script_up, clr_config_net_script_up, }, { "lxc.net.type", set_config_net_type, get_config_net_type, clr_config_net_type, }, { "lxc.net.vlan.id", set_config_net_vlan_id, get_config_net_vlan_id, clr_config_net_vlan_id, }, { "lxc.net.veth.pair", set_config_net_veth_pair, get_config_net_veth_pair, clr_config_net_veth_pair, }, { "lxc.net.", set_config_net_nic, get_config_net_nic, clr_config_net_nic, }, { "lxc.net", set_config_net, get_config_net, clr_config_net, }, { "lxc.no_new_privs", set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, }, { "lxc.prlimit", set_config_prlimit, get_config_prlimit, clr_config_prlimit, }, { "lxc.pty.max", set_config_pty_max, get_config_pty_max, clr_config_pty_max, }, { "lxc.rootfs.mount", set_config_rootfs_mount, get_config_rootfs_mount, clr_config_rootfs_mount, }, { "lxc.rootfs.options", set_config_rootfs_options, get_config_rootfs_options, clr_config_rootfs_options, }, { "lxc.rootfs.path", set_config_rootfs_path, get_config_rootfs_path, clr_config_rootfs_path, }, { "lxc.seccomp.profile", set_config_seccomp_profile, get_config_seccomp_profile, clr_config_seccomp_profile, }, { "lxc.selinux.context", set_config_selinux_context, get_config_selinux_context, clr_config_selinux_context, }, { "lxc.signal.halt", set_config_signal_halt, get_config_signal_halt, clr_config_signal_halt, }, { "lxc.signal.reboot", set_config_signal_reboot, get_config_signal_reboot, clr_config_signal_reboot, }, { "lxc.signal.stop", set_config_signal_stop, get_config_signal_stop, clr_config_signal_stop, }, { "lxc.start.auto", set_config_start, get_config_start, clr_config_start, }, { "lxc.start.delay", set_config_start, get_config_start, clr_config_start, }, { "lxc.start.order", set_config_start, get_config_start, clr_config_start, }, { "lxc.tty.dir", set_config_tty_dir, get_config_tty_dir, clr_config_tty_dir, }, { "lxc.tty.max", set_config_tty_max, get_config_tty_max, clr_config_tty_max, }, { "lxc.uts.name", set_config_uts_name, get_config_uts_name, clr_config_uts_name, }, { "lxc.sysctl", set_config_sysctl, get_config_sysctl, clr_config_sysctl, }, { "lxc.proc", set_config_proc, get_config_proc, clr_config_proc, }, }; static const size_t config_jump_table_size = sizeof(config_jump_table) / sizeof(struct lxc_config_t); struct lxc_config_t *lxc_get_config(const char *key) { size_t i; for (i = 0; i < config_jump_table_size; i++) if (!strncmp(config_jump_table[i].name, key, strlen(config_jump_table[i].name))) return &config_jump_table[i]; return NULL; } static int set_config_net(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (!lxc_config_value_empty(value)) { ERROR("lxc.net must not have a value"); return -1; } return clr_config_net(key, lxc_conf, data); } static int set_config_net_type(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_type(key, lxc_conf, data); if (!netdev) return -1; 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; } static int set_config_net_flags(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_flags(key, lxc_conf, data); 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 netns_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 (netns_getifaddrs(&ifaddr, -1, &(bool){false}) < 0) { SYSERROR("Failed to get network interfaces"); 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_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 { ERROR("Failed to create matched ifnames"); break; } } } netns_freeifaddrs(ifaddr); ifaddr = NULL; return ret; } static int set_config_net_link(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; int ret = 0; if (lxc_config_value_empty(value)) return clr_config_net_link(key, lxc_conf, data); if (!netdev) return -1; 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, sizeof(netdev->link)); return ret; } static int set_config_net_name(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_name(key, lxc_conf, data); if (!netdev) return -1; return network_ifname(netdev->name, value, sizeof(netdev->name)); } static int set_config_net_veth_pair(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_veth_pair(key, lxc_conf, data); if (!netdev) return -1; return network_ifname(netdev->priv.veth_attr.pair, value, sizeof(netdev->priv.veth_attr.pair)); } static int set_config_net_macvlan_mode(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_macvlan_mode(key, lxc_conf, data); if (!netdev) return -1; return lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, value); } static int set_config_net_hwaddr(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; char *new_value; if (lxc_config_value_empty(value)) return clr_config_net_hwaddr(key, lxc_conf, data); if (!netdev) return -1; new_value = strdup(value); if (!new_value) return -1; rand_complete_hwaddr(new_value); if (lxc_config_value_empty(new_value)) { free(new_value); netdev->hwaddr = NULL; return 0; } netdev->hwaddr = new_value; return 0; } static int set_config_net_vlan_id(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_vlan_id(key, lxc_conf, data); if (!netdev) return -1; ret = get_u16(&netdev->priv.vlan_attr.vid, value, 0); if (ret < 0) return -1; return 0; } static int set_config_net_mtu(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_mtu(key, lxc_conf, data); if (!netdev) return -1; return set_config_string_item(&netdev->mtu, value); } 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 = data; 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_net_ipv4_address(key, lxc_conf, data); if (!netdev) return -1; inetdev = malloc(sizeof(*inetdev)); if (!inetdev) return -1; memset(inetdev, 0, sizeof(*inetdev)); list = malloc(sizeof(*list)); if (!list) { free(inetdev); return -1; } lxc_list_init(list); list->elem = inetdev; addr = strdup(value); if (!addr) { 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; } 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) { 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. */ if (prefix) { ret = lxc_safe_uint(prefix, &inetdev->prefix); if (ret < 0) { free(inetdev); free(list); free(addr); return -1; } } else { inetdev->prefix = config_ip_prefix(&inetdev->addr); } /* If no broadcast address, let compute one from the * prefix and address. */ if (!bcast) { inetdev->bcast.s_addr = inetdev->addr.s_addr; inetdev->bcast.s_addr |= htonl(INADDR_BROADCAST >> inetdev->prefix); } lxc_list_add_tail(&netdev->ipv4, list); free(addr); return 0; } static int set_config_net_ipv4_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_ipv4_gateway(key, lxc_conf, data); if (!netdev) return -1; free(netdev->ipv4_gateway); 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) return -1; ret = inet_pton(AF_INET, value, gw); if (!ret || ret < 0) { SYSERROR("Invalid ipv4 gateway address \"%s\"", value); free(gw); return -1; } netdev->ipv4_gateway = gw; netdev->ipv4_gateway_auto = false; } return 0; } static int set_config_net_ipv6_address(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; struct lxc_netdev *netdev = data; struct lxc_inet6dev *inet6dev; struct lxc_list *list; char *slash, *valdup, *netmask; if (lxc_config_value_empty(value)) return clr_config_net_ipv6_address(key, lxc_conf, data); if (!netdev) return -1; inet6dev = malloc(sizeof(*inet6dev)); if (!inet6dev) return -1; memset(inet6dev, 0, sizeof(*inet6dev)); list = malloc(sizeof(*list)); if (!list) { free(inet6dev); return -1; } lxc_list_init(list); list->elem = inet6dev; valdup = strdup(value); if (!valdup) { free(list); free(inet6dev); return -1; } inet6dev->prefix = 64; slash = strstr(valdup, "/"); if (slash) { *slash = '\0'; netmask = slash + 1; ret = lxc_safe_uint(netmask, &inet6dev->prefix); if (ret < 0) { free(list); free(inet6dev); free(valdup); return -1; } } ret = inet_pton(AF_INET6, valdup, &inet6dev->addr); if (!ret || ret < 0) { SYSERROR("Invalid ipv6 address \"%s\"", valdup); free(list); free(inet6dev); free(valdup); return -1; } lxc_list_add_tail(&netdev->ipv6, list); free(valdup); return 0; } static int set_config_net_ipv6_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_ipv6_gateway(key, lxc_conf, data); if (!netdev) return -1; free(netdev->ipv6_gateway); 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) return -1; ret = inet_pton(AF_INET6, value, gw); if (!ret || ret < 0) { SYSERROR("Invalid ipv6 gateway address \"%s\"", value); free(gw); return -1; } netdev->ipv6_gateway = gw; netdev->ipv6_gateway_auto = false; } return 0; } static int set_config_net_script_up(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_script_up(key, lxc_conf, data); if (!netdev) return -1; return set_config_string_item(&netdev->upscript, value); } static int set_config_net_script_down(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_netdev *netdev = data; if (lxc_config_value_empty(value)) return clr_config_net_script_down(key, lxc_conf, data); if (!netdev) return -1; return set_config_string_item(&netdev->downscript, value); } static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) { struct lxc_list *hooklist; hooklist = malloc(sizeof(*hooklist)); if (!hooklist) { free(hook); return -1; } hooklist->elem = hook; lxc_list_add_tail(&lxc_conf->hooks[which], hooklist); return 0; } static int set_config_seccomp_profile(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->seccomp, value); } static int set_config_execute_cmd(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->execute_cmd, value); } static int set_config_init_cmd(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->init_cmd, value); } static int set_config_init_cwd(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->init_cwd, value); } 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 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 set_config_hooks(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *copy; if (lxc_config_value_empty(value)) return lxc_clear_hooks(lxc_conf, key); if (strcmp(key + 4, "hook") == 0) { ERROR("lxc.hook must not have a value"); return -1; } copy = strdup(value); if (!copy) return -1; if (strcmp(key + 9, "pre-start") == 0) return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); else if (strcmp(key + 9, "start-host") == 0) return add_hook(lxc_conf, LXCHOOK_START_HOST, copy); else if (strcmp(key + 9, "pre-mount") == 0) return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy); else if (strcmp(key + 9, "autodev") == 0) return add_hook(lxc_conf, LXCHOOK_AUTODEV, copy); else if (strcmp(key + 9, "mount") == 0) return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); else if (strcmp(key + 9, "start") == 0) return add_hook(lxc_conf, LXCHOOK_START, copy); else if (strcmp(key + 9, "stop") == 0) return add_hook(lxc_conf, LXCHOOK_STOP, copy); else if (strcmp(key + 9, "post-stop") == 0) return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); else if (strcmp(key + 9, "clone") == 0) return add_hook(lxc_conf, LXCHOOK_CLONE, copy); else if (strcmp(key + 9, "destroy") == 0) return add_hook(lxc_conf, LXCHOOK_DESTROY, copy); free(copy); return -1; } static int set_config_hooks_version(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; unsigned int tmp; if (lxc_config_value_empty(value)) return clr_config_hooks_version(key, lxc_conf, NULL); ret = lxc_safe_uint(value, &tmp); if (ret < 0) return -1; if (tmp > 1) { ERROR("Invalid hook version specified. Currently only 0 " "(legacy) and 1 are supported"); return -1; } lxc_conf->hooks_version = tmp; return 0; } static int set_config_personality(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { signed long personality = lxc_config_parse_arch(value); if (personality >= 0) lxc_conf->personality = personality; else WARN("Unsupported personality \"%s\"", value); return 0; } static int set_config_pty_max(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; unsigned int max = 0; if (lxc_config_value_empty(value)) { lxc_conf->pty_max = 0; return 0; } ret = lxc_safe_uint(value, &max); if (ret < 0) return -1; lxc_conf->pty_max = max; return 0; } /* We only need to check whether the first byte of the key after the lxc.start. * prefix matches our expectations since they fortunately all start with a * different letter. If anything was wrong with the key we would have already * noticed when the callback was called. */ static int set_config_start(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { bool is_empty; is_empty = lxc_config_value_empty(value); if (*(key + 10) == 'a') { /* lxc.start.auto */ 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); } return -1; } static int set_config_monitor(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { lxc_conf->monitor_unshare = 0; return 0; } if (strcmp(key + 12, "unshare") == 0) return lxc_safe_uint(value, &lxc_conf->monitor_unshare); return -1; } static int set_config_group(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *groups, *token; struct lxc_list *grouplist; int ret = 0; if (lxc_config_value_empty(value)) return lxc_clear_groups(lxc_conf); groups = strdup(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. */ lxc_iterate_parts(token, groups, " \t") { grouplist = malloc(sizeof(*grouplist)); if (!grouplist) { ret = -1; break; } grouplist->elem = strdup(token); if (!grouplist->elem) { free(grouplist); ret = -1; break; } lxc_list_add_tail(&lxc_conf->groups, grouplist); } free(groups); return ret; } static int set_config_environment(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_list *list_item = NULL; if (lxc_config_value_empty(value)) return lxc_clear_environment(lxc_conf); list_item = malloc(sizeof(*list_item)); if (!list_item) goto on_error; list_item->elem = strdup(value); if (!list_item->elem) goto on_error; lxc_list_add_tail(&lxc_conf->environment, list_item); return 0; on_error: free(list_item); return -1; } static int set_config_tty_max(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; unsigned int nbtty = 0; if (lxc_config_value_empty(value)) { lxc_conf->ttys.max = 0; return 0; } ret = lxc_safe_uint(value, &nbtty); if (ret < 0) return -1; lxc_conf->ttys.max = nbtty; return 0; } static int set_config_tty_dir(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item_max(&lxc_conf->ttys.dir, value, NAME_MAX + 1); } static int set_config_apparmor_profile(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item(&lxc_conf->lsm_aa_profile, value); } static int set_config_apparmor_allow_incomplete(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { lxc_conf->lsm_aa_allow_incomplete = 0; return 0; } if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_incomplete) < 0) return -1; if (lxc_conf->lsm_aa_allow_incomplete > 1) return -1; return 0; } static int set_config_selinux_context(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_string_item(&lxc_conf->lsm_se_context, value); } static int set_config_log_file(const char *key, const char *value, struct lxc_conf *c, void *data) { int ret; if (lxc_config_value_empty(value)) { free(c->logfile); c->logfile = NULL; return 0; } /* Store these values in the lxc_conf, and then try to set for actual * current logging. */ ret = set_config_path_item(&c->logfile, value); if (ret == 0) ret = lxc_log_set_file(&c->logfd, c->logfile); return ret; } static int set_config_log_level(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int newlevel; 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) return -1; } else { newlevel = lxc_log_priority_to_int(value); } /* Store these values in the lxc_conf, and then try to set for actual * current logging. */ lxc_conf->loglevel = newlevel; return lxc_log_set_level(&lxc_conf->loglevel, newlevel); } static int set_config_autodev(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { 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) return -1; return 0; } static int set_config_signal_halt(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { 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 set_config_signal_reboot(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { 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 set_config_signal_stop(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { 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 __set_config_cgroup_controller(const char *key, const char *value, struct lxc_conf *lxc_conf, int version) { const char *subkey, *token; size_t token_len; struct lxc_list *cglist = NULL; struct lxc_cgroup *cgelem = NULL; if (lxc_config_value_empty(value)) return lxc_clear_cgroups(lxc_conf, key, version); if (version == CGROUP2_SUPER_MAGIC) { token = "lxc.cgroup2."; token_len = 12; } else if (version == CGROUP_SUPER_MAGIC) { token = "lxc.cgroup."; token_len = 11; } else { return -EINVAL; } if (strncmp(key, token, token_len) != 0) return -EINVAL; subkey = key + token_len; if (*subkey == '\0') return -EINVAL; cglist = malloc(sizeof(*cglist)); if (!cglist) goto out; cgelem = malloc(sizeof(*cgelem)); if (!cgelem) goto out; memset(cgelem, 0, sizeof(*cgelem)); cgelem->subsystem = strdup(subkey); if (!cgelem->subsystem) goto out; cgelem->value = strdup(value); if (!cgelem->value) goto out; cgelem->version = version; lxc_list_add_elem(cglist, cgelem); if (version == CGROUP2_SUPER_MAGIC) lxc_list_add_tail(&lxc_conf->cgroup2, cglist); else lxc_list_add_tail(&lxc_conf->cgroup, cglist); return 0; out: free(cglist); if (cgelem) { free(cgelem->subsystem); free(cgelem->value); free(cgelem); } return -1; } static int set_config_cgroup_controller(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return __set_config_cgroup_controller(key, value, lxc_conf, CGROUP_SUPER_MAGIC); } static int set_config_cgroup2_controller(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return __set_config_cgroup_controller(key, value, lxc_conf, CGROUP2_SUPER_MAGIC); } static int set_config_cgroup_dir(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) return clr_config_cgroup_dir(key, lxc_conf, NULL); return set_config_string_item(&lxc_conf->cgroup_meta.dir, value); } 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; rlim_t 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.prlimit.", STRLITERALLEN("lxc.prlimit.")) != 0) return -1; key += STRLITERALLEN("lxc.prlimit."); /* 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 on_error; limelem = malloc(sizeof(*limelem)); if (!limelem) goto on_error; memset(limelem, 0, sizeof(*limelem)); limelem->resource = strdup(key); if (!limelem->resource) goto on_error; limelem->limit = limit; lxc_list_add_elem(limlist, limelem);; lxc_list_add_tail(&lxc_conf->limits, limlist); return 0; on_error: free(limlist); if (limelem) { free(limelem->resource); free(limelem); } return -1; } static int set_config_sysctl(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { struct lxc_list *iter; char *replace_value = NULL; struct lxc_list *sysctl_list = NULL; struct lxc_sysctl *sysctl_elem = NULL; if (lxc_config_value_empty(value)) return clr_config_sysctl(key, lxc_conf, NULL); if (strncmp(key, "lxc.sysctl.", STRLITERALLEN("lxc.sysctl.")) != 0) return -1; key += STRLITERALLEN("lxc.sysctl."); /* find existing list element */ lxc_list_for_each(iter, &lxc_conf->sysctls) { sysctl_elem = iter->elem; if (strcmp(key, sysctl_elem->key) != 0) continue; replace_value = strdup(value); if (!replace_value) return -1; free(sysctl_elem->value); sysctl_elem->value = replace_value; return 0; } /* allocate list element */ sysctl_list = malloc(sizeof(*sysctl_list)); if (!sysctl_list) goto on_error; sysctl_elem = malloc(sizeof(*sysctl_elem)); if (!sysctl_elem) goto on_error; memset(sysctl_elem, 0, sizeof(*sysctl_elem)); sysctl_elem->key = strdup(key); if (!sysctl_elem->key) goto on_error; sysctl_elem->value = strdup(value); if (!sysctl_elem->value) goto on_error; lxc_list_add_elem(sysctl_list, sysctl_elem); lxc_list_add_tail(&lxc_conf->sysctls, sysctl_list); return 0; on_error: free(sysctl_list); if (sysctl_elem) { free(sysctl_elem->key); free(sysctl_elem->value); free(sysctl_elem); } return -1; } static int set_config_proc(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { const char *subkey; struct lxc_list *proclist = NULL; struct lxc_proc *procelem = NULL; if (lxc_config_value_empty(value)) return clr_config_proc(key, lxc_conf, NULL); if (strncmp(key, "lxc.proc.", STRLITERALLEN("lxc.proc.")) != 0) return -1; subkey = key + STRLITERALLEN("lxc.proc."); if (*subkey == '\0') return -EINVAL; proclist = malloc(sizeof(*proclist)); if (!proclist) goto on_error; procelem = malloc(sizeof(*procelem)); if (!procelem) goto on_error; memset(procelem, 0, sizeof(*procelem)); procelem->filename = strdup(subkey); procelem->value = strdup(value); if (!procelem->filename || !procelem->value) goto on_error; proclist->elem = procelem; lxc_list_add_tail(&lxc_conf->procs, proclist); return 0; on_error: free(proclist); if (procelem) { free(procelem->filename); free(procelem->value); free(procelem); } return -1; } static int set_config_idmaps(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { unsigned long hostid, nsid, range; char type; int ret; struct lxc_list *idmaplist = NULL; struct id_map *idmap = NULL; if (lxc_config_value_empty(value)) return lxc_clear_idmaps(lxc_conf); idmaplist = malloc(sizeof(*idmaplist)); if (!idmaplist) goto on_error; idmap = malloc(sizeof(*idmap)); if (!idmap) goto on_error; memset(idmap, 0, sizeof(*idmap)); ret = parse_idmaps(value, &type, &nsid, &hostid, &range); if (ret < 0) { ERROR("Failed to parse id mappings"); goto on_error; } INFO("Read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range); if (type == 'u') idmap->idtype = ID_TYPE_UID; else if (type == 'g') idmap->idtype = ID_TYPE_GID; else goto on_error; idmap->hostid = hostid; idmap->nsid = nsid; idmap->range = range; idmaplist->elem = idmap; lxc_list_add_tail(&lxc_conf->id_map, idmaplist); if (!lxc_conf->root_nsuid_map && idmap->idtype == ID_TYPE_UID) if (idmap->nsid == 0) lxc_conf->root_nsuid_map = idmap; if (!lxc_conf->root_nsgid_map && idmap->idtype == ID_TYPE_GID) if (idmap->nsid == 0) lxc_conf->root_nsgid_map = idmap; idmap = NULL; return 0; on_error: free(idmaplist); free(idmap); return -1; } static int set_config_mount_fstab(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { clr_config_mount_fstab(key, lxc_conf, NULL); return -1; } return set_config_path_item(&lxc_conf->fstab, value); } static int set_config_mount_auto(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *autos, *token; int i; int ret = -1; static struct { const char *token; int mask; int flag; } allowed_auto_mounts[] = { { "proc", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, { "proc:mixed", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED }, { "proc:rw", LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW }, { "sys", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, { "sys:ro", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO }, { "sys:mixed", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED }, { "sys:rw", LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW }, { "cgroup", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC }, { "cgroup:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED }, { "cgroup:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO }, { "cgroup:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW }, { "cgroup:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_NOSPEC | LXC_AUTO_CGROUP_FORCE }, { "cgroup:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_MIXED | LXC_AUTO_CGROUP_FORCE }, { "cgroup:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RO | LXC_AUTO_CGROUP_FORCE }, { "cgroup:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_RW | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC }, { "cgroup-full:mixed", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED }, { "cgroup-full:ro", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO }, { "cgroup-full:rw", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW }, { "cgroup-full:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_NOSPEC | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW | LXC_AUTO_CGROUP_FORCE }, /* For adding anything that is just a single on/off, but has no * options: keep mask and flag identical and just define the enum * value as an unused bit so far */ { NULL, 0, 0 } }; if (lxc_config_value_empty(value)) { lxc_conf->auto_mounts = 0; return 0; } autos = strdup(value); if (!autos) return -1; lxc_iterate_parts(token, autos, " \t") { for (i = 0; allowed_auto_mounts[i].token; i++) { if (!strcmp(allowed_auto_mounts[i].token, token)) break; } if (!allowed_auto_mounts[i].token) { ERROR("Invalid filesystem to automount \"%s\"", token); goto on_error; } lxc_conf->auto_mounts &= ~allowed_auto_mounts[i].mask; lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag; } ret = 0; on_error: free(autos); return ret; } static int set_config_mount(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *mntelem; struct lxc_list *mntlist; if (lxc_config_value_empty(value)) return lxc_clear_mount_entries(lxc_conf); mntlist = malloc(sizeof(*mntlist)); if (!mntlist) return -1; mntelem = strdup(value); if (!mntelem) { free(mntlist); return -1; } mntlist->elem = mntelem; lxc_list_add_tail(&lxc_conf->mount_list, mntlist); return 0; } static int set_config_cap_keep(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *keepcaps, *token; struct lxc_list *keeplist; int ret = -1; if (lxc_config_value_empty(value)) return lxc_clear_config_keepcaps(lxc_conf); keepcaps = strdup(value); if (!keepcaps) return -1; /* In case several capability keep is specified in a single line * split these caps in a single element for the list. */ lxc_iterate_parts(token, keepcaps, " \t") { if (!strcmp(token, "none")) lxc_clear_config_keepcaps(lxc_conf); keeplist = malloc(sizeof(*keeplist)); if (!keeplist) goto on_error; keeplist->elem = strdup(token); if (!keeplist->elem) { free(keeplist); goto on_error; } lxc_list_add_tail(&lxc_conf->keepcaps, keeplist); } ret = 0; on_error: free(keepcaps); return ret; } static int set_config_cap_drop(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *dropcaps, *token; struct lxc_list *droplist; int ret = -1; if (lxc_config_value_empty(value)) return lxc_clear_config_caps(lxc_conf); dropcaps = strdup(value); if (!dropcaps) return -1; /* In case several capability drop is specified in a single line * split these caps in a single element for the list. */ lxc_iterate_parts(token, dropcaps, " \t") { droplist = malloc(sizeof(*droplist)); if (!droplist) goto on_error; droplist->elem = strdup(token); if (!droplist->elem) { free(droplist); goto on_error; } lxc_list_add_tail(&lxc_conf->caps, droplist); } ret = 0; on_error: free(dropcaps); return ret; } static int set_config_console_path(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->console.path, value); } static int set_config_console_rotate(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { if (lxc_config_value_empty(value)) { lxc_conf->console.log_rotate = 0; return 0; } if (lxc_safe_uint(value, &lxc_conf->console.log_rotate) < 0) return -1; if (lxc_conf->console.log_rotate > 1) { ERROR("The \"lxc.console.rotate\" config key can only be set " "to 0 or 1"); return -1; } return 0; } static int set_config_console_logfile(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->console.log_path, value); } static int set_config_console_buffer_size(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; int64_t size; uint64_t buffer_size, pgsz; if (lxc_config_value_empty(value)) { lxc_conf->console.buffer_size = 0; return 0; } /* If the user specified "auto" the default log size is 2^17 = 128 Kib */ if (!strcmp(value, "auto")) { lxc_conf->console.buffer_size = 1 << 17; return 0; } ret = parse_byte_size_string(value, &size); if (ret < 0) return -1; if (size < 0) return -EINVAL; /* must be at least a page size */ pgsz = lxc_getpagesize(); if ((uint64_t)size < pgsz) { NOTICE("Requested ringbuffer size for the console is %" PRId64 " but must be at least %" PRId64 " bytes. Setting ringbuffer size to %" PRId64 " bytes", size, pgsz, pgsz); size = pgsz; } buffer_size = lxc_find_next_power2((uint64_t)size); if (buffer_size == 0) return -EINVAL; if (buffer_size != size) NOTICE("Passed size was not a power of 2. Rounding log size to " "next power of two: %" PRIu64 " bytes", buffer_size); lxc_conf->console.buffer_size = buffer_size; return 0; } static int set_config_console_size(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; int64_t size; uint64_t log_size, pgsz; if (lxc_config_value_empty(value)) { lxc_conf->console.log_size = 0; return 0; } /* If the user specified "auto" the default log size is 2^17 = 128 Kib */ if (!strcmp(value, "auto")) { lxc_conf->console.log_size = 1 << 17; return 0; } ret = parse_byte_size_string(value, &size); if (ret < 0) return -1; if (size < 0) return -EINVAL; /* must be at least a page size */ pgsz = lxc_getpagesize(); if ((uint64_t)size < pgsz) { NOTICE("Requested ringbuffer size for the console is %" PRId64 " but must be at least %" PRId64 " bytes. Setting ringbuffer size to %" PRId64 " bytes", size, pgsz, pgsz); size = pgsz; } log_size = lxc_find_next_power2((uint64_t)size); if (log_size == 0) return -EINVAL; if (log_size != size) NOTICE("Passed size was not a power of 2. Rounding log size to " "next power of two: %" PRIu64 " bytes", log_size); lxc_conf->console.log_size = log_size; return 0; } int append_unexp_config_line(const char *line, struct lxc_conf *conf) { size_t linelen; size_t len = conf->unexpanded_len; update_hwaddr(line); linelen = strlen(line); while (conf->unexpanded_alloced <= len + linelen + 2) { char *tmp = realloc(conf->unexpanded_config, conf->unexpanded_alloced + 1024); if (!tmp) return -1; if (!conf->unexpanded_config) *tmp = '\0'; conf->unexpanded_config = tmp; conf->unexpanded_alloced += 1024; } memcpy(conf->unexpanded_config + conf->unexpanded_len, line, linelen); conf->unexpanded_len += linelen; if (line[linelen - 1] != '\n') conf->unexpanded_config[conf->unexpanded_len++] = '\n'; conf->unexpanded_config[conf->unexpanded_len] = '\0'; return 0; } static int do_includedir(const char *dirp, struct lxc_conf *lxc_conf) { struct dirent *direntp; DIR *dir; char path[PATH_MAX]; int len; int ret = -1; dir = opendir(dirp); if (!dir) return -1; while ((direntp = readdir(dir))) { const char *fnam; fnam = direntp->d_name; if (!strcmp(fnam, ".")) continue; if (!strcmp(fnam, "..")) continue; len = strlen(fnam); if (len < 6 || strncmp(fnam + len - 5, ".conf", 5) != 0) continue; len = snprintf(path, PATH_MAX, "%s/%s", dirp, fnam); if (len < 0 || len >= PATH_MAX) { ret = -1; goto out; } ret = lxc_config_read(path, lxc_conf, true); if (ret < 0) goto out; } ret = 0; out: closedir(dir); return ret; } 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 set_config_rootfs_path(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; char *dup, *tmp; const char *container_path; if (lxc_config_value_empty(value)) { free(lxc_conf->rootfs.path); lxc_conf->rootfs.path = NULL; return 0; } dup = strdup(value); if (!dup) return -1; /* Split : into and * . Set "rootfs.bdev_type" to and * "rootfs.path" to . */ tmp = strchr(dup, ':'); if (tmp) { *tmp = '\0'; ret = set_config_path_item(&lxc_conf->rootfs.bdev_type, dup); if (ret < 0) { free(dup); return -1; } tmp++; container_path = tmp; } else { container_path = value; } ret = set_config_path_item(&lxc_conf->rootfs.path, container_path); free(dup); return ret; } static int set_config_rootfs_mount(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { return set_config_path_item(&lxc_conf->rootfs.mount, value); } static int set_config_rootfs_options(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ret; unsigned long mflags = 0, pflags = 0; char *mdata = NULL, *opts = NULL; struct lxc_rootfs *rootfs = &lxc_conf->rootfs; ret = parse_mntopts(value, &mflags, &mdata); if (ret < 0) return -EINVAL; ret = parse_propagationopts(value, &pflags); if (ret < 0) { free(mdata); return -EINVAL; } ret = set_config_string_item(&opts, value); if (ret < 0) { free(mdata); return -ENOMEM; } rootfs->mountflags = mflags | pflags; rootfs->options = opts; rootfs->data = mdata; return 0; } 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) return -1; if (strlen(value) >= sizeof(utsname->nodename)) { free(utsname); return -1; } (void)strlcpy(utsname->nodename, value, sizeof(utsname->nodename)); free(lxc_conf->utsname); lxc_conf->utsname = utsname; return 0; } static int set_config_namespace_clone(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *ns, *token; int cloneflag = 0; if (lxc_config_value_empty(value)) return clr_config_namespace_clone(key, lxc_conf, data); if (lxc_conf->ns_keep != 0) { errno = EINVAL; SYSERROR("Cannot set both \"lxc.namespace.clone\" and " "\"lxc.namespace.keep\""); return -EINVAL; } ns = strdup(value); if (!ns) return -1; lxc_iterate_parts(token, ns, " \t") { token += lxc_char_left_gc(token, strlen(token)); token[lxc_char_right_gc(token, strlen(token))] = '\0'; cloneflag = lxc_namespace_2_cloneflag(token); if (cloneflag < 0) { free(ns); return -EINVAL; } lxc_conf->ns_clone |= cloneflag; } free(ns); return 0; } static int set_config_namespace_keep(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { char *ns, *token; int cloneflag = 0; if (lxc_config_value_empty(value)) return clr_config_namespace_keep(key, lxc_conf, data); if (lxc_conf->ns_clone != 0) { errno = EINVAL; SYSERROR("Cannot set both \"lxc.namespace.clone\" and " "\"lxc.namespace.keep\""); return -EINVAL; } ns = strdup(value); if (!ns) return -1; lxc_iterate_parts(token, ns, " \t") { token += lxc_char_left_gc(token, strlen(token)); token[lxc_char_right_gc(token, strlen(token))] = '\0'; cloneflag = lxc_namespace_2_cloneflag(token); if (cloneflag < 0) { free(ns); return -EINVAL; } lxc_conf->ns_keep |= cloneflag; } free(ns); return 0; } static int set_config_namespace_share(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { int ns_idx; const char *namespace; if (lxc_config_value_empty(value)) return clr_config_namespace_share(key, lxc_conf, data); namespace = key + STRLITERALLEN("lxc.namespace.share."); ns_idx = lxc_namespace_2_ns_idx(namespace); if (ns_idx < 0) return ns_idx; return set_config_string_item(&lxc_conf->ns_share[ns_idx], value); } struct parse_line_conf { struct lxc_conf *conf; bool from_include; }; static int parse_line(char *buffer, void *data) { char *dot, *key, *line, *linep, *value; bool empty_line; struct lxc_config_t *config; int ret = 0; char *dup = buffer; struct parse_line_conf *plc = data; /* If there are newlines in the config file we should keep them. */ empty_line = lxc_is_line_empty(dup); if (empty_line) dup = "\n"; /* We have to dup the buffer otherwise, at the re-exec for reboot we * modified the original string on the stack by replacing '=' by '\0' * below. */ linep = line = strdup(dup); if (!line) return -1; if (!plc->from_include) { ret = append_unexp_config_line(line, plc->conf); if (ret < 0) goto on_error; } if (empty_line) goto on_error; line += lxc_char_left_gc(line, strlen(line)); /* ignore comments */ if (line[0] == '#') goto on_error; /* martian option - don't add it to the config itself */ if (strncmp(line, "lxc.", 4)) goto on_error; ret = -1; dot = strchr(line, '='); if (!dot) { ERROR("Invalid configuration line: %s", line); goto on_error; } *dot = '\0'; value = dot + 1; key = line; key[lxc_char_right_gc(key, strlen(key))] = '\0'; value += lxc_char_left_gc(value, strlen(value)); value[lxc_char_right_gc(value, strlen(value))] = '\0'; if (*value == '\'' || *value == '\"') { size_t len; len = strlen(value); if (len > 1 && value[len - 1] == *value) { value[len - 1] = '\0'; value++; } } config = lxc_get_config(key); if (!config) { ERROR("Unknown configuration key \"%s\"", key); goto on_error; } ret = config->set(key, value, plc->conf, NULL); on_error: free(linep); return ret; } static struct new_config_item *parse_new_conf_line(char *buffer) { char *dot, *key, *line, *linep, *value; int ret = 0; char *dup = buffer; struct new_config_item *new = NULL; linep = line = strdup(dup); if (!line) return NULL; line += lxc_char_left_gc(line, strlen(line)); /* martian option - don't add it to the config itself */ if (strncmp(line, "lxc.", 4)) goto on_error; ret = -1; dot = strchr(line, '='); if (!dot) { ERROR("Invalid configuration item: %s", line); goto on_error; } *dot = '\0'; value = dot + 1; key = line; key[lxc_char_right_gc(key, strlen(key))] = '\0'; value += lxc_char_left_gc(value, strlen(value)); value[lxc_char_right_gc(value, strlen(value))] = '\0'; if (*value == '\'' || *value == '\"') { size_t len; len = strlen(value); if (len > 1 && value[len - 1] == *value) { value[len - 1] = '\0'; value++; } } ret = -1; new = malloc(sizeof(struct new_config_item)); if (!new) goto on_error; new->key = strdup(key); new->val = strdup(value); if (!new->val || !new->key) goto on_error; ret = 0; on_error: free(linep); if (ret < 0 && new) { free(new->key); free(new->val); free(new); new = NULL; } return new; } int lxc_config_read(const char *file, struct lxc_conf *conf, bool from_include) { struct parse_line_conf c; c.conf = conf; c.from_include = from_include; /* Catch only the top level config file name in the structure. */ if (!conf->rcfile) conf->rcfile = strdup(file); return lxc_file_for_each_line_mmap(file, parse_line, &c); } int lxc_config_define_add(struct lxc_list *defines, char *arg) { struct lxc_list *dent; dent = malloc(sizeof(struct lxc_list)); if (!dent) return -1; dent->elem = parse_new_conf_line(arg); if (!dent->elem) { free(dent); return -1; } lxc_list_add_tail(defines, dent); return 0; } bool lxc_config_define_load(struct lxc_list *defines, struct lxc_container *c) { struct lxc_list *it; bool bret = true; lxc_list_for_each(it, defines) { struct new_config_item *new_item = it->elem; bret = c->set_config_item(c, new_item->key, new_item->val); if (!bret) break; } lxc_config_define_free(defines); return bret; } void lxc_config_define_free(struct lxc_list *defines) { struct lxc_list *it, *next; lxc_list_for_each_safe(it, defines, next) { struct new_config_item *new_item = it->elem; free(new_item->key); free(new_item->val); lxc_list_del(it); free(it); } } signed long lxc_config_parse_arch(const char *arch) { #if HAVE_SYS_PERSONALITY_H size_t i; struct per_name { char *name; unsigned long per; } pername[] = { { "arm", PER_LINUX32 }, { "armel", PER_LINUX32 }, { "armhf", PER_LINUX32 }, { "armv7l", PER_LINUX32 }, { "athlon", PER_LINUX32 }, { "i386", PER_LINUX32 }, { "i486", PER_LINUX32 }, { "i586", PER_LINUX32 }, { "i686", PER_LINUX32 }, { "linux32", PER_LINUX32 }, { "mips", PER_LINUX32 }, { "mipsel", PER_LINUX32 }, { "ppc", PER_LINUX32 }, { "powerpc", PER_LINUX32 }, { "x86", PER_LINUX32 }, { "amd64", PER_LINUX }, { "arm64", PER_LINUX }, { "linux64", PER_LINUX }, { "mips64", PER_LINUX }, { "mips64el", PER_LINUX }, { "ppc64", PER_LINUX }, { "ppc64el", PER_LINUX }, { "ppc64le", PER_LINUX }, { "powerpc64", PER_LINUX }, { "s390x", PER_LINUX }, { "x86_64", PER_LINUX }, }; size_t len = sizeof(pername) / sizeof(pername[0]); for (i = 0; i < len; i++) if (!strcmp(pername[i].name, arch)) return pername[i].per; #endif return -1; } int lxc_fill_elevated_privileges(char *flaglist, int *flags) { char *token; int i, aflag; struct { const char *token; int flag; } all_privs[] = { { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, { "LSM", LXC_ATTACH_LSM_EXEC }, { NULL, 0 } }; if (!flaglist) { /* For the sake of backward compatibility, drop all privileges * if none is specified. */ for (i = 0; all_privs[i].token; i++) *flags |= all_privs[i].flag; return 0; } lxc_iterate_parts(token, flaglist, "|") { aflag = -1; for (i = 0; all_privs[i].token; i++) if (!strcmp(all_privs[i].token, token)) aflag = all_privs[i].flag; if (aflag < 0) return -1; *flags |= aflag; } return 0; } /* Write out a configuration file. */ int write_config(int fd, const struct lxc_conf *conf) { int ret; size_t len = conf->unexpanded_len; if (len == 0) return 0; ret = lxc_write_nointr(fd, conf->unexpanded_config, len); if (ret < 0) { SYSERROR("Failed to write configuration file"); return -1; } return 0; } bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key, const char *v) { int ret; size_t len; char *tmp; len = strlen(key) + strlen(v) + 4; tmp = alloca(len); if (lxc_config_value_empty(v)) ret = snprintf(tmp, len, "%s =", key); else ret = snprintf(tmp, len, "%s = %s", key, v); if (ret < 0 || ret >= len) return false; /* Save the line verbatim into unexpanded_conf */ if (append_unexp_config_line(tmp, conf)) return false; return true; } void clear_unexp_config_line(struct lxc_conf *conf, const char *key, bool rm_subkeys) { char *lend; char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return; while (*lstart) { lend = strchr(lstart, '\n'); char v; if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) { lstart = lend; continue; } if (!rm_subkeys) { v = lstart[strlen(key)]; if (!isspace(v) && v != '=') { lstart = lend; continue; } } conf->unexpanded_len -= (lend - lstart); if (*lend == '\0') { *lstart = '\0'; return; } memmove(lstart, lend, strlen(lend) + 1); } } bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname, const char *ovldir) { int ret; char *lend, *newdir, *olddir, *p, *q; size_t newdirlen, olddirlen; char *lstart = conf->unexpanded_config; const char *key = "lxc.mount.entry"; olddirlen = strlen(ovldir) + strlen(oldpath) + strlen(oldname) + 2; olddir = alloca(olddirlen + 1); ret = snprintf(olddir, olddirlen + 1, "%s=%s/%s", ovldir, oldpath, oldname); if (ret < 0 || ret >= olddirlen + 1) return false; newdirlen = strlen(ovldir) + strlen(newpath) + strlen(newname) + 2; newdir = alloca(newdirlen + 1); ret = snprintf(newdir, newdirlen + 1, "%s=%s/%s", ovldir, newpath, newname); if (ret < 0 || ret >= newdirlen + 1) return false; if (!conf->unexpanded_config) return true; while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) goto next; p = strchr(lstart + strlen(key), '='); if (!p) goto next; p++; while (isblank(*p)) p++; if (p >= lend) goto next; /* Whenever a lxc.mount.entry entry is found in a line we check * if the substring "overlay" is present before doing any * further work. We check for "overlay" because 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.) */ q = strstr(p, " overlay"); if (!q || q >= lend) goto next; if (!(q = strstr(p, olddir)) || (q >= lend)) goto next; /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { size_t diff = olddirlen - newdirlen; memcpy(q, newdir, newdirlen); if (olddirlen != newdirlen) { memmove(q + newdirlen, q + newdirlen + diff, strlen(q) - newdirlen - diff + 1); lend -= diff; conf->unexpanded_len -= diff; } } else { char *new; size_t diff = newdirlen - olddirlen; size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = q - conf->unexpanded_config; new = realloc(conf->unexpanded_config, newlen + 1); if (!new) return false; conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); /* Move over the remainder to make room for the newdir. */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); conf->unexpanded_config = new; memcpy(new + poffset, newdir, newdirlen); lend += diff; } next: lstart = lend; } return true; } bool clone_update_unexp_hooks(struct lxc_conf *conf, const char *oldpath, const char *newpath, const char *oldname, const char *newname) { int ret; char *lend, *newdir, *olddir, *p; char *lstart = conf->unexpanded_config; size_t newdirlen, olddirlen; const char *key = "lxc.hook"; olddirlen = strlen(oldpath) + strlen(oldname) + 1; olddir = alloca(olddirlen + 1); ret = snprintf(olddir, olddirlen + 1, "%s/%s", oldpath, oldname); if (ret < 0 || ret >= olddirlen + 1) 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) return false; if (!conf->unexpanded_config) return true; while (*lstart) { lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (strncmp(lstart, key, strlen(key)) != 0) goto next; p = strchr(lstart + strlen(key), '='); if (!p) goto next; p++; while (isblank(*p)) p++; if (p >= lend) goto next; if (strncmp(p, olddir, strlen(olddir)) != 0) goto next; /* replace the olddir with newdir */ if (olddirlen >= newdirlen) { size_t diff = olddirlen - newdirlen; memcpy(p, newdir, newdirlen); if (olddirlen != newdirlen) { memmove(p + newdirlen, p + newdirlen + diff, strlen(p) - newdirlen - diff + 1); lend -= diff; conf->unexpanded_len -= diff; } } else { char *new; size_t diff = newdirlen - olddirlen; size_t oldlen = conf->unexpanded_len; size_t newlen = oldlen + diff; size_t poffset = p - conf->unexpanded_config; new = realloc(conf->unexpanded_config, newlen + 1); if (!new) return false; conf->unexpanded_len = newlen; conf->unexpanded_alloced = newlen + 1; new[newlen - 1] = '\0'; lend = new + (lend - conf->unexpanded_config); /* Move over the remainder to make room for the newdir. */ memmove(new + poffset + newdirlen, new + poffset + olddirlen, oldlen - poffset - olddirlen + 1); conf->unexpanded_config = new; memcpy(new + poffset, newdir, newdirlen); lend += diff; } next: lstart = lend; } return true; } #define DO(cmd) \ { \ if (!(cmd)) { \ ERROR("Error writing to new config"); \ return false; \ } \ } /* 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; char *lstart = conf->unexpanded_config; if (!conf->unexpanded_config) return true; while (*lstart) { char newhwaddr[18], oldhwaddr[17]; lend = strchr(lstart, '\n'); if (!lend) lend = lstart + strlen(lstart); else lend++; if (!lxc_config_net_hwaddr(lstart)) { lstart = lend; continue; } p = strchr(lstart, '='); if (!p) { lstart = lend; continue; } p++; while (isblank(*p)) p++; if (!*p) return true; p2 = p; while (*p2 && !isblank(*p2) && *p2 != '\n') p2++; if ((p2 - p) != 17) { WARN("Bad hwaddr entry"); lstart = lend; continue; } memcpy(oldhwaddr, p, 17); if (!new_hwaddr(newhwaddr)) return false; memcpy(p, newhwaddr, 17); lxc_list_for_each(it, &conf->network) { struct lxc_netdev *n = it->elem; if (n->hwaddr && memcmp(oldhwaddr, n->hwaddr, 17) == 0) memcpy(n->hwaddr, newhwaddr, 17); } lstart = lend; } return true; } static int set_config_ephemeral(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { 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) 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_size_t(c, retv, inlen, c->pty_max); } static int get_config_tty_max(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_size_t(c, retv, inlen, c->ttys.max); } 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->ttys.dir); } 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 ambiguous, 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, int version) { int len; size_t namespaced_token_len; char *global_token, *namespaced_token; struct lxc_list *it; int fulllen = 0; bool get_all = false; if (!retv) inlen = 0; else memset(retv, 0, inlen); if (version == CGROUP2_SUPER_MAGIC) { global_token = "lxc.cgroup2"; namespaced_token = "lxc.cgroup2."; namespaced_token_len = STRLITERALLEN("lxc.cgroup2."); } else if (version == CGROUP_SUPER_MAGIC) { global_token = "lxc.cgroup"; namespaced_token = "lxc.cgroup."; namespaced_token_len = STRLITERALLEN("lxc.cgroup."); } else { return -1; } if (strcmp(key, global_token) == 0) get_all = true; else if (strncmp(key, namespaced_token, namespaced_token_len) == 0) key += namespaced_token_len; else return -1; lxc_list_for_each(it, &c->cgroup) { struct lxc_cgroup *cg = it->elem; if (get_all) { if (version != cg->version) continue; strprint(retv, inlen, "%s.%s = %s\n", global_token, cg->subsystem, cg->value); } else if (strcmp(cg->subsystem, key) == 0) { strprint(retv, inlen, "%s\n", cg->value); } } return fulllen; } static int get_config_cgroup_controller(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return __get_config_cgroup_controller(key, retv, inlen, c, CGROUP_SUPER_MAGIC); } static int get_config_cgroup2_controller(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return __get_config_cgroup_controller(key, retv, inlen, c, CGROUP2_SUPER_MAGIC); } 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(uint32_t) * + * sizeof(" ") * + * sizeof(uint32_t) * + * sizeof(" ") * + * sizeof(uint32_t) * + * \0 */ #define __LXC_IDMAP_STR_BUF (3 * INTTYPE_TO_STRLEN(uint32_t) + 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); break; case LXC_AUTO_CGROUP_MIXED: strprint(retv, inlen, "%scgroup:mixed", sep); break; case LXC_AUTO_CGROUP_RO: strprint(retv, inlen, "%scgroup:ro", sep); break; case LXC_AUTO_CGROUP_RW: strprint(retv, inlen, "%scgroup:rw", sep); break; case LXC_AUTO_CGROUP_FULL_NOSPEC: strprint(retv, inlen, "%scgroup-full", sep); break; case LXC_AUTO_CGROUP_FULL_MIXED: strprint(retv, inlen, "%scgroup-full:mixed", sep); break; case LXC_AUTO_CGROUP_FULL_RO: strprint(retv, inlen, "%scgroup-full:ro", sep); break; case LXC_AUTO_CGROUP_FULL_RW: strprint(retv, inlen, "%scgroup-full:rw", sep); break; default: break; } return fulllen; } static int get_config_mount(const char *key, char *retv, int inlen, struct lxc_conf *c, 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_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) return -1; subkey = strchr(subkey + 1, '.'); if (!subkey) return -1; subkey++; if (*subkey == '\0') 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_hooks_version(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_int(c, retv, inlen, c->hooks_version); } 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_console_rotate(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_int(c, retv, inlen, c->console.log_rotate); } static int get_config_console_buffer_size(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_uint64(c, retv, inlen, c->console.buffer_size); } static int get_config_console_size(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_uint64(c, retv, inlen, c->console.log_size); } 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_execute_cmd(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_str(retv, inlen, c->execute_cmd); } 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_cwd(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { return lxc_get_conf_str(retv, inlen, c->init_cwd); } 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) { /* 2 colon separated 64 bit integers or the word 'unlimited' */ char buf[INTTYPE_TO_STRLEN(uint64_t) * 2 + 2]; int partlen; struct lxc_limit *lim = it->elem; if (lim->limit.rlim_cur == RLIM_INFINITY) { memcpy(buf, "unlimited", STRLITERALLEN("unlimited") + 1); partlen = STRLITERALLEN("unlimited"); } 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", STRLITERALLEN(":unlimited") + 1); 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) == 0) { strprint(retv, inlen, "%s", buf); } } return fulllen; } /* If you ask for a specific value, i.e. lxc.sysctl.net.ipv4.ip_forward, then * just the value will be printed. If you ask for 'lxc.sysctl', then all sysctl * entries will be printed, in 'lxc.sysctl.key = value' format. */ static int get_config_sysctl(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { int len; struct lxc_list *it; int fulllen = 0; bool get_all = false; if (!retv) inlen = 0; else memset(retv, 0, inlen); if (strcmp(key, "lxc.sysctl") == 0) get_all = true; else if (strncmp(key, "lxc.sysctl.", STRLITERALLEN("lxc.sysctl.")) == 0) key += STRLITERALLEN("lxc.sysctl."); else return -1; lxc_list_for_each(it, &c->sysctls) { struct lxc_sysctl *elem = it->elem; if (get_all) { strprint(retv, inlen, "lxc.sysctl.%s = %s\n", elem->key, elem->value); } else if (strcmp(elem->key, key) == 0) { strprint(retv, inlen, "%s", elem->value); } } return fulllen; } static int get_config_proc(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.proc") == 0) get_all = true; else if (strncmp(key, "lxc.proc.", STRLITERALLEN("lxc.proc.")) == 0) key += STRLITERALLEN("lxc.proc."); else return -1; lxc_list_for_each(it, &c->procs) { struct lxc_proc *proc = it->elem; if (get_all) { strprint(retv, inlen, "lxc.proc.%s = %s\n", proc->filename, proc->value); } else if (strcmp(proc->filename, key) == 0) { strprint(retv, inlen, "%s", proc->value); } } return fulllen; } static int get_config_namespace_clone(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { int i, len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); for (i = 0; i < LXC_NS_MAX; i++) { if (c->ns_clone & ns_info[i].clone_flag) strprint(retv, inlen, "%s\n", ns_info[i].proc_name); } return fulllen; } static int get_config_namespace_keep(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { int i, len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); for (i = 0; i < LXC_NS_MAX; i++) { if (c->ns_keep & ns_info[i].clone_flag) strprint(retv, inlen, "%s\n", ns_info[i].proc_name); } return fulllen; } static int get_config_namespace_share(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { int len, ns_idx; const char *namespace; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); namespace = key + STRLITERALLEN("lxc.namespace.share."); ns_idx = lxc_namespace_2_ns_idx(namespace); if (ns_idx < 0) return ns_idx; strprint(retv, inlen, "%s", c->ns_share[ns_idx]); 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->pty_max = 0; return 0; } static inline int clr_config_tty_max(const char *key, struct lxc_conf *c, void *data) { c->ttys.tty = 0; return 0; } static inline int clr_config_tty_dir(const char *key, struct lxc_conf *c, void *data) { free(c->ttys.dir); c->ttys.dir = 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, CGROUP_SUPER_MAGIC); } static inline int clr_config_cgroup2_controller(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_cgroups(c, key, CGROUP2_SUPER_MAGIC); } 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; free(c->rootfs.data); c->rootfs.data = NULL; 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_hooks_version(const char *key, struct lxc_conf *c, void *data) { /* default to legacy hooks version */ c->hooks_version = 0; return 0; } 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_console_rotate(const char *key, struct lxc_conf *c, void *data) { c->console.log_rotate = 0; return 0; } static inline int clr_config_console_buffer_size(const char *key, struct lxc_conf *c, void *data) { c->console.buffer_size = 0; return 0; } static inline int clr_config_console_size(const char *key, struct lxc_conf *c, void *data) { c->console.log_size = 0; 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_execute_cmd(const char *key, struct lxc_conf *c, void *data) { free(c->execute_cmd); c->execute_cmd = NULL; return 0; } 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_cwd(const char *key, struct lxc_conf *c, void *data) { free(c->init_cwd); c->init_cwd = 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_sysctl(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_sysctls(c, key); } static inline int clr_config_proc(const char *key, struct lxc_conf *c, void *data) { return lxc_clear_procs(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 clr_config_namespace_clone(const char *key, struct lxc_conf *lxc_conf, void *data) { lxc_conf->ns_clone = 0; return 0; } static int clr_config_namespace_keep(const char *key, struct lxc_conf *lxc_conf, void *data) { lxc_conf->ns_keep = 0; return 0; } static int clr_config_namespace_share(const char *key, struct lxc_conf *lxc_conf, void *data) { int ns_idx; const char *namespace; namespace = key + STRLITERALLEN("lxc.namespace.share."); ns_idx = lxc_namespace_2_ns_idx(namespace); if (ns_idx < 0) return ns_idx; free(lxc_conf->ns_share[ns_idx]); lxc_conf->ns_share[ns_idx] = NULL; 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) { errno = -ret; SYSERROR("Failed to parse unsigned integer from string \"%s\"", idx_start + 1); *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 = '.'; if (strlen(idx_end + 1) == 0) { ERROR("No subkey in network configuration key \"%s\"", key); goto on_error; } 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = data; struct lxc_list *cur, *next; 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 = 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 = data; struct lxc_list *cur, *next; 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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) { int len; int fulllen = 0; const char *mode; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; char buf[INET_ADDRSTRLEN]; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; size_t listlen; char buf[INET_ADDRSTRLEN]; struct lxc_list *it; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; char buf[INET6_ADDRSTRLEN]; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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; size_t listlen; char buf[INET6_ADDRSTRLEN]; struct lxc_list *it; int fulllen = 0; struct lxc_netdev *netdev = data; if (!retv) inlen = 0; else memset(retv, 0, inlen); 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_jump_table_size; i++) { char *s = config_jump_table[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-host\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; } lxc-3.0.3/src/lxc/string_utils.h0000644061062106075000000001103113375633353013517 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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_STRING_UTILS_H #define __LXC_STRING_UTILS_H #include "config.h" #include "initutils.h" #include "macro.h" /* convert variadic argument lists to arrays (for execl type argument lists) */ extern char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup); extern const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip); /* * Some simple string functions; if they return pointers, they are allocated * buffers. */ extern char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack); extern bool lxc_string_in_array(const char *needle, const char **haystack); extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix); /* * Normalize and split path: Leading and trailing / are removed, multiple * / are compactified, .. and . are resolved (.. on the top level is considered * identical to .). * Examples: * / -> { NULL } * foo/../bar -> { bar, NULL } * ../../ -> { NULL } * ./bar/baz/.. -> { bar, NULL } * foo//bar -> { foo, bar, NULL } */ extern char **lxc_normalize_path(const char *path); /* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ extern char *lxc_deslashify(const char *path); extern char *lxc_append_paths(const char *first, const char *second); /* * Note: the following two functions use strtok(), so they will never * consider an empty element, even if two delimiters are next to * each other. */ extern bool lxc_string_in_list(const char *needle, const char *haystack, char sep); extern char **lxc_string_split(const char *string, char sep); extern char **lxc_string_split_and_trim(const char *string, char sep); extern char **lxc_string_split_quoted(char *string); /* Append string to NULL-terminated string array. */ extern int lxc_append_string(char ***list, char *entry); /* Some simple array manipulation utilities */ typedef void (*lxc_free_fn)(void *); typedef void *(*lxc_dup_fn)(void *); extern int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment); extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); extern size_t lxc_array_len(void **array); extern void **lxc_append_null_to_array(void **array, size_t count); extern void remove_trailing_newlines(char *l); /* Helper functions to parse numbers. */ extern int lxc_safe_uint(const char *numstr, unsigned int *converted); extern int lxc_safe_int(const char *numstr, int *converted); extern int lxc_safe_long(const char *numstr, long int *converted); extern int lxc_safe_long_long(const char *numstr, long long int *converted); extern int lxc_safe_ulong(const char *numstr, unsigned long *converted); extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base); /* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */ extern int parse_byte_size_string(const char *s, int64_t *converted); /* * Concatenate all passed-in strings into one path. Do not fail. If any piece * is not prefixed with '/', add a '/'. */ __attribute__((sentinel)) extern char *must_concat(const char *first, ...); __attribute__((sentinel)) extern char *must_make_path(const char *first, ...); __attribute__((sentinel)) extern char *must_append_path(char *first, ...); /* Return copy of string @entry. Do not fail. */ extern char *must_copy_string(const char *entry); /* Re-allocate a pointer, do not fail */ extern void *must_realloc(void *orig, size_t sz); extern int lxc_char_left_gc(const char *buffer, size_t len); extern int lxc_char_right_gc(const char *buffer, size_t len); extern char *lxc_trim_whitespace_in_place(char *buffer); extern int lxc_is_line_empty(const char *line); extern void remove_trailing_slashes(char *p); #endif /* __LXC_STRING_UTILS_H */ lxc-3.0.3/src/lxc/log.c0000644061062106075000000005070013375633353011553 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Cedric Le Goater * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "caps.h" #include "config.h" #include "file_utils.h" #include "log.h" #include "lxccontainer.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #if HAVE_DLOG #include #undef LOG_TAG #define LOG_TAG "LXC" #endif /* We're logging in seconds and nanoseconds. Assuming that the underlying * datatype is currently at maximum a 64bit integer, we have a date string that * is of maximum length (2^64 - 1) * 2 = (21 + 21) = 42. */ #define LXC_LOG_TIME_SIZE ((INTTYPE_TO_STRLEN(uint64_t)) * 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(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 const char *lxc_log_get_container_name() { #ifndef NO_LXC_CONF if (current_config && !log_vmname) return current_config->name; #endif return log_vmname; } static char *lxc_log_get_va_msg(struct lxc_log_event *event) { char *msg; int rc, len; va_list args; if (!event) return NULL; va_copy(args, *event->vap); len = vsnprintf(NULL, 0, event->fmt, args) + 1; va_end(args); msg = malloc(len * sizeof(char)); if (!msg) return NULL; rc = vsnprintf(msg, len, event->fmt, *event->vap); if (rc == -1 || rc >= len) { free(msg); return NULL; } return msg; } /*---------------------------------------------------------------------------*/ static int log_append_syslog(const struct lxc_log_appender *appender, struct lxc_log_event *event) { char *msg; const char *log_container_name; if (!syslog_enable) return 0; log_container_name = lxc_log_get_container_name(); msg = lxc_log_get_va_msg(event); if (!msg) return 0; syslog(lxc_log_priority_to_syslog(event->priority), "%s%s %s - %s:%s:%d - %s" , log_container_name ? log_container_name : "", log_container_name ? ":" : "", 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) { const char *log_container_name; if (event->priority < LXC_LOG_LEVEL_ERROR) return 0; log_container_name = lxc_log_get_container_name(); fprintf(stderr, "%s: %s%s", log_prefix, log_container_name ? log_container_name : "", log_container_name ? ": " : ""); fprintf(stderr, "%s: %s: %d ", event->locinfo->file, event->locinfo->func, event->locinfo->line); vfprintf(stderr, event->fmt, *event->vap); fprintf(stderr, "\n"); return 0; } /*---------------------------------------------------------------------------*/ static int lxc_unix_epoch_to_utc(char *buf, size_t bufsize, const struct timespec *time) { int64_t epoch_to_days, z, era, doe, yoe, year, doy, mp, day, month, d_in_s, hours, h_in_s, minutes, seconds; char nanosec[INTTYPE_TO_STRLEN(int64_t)]; int ret; /* See https://howardhinnant.github.io/date_algorithms.html for an * explanation of the algorithm used here. */ /* Convert Epoch in seconds to number of days. */ epoch_to_days = time->tv_sec / 86400; /* Shift the Epoch from 1970-01-01 to 0000-03-01. */ z = epoch_to_days + 719468; /* compute the era from the serial date by simply dividing by the number * of days in an era (146097). */ era = (z >= 0 ? z : z - 146096) / 146097; /* The day-of-era (doe) can then be found by subtracting the era number * times the number of days per era, from the serial date. */ doe = (z - era * 146097); /* From the day-of-era (doe), the year-of-era (yoe, range [0, 399]) can * be computed. */ yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; /* Given year-of-era, and era, one can now compute the year. */ year = yoe + era * 400; /* Also the day-of-year, again with the year beginning on Mar. 1, can be * computed from the day-of-era and year-of-era. */ doy = doe - (365 * yoe + yoe / 4 - yoe / 100); /* Given day-of-year, find the month number. */ mp = (5 * doy + 2) / 153; /* From day-of-year and month-of-year we can now easily compute * day-of-month. */ day = doy - (153 * mp + 2) / 5 + 1; /* Transform the month number from the [0, 11] / [Mar, Feb] system to * the civil system: [1, 12] to find the correct month. */ month = mp + (mp < 10 ? 3 : -9); /* The algorithm assumes that a year begins on 1 March, so add 1 before * that. */ if (month < 3) year++; /* Transform days in the epoch to seconds. */ d_in_s = epoch_to_days * 86400; /* To find the current hour simply substract the Epoch_to_days from the * total Epoch and divide by the number of seconds in an hour. */ hours = (time->tv_sec - d_in_s) / 3600; /* Transform hours to seconds. */ h_in_s = hours * 3600; /* Calculate minutes by subtracting the seconds for all days in the * epoch and for all hours in the epoch and divide by the number of * minutes in an hour. */ minutes = (time->tv_sec - d_in_s - h_in_s) / 60; /* Calculate the seconds by subtracting the seconds for all days in the * epoch, hours in the epoch and minutes in the epoch. */ seconds = (time->tv_sec - d_in_s - h_in_s - (minutes * 60)); /* Make string from nanoseconds. */ ret = snprintf(nanosec, sizeof(nanosec), "%"PRId64, (int64_t)time->tv_nsec); if (ret < 0 || ret >= sizeof(nanosec)) return -1; /* Create final timestamp for the log and shorten nanoseconds to 3 * digit precision. */ ret = snprintf(buf, bufsize, "%" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64 ".%.3s", year, month, day, hours, minutes, seconds, nanosec); if (ret < 0 || (size_t)ret >= bufsize) return -1; return 0; } /* This function needs to make extra sure that it is thread-safe. We had some * problems with that before. This especially involves time-conversion * functions. I don't want to find any localtime() or gmtime() functions or * relatives in here. Not even localtime_r() or gmtime_r() or relatives. They * all fiddle with global variables and locking in various libcs. They cause * deadlocks when liblxc is used multi-threaded and no matter how smart you * think you are, you __will__ cause trouble using them. * (As a short example how this can cause trouble: LXD uses forkstart to fork * off a new process that runs the container. At the same time the go runtime * LXD relies on does its own multi-threading thing which we can't control. The * fork()ing + threading then seems to mess with the locking states in these * time functions causing deadlocks.) * The current solution is to be good old unix people and use the Epoch as our * reference point and simply use the seconds and nanoseconds that have past * since then. This relies on clock_gettime() which is explicitly marked MT-Safe * with no restrictions! This way, anyone who is really strongly invested in * getting the actual time the log entry was created, can just convert it for * themselves. Our logging is mostly done for debugging purposes so don't try * to make it pretty. Pretty might cost you thread-safety. */ static int log_append_logfile(const struct lxc_log_appender *appender, struct lxc_log_event *event) { char buffer[LXC_LOG_BUFFER_SIZE]; char date_time[LXC_LOG_TIME_SIZE]; int n; ssize_t ret; int fd_to_use = -1; const char *log_container_name; #ifndef NO_LXC_CONF if (current_config) if (!lxc_log_use_global_fd) fd_to_use = current_config->logfd; #endif log_container_name = lxc_log_get_container_name(); if (fd_to_use == -1) fd_to_use = lxc_log_fd; if (fd_to_use == -1) return 0; if (lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp) < 0) return -1; n = snprintf(buffer, sizeof(buffer), "%s%s%s %s %-8s %s - %s:%s:%d - ", log_prefix, log_container_name ? " " : "", log_container_name ? log_container_name : "", date_time, lxc_log_priority_to_string(event->priority), event->category, event->locinfo->file, event->locinfo->func, event->locinfo->line); if (n < 0) return n; if ((size_t)n < STRARRAYLEN(buffer)) { ret = vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap); if (ret < 0) return 0; n += ret; } if ((size_t)n >= sizeof(buffer)) n = STRARRAYLEN(buffer); buffer[n] = '\n'; return lxc_write_nointr(fd_to_use, buffer, n + 1); } #if HAVE_DLOG static int log_append_dlog(const struct lxc_log_appender *appender, struct lxc_log_event *event) { char *msg = lxc_log_get_va_msg(event); const char *log_container_name = lxc_log_get_container_name(); switch (event->priority) { case LXC_LOG_LEVEL_TRACE: case LXC_LOG_LEVEL_DEBUG: print_log(DLOG_DEBUG, LOG_TAG, "%s: %s(%d) > [%s] %s", event->locinfo->file, event->locinfo->func, event->locinfo->line, log_container_name ? log_container_name : "-", msg ? msg : "-"); break; case LXC_LOG_LEVEL_INFO: print_log(DLOG_INFO, LOG_TAG, "%s: %s(%d) > [%s] %s", event->locinfo->file, event->locinfo->func, event->locinfo->line, log_container_name ? log_container_name : "-", msg ? msg : "-"); break; case LXC_LOG_LEVEL_NOTICE: case LXC_LOG_LEVEL_WARN: print_log(DLOG_WARN, LOG_TAG, "%s: %s(%d) > [%s] %s", event->locinfo->file, event->locinfo->func, event->locinfo->line, log_container_name ? log_container_name : "-", msg ? msg : "-"); break; case LXC_LOG_LEVEL_ERROR: print_log(DLOG_ERROR, LOG_TAG, "%s: %s(%d) > [%s] %s", event->locinfo->file, event->locinfo->func, event->locinfo->line, log_container_name ? log_container_name : "-", msg ? msg : "-"); break; case LXC_LOG_LEVEL_CRIT: case LXC_LOG_LEVEL_ALERT: case LXC_LOG_LEVEL_FATAL: print_log(DLOG_FATAL, LOG_TAG, "%s: %s(%d) > [%s] %s", event->locinfo->file, event->locinfo->func, event->locinfo->line, log_container_name ? log_container_name : "-", msg ? msg : "-"); break; default: break; } free(msg); return 0; } #endif 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, .next = NULL, }; static struct lxc_log_appender log_appender_logfile = { .name = "logfile", .append = log_append_logfile, .next = NULL, }; #if HAVE_DLOG static struct lxc_log_appender log_appender_dlog = { .name = "dlog", .append = log_append_dlog, .next = NULL, }; #endif static struct lxc_log_category log_root = { .name = "root", .priority = LXC_LOG_LEVEL_ERROR, .appender = NULL, .parent = NULL, }; #if HAVE_DLOG struct lxc_log_category lxc_log_category_lxc = { .name = "lxc", .priority = LXC_LOG_LEVEL_TRACE, .appender = &log_appender_dlog, .parent = &log_root }; #else struct lxc_log_category lxc_log_category_lxc = { .name = "lxc", .priority = LXC_LOG_LEVEL_ERROR, .appender = &log_appender_logfile, .parent = &log_root }; #endif /*---------------------------------------------------------------------------*/ static int build_dir(const char *name) { char *e, *n, *p; /* Make copy of the string since we'll be modifying it. */ n = strdup(name); if (!n) return -1; e = &n[strlen(n)]; for (p = n + 1; p < e; p++) { int ret; if (*p != '/') continue; *p = '\0'; ret = lxc_unpriv(mkdir(n, 0755)); if (ret && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", n); free(n); return -1; } *p = '/'; } free(n); return 0; } /*---------------------------------------------------------------------------*/ static int log_open(const char *name) { int fd; int newfd; fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0660)); if (fd < 0) { SYSERROR("Failed to open log file \"%s\"", name); return -1; } if (fd > 2) return fd; newfd = fcntl(fd, F_DUPFD_CLOEXEC, STDERR_FILENO); if (newfd == -1) SYSERROR("Failed to dup log fd %d", fd); close(fd); return newfd; } /* * Build the path to the log file * @name : the name of the container * @lxcpath : the lxcpath to use as a basename or NULL to use LOGPATH * Returns malloced path on success, or NULL on failure */ static char *build_log_path(const char *name, const char *lxcpath) { char *p; int ret; size_t len; bool use_dir; if (!name) return NULL; #if USE_CONFIGPATH_LOGS use_dir = true; #else use_dir = false; #endif /* * If USE_CONFIGPATH_LOGS is true or lxcpath is given, the resulting * path will be: * '$logpath' + '/' + '$name' + '/' + '$name' + '.log' + '\0' * * If USE_CONFIGPATH_LOGS is false the resulting path will be: * '$logpath' + '/' + '$name' + '.log' + '\0' */ len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */ if (lxcpath) use_dir = true; else lxcpath = LOGPATH; if (use_dir) len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */ else len += strlen(lxcpath) + 1; p = malloc(len); if (!p) return p; if (use_dir) ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name); else ret = snprintf(p, len, "%s/%s.log", lxcpath, name); if (ret < 0 || (size_t)ret >= len) { free(p); return NULL; } return p; } /* * 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.loge can override this. * 2. when a program calls lxc_log_init with a logfile parameter. In this * 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) { /* we are overriding the default. */ if (lxc_log_fd != -1) lxc_log_close(); if (!fname) return -1; if (strlen(fname) == 0) { log_fname = NULL; return -1; } #if USE_CONFIGPATH_LOGS /* We don't build_dir for the default if the default is i.e. * /var/lib/lxc/$container/$container.log. */ if (create_dirs) #endif if (build_dir(fname)) { SYSERROR("Failed to create dir for log file \"%s\"", fname); return -1; } lxc_log_fd = log_open(fname); if (lxc_log_fd == -1) return -1; log_fname = strdup(fname); return 0; } static int _lxc_log_set_file(const char *name, const char *lxcpath, int create_dirs) { char *logfile; int ret; logfile = build_log_path(name, lxcpath); if (!logfile) { ERROR("Could not build log path"); return -1; } ret = __lxc_log_set_file(logfile, create_dirs); free(logfile); return ret; } /* * lxc_log_init: * Called from lxc front-end programs (like lxc-create, lxc-start) to * initialize the log defaults. */ int lxc_log_init(struct lxc_log *log) { int ret; int lxc_priority = LXC_LOG_LEVEL_ERROR; if (!log) return -1; if (lxc_log_fd != -1) { WARN("Log already initialized"); return 0; } if (log->level) lxc_priority = lxc_log_priority_to_int(log->level); if (!lxc_loglevel_specified) { lxc_log_category_lxc.priority = lxc_priority; lxc_loglevel_specified = 1; } if (!lxc_quiet_specified) if (!log->quiet) lxc_log_category_lxc.appender->next = &log_appender_stderr; if (log->prefix) lxc_log_set_prefix(log->prefix); if (log->name) log_vmname = strdup(log->name); if (log->file) { if (strcmp(log->file, "none") == 0) return 0; ret = __lxc_log_set_file(log->file, 1); if (ret < 0) { ERROR("Failed to enable logfile"); return -1; } lxc_log_use_global_fd = 1; } else { /* if no name was specified, there nothing to do */ if (!log->name) return 0; ret = -1; if (!log->lxcpath) log->lxcpath = LOGPATH; /* try LOGPATH if lxcpath is the default for the privileged containers */ if (!geteuid() && strcmp(LXCPATH, log->lxcpath) == 0) ret = _lxc_log_set_file(log->name, NULL, 0); /* try in lxcpath */ if (ret < 0) ret = _lxc_log_set_file(log->name, log->lxcpath, 1); /* try LOGPATH in case its writable by the caller */ if (ret < 0) ret = _lxc_log_set_file(log->name, NULL, 0); } /* * If !file, that is, if the user did not request this logpath, then * ignore failures and continue logging to console */ if (!log->file && ret != 0) { INFO("Ignoring failure to open default logfile"); ret = 0; } if (lxc_log_fd != -1) { lxc_log_category_lxc.appender = &log_appender_logfile; lxc_log_category_lxc.appender->next = &log_appender_stderr; } return ret; } void lxc_log_close(void) { closelog(); free(log_vmname); log_vmname = NULL; if (lxc_log_fd == -1) return; close(lxc_log_fd); lxc_log_fd = -1; free(log_fname); log_fname = NULL; } 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; /* Check if syslog was already added, to avoid creating a loop */ while (appender) { if (appender == &log_appender_syslog) { /* not an error: openlog re-opened the connection */ return 0; } appender = appender->next; } appender = lxc_log_category_lxc.appender; while (appender->next != NULL) appender = appender->next; appender->next = &log_appender_syslog; return 0; } inline void lxc_log_enable_syslog(void) { syslog_enable = 1; } /* * 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. */ int lxc_log_set_level(int *dest, int level) { if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET) { ERROR("Invalid log priority %d", level); return -1; } *dest = level; return 0; } inline int lxc_log_get_level(void) { return lxc_log_category_lxc.priority; } bool lxc_log_has_valid_level(void) { int log_level; log_level = lxc_log_get_level(); if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET) return false; return true; } /* * This is called when we read a lxc.logfile entry in a lxc.conf file. This * happens after processing command line arguments, which override the .conf * settings. So only set the file if previously unset. */ int lxc_log_set_file(int *fd, const char *fname) { if (*fd >= 0) { close(*fd); *fd = -1; } if (build_dir(fname)) return -1; *fd = log_open(fname); if (*fd < 0) return -1; return 0; } inline const char *lxc_log_get_file(void) { return log_fname; } inline void lxc_log_set_prefix(const char *prefix) { /* We don't care if the prefix is truncated. */ (void)strlcpy(log_prefix, prefix, sizeof(log_prefix)); } inline const char *lxc_log_get_prefix(void) { return log_prefix; } inline void lxc_log_options_no_override() { lxc_quiet_specified = 1; lxc_loglevel_specified = 1; } lxc-3.0.3/src/lxc/sync.c0000644061062106075000000000712113375633353011745 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include "config.h" #include "log.h" #include "start.h" #include "sync.h" #include "utils.h" lxc_log_define(sync, lxc); static int __sync_wait(int fd, int sequence) { int sync = -1; ssize_t ret; ret = lxc_read_nointr(fd, &sync, sizeof(sync)); if (ret < 0) { SYSERROR("Sync wait failure"); return -1; } if (!ret) return 0; if ((size_t)ret != sizeof(sync)) { ERROR("Unexpected sync size: %zu expected %zu", (size_t)ret, sizeof(sync)); return -1; } if (sync == LXC_SYNC_ERROR) { ERROR("An error occurred in another process " "(expected sequence number %d)", sequence); return -1; } if (sync != sequence) { ERROR("Invalid sequence number %d. Expected sequence number %d", sync, sequence); return -1; } return 0; } static int __sync_wake(int fd, int sequence) { int sync = sequence; if (lxc_write_nointr(fd, &sync, sizeof(sync)) < 0) { SYSERROR("Sync wake failure"); return -1; } return 0; } static int __sync_barrier(int fd, int sequence) { if (__sync_wake(fd, sequence)) return -1; return __sync_wait(fd, sequence+1); } int lxc_sync_barrier_parent(struct lxc_handler *handler, int sequence) { return __sync_barrier(handler->sync_sock[0], sequence); } int lxc_sync_barrier_child(struct lxc_handler *handler, int sequence) { return __sync_barrier(handler->sync_sock[1], sequence); } int lxc_sync_wake_parent(struct lxc_handler *handler, int sequence) { return __sync_wake(handler->sync_sock[0], sequence); } int lxc_sync_wait_parent(struct lxc_handler *handler, int sequence) { return __sync_wait(handler->sync_sock[0], sequence); } int lxc_sync_wait_child(struct lxc_handler *handler, int sequence) { return __sync_wait(handler->sync_sock[1], sequence); } int lxc_sync_wake_child(struct lxc_handler *handler, int sequence) { return __sync_wake(handler->sync_sock[1], sequence); } int lxc_sync_init(struct lxc_handler *handler) { int ret; ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, handler->sync_sock); if (ret) { SYSERROR("failed to create synchronization socketpair"); return -1; } /* Be sure we don't inherit this after the exec */ fcntl(handler->sync_sock[0], F_SETFD, FD_CLOEXEC); return 0; } void lxc_sync_fini_child(struct lxc_handler *handler) { if (handler->sync_sock[0] != -1) { close(handler->sync_sock[0]); handler->sync_sock[0] = -1; } } void lxc_sync_fini_parent(struct lxc_handler *handler) { if (handler->sync_sock[1] != -1) { close(handler->sync_sock[1]); handler->sync_sock[1] = -1; } } void lxc_sync_fini(struct lxc_handler *handler) { lxc_sync_fini_child(handler); lxc_sync_fini_parent(handler); } lxc-3.0.3/src/lxc/namespace.c0000644061062106075000000001175613375633353012736 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2009 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "namespace.h" #include "utils.h" lxc_log_define(namespace, lxc); struct clone_arg { int (*fn)(void *); void *arg; }; static int do_clone(void *arg) { struct clone_arg *clone_arg = arg; return clone_arg->fn(clone_arg->arg); } pid_t lxc_clone(int (*fn)(void *), void *arg, int flags) { struct clone_arg clone_arg = { .fn = fn, .arg = arg, }; size_t stack_size = lxc_getpagesize(); void *stack = alloca(stack_size); pid_t ret; #ifdef __ia64__ ret = __clone2(do_clone, stack, stack_size, flags | SIGCHLD, &clone_arg); #else ret = clone(do_clone, stack + stack_size, flags | SIGCHLD, &clone_arg); #endif if (ret < 0) SYSERROR("Failed to clone (%#x)", flags); return ret; } /* Leave the user namespace at the first position in the array of structs so * that we always attach to it first when iterating over the struct and using * setns() to switch namespaces. This especially affects lxc_attach(): Suppose * you cloned a new user namespace and mount namespace as an unprivileged user * on the host and want to setns() to the mount namespace. This requires you to * attach to the user namespace first otherwise the kernel will fail this check: * * if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || * !ns_capable(current_user_ns(), CAP_SYS_CHROOT) || * !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) * return -EPERM; * * in * * linux/fs/namespace.c:mntns_install(). */ const struct ns_info ns_info[LXC_NS_MAX] = { [LXC_NS_USER] = { "user", CLONE_NEWUSER, "CLONE_NEWUSER", "LXC_USER_NS" }, [LXC_NS_MNT] = { "mnt", CLONE_NEWNS, "CLONE_NEWNS", "LXC_MNT_NS" }, [LXC_NS_PID] = { "pid", CLONE_NEWPID, "CLONE_NEWPID", "LXC_PID_NS" }, [LXC_NS_UTS] = { "uts", CLONE_NEWUTS, "CLONE_NEWUTS", "LXC_UTS_NS" }, [LXC_NS_IPC] = { "ipc", CLONE_NEWIPC, "CLONE_NEWIPC", "LXC_IPC_NS" }, [LXC_NS_NET] = { "net", CLONE_NEWNET, "CLONE_NEWNET", "LXC_NET_NS" }, [LXC_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP, "CLONE_NEWCGROUP", "LXC_CGROUP_NS" } }; int lxc_namespace_2_cloneflag(const char *namespace) { int i; for (i = 0; i < LXC_NS_MAX; i++) if (!strcasecmp(ns_info[i].proc_name, namespace)) return ns_info[i].clone_flag; ERROR("Invalid namespace name \"%s\"", namespace); return -EINVAL; } int lxc_namespace_2_ns_idx(const char *namespace) { int i; for (i = 0; i < LXC_NS_MAX; i++) if (!strcmp(ns_info[i].proc_name, namespace)) return i; ERROR("Invalid namespace name \"%s\"", namespace); return -EINVAL; } extern int lxc_namespace_2_std_identifiers(char *namespaces) { char **it; char *del; /* The identifiers for namespaces used with lxc-attach and lxc-unshare * as given on the manpage do not align with the standard identifiers. * This affects network, mount, and uts namespaces. The standard identifiers * are: "mnt", "uts", and "net" whereas lxc-attach and lxc-unshare uses * "MOUNT", "UTSNAME", and "NETWORK". So let's use some cheap memmove()s * to replace them by their standard identifiers. * Let's illustrate this with an example: * Assume the string: * * "IPC|MOUNT|PID" * * then we memmove() * * dest: del + 1 == OUNT|PID * src: del + 3 == NT|PID */ if (!namespaces) return -1; while ((del = strstr(namespaces, "MOUNT"))) memmove(del + 1, del + 3, strlen(del) - 2); for (it = (char *[]){"NETWORK", "UTSNAME", NULL}; it && *it; it++) while ((del = strstr(namespaces, *it))) memmove(del + 3, del + 7, strlen(del) - 6); return 0; } int lxc_fill_namespace_flags(char *flaglist, int *flags) { char *token; int aflag; if (!flaglist) { ERROR("At least one namespace is needed."); return -1; } lxc_iterate_parts(token, flaglist, "|") { aflag = lxc_namespace_2_cloneflag(token); if (aflag < 0) return -1; *flags |= aflag; } return 0; } lxc-3.0.3/src/lxc/terminal.c0000644061062106075000000006672313375633353012621 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "caps.h" #include "commands.h" #include "conf.h" #include "config.h" #include "log.h" #include "lxclock.h" #include "mainloop.h" #include "start.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #if HAVE_PTY_H #include #else #include <../include/openpty.h> #endif #define LXC_TERMINAL_BUFFER_SIZE 1024 lxc_log_define(terminal, lxc); static struct lxc_list lxc_ttys; typedef void (*sighandler_t)(int); __attribute__((constructor)) void lxc_terminal_init_global(void) { lxc_list_init(&lxc_ttys); } void lxc_terminal_winsz(int srcfd, int dstfd) { int ret; struct winsize wsz; if (!isatty(srcfd)) return; ret = ioctl(srcfd, TIOCGWINSZ, &wsz); if (ret < 0) { WARN("Failed to get window size"); return; } ret = ioctl(dstfd, TIOCSWINSZ, &wsz); if (ret < 0) WARN("Failed to set window size"); else DEBUG("Set window size to %d columns and %d rows", wsz.ws_col, wsz.ws_row); return; } static void lxc_terminal_winch(struct lxc_terminal_state *ts) { lxc_terminal_winsz(ts->stdinfd, ts->masterfd); if (ts->winch_proxy) lxc_cmd_terminal_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); } void lxc_terminal_sigwinch(int sig) { struct lxc_list *it; struct lxc_terminal_state *ts; lxc_list_for_each(it, &lxc_ttys) { ts = it->elem; lxc_terminal_winch(ts); } } int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { ssize_t ret; struct signalfd_siginfo siginfo; struct lxc_terminal_state *ts = cbdata; ret = lxc_read_nointr(fd, &siginfo, sizeof(siginfo)); if (ret < 0 || (size_t)ret < sizeof(siginfo)) { ERROR("Failed to read signal info"); return LXC_MAINLOOP_ERROR; } if (siginfo.ssi_signo == SIGTERM) { DEBUG("Received SIGTERM. Detaching from the terminal"); return LXC_MAINLOOP_CLOSE; } if (siginfo.ssi_signo == SIGWINCH) lxc_terminal_winch(ts); return LXC_MAINLOOP_CONTINUE; } struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd) { int ret; bool istty = false; sigset_t mask; struct lxc_terminal_state *ts; ts = malloc(sizeof(*ts)); if (!ts) return NULL; memset(ts, 0, sizeof(*ts)); ts->stdinfd = srcfd; ts->masterfd = dstfd; ts->sigfd = -1; ret = sigemptyset(&mask); if (ret < 0) { SYSERROR("Failed to initialize an empty signal set"); goto on_error; } istty = (isatty(srcfd) == 1); if (!istty) { INFO("fd %d does not refer to a tty device", srcfd); } else { /* Add tty to list to be scanned at SIGWINCH time. */ lxc_list_add_elem(&ts->node, ts); lxc_list_add_tail(&lxc_ttys, &ts->node); ret = sigaddset(&mask, SIGWINCH); if (ret < 0) SYSNOTICE("Failed to add SIGWINCH to signal set"); } /* Exit the mainloop cleanly on SIGTERM. */ ret = sigaddset(&mask, SIGTERM); if (ret < 0) { SYSERROR("Failed to add SIGWINCH to signal set"); goto on_error; } ret = pthread_sigmask(SIG_BLOCK, &mask, &ts->oldmask); if (ret < 0) { WARN("Failed to block signals"); goto on_error; } ts->sigfd = signalfd(-1, &mask, SFD_CLOEXEC); if (ts->sigfd < 0) { WARN("Failed to create signal fd"); (void)pthread_sigmask(SIG_SETMASK, &ts->oldmask, NULL); goto on_error; } DEBUG("Created signal fd %d", ts->sigfd); return ts; on_error: ERROR("Failed to create signal fd"); if (ts->sigfd >= 0) { close(ts->sigfd); ts->sigfd = -1; } if (istty) lxc_list_del(&ts->node); return ts; } void lxc_terminal_signal_fini(struct lxc_terminal_state *ts) { if (ts->sigfd >= 0) { close(ts->sigfd); if (pthread_sigmask(SIG_SETMASK, &ts->oldmask, NULL) < 0) SYSWARN("Failed to restore signal mask"); } if (isatty(ts->stdinfd)) lxc_list_del(&ts->node); free(ts); } static int lxc_terminal_truncate_log_file(struct lxc_terminal *terminal) { /* be very certain things are kosher */ if (!terminal->log_path || terminal->log_fd < 0) return -EBADF; return lxc_unpriv(ftruncate(terminal->log_fd, 0)); } static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal) { int ret; size_t len; char *tmp; if (!terminal->log_path || terminal->log_rotate == 0) return -EOPNOTSUPP; /* be very certain things are kosher */ if (terminal->log_fd < 0) return -EBADF; len = strlen(terminal->log_path) + sizeof(".1"); tmp = alloca(len); ret = snprintf(tmp, len, "%s.1", terminal->log_path); if (ret < 0 || (size_t)ret >= len) return -EFBIG; close(terminal->log_fd); terminal->log_fd = -1; ret = lxc_unpriv(rename(terminal->log_path, tmp)); if (ret < 0) return ret; return lxc_terminal_create_log_file(terminal); } static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf, int bytes_read) { int ret; struct stat st; int64_t space_left = -1; if (terminal->log_fd < 0) return 0; /* A log size <= 0 means that there's no limit on the size of the log * file at which point we simply ignore whether the log is supposed to * be rotated or not. */ if (terminal->log_size <= 0) return lxc_write_nointr(terminal->log_fd, buf, bytes_read); /* Get current size of the log file. */ ret = fstat(terminal->log_fd, &st); if (ret < 0) { SYSERROR("Failed to stat the terminal log file descriptor"); return -1; } /* handle non-regular files */ if ((st.st_mode & S_IFMT) != S_IFREG) { /* This isn't a regular file. so rotating the file seems a * dangerous thing to do, size limits are also very * questionable. Let's not risk anything and tell the user that * he's requesting us to do weird stuff. */ if (terminal->log_rotate > 0 || terminal->log_size > 0) return -EINVAL; /* I mean, sure log wherever you want to. */ return lxc_write_nointr(terminal->log_fd, buf, bytes_read); } space_left = terminal->log_size - st.st_size; /* User doesn't want to rotate the log file and there's no more space * left so simply truncate it. */ if (space_left <= 0 && terminal->log_rotate <= 0) { ret = lxc_terminal_truncate_log_file(terminal); if (ret < 0) return ret; if (bytes_read <= terminal->log_size) return lxc_write_nointr(terminal->log_fd, buf, bytes_read); /* Write as much as we can into the buffer and loose the rest. */ return lxc_write_nointr(terminal->log_fd, buf, terminal->log_size); } /* There's enough space left. */ if (bytes_read <= space_left) return lxc_write_nointr(terminal->log_fd, buf, bytes_read); /* There's not enough space left but at least write as much as we can * into the old log file. */ ret = lxc_write_nointr(terminal->log_fd, buf, space_left); if (ret < 0) return -1; /* Calculate how many bytes we still need to write. */ bytes_read -= space_left; /* There'd be more to write but we aren't instructed to rotate the log * file so simply return. There's no error on our side here. */ if (terminal->log_rotate > 0) ret = lxc_terminal_rotate_log_file(terminal); else ret = lxc_terminal_truncate_log_file(terminal); if (ret < 0) return ret; if (terminal->log_size < bytes_read) { /* Well, this is unfortunate because it means that there is more * to write than the user has granted us space. There are * multiple ways to handle this but let's use the simplest one: * write as much as we can, tell the user that there was more * stuff to write and move on. * Note that this scenario shouldn't actually happen with the * standard pty-based terminal that LXC allocates since it will * be switched into raw mode. In raw mode only 1 byte at a time * should be read and written. */ WARN("Size of terminal log file is smaller than the bytes to write"); ret = lxc_write_nointr(terminal->log_fd, buf, terminal->log_size); if (ret < 0) return -1; bytes_read -= ret; return bytes_read; } /* Yay, we made it. */ ret = lxc_write_nointr(terminal->log_fd, buf, bytes_read); if (ret < 0) return -1; bytes_read -= ret; return bytes_read; } int lxc_terminal_io_cb(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { struct lxc_terminal *terminal = data; char buf[LXC_TERMINAL_BUFFER_SIZE]; int r, w, w_log, w_rbuf; w = r = lxc_read_nointr(fd, buf, sizeof(buf)); if (r <= 0) { INFO("Terminal client on fd %d has exited", fd); lxc_mainloop_del_handler(descr, fd); if (fd == terminal->master) { terminal->master = -EBADF; } else if (fd == terminal->peer) { if (terminal->tty_state) { lxc_terminal_signal_fini(terminal->tty_state); terminal->tty_state = NULL; } terminal->peer = -EBADF; } else { ERROR("Handler received unexpected file descriptor"); } close(fd); return LXC_MAINLOOP_CLOSE; } if (fd == terminal->peer) w = lxc_write_nointr(terminal->master, buf, r); w_rbuf = w_log = 0; if (fd == terminal->master) { /* write to peer first */ if (terminal->peer >= 0) w = lxc_write_nointr(terminal->peer, buf, r); /* write to terminal ringbuffer */ if (terminal->buffer_size > 0) w_rbuf = lxc_ringbuf_write(&terminal->ringbuf, buf, r); /* write to terminal log */ if (terminal->log_fd >= 0) w_log = lxc_terminal_write_log_file(terminal, buf, r); } if (w != r) WARN("Short write on terminal r:%d != w:%d", r, w); if (w_rbuf < 0) { errno = -w_rbuf; SYSTRACE("Failed to write %d bytes to terminal ringbuffer", r); } if (w_log < 0) TRACE("Failed to write %d bytes to terminal log", r); return LXC_MAINLOOP_CONTINUE; } static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal) { int ret; if (terminal->peer >= 0) { ret = lxc_mainloop_add_handler(terminal->descr, terminal->peer, lxc_terminal_io_cb, terminal); if (ret < 0) { WARN("Failed to add terminal peer handler to mainloop"); return -1; } } if (!terminal->tty_state || terminal->tty_state->sigfd < 0) return 0; ret = lxc_mainloop_add_handler(terminal->descr, terminal->tty_state->sigfd, lxc_terminal_signalfd_cb, terminal->tty_state); if (ret < 0) { WARN("Failed to add signal handler to mainloop"); return -1; } return 0; } int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_terminal *terminal) { int ret; if (terminal->master < 0) { INFO("Terminal is not initialized"); return 0; } ret = lxc_mainloop_add_handler(descr, terminal->master, lxc_terminal_io_cb, terminal); if (ret < 0) { ERROR("Failed to add handler for terminal master fd %d to " "mainloop", terminal->master); return -1; } /* We cache the descr so that we can add an fd to it when someone * does attach to it in lxc_terminal_allocate(). */ terminal->descr = descr; return lxc_terminal_mainloop_add_peer(terminal); } int lxc_setup_tios(int fd, struct termios *oldtios) { int ret; struct termios newtios; if (!isatty(fd)) { ERROR("File descriptor %d does not refert to a terminal", fd); return -1; } /* Get current termios. */ ret = tcgetattr(fd, oldtios); if (ret < 0) { SYSERROR("Failed to get current terminal settings"); return -1; } /* ensure we don't end up in an endless loop: * The kernel might fire SIGTTOU while an * ioctl() in tcsetattr() is executed. When the ioctl() * is resumed and retries, the signal handler interrupts it again. */ signal (SIGTTIN, SIG_IGN); signal (SIGTTOU, SIG_IGN); newtios = *oldtios; /* We use the same settings that ssh does. */ newtios.c_iflag |= IGNPAR; newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); #ifdef IUCLC newtios.c_iflag &= ~IUCLC; #endif newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); #ifdef IEXTEN newtios.c_lflag &= ~IEXTEN; #endif newtios.c_oflag &= ~ONLCR; newtios.c_oflag |= OPOST; newtios.c_cc[VMIN] = 1; newtios.c_cc[VTIME] = 0; /* Set new attributes. */ ret = tcsetattr(fd, TCSAFLUSH, &newtios); if (ret < 0) { ERROR("Failed to set new terminal settings"); return -1; } return 0; } static void lxc_terminal_peer_proxy_free(struct lxc_terminal *terminal) { if (terminal->tty_state) { lxc_terminal_signal_fini(terminal->tty_state); terminal->tty_state = NULL; } close(terminal->proxy.master); terminal->proxy.master = -1; close(terminal->proxy.slave); terminal->proxy.slave = -1; terminal->proxy.busy = -1; terminal->proxy.name[0] = '\0'; terminal->peer = -1; } static int lxc_terminal_peer_proxy_alloc(struct lxc_terminal *terminal, int sockfd) { int ret; struct termios oldtermio; struct lxc_terminal_state *ts; if (terminal->master < 0) { ERROR("Terminal not set up"); return -1; } if (terminal->proxy.busy != -1 || terminal->peer != -1) { NOTICE("Terminal already in use"); return -1; } if (terminal->tty_state) { ERROR("Terminal has already been initialized"); return -1; } /* This is the proxy terminal that will be given to the client, and * that the real terminal master will send to / recv from. */ ret = openpty(&terminal->proxy.master, &terminal->proxy.slave, NULL, NULL, NULL); if (ret < 0) { SYSERROR("Failed to open proxy terminal"); return -1; } ret = ttyname_r(terminal->proxy.slave, terminal->proxy.name, sizeof(terminal->proxy.name)); if (ret < 0) { SYSERROR("Failed to retrieve name of proxy terminal slave"); goto on_error; } ret = fd_cloexec(terminal->proxy.master, true); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on proxy terminal master"); goto on_error; } ret = fd_cloexec(terminal->proxy.slave, true); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on proxy terminal slave"); goto on_error; } ret = lxc_setup_tios(terminal->proxy.slave, &oldtermio); if (ret < 0) goto on_error; ts = lxc_terminal_signal_init(terminal->proxy.master, terminal->master); if (!ts) goto on_error; terminal->tty_state = ts; terminal->peer = terminal->proxy.slave; terminal->proxy.busy = sockfd; ret = lxc_terminal_mainloop_add_peer(terminal); if (ret < 0) goto on_error; NOTICE("Opened proxy terminal with master fd %d and slave fd %d", terminal->proxy.master, terminal->proxy.slave); return 0; on_error: lxc_terminal_peer_proxy_free(terminal); return -1; } int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) { int ttynum; int masterfd = -1; struct lxc_tty_info *ttys = &conf->ttys; struct lxc_terminal *terminal = &conf->console; if (*ttyreq == 0) { int ret; ret = lxc_terminal_peer_proxy_alloc(terminal, sockfd); if (ret < 0) goto out; masterfd = terminal->proxy.master; goto out; } if (*ttyreq > 0) { if (*ttyreq > ttys->max) goto out; if (ttys->tty[*ttyreq - 1].busy) goto out; /* The requested tty is available. */ ttynum = *ttyreq; goto out_tty; } /* Search for next available tty, fixup index tty1 => [0]. */ for (ttynum = 1; ttynum <= ttys->max && ttys->tty[ttynum - 1].busy; ttynum++) { ; } /* We didn't find any available slot for tty. */ if (ttynum > ttys->max) goto out; *ttyreq = ttynum; out_tty: ttys->tty[ttynum - 1].busy = sockfd; masterfd = ttys->tty[ttynum - 1].master; out: return masterfd; } void lxc_terminal_free(struct lxc_conf *conf, int fd) { int i; struct lxc_tty_info *ttys = &conf->ttys; struct lxc_terminal *terminal = &conf->console; for (i = 0; i < ttys->max; i++) if (ttys->tty[i].busy == fd) ttys->tty[i].busy = 0; if (terminal->proxy.busy != fd) return; lxc_mainloop_del_handler(terminal->descr, terminal->proxy.slave); lxc_terminal_peer_proxy_free(terminal); } static int lxc_terminal_peer_default(struct lxc_terminal *terminal) { struct lxc_terminal_state *ts; const char *path; int ret = 0; if (terminal->path) path = terminal->path; else path = "/dev/tty"; terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC)); if (terminal->peer < 0) { if (!terminal->path) { errno = ENODEV; SYSDEBUG("The process does not have a controlling terminal"); goto on_succes; } SYSERROR("Failed to open proxy terminal \"%s\"", path); return -ENOTTY; } DEBUG("Using terminal \"%s\" as proxy", path); if (!isatty(terminal->peer)) { ERROR("File descriptor for \"%s\" does not refer to a terminal", path); goto on_error_free_tios; } ts = lxc_terminal_signal_init(terminal->peer, terminal->master); terminal->tty_state = ts; if (!ts) { WARN("Failed to install signal handler"); goto on_error_free_tios; } lxc_terminal_winsz(terminal->peer, terminal->master); terminal->tios = malloc(sizeof(*terminal->tios)); if (!terminal->tios) goto on_error_free_tios; ret = lxc_setup_tios(terminal->peer, terminal->tios); if (ret < 0) goto on_error_close_peer; else goto on_succes; on_error_free_tios: free(terminal->tios); terminal->tios = NULL; on_error_close_peer: close(terminal->peer); terminal->peer = -1; ret = -ENOTTY; on_succes: return ret; } int lxc_terminal_write_ringbuffer(struct lxc_terminal *terminal) { char *r_addr; ssize_t ret; uint64_t used; struct lxc_ringbuf *buf = &terminal->ringbuf; /* There's not log file where we can dump the ringbuffer to. */ if (terminal->log_fd < 0) return 0; used = lxc_ringbuf_used(buf); if (used == 0) return 0; ret = lxc_terminal_truncate_log_file(terminal); if (ret < 0) return ret; /* Write as much as we can without exceeding the limit. */ if (terminal->log_size < used) used = terminal->log_size; r_addr = lxc_ringbuf_get_read_addr(buf); ret = lxc_write_nointr(terminal->log_fd, r_addr, used); if (ret < 0) return -EIO; return 0; } void lxc_terminal_delete(struct lxc_terminal *terminal) { int ret; ret = lxc_terminal_write_ringbuffer(terminal); if (ret < 0) WARN("Failed to write terminal log to disk"); if (terminal->tios && terminal->peer >= 0) { ret = tcsetattr(terminal->peer, TCSAFLUSH, terminal->tios); if (ret < 0) SYSWARN("Failed to set old terminal settings"); } free(terminal->tios); terminal->tios = NULL; if (terminal->peer >= 0) close(terminal->peer); terminal->peer = -1; if (terminal->master >= 0) close(terminal->master); terminal->master = -1; if (terminal->slave >= 0) close(terminal->slave); terminal->slave = -1; if (terminal->log_fd >= 0) close(terminal->log_fd); terminal->log_fd = -1; } /** * Note that this function needs to run before the mainloop starts. Since we * register a handler for the terminal's masterfd when we create the mainloop * the terminal handler needs to see an allocated ringbuffer. */ static int lxc_terminal_create_ringbuf(struct lxc_terminal *terminal) { int ret; struct lxc_ringbuf *buf = &terminal->ringbuf; uint64_t size = terminal->buffer_size; /* no ringbuffer previously allocated and no ringbuffer requested */ if (!buf->addr && size <= 0) return 0; /* ringbuffer allocated but no new ringbuffer requested */ if (buf->addr && size <= 0) { lxc_ringbuf_release(buf); buf->addr = NULL; buf->r_off = 0; buf->w_off = 0; buf->size = 0; TRACE("Deallocated terminal ringbuffer"); return 0; } if (size <= 0) return 0; /* check wether the requested size for the ringbuffer has changed */ if (buf->addr && buf->size != size) { TRACE("Terminal ringbuffer size changed from %" PRIu64 " to %" PRIu64 " bytes. Deallocating terminal ringbuffer", buf->size, size); lxc_ringbuf_release(buf); } ret = lxc_ringbuf_create(buf, size); if (ret < 0) { ERROR("Failed to setup %" PRIu64 " byte terminal ringbuffer", size); return -1; } TRACE("Allocated %" PRIu64 " byte terminal ringbuffer", size); return 0; } /** * This is the terminal log file. Please note that the terminal log file is * (implementation wise not content wise) independent of the terminal ringbuffer. */ int lxc_terminal_create_log_file(struct lxc_terminal *terminal) { if (!terminal->log_path) return 0; terminal->log_fd = lxc_unpriv(open(terminal->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600)); if (terminal->log_fd < 0) { SYSERROR("Failed to open terminal log file \"%s\"", terminal->log_path); return -1; } DEBUG("Using \"%s\" as terminal log file", terminal->log_path); return 0; } int lxc_terminal_create(struct lxc_terminal *terminal) { int ret; ret = openpty(&terminal->master, &terminal->slave, NULL, NULL, NULL); if (ret < 0) { SYSERROR("Failed to open terminal"); return -1; } ret = ttyname_r(terminal->slave, terminal->name, sizeof(terminal->name)); if (ret < 0) { SYSERROR("Failed to retrieve name of terminal slave"); goto err; } ret = fd_cloexec(terminal->master, true); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on terminal master"); goto err; } ret = fd_cloexec(terminal->slave, true); if (ret < 0) { SYSERROR("Failed to set FD_CLOEXEC flag on terminal slave"); goto err; } ret = lxc_terminal_peer_default(terminal); if (ret < 0) { ERROR("Failed to allocate proxy terminal"); goto err; } return 0; err: lxc_terminal_delete(terminal); return -ENODEV; } int lxc_terminal_setup(struct lxc_conf *conf) { int ret; struct lxc_terminal *terminal = &conf->console; if (terminal->path && strcmp(terminal->path, "none") == 0) { INFO("No terminal requested"); return 0; } ret = lxc_terminal_create(terminal); if (ret < 0) return -1; ret = lxc_terminal_create_log_file(terminal); if (ret < 0) goto err; ret = lxc_terminal_create_ringbuf(terminal); if (ret < 0) goto err; return 0; err: lxc_terminal_delete(terminal); return -ENODEV; } static bool __terminal_dup2(int duplicate, int original) { int ret; if (!isatty(original)) return true; ret = dup2(duplicate, original); if (ret < 0) { SYSERROR("Failed to dup2(%d, %d)", duplicate, original); return false; } return true; } int lxc_terminal_set_stdfds(int fd) { int i; if (fd < 0) return 0; for (i = 0; i < 3; i++) if (!__terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}[i])) return -1; return 0; } int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { int ret; char c; struct lxc_terminal_state *ts = cbdata; if (fd != ts->stdinfd) return LXC_MAINLOOP_CLOSE; ret = lxc_read_nointr(ts->stdinfd, &c, 1); if (ret <= 0) return LXC_MAINLOOP_CLOSE; if (ts->escape >= 1) { /* we want to exit the terminal with Ctrl+a q */ if (c == ts->escape && !ts->saw_escape) { ts->saw_escape = 1; return LXC_MAINLOOP_CONTINUE; } if (c == 'q' && ts->saw_escape) return LXC_MAINLOOP_CLOSE; ts->saw_escape = 0; } ret = lxc_write_nointr(ts->masterfd, &c, 1); if (ret <= 0) return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CONTINUE; } int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr) { int r, w; char buf[LXC_TERMINAL_BUFFER_SIZE]; struct lxc_terminal_state *ts = cbdata; if (fd != ts->masterfd) return LXC_MAINLOOP_CLOSE; r = lxc_read_nointr(fd, buf, sizeof(buf)); if (r <= 0) return LXC_MAINLOOP_CLOSE; w = lxc_write_nointr(ts->stdoutfd, buf, r); if (w <= 0 || w != r) return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CONTINUE; } int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path); } int lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { int masterfd, ret, ttyfd; struct lxc_epoll_descr descr; struct termios oldtios; struct lxc_terminal_state *ts; int istty = 0; ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); if (ttyfd < 0) return -1; ret = setsid(); if (ret < 0) TRACE("Process is already group leader"); ts = lxc_terminal_signal_init(stdinfd, masterfd); if (!ts) { ret = -1; goto close_fds; } ts->escape = escape; ts->winch_proxy = c->name; ts->winch_proxy_lxcpath = c->config_path; ts->stdoutfd = stdoutfd; istty = isatty(stdinfd); if (istty) { lxc_terminal_winsz(stdinfd, masterfd); lxc_cmd_terminal_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); } else { INFO("File descriptor %d does not refer to a terminal", stdinfd); } ret = lxc_mainloop_open(&descr); if (ret) { ERROR("Failed to create mainloop"); goto sigwinch_fini; } if (ts->sigfd != -1) { ret = lxc_mainloop_add_handler(&descr, ts->sigfd, lxc_terminal_signalfd_cb, ts); if (ret < 0) { ERROR("Failed to add signal handler to mainloop"); goto close_mainloop; } } ret = lxc_mainloop_add_handler(&descr, ts->stdinfd, lxc_terminal_stdin_cb, ts); if (ret < 0) { ERROR("Failed to add stdin handler"); goto close_mainloop; } ret = lxc_mainloop_add_handler(&descr, ts->masterfd, lxc_terminal_master_cb, ts); if (ret < 0) { ERROR("Failed to add master handler"); goto close_mainloop; } if (ts->escape >= 1) { fprintf(stderr, "\n" "Connected to tty %1$d\n" "Type to exit the console, " " to enter Ctrl+%2$c itself\n", ttynum, 'a' + escape - 1); } if (istty) { ret = lxc_setup_tios(stdinfd, &oldtios); if (ret < 0) goto close_mainloop; } ret = lxc_mainloop(&descr, -1); if (ret < 0) { ERROR("The mainloop returned an error"); goto restore_tios; } ret = 0; restore_tios: if (istty) { istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios); if (istty < 0) SYSWARN("Failed to restore terminal properties"); } close_mainloop: lxc_mainloop_close(&descr); sigwinch_fini: lxc_terminal_signal_fini(ts); close_fds: close(masterfd); close(ttyfd); return ret; } int lxc_make_controlling_terminal(int fd) { int ret; setsid(); ret = ioctl(fd, TIOCSCTTY, (char *)NULL); if (ret < 0) return -1; return 0; } int lxc_terminal_prepare_login(int fd) { int ret; ret = lxc_make_controlling_terminal(fd); if (ret < 0) return -1; ret = lxc_terminal_set_stdfds(fd); if (ret < 0) return -1; if (fd > STDERR_FILENO) close(fd); return 0; } void lxc_terminal_info_init(struct lxc_terminal_info *terminal) { terminal->name[0] = '\0'; terminal->master = -EBADF; terminal->slave = -EBADF; terminal->busy = -1; } void lxc_terminal_init(struct lxc_terminal *terminal) { memset(terminal, 0, sizeof(*terminal)); terminal->slave = -EBADF; terminal->master = -EBADF; terminal->peer = -EBADF; terminal->log_fd = -EBADF; lxc_terminal_info_init(&terminal->proxy); } void lxc_terminal_conf_free(struct lxc_terminal *terminal) { free(terminal->log_path); free(terminal->path); if (terminal->buffer_size > 0 && terminal->ringbuf.addr) lxc_ringbuf_release(&terminal->ringbuf); } int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal) { int ret; if (lxc_list_empty(&c->id_map)) return 0; if (strcmp(terminal->name, "") == 0) return 0; ret = chown_mapped_root(terminal->name, c); if (ret < 0) { ERROR("Failed to chown terminal \"%s\"", terminal->name); return -1; } TRACE("Chowned terminal \"%s\"", terminal->name); return 0; } lxc-3.0.3/src/lxc/confile_utils.c0000644061062106075000000004603713375633353013641 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include "conf.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "error.h" #include "list.h" #include "log.h" #include "lxccontainer.h" #include "macro.h" #include "network.h" #include "parse.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(confile_utils, lxc); int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, unsigned long *hostid, unsigned long *range) { int ret = -1; unsigned long tmp_hostid, tmp_nsid, tmp_range; char tmp_type; char *window, *slide; char *dup = NULL; /* Duplicate string. */ dup = strdup(idmap); if (!dup) goto on_error; /* A prototypical idmap entry would be: "u 1000 1000000 65536" */ /* align */ slide = window = dup; /* skip whitespace */ slide += strspn(slide, " \t\r"); if (slide != window && *slide == '\0') goto on_error; /* Validate type. */ if (*slide != 'u' && *slide != 'g') { ERROR("Invalid id mapping type: %c", *slide); goto on_error; } /* Assign type. */ tmp_type = *slide; /* move beyond type */ slide++; /* align */ window = slide; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* There must be whitespace. */ if (slide == window) goto on_error; /* Mark beginning of nsid. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window || *slide == '\0') goto on_error; /* Mark end of nsid. */ *slide = '\0'; /* Parse nsid. */ if (lxc_safe_ulong(window, &tmp_nsid) < 0) { ERROR("Failed to parse nsid: %s", window); goto on_error; } /* Move beyond \0. */ slide++; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* If there was only one whitespace then we whiped it with our \0 above. * So only ensure that we're not at the end of the string. */ if (*slide == '\0') goto on_error; /* Mark beginning of hostid. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window || *slide == '\0') goto on_error; /* Mark end of nsid. */ *slide = '\0'; /* Parse hostid. */ if (lxc_safe_ulong(window, &tmp_hostid) < 0) { ERROR("Failed to parse hostid: %s", window); goto on_error; } /* Move beyond \0. */ slide++; /* Validate that only whitespace follows. */ slide += strspn(slide, " \t\r"); /* If there was only one whitespace then we whiped it with our \0 above. * So only ensure that we're not at the end of the string. */ if (*slide == '\0') goto on_error; /* Mark beginning of range. */ window = slide; /* Validate that non-whitespace follows. */ slide += strcspn(slide, " \t\r"); /* There must be non-whitespace. */ if (slide == window) goto on_error; /* The range is the last valid entry we expect. So make sure that there * is no trailing garbage and if there is, error out. */ if (*(slide + strspn(slide, " \t\r\n")) != '\0') goto on_error; /* Mark end of range. */ *slide = '\0'; /* Parse range. */ if (lxc_safe_ulong(window, &tmp_range) < 0) { ERROR("Failed to parse id mapping range: %s", window); goto on_error; } *type = tmp_type; *nsid = tmp_nsid; *hostid = tmp_hostid; *range = tmp_range; /* Yay, we survived. */ ret = 0; on_error: free(dup); return ret; } bool lxc_config_value_empty(const char *value) { if (value && strlen(value) > 0) return false; return true; } struct lxc_netdev *lxc_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); /* 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 *mode; mode = lxc_macvlan_flag_to_mode( netdev->priv.macvlan_attr.mode); TRACE("macvlan mode: %s", mode ? 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 lxc_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, size_t size) { size_t retlen; if (!valuep || !value) return -1; retlen = strlcpy(valuep, value, size); if (retlen >= size) ERROR("Network device name \"%s\" is too long (>= %zu)", value, size); return 0; } void rand_complete_hwaddr(char *hwaddr) { const char hex[] = "0123456789abcdef"; char *curs = hwaddr; #ifdef HAVE_RAND_R unsigned int seed; seed = randseed(false); #else (void)randseed(true); #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++; } } bool lxc_config_net_hwaddr(const char *line) { unsigned index; char tmp[7]; if (strncmp(line, "lxc.net", 7) != 0) return false; if (strncmp(line, "lxc.net.hwaddr", 14) == 0) return true; if (strncmp(line, "lxc.network.hwaddr", 18) == 0) return true; if (sscanf(line, "lxc.net.%u.%6s", &index, tmp) == 2 || sscanf(line, "lxc.network.%u.%6s", &index, tmp) == 2) return strncmp(tmp, "hwaddr", 6) == 0; 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; #ifdef HAVE_RAND_R unsigned int seed; seed = randseed(false); ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand_r(&seed) % 255, rand_r(&seed) % 255, rand_r(&seed) % 255); #else (void)randseed(true); ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255, rand() % 255, rand() % 255); #endif 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) { size_t value_len; if (!value) return 0; value_len = strlen(value); if (retv && inlen >= value_len + 1) memcpy(retv, value, value_len + 1); return value_len; } int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) { int len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); strprint(retv, inlen, "%d", v); return fulllen; } int lxc_get_conf_size_t(struct lxc_conf *c, char *retv, int inlen, size_t v) { int len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); strprint(retv, inlen, "%zu", v); return fulllen; } int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v) { int len; int fulllen = 0; if (!retv) inlen = 0; else memset(retv, 0, inlen); strprint(retv, inlen, "%"PRIu64, v); return fulllen; } bool parse_limit_value(const char **value, rlim_t *res) { char *endptr = NULL; if (strncmp(*value, "unlimited", STRLITERALLEN("unlimited")) == 0) { *res = RLIM_INFINITY; *value += STRLITERALLEN("unlimited"); return true; } errno = 0; *res = strtoull(*value, &endptr, 10); if (errno || !endptr) return false; *value = endptr; return true; } static int lxc_container_name_to_pid(const char *lxcname_or_pid, const char *lxcpath) { int ret; signed long int pid; char *err = NULL; pid = strtol(lxcname_or_pid, &err, 10); if (*err != '\0' || pid < 1) { struct lxc_container *c; c = lxc_container_new(lxcname_or_pid, lxcpath); if (!c) { ERROR("\"%s\" is not a valid pid nor a container name", lxcname_or_pid); return -1; } if (!c->may_control(c)) { ERROR("Insufficient privileges to control container " "\"%s\"", c->name); lxc_container_put(c); return -1; } pid = c->init_pid(c); if (pid < 1) { ERROR("Container \"%s\" is not running", c->name); lxc_container_put(c); return -1; } lxc_container_put(c); } ret = kill(pid, 0); if (ret < 0) { SYSERROR("Failed to send signal to pid %d", (int)pid); return -1; } return pid; } int lxc_inherit_namespace(const char *lxcname_or_pid, const char *lxcpath, const char *namespace) { int fd, pid; char *dup, *lastslash; lastslash = strrchr(lxcname_or_pid, '/'); if (lastslash) { dup = strdup(lxcname_or_pid); if (!dup) return -1; dup[lastslash - lxcname_or_pid] = '\0'; pid = lxc_container_name_to_pid(lastslash + 1, dup); free(dup); } else { pid = lxc_container_name_to_pid(lxcname_or_pid, lxcpath); } if (pid < 0) return -1; fd = lxc_preserve_ns(pid, namespace); if (fd < 0) return -1; return fd; } struct signame { int num; const char *name; }; static const struct signame signames[] = { { SIGHUP, "HUP" }, { SIGINT, "INT" }, { SIGQUIT, "QUIT" }, { SIGILL, "ILL" }, { SIGABRT, "ABRT" }, { SIGFPE, "FPE" }, { SIGKILL, "KILL" }, { SIGSEGV, "SEGV" }, { SIGPIPE, "PIPE" }, { SIGALRM, "ALRM" }, { SIGTERM, "TERM" }, { SIGUSR1, "USR1" }, { SIGUSR2, "USR2" }, { SIGCHLD, "CHLD" }, { SIGCONT, "CONT" }, { SIGSTOP, "STOP" }, { SIGTSTP, "TSTP" }, { SIGTTIN, "TTIN" }, { SIGTTOU, "TTOU" }, #ifdef SIGTRAP { SIGTRAP, "TRAP" }, #endif #ifdef SIGIOT { SIGIOT, "IOT" }, #endif #ifdef SIGEMT { SIGEMT, "EMT" }, #endif #ifdef SIGBUS { SIGBUS, "BUS" }, #endif #ifdef SIGSTKFLT { SIGSTKFLT, "STKFLT" }, #endif #ifdef SIGCLD { SIGCLD, "CLD" }, #endif #ifdef SIGURG { SIGURG, "URG" }, #endif #ifdef SIGXCPU { SIGXCPU, "XCPU" }, #endif #ifdef SIGXFSZ { SIGXFSZ, "XFSZ" }, #endif #ifdef SIGVTALRM { SIGVTALRM, "VTALRM" }, #endif #ifdef SIGPROF { SIGPROF, "PROF" }, #endif #ifdef SIGWINCH { SIGWINCH, "WINCH" }, #endif #ifdef SIGIO { SIGIO, "IO" }, #endif #ifdef SIGPOLL { SIGPOLL, "POLL" }, #endif #ifdef SIGINFO { SIGINFO, "INFO" }, #endif #ifdef SIGLOST { SIGLOST, "LOST" }, #endif #ifdef SIGPWR { SIGPWR, "PWR" }, #endif #ifdef SIGUNUSED { SIGUNUSED, "UNUSED" }, #endif #ifdef SIGSYS { SIGSYS, "SYS" }, #endif }; static int sig_num(const char *sig) { unsigned int signum; if (lxc_safe_uint(sig, &signum) < 0) return -1; return signum; } static int rt_sig_num(const char *signame) { int rtmax = 0, sig_n = 0; if (strncasecmp(signame, "max-", 4) == 0) rtmax = 1; signame += 4; if (!isdigit(*signame)) return -1; sig_n = sig_num(signame); sig_n = rtmax ? SIGRTMAX - sig_n : SIGRTMIN + sig_n; if (sig_n > SIGRTMAX || sig_n < SIGRTMIN) return -1; return sig_n; } int sig_parse(const char *signame) { size_t n; if (isdigit(*signame)) { return sig_num(signame); } else if (strncasecmp(signame, "sig", 3) == 0) { signame += 3; if (strncasecmp(signame, "rt", 2) == 0) return rt_sig_num(signame + 2); for (n = 0; n < sizeof(signames) / sizeof((signames)[0]); n++) if (strcasecmp(signames[n].name, signame) == 0) return signames[n].num; } return -1; } lxc-3.0.3/src/lxc/utils.c0000644061062106075000000010617213375633353012137 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "lxclock.h" #include "namespace.h" #include "parse.h" #include "raw_syscalls.h" #include "syscall_wrappers.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef HAVE_STRLCAT #include "include/strlcat.h" #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 #endif lxc_log_define(utils, lxc); /* * if path is btrfs, tries to remove it and any subvolumes beneath it */ extern bool btrfs_try_remove_subvol(const char *path); static int _recursive_rmdir(const char *dirname, dev_t pdev, const char *exclude, int level, bool onedev) { struct dirent *direntp; DIR *dir; int ret, failed = 0; char pathname[PATH_MAX]; bool hadexclude = false; dir = opendir(dirname); if (!dir) { ERROR("Failed to open \"%s\"", dirname); return -1; } while ((direntp = readdir(dir))) { struct stat mystat; int rc; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; rc = snprintf(pathname, PATH_MAX, "%s/%s", dirname, direntp->d_name); if (rc < 0 || rc >= PATH_MAX) { ERROR("The name of path is too long"); failed=1; continue; } if (!level && exclude && !strcmp(direntp->d_name, exclude)) { ret = rmdir(pathname); if (ret < 0) { switch(errno) { case ENOTEMPTY: INFO("Not deleting snapshot \"%s\"", pathname); hadexclude = true; break; case ENOTDIR: ret = unlink(pathname); if (ret) INFO("Failed to remove \"%s\"", pathname); break; default: SYSERROR("Failed to rmdir \"%s\"", pathname); failed = 1; break; } } continue; } ret = lstat(pathname, &mystat); if (ret) { SYSERROR("Failed to stat \"%s\"", pathname); failed = 1; continue; } if (onedev && mystat.st_dev != pdev) { /* TODO should we be checking /proc/self/mountinfo for * pathname and not doing this if found? */ if (btrfs_try_remove_subvol(pathname)) INFO("Removed btrfs subvolume at \"%s\"", pathname); continue; } if (S_ISDIR(mystat.st_mode)) { if (_recursive_rmdir(pathname, pdev, exclude, level+1, onedev) < 0) failed=1; } else { if (unlink(pathname) < 0) { SYSERROR("Failed to delete \"%s\"", pathname); failed=1; } } } if (rmdir(dirname) < 0 && !btrfs_try_remove_subvol(dirname) && !hadexclude) { SYSERROR("Failed to delete \"%s\"", dirname); failed=1; } ret = closedir(dir); if (ret) { SYSERROR("Failed to close directory \"%s\"", dirname); failed=1; } return failed ? -1 : 0; } /* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the * lxc_rmdir_onedev() */ static bool is_native_overlayfs(const char *path) { if (has_fs_type(path, OVERLAY_SUPER_MAGIC) || has_fs_type(path, OVERLAYFS_SUPER_MAGIC)) return true; return false; } /* returns 0 on success, -1 if there were any failures */ extern int lxc_rmdir_onedev(const char *path, const char *exclude) { struct stat mystat; bool onedev = true; if (is_native_overlayfs(path)) onedev = false; if (lstat(path, &mystat) < 0) { if (errno == ENOENT) return 0; SYSERROR("Failed to stat \"%s\"", path); return -1; } return _recursive_rmdir(path, mystat.st_dev, exclude, 0, onedev); } /* borrowed from iproute2 */ extern int get_u16(unsigned short *val, const char *arg, int base) { unsigned long res; char *ptr; if (!arg || !*arg) return -1; errno = 0; res = strtoul(arg, &ptr, base); if (!ptr || ptr == arg || *ptr || res > 0xFFFF || errno != 0) return -1; *val = res; return 0; } int mkdir_p(const char *dir, mode_t mode) { const char *tmp = dir; const char *orig = dir; do { int ret; char *makeme; dir = tmp + strspn(tmp, "/"); tmp = dir + strcspn(dir, "/"); errno = ENOMEM; makeme = strndup(orig, dir - orig); if (!makeme) return -1; ret = mkdir(makeme, mode); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create directory \"%s\"", makeme); free(makeme); return -1; } free(makeme); } while (tmp != dir); return 0; } char *get_rundir() { char *rundir; const char *homedir; struct stat sb; if (stat(RUNTIME_PATH, &sb) < 0) return NULL; if (geteuid() == sb.st_uid || getegid() == sb.st_gid) { rundir = strdup(RUNTIME_PATH); return rundir; } rundir = getenv("XDG_RUNTIME_DIR"); if (rundir) { rundir = strdup(rundir); return rundir; } INFO("XDG_RUNTIME_DIR isn't set in the environment"); homedir = getenv("HOME"); if (!homedir) { ERROR("HOME isn't set in the environment"); return NULL; } rundir = malloc(sizeof(char) * (17 + strlen(homedir))); if (!rundir) return NULL; sprintf(rundir, "%s/.cache/lxc/run/", homedir); return rundir; } int wait_for_pid(pid_t pid) { int status, ret; again: ret = waitpid(pid, &status, 0); if (ret == -1) { if (errno == EINTR) goto again; return -1; } if (ret != pid) goto again; if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return -1; return 0; } int lxc_wait_for_pid_status(pid_t pid) { int status, ret; again: ret = waitpid(pid, &status, 0); if (ret == -1) { if (errno == EINTR) goto again; return -1; } if (ret != pid) goto again; return status; } #if HAVE_LIBGNUTLS #include #include __attribute__((constructor)) static void gnutls_lxc_init(void) { gnutls_global_init(); } int sha1sum_file(char *fnam, unsigned char *digest) { char *buf; int ret; FILE *f; long flen; if (!fnam) return -1; f = fopen_cloexec(fnam, "r"); if (!f) { SYSERROR("Failed to open template \"%s\"", fnam); return -1; } if (fseek(f, 0, SEEK_END) < 0) { SYSERROR("Failed to seek to end of template"); fclose(f); return -1; } if ((flen = ftell(f)) < 0) { SYSERROR("Failed to tell size of template"); fclose(f); return -1; } if (fseek(f, 0, SEEK_SET) < 0) { SYSERROR("Failed to seek to start of template"); fclose(f); return -1; } if ((buf = malloc(flen+1)) == NULL) { SYSERROR("Out of memory"); fclose(f); return -1; } if (fread(buf, 1, flen, f) != flen) { SYSERROR("Failed to read template"); free(buf); fclose(f); return -1; } if (fclose(f) < 0) { SYSERROR("Failed to close template"); free(buf); return -1; } buf[flen] = '\0'; ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, buf, flen, (void *)digest); free(buf); return ret; } #endif struct lxc_popen_FILE *lxc_popen(const char *command) { int ret; int pipe_fds[2]; pid_t child_pid; struct lxc_popen_FILE *fp = NULL; ret = pipe2(pipe_fds, O_CLOEXEC); if (ret < 0) return NULL; child_pid = fork(); if (child_pid < 0) goto on_error; if (!child_pid) { sigset_t mask; close(pipe_fds[0]); /* duplicate stdout */ if (pipe_fds[1] != STDOUT_FILENO) ret = dup2(pipe_fds[1], STDOUT_FILENO); else ret = fcntl(pipe_fds[1], F_SETFD, 0); if (ret < 0) { close(pipe_fds[1]); _exit(EXIT_FAILURE); } /* duplicate stderr */ if (pipe_fds[1] != STDERR_FILENO) ret = dup2(pipe_fds[1], STDERR_FILENO); else ret = fcntl(pipe_fds[1], F_SETFD, 0); close(pipe_fds[1]); if (ret < 0) _exit(EXIT_FAILURE); /* unblock all signals */ ret = sigfillset(&mask); if (ret < 0) _exit(EXIT_FAILURE); ret = pthread_sigmask(SIG_UNBLOCK, &mask, NULL); if (ret < 0) _exit(EXIT_FAILURE); execl("/bin/sh", "sh", "-c", command, (char *)NULL); _exit(127); } close(pipe_fds[1]); pipe_fds[1] = -1; fp = malloc(sizeof(*fp)); if (!fp) goto on_error; memset(fp, 0, sizeof(*fp)); fp->child_pid = child_pid; fp->pipe = pipe_fds[0]; /* From now on, closing fp->f will also close fp->pipe. So only ever * call fclose(fp->f). */ fp->f = fdopen(pipe_fds[0], "r"); if (!fp->f) goto on_error; return fp; on_error: /* We can only close pipe_fds[0] if fdopen() didn't succeed or wasn't * called yet. Otherwise the fd belongs to the file opened by fdopen() * since it isn't dup()ed. */ if (fp && !fp->f && pipe_fds[0] >= 0) close(pipe_fds[0]); if (pipe_fds[1] >= 0) close(pipe_fds[1]); if (fp && fp->f) fclose(fp->f); if (fp) free(fp); return NULL; } int lxc_pclose(struct lxc_popen_FILE *fp) { pid_t wait_pid; int wstatus = 0; if (!fp) return -1; do { wait_pid = waitpid(fp->child_pid, &wstatus, 0); } while (wait_pid < 0 && errno == EINTR); fclose(fp->f); free(fp); if (wait_pid < 0) return -1; return wstatus; } int randseed(bool srand_it) { FILE *f; /* * srand pre-seed function based on /dev/urandom */ unsigned int seed = time(NULL) + getpid(); f = fopen("/dev/urandom", "r"); if (f) { int ret = fread(&seed, sizeof(seed), 1, f); if (ret != 1) SYSDEBUG("Unable to fread /dev/urandom, fallback to time+pid rand seed"); fclose(f); } if (srand_it) srand(seed); return seed; } uid_t get_ns_uid(uid_t orig) { char *line = NULL; size_t sz = 0; uid_t nsid, hostid, range; FILE *f; f = fopen("/proc/self/uid_map", "r"); if (!f) { SYSERROR("Failed to open uid_map"); return 0; } while (getline(&line, &sz, f) != -1) { if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3) continue; if (hostid <= orig && hostid + range > orig) { nsid += orig - hostid; goto found; } } nsid = LXC_INVALID_UID; found: fclose(f); free(line); return nsid; } gid_t get_ns_gid(gid_t orig) { char *line = NULL; size_t sz = 0; gid_t nsid, hostid, range; FILE *f; f = fopen("/proc/self/gid_map", "r"); if (!f) { SYSERROR("Failed to open gid_map"); return 0; } while (getline(&line, &sz, f) != -1) { if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3) continue; if (hostid <= orig && hostid + range > orig) { nsid += orig - hostid; goto found; } } nsid = LXC_INVALID_GID; found: fclose(f); free(line); return nsid; } bool dir_exists(const char *path) { struct stat sb; int ret; ret = stat(path, &sb); if (ret < 0) /* Could be something other than eexist, just say "no". */ return false; return S_ISDIR(sb.st_mode); } /* Note we don't use SHA-1 here as we don't want to depend on HAVE_GNUTLS. * FNV has good anti collision properties and we're not worried * about pre-image resistance or one-way-ness, we're just trying to make * the name unique in the 108 bytes of space we have. */ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval) { unsigned char *bp; for(bp = buf; bp < (unsigned char *)buf + len; bp++) { /* xor the bottom with the current octet */ hval ^= (uint64_t)*bp; /* gcc optimised: * multiply by the 64 bit FNV magic prime mod 2^64 */ hval += (hval << 1) + (hval << 4) + (hval << 5) + (hval << 7) + (hval << 8) + (hval << 40); } return hval; } /* * Detect whether / is mounted MS_SHARED. The only way I know of to * check that is through /proc/self/mountinfo. * I'm only checking for /. If the container rootfs or mount location * is MS_SHARED, but not '/', then you're out of luck - figuring that * out would be too much work to be worth it. */ int detect_shared_rootfs(void) { char buf[LXC_LINELEN], *p; FILE *f; int i; char *p2; f = fopen("/proc/self/mountinfo", "r"); if (!f) return 0; while (fgets(buf, LXC_LINELEN, f)) { for (p = buf, i = 0; p && i < 4; i++) p = strchr(p + 1, ' '); if (!p) continue; p2 = strchr(p + 1, ' '); if (!p2) continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { /* This is '/'. Is it shared? */ p = strchr(p2 + 1, ' '); if (p && strstr(p, "shared:")) { fclose(f); return 1; } } } fclose(f); return 0; } bool switch_to_ns(pid_t pid, const char *ns) { int fd, ret; char nspath[PATH_MAX]; /* Switch to new ns */ ret = snprintf(nspath, PATH_MAX, "/proc/%d/ns/%s", pid, ns); if (ret < 0 || ret >= PATH_MAX) return false; fd = open(nspath, O_RDONLY); if (fd < 0) { SYSERROR("Failed to open \"%s\"", nspath); return false; } ret = setns(fd, 0); if (ret) { SYSERROR("Failed to set process %d to \"%s\" of %d.", pid, ns, fd); close(fd); return false; } close(fd); return true; } /* * looking at fs/proc_namespace.c, it appears we can * actually expect the rootfs entry to very specifically contain * " - rootfs rootfs " * IIUC, so long as we've chrooted so that rootfs is not our root, * the rootfs entry should always be skipped in mountinfo contents. */ bool detect_ramfs_rootfs(void) { FILE *f; char *p, *p2; char *line = NULL; size_t len = 0; int i; f = fopen("/proc/self/mountinfo", "r"); if (!f) { SYSERROR("Failed to open mountinfo"); return false; } while (getline(&line, &len, f) != -1) { for (p = line, i = 0; p && i < 4; i++) p = strchr(p + 1, ' '); if (!p) continue; p2 = strchr(p + 1, ' '); if (!p2) continue; *p2 = '\0'; if (strcmp(p + 1, "/") == 0) { /* This is '/'. Is it the ramfs? */ p = strchr(p2 + 1, '-'); if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) { free(line); fclose(f); INFO("Rootfs is located on ramfs"); return true; } } } free(line); fclose(f); return false; } char *on_path(const char *cmd, const char *rootfs) { char *entry = NULL, *path = NULL; char cmdpath[PATH_MAX]; int ret; path = getenv("PATH"); if (!path) return NULL; path = strdup(path); if (!path) return NULL; lxc_iterate_parts (entry, path, ":") { if (rootfs) ret = snprintf(cmdpath, PATH_MAX, "%s/%s/%s", rootfs, entry, cmd); else ret = snprintf(cmdpath, PATH_MAX, "%s/%s", entry, cmd); if (ret < 0 || ret >= PATH_MAX) continue; if (access(cmdpath, X_OK) == 0) { free(path); return strdup(cmdpath); } } free(path); return NULL; } bool cgns_supported(void) { return file_exists("/proc/self/ns/cgroup"); } /* historically lxc-init has been under /usr/lib/lxc and under * /usr/lib/$ARCH/lxc. It now lives as $prefix/sbin/init.lxc. */ char *choose_init(const char *rootfs) { char *retv = NULL; const char *empty = "", *tmp; int ret, env_set = 0; if (!getenv("PATH")) { if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 0)) SYSERROR("Failed to setenv"); env_set = 1; } retv = on_path("init.lxc", rootfs); if (env_set) if (unsetenv("PATH")) SYSERROR("Failed to unsetenv"); if (retv) return retv; retv = malloc(PATH_MAX); if (!retv) return NULL; if (rootfs) tmp = rootfs; else tmp = empty; ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, SBINDIR, "/init.lxc"); if (ret < 0 || ret >= PATH_MAX) { ERROR("The name of path is too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, LXCINITDIR, "/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("The name of path is too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/usr/lib/lxc/lxc-init", tmp); if (ret < 0 || ret >= PATH_MAX) { ERROR("The name of path is too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; ret = snprintf(retv, PATH_MAX, "%s/sbin/lxc-init", tmp); if (ret < 0 || ret >= PATH_MAX) { ERROR("The name of path is too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; /* * Last resort, look for the statically compiled init.lxc which we * hopefully bind-mounted in. * If we are called during container setup, and we get to this point, * then the init.lxc.static from the host will need to be bind-mounted * in. So we return NULL here to indicate that. */ if (rootfs) goto out1; ret = snprintf(retv, PATH_MAX, "/init.lxc.static"); if (ret < 0 || ret >= PATH_MAX) { WARN("Nonsense - name /lxc.init.static too long"); goto out1; } if (access(retv, X_OK) == 0) return retv; out1: free(retv); return NULL; } /* * Given the '-t' template option to lxc-create, figure out what to * do. If the template is a full executable path, use that. If it * is something like 'sshd', then return $templatepath/lxc-sshd. * On success return the template, on error return NULL. */ char *get_template_path(const char *t) { int ret, len; char *tpath; if (t[0] == '/' && access(t, X_OK) == 0) { tpath = strdup(t); return tpath; } len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; tpath = malloc(len); if (!tpath) return NULL; ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); if (ret < 0 || ret >= len) { free(tpath); return NULL; } if (access(tpath, X_OK) < 0) { SYSERROR("bad template: %s", t); free(tpath); return NULL; } return tpath; } /* * @path: a pathname where / replaced with '\0'. * @offsetp: pointer to int showing which path segment was last seen. * Updated on return to reflect the next segment. * @fulllen: full original path length. * Returns a pointer to the next path segment, or NULL if done. */ static char *get_nextpath(char *path, int *offsetp, int fulllen) { int offset = *offsetp; if (offset >= fulllen) return NULL; while (offset < fulllen && path[offset] != '\0') offset++; while (offset < fulllen && path[offset] == '\0') offset++; *offsetp = offset; return (offset < fulllen) ? &path[offset] : NULL; } /* * Check that @subdir is a subdir of @dir. @len is the length of * @dir (to avoid having to recalculate it). */ static bool is_subdir(const char *subdir, const char *dir, size_t len) { size_t subdirlen = strlen(subdir); if (subdirlen < len) return false; if (strncmp(subdir, dir, len) != 0) return false; if (dir[len-1] == '/') return true; if (subdir[len] == '/' || subdirlen == len) return true; return false; } /* * Check if the open fd is a symlink. Return -ELOOP if it is. Return * -ENOENT if we couldn't fstat. Return 0 if the fd is ok. */ static int check_symlink(int fd) { struct stat sb; int ret; ret = fstat(fd, &sb); if (ret < 0) return -ENOENT; if (S_ISLNK(sb.st_mode)) return -ELOOP; return 0; } /* * Open a file or directory, provided that it contains no symlinks. * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init */ static int open_if_safe(int dirfd, const char *nextpath) { int newfd = openat(dirfd, nextpath, O_RDONLY | O_NOFOLLOW); if (newfd >= 0) /* Was not a symlink, all good. */ return newfd; if (errno == ELOOP) return newfd; if (errno == EPERM || errno == EACCES) { /* We're not root (cause we got EPERM) so try opening with * O_PATH. */ newfd = openat(dirfd, nextpath, O_PATH | O_NOFOLLOW); if (newfd >= 0) { /* O_PATH will return an fd for symlinks. We know * nextpath wasn't a symlink at last openat, so if fd is * now a link, then something * fishy is going on. */ int ret = check_symlink(newfd); if (ret < 0) { close(newfd); newfd = ret; } } } return newfd; } /* * Open a path intending for mounting, ensuring that the final path * is inside the container's rootfs. * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init * * @target: path to be opened * @prefix_skip: a part of @target in which to ignore symbolic links. This * would be the container's rootfs. * * Return an open fd for the path, or <0 on error. */ static int open_without_symlink(const char *target, const char *prefix_skip) { int curlen = 0, dirfd, fulllen, i; char *dup; fulllen = strlen(target); /* make sure prefix-skip makes sense */ if (prefix_skip && strlen(prefix_skip) > 0) { curlen = strlen(prefix_skip); if (!is_subdir(target, prefix_skip, curlen)) { ERROR("WHOA there - target \"%s\" didn't start with prefix \"%s\"", target, prefix_skip); return -EINVAL; } /* * get_nextpath() expects the curlen argument to be * on a (turned into \0) / or before it, so decrement * curlen to make sure that happens */ if (curlen) curlen--; } else { prefix_skip = "/"; curlen = 0; } /* Make a copy of target which we can hack up, and tokenize it */ if ((dup = strdup(target)) == NULL) { ERROR("Out of memory checking for symbolic link"); return -ENOMEM; } for (i = 0; i < fulllen; i++) { if (dup[i] == '/') dup[i] = '\0'; } dirfd = open(prefix_skip, O_RDONLY); if (dirfd < 0) { SYSERROR("Failed to open path \"%s\"", prefix_skip); goto out; } while (1) { int newfd, saved_errno; char *nextpath; if ((nextpath = get_nextpath(dup, &curlen, fulllen)) == NULL) goto out; newfd = open_if_safe(dirfd, nextpath); saved_errno = errno; close(dirfd); dirfd = newfd; if (newfd < 0) { errno = saved_errno; if (errno == ELOOP) SYSERROR("%s in %s was a symbolic link!", nextpath, target); goto out; } } out: free(dup); return dirfd; } /* * Safely mount a path into a container, ensuring that the mount target * is under the container's @rootfs. (If @rootfs is NULL, then the container * uses the host's /) * * CAVEAT: This function must not be used for other purposes than container * setup before executing the container's init */ int safe_mount(const char *src, const char *dest, const char *fstype, unsigned long flags, const void *data, const char *rootfs) { int 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) rootfs = ""; /* todo - allow symlinks for relative paths if 'allowsymlinks' option is passed */ if (flags & MS_BIND && src && src[0] != '/') { INFO("This is a relative bind mount"); srcfd = open_without_symlink(src, NULL); if (srcfd < 0) return srcfd; ret = snprintf(srcbuf, 50, "/proc/self/fd/%d", srcfd); if (ret < 0 || ret > 50) { close(srcfd); ERROR("Out of memory"); return -EINVAL; } mntsrc = srcbuf; } destfd = open_without_symlink(dest, rootfs); if (destfd < 0) { if (srcfd != -1) { saved_errno = errno; close(srcfd); errno = saved_errno; } return destfd; } ret = snprintf(destbuf, 50, "/proc/self/fd/%d", destfd); if (ret < 0 || ret > 50) { if (srcfd != -1) close(srcfd); close(destfd); ERROR("Out of memory"); return -EINVAL; } ret = mount(mntsrc, destbuf, fstype, flags, data); saved_errno = errno; if (srcfd != -1) close(srcfd); close(destfd); if (ret < 0) { errno = saved_errno; SYSERROR("Failed to mount \"%s\" onto \"%s\"", src ? src : "(null)", dest); return ret; } return 0; } /* * Mount a proc under @rootfs if proc self points to a pid other than * my own. This is needed to have a known-good proc mount for setting * up LSMs both at container startup and attach. * * @rootfs : the rootfs where proc should be mounted * * Returns < 0 on failure, 0 if the correct proc was already mounted * and 1 if a new proc was mounted. * * NOTE: not to be called from inside the container namespace! */ int lxc_mount_proc_if_needed(const char *rootfs) { char path[PATH_MAX] = {0}; int link_to_pid, linklen, mypid, ret; char link[INTTYPE_TO_STRLEN(pid_t)] = {0}; ret = snprintf(path, PATH_MAX, "%s/proc/self", rootfs); if (ret < 0 || ret >= PATH_MAX) { SYSERROR("The name of proc path is too long"); return -1; } linklen = readlink(path, link, sizeof(link)); ret = snprintf(path, PATH_MAX, "%s/proc", rootfs); if (ret < 0 || ret >= PATH_MAX) { SYSERROR("The name of proc path is too long"); return -1; } /* /proc not mounted */ if (linklen < 0) { if (mkdir(path, 0755) && errno != EEXIST) return -1; goto domount; } else if (linklen >= sizeof(link)) { link[linklen - 1] = '\0'; ERROR("Readlink returned truncated content: \"%s\"", link); return -1; } mypid = lxc_raw_getpid(); INFO("I am %d, /proc/self points to \"%s\"", mypid, link); if (lxc_safe_int(link, &link_to_pid) < 0) return -1; /* correct procfs is already mounted */ if (link_to_pid == mypid) return 0; ret = umount2(path, MNT_DETACH); if (ret < 0) SYSWARN("Failed to umount \"%s\" with MNT_DETACH", path); domount: /* rootfs is NULL */ if (!strcmp(rootfs, "")) ret = mount("proc", path, "proc", 0, NULL); else ret = safe_mount("proc", path, "proc", 0, NULL, rootfs); if (ret < 0) return -1; INFO("Mounted /proc in container for security transition"); return 1; } int open_devnull(void) { int fd = open("/dev/null", O_RDWR); if (fd < 0) SYSERROR("Can't open /dev/null"); return fd; } int set_stdfds(int fd) { int ret; if (fd < 0) return -1; ret = dup2(fd, STDIN_FILENO); if (ret < 0) return -1; ret = dup2(fd, STDOUT_FILENO); if (ret < 0) return -1; ret = dup2(fd, STDERR_FILENO); if (ret < 0) return -1; return 0; } int null_stdfds(void) { int ret = -1; int fd; fd = open_devnull(); if (fd >= 0) { ret = set_stdfds(fd); close(fd); } return ret; } /* Check whether a signal is blocked by a process. */ /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ #define __PROC_STATUS_LEN (6 + INTTYPE_TO_STRLEN(pid_t) + 7 + 1) bool task_blocks_signal(pid_t pid, int signal) { int ret; char status[__PROC_STATUS_LEN] = {0}; FILE *f; uint64_t sigblk = 0, one = 1; size_t n = 0; bool bret = false; char *line = NULL; ret = snprintf(status, __PROC_STATUS_LEN, "/proc/%d/status", pid); if (ret < 0 || ret >= __PROC_STATUS_LEN) return bret; f = fopen(status, "r"); if (!f) return bret; while (getline(&line, &n, f) != -1) { char *numstr; if (strncmp(line, "SigBlk:", 7)) continue; numstr = lxc_trim_whitespace_in_place(line + 7); ret = lxc_safe_uint64(numstr, &sigblk, 16); if (ret < 0) goto out; break; } if (sigblk & (one << (signal - 1))) bret = true; out: free(line); fclose(f); return bret; } int lxc_preserve_ns(const int pid, const char *ns) { int ret; /* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */ #define __NS_PATH_LEN 50 char path[__NS_PATH_LEN]; /* This way we can use this function to also check whether namespaces * are supported by the kernel by passing in the NULL or the empty * string. */ ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid, !ns || strcmp(ns, "") == 0 ? "" : "/", !ns || strcmp(ns, "") == 0 ? "" : ns); if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) { errno = EFBIG; return -1; } return open(path, O_RDONLY | O_CLOEXEC); } bool lxc_switch_uid_gid(uid_t uid, gid_t gid) { int ret = 0; if (gid != LXC_INVALID_GID) { ret = setgid(gid); if (ret < 0) { SYSERROR("Failed to switch to gid %d", gid); return false; } NOTICE("Switched to gid %d", gid); } if (uid != LXC_INVALID_UID) { ret = setuid(uid); if (ret < 0) { SYSERROR("Failed to switch to uid %d", uid); return false; } NOTICE("Switched to uid %d", uid); } return true; } /* Simple convenience function which enables uniform logging. */ bool lxc_setgroups(int size, gid_t list[]) { if (setgroups(size, list) < 0) { SYSERROR("Failed to setgroups()"); return false; } NOTICE("Dropped additional groups"); return true; } static int lxc_get_unused_loop_dev_legacy(char *loop_name) { struct dirent *dp; struct loop_info64 lo64; DIR *dir; int dfd = -1, fd = -1, ret = -1; dir = opendir("/dev"); if (!dir) { SYSERROR("Failed to open \"/dev\""); return -1; } while ((dp = readdir(dir))) { if (strncmp(dp->d_name, "loop", 4) != 0) continue; dfd = dirfd(dir); if (dfd < 0) continue; fd = openat(dfd, dp->d_name, O_RDWR); if (fd < 0) continue; ret = ioctl(fd, LOOP_GET_STATUS64, &lo64); if (ret < 0) { if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0 || errno != ENXIO) { close(fd); fd = -1; continue; } } ret = snprintf(loop_name, LO_NAME_SIZE, "/dev/%s", dp->d_name); if (ret < 0 || ret >= LO_NAME_SIZE) { close(fd); fd = -1; continue; } break; } closedir(dir); if (fd < 0) return -1; return fd; } static int lxc_get_unused_loop_dev(char *name_loop) { int loop_nr, ret; int fd_ctl = -1, fd_tmp = -1; fd_ctl = open("/dev/loop-control", O_RDWR | O_CLOEXEC); if (fd_ctl < 0) { SYSERROR("Failed to open loop control"); return -ENODEV; } loop_nr = ioctl(fd_ctl, LOOP_CTL_GET_FREE); if (loop_nr < 0) { SYSERROR("Failed to get loop control"); goto on_error; } ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/loop%d", loop_nr); if (ret < 0 || ret >= LO_NAME_SIZE) goto on_error; fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC); if (fd_tmp < 0) SYSERROR("Failed to open loop \"%s\"", name_loop); on_error: close(fd_ctl); return fd_tmp; } int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags) { int ret; struct loop_info64 lo64; int fd_img = -1, fret = -1, fd_loop = -1; fd_loop = lxc_get_unused_loop_dev(loop_dev); if (fd_loop < 0) { if (fd_loop != -ENODEV) goto on_error; fd_loop = lxc_get_unused_loop_dev_legacy(loop_dev); if (fd_loop < 0) goto on_error; } fd_img = open(source, O_RDWR | O_CLOEXEC); if (fd_img < 0) { SYSERROR("Failed to open source \"%s\"", source); goto on_error; } ret = ioctl(fd_loop, LOOP_SET_FD, fd_img); if (ret < 0) { SYSERROR("Failed to set loop fd"); goto on_error; } memset(&lo64, 0, sizeof(lo64)); lo64.lo_flags = flags; ret = ioctl(fd_loop, LOOP_SET_STATUS64, &lo64); if (ret < 0) { SYSERROR("Failed to set loop status64"); goto on_error; } fret = 0; on_error: if (fd_img >= 0) close(fd_img); if (fret < 0 && fd_loop >= 0) { close(fd_loop); fd_loop = -1; } return fd_loop; } int lxc_unstack_mountpoint(const char *path, bool lazy) { int ret; int umounts = 0; pop_stack: ret = umount2(path, lazy ? MNT_DETACH : 0); if (ret < 0) { /* We consider anything else than EINVAL deadly to prevent going * into an infinite loop. (The other alternative is constantly * parsing /proc/self/mountinfo which is yucky and probably * racy.) */ if (errno != EINVAL) return -errno; } else { /* Just stop counting when this happens. That'd just be so * stupid that we won't even bother trying to report back the * correct value anymore. */ if (umounts != INT_MAX) umounts++; /* We succeeded in umounting. Make sure that there's no other * mountpoint stacked underneath. */ goto pop_stack; } return umounts; } int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) { pid_t child; int ret, fret, pipefd[2]; ssize_t bytes; /* Make sure our callers do not receive uninitialized memory. */ if (buf_size > 0 && buf) buf[0] = '\0'; if (pipe(pipefd) < 0) { SYSERROR("Failed to create pipe"); return -1; } child = lxc_raw_clone(0); if (child < 0) { close(pipefd[0]); close(pipefd[1]); SYSERROR("Failed to create new process"); return -1; } if (child == 0) { /* Close the read-end of the pipe. */ close(pipefd[0]); /* Redirect std{err,out} to write-end of the * pipe. */ ret = dup2(pipefd[1], STDOUT_FILENO); if (ret >= 0) ret = dup2(pipefd[1], STDERR_FILENO); /* Close the write-end of the pipe. */ close(pipefd[1]); if (ret < 0) { SYSERROR("Failed to duplicate std{err,out} file descriptor"); _exit(EXIT_FAILURE); } /* Does not return. */ child_fn(args); ERROR("Failed to exec command"); _exit(EXIT_FAILURE); } /* close the write-end of the pipe */ close(pipefd[1]); if (buf && buf_size > 0) { bytes = lxc_read_nointr(pipefd[0], buf, buf_size - 1); if (bytes > 0) buf[bytes - 1] = '\0'; } fret = wait_for_pid(child); /* close the read-end of the pipe */ close(pipefd[0]); return fret; } 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; } uint64_t lxc_find_next_power2(uint64_t n) { /* 0 is not valid input. We return 0 to the caller since 0 is not a * valid power of two. */ if (n == 0) return 0; if (!(n & (n - 1))) return n; while (n & (n - 1)) n = n & (n - 1); n = n << 1; return n; } int lxc_set_death_signal(int signal, pid_t parent) { int ret; pid_t ppid; ret = prctl(PR_SET_PDEATHSIG, prctl_arg(signal), prctl_arg(0), prctl_arg(0), prctl_arg(0)); /* Check whether we have been orphaned. */ ppid = (pid_t)syscall(SYS_getppid); if (ppid != parent) { ret = raise(SIGKILL); if (ret < 0) return -1; } if (ret < 0) return -1; return 0; } int fd_cloexec(int fd, bool cloexec) { int oflags, nflags; oflags = fcntl(fd, F_GETFD, 0); if (oflags < 0) return -errno; if (cloexec) nflags = oflags | FD_CLOEXEC; else nflags = oflags & ~FD_CLOEXEC; if (nflags == oflags) return 0; if (fcntl(fd, F_SETFD, nflags) < 0) return -errno; return 0; } int recursive_destroy(char *dirname) { int ret; struct dirent *direntp; DIR *dir; int r = 0; dir = opendir(dirname); if (!dir) { SYSERROR("Failed to open dir \"%s\"", dirname); return -1; } while ((direntp = readdir(dir))) { char *pathname; struct stat mystat; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; pathname = must_make_path(dirname, direntp->d_name, NULL); ret = lstat(pathname, &mystat); if (ret < 0) { if (!r) SYSWARN("Failed to stat \"%s\"", pathname); r = -1; goto next; } if (!S_ISDIR(mystat.st_mode)) goto next; ret = recursive_destroy(pathname); if (ret < 0) r = -1; next: free(pathname); } ret = rmdir(dirname); if (ret < 0) { if (!r) SYSWARN("Failed to delete \"%s\"", dirname); r = -1; } ret = closedir(dir); if (ret < 0) { if (!r) SYSWARN("Failed to delete \"%s\"", dirname); r = -1; } return r; } int lxc_setup_keyring(void) { key_serial_t keyring; int ret = 0; /* Try to allocate a new session keyring for the container to prevent * information leaks. */ keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, prctl_arg(0), prctl_arg(0), prctl_arg(0), prctl_arg(0)); if (keyring < 0) { switch (errno) { case ENOSYS: DEBUG("The keyctl() syscall is not supported or blocked"); break; case EACCES: __fallthrough; case EPERM: DEBUG("Failed to access kernel keyring. Continuing..."); break; default: SYSERROR("Failed to create kernel keyring"); break; } } return ret; } lxc-3.0.3/src/lxc/caps.h0000644061062106075000000000634413375633353011732 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CAPS_H #define __LXC_CAPS_H #include #include "config.h" #if HAVE_LIBCAP #include /* workaround for libcap < 2.17 bug */ #include extern int lxc_caps_down(void); extern int lxc_caps_up(void); extern int lxc_ambient_caps_up(void); extern int lxc_ambient_caps_down(void); extern int lxc_caps_init(void); extern int lxc_caps_last_cap(void); extern bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag); extern bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag); #else static inline int lxc_caps_down(void) { return 0; } static inline int lxc_caps_up(void) { return 0; } static inline int lxc_ambient_caps_up(void) { return 0; } static inline int lxc_ambient_caps_down(void) { return 0; } static inline int lxc_caps_init(void) { return 0; } static inline int lxc_caps_last_cap(void) { return 0; } typedef int cap_value_t; typedef int cap_flag_t; static inline bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) { return false; } static inline bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) { return false; } #endif #define lxc_priv(__lxc_function) \ ({ \ __label__ out; \ int __ret, __ret2, ___errno = 0; \ __ret = lxc_caps_up(); \ if (__ret) \ goto out; \ __ret = __lxc_function; \ if (__ret) \ ___errno = errno; \ __ret2 = lxc_caps_down(); \ out: \ __ret ? errno = ___errno, __ret : __ret2; \ }) #define lxc_unpriv(__lxc_function) \ ({ \ __label__ out; \ int __ret, __ret2, ___errno = 0; \ __ret = lxc_caps_down(); \ if (__ret) \ goto out; \ __ret = __lxc_function; \ if (__ret) \ ___errno = errno; \ __ret2 = lxc_caps_up(); \ out: \ __ret ? errno = ___errno, __ret : __ret2; \ }) #endif lxc-3.0.3/src/lxc/ringbuf.c0000644061062106075000000000724613375633353012435 00000000000000/* liblxcapi * * Copyright © 2017 Christian Brauner . * Copyright © 2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include "config.h" #include "ringbuf.h" #include "syscall_wrappers.h" #include "utils.h" int lxc_ringbuf_create(struct lxc_ringbuf *buf, size_t size) { char *tmp; int ret; int memfd = -1; buf->size = size; buf->r_off = 0; buf->w_off = 0; /* verify that we are at least given the multiple of a page size */ if (buf->size % lxc_getpagesize()) return -EINVAL; buf->addr = mmap(NULL, buf->size * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (buf->addr == MAP_FAILED) return -EINVAL; memfd = memfd_create(".lxc_ringbuf", MFD_CLOEXEC); if (memfd < 0) { char template[] = P_tmpdir "/.lxc_ringbuf_XXXXXX"; if (errno != ENOSYS) goto on_error; memfd = lxc_make_tmpfile(template, true); } if (memfd < 0) goto on_error; ret = ftruncate(memfd, buf->size); if (ret < 0) goto on_error; tmp = mmap(buf->addr, buf->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0); if (tmp == MAP_FAILED || tmp != buf->addr) goto on_error; tmp = mmap(buf->addr + buf->size, buf->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0); if (tmp == MAP_FAILED || tmp != (buf->addr + buf->size)) goto on_error; close(memfd); return 0; on_error: lxc_ringbuf_release(buf); if (memfd >= 0) close(memfd); return -1; } void lxc_ringbuf_move_read_addr(struct lxc_ringbuf *buf, size_t len) { buf->r_off += len; if (buf->r_off < buf->size) return; /* wrap around */ buf->r_off -= buf->size; buf->w_off -= buf->size; } /** * lxc_ringbuf_write - write a message to the ringbuffer * - The size of the message should never be greater than the size of the whole * ringbuffer. * - The write method will always succeed i.e. it will always advance the r_off * if it detects that there's not enough space available to write the * message. */ int lxc_ringbuf_write(struct lxc_ringbuf *buf, const char *msg, size_t len) { char *w_addr; uint64_t free; /* sanity check: a write should never exceed the ringbuffer's total size */ if (len > buf->size) return -EFBIG; free = lxc_ringbuf_free(buf); /* not enough space left so advance read address */ if (len > free) lxc_ringbuf_move_read_addr(buf, len); w_addr = lxc_ringbuf_get_write_addr(buf); memcpy(w_addr, msg, len); lxc_ringbuf_move_write_addr(buf, len); return 0; } int lxc_ringbuf_read(struct lxc_ringbuf *buf, char *out, size_t *len) { uint64_t used; /* there's nothing to read */ if (buf->r_off == buf->w_off) return -ENODATA; /* read maximum amount available */ used = lxc_ringbuf_used(buf); if (used < *len) *len = used; /* copy data to reader but don't advance addr */ memcpy(out, lxc_ringbuf_get_read_addr(buf), *len); out[*len - 1] = '\0'; return 0; } lxc-3.0.3/src/lxc/lxcseccomp.h0000644061062106075000000000262113375633353013136 00000000000000/* * lxc: linux Container library * * (C) Copyright Canonical, Inc. 2012 * * Authors: * Serge Hallyn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_LXCSECCOMP_H #define __LXC_LXCSECCOMP_H #include "conf.h" #ifdef HAVE_SECCOMP extern int lxc_seccomp_load(struct lxc_conf *conf); extern int lxc_read_seccomp_config(struct lxc_conf *conf); extern void lxc_seccomp_free(struct lxc_conf *conf); #else static inline int lxc_seccomp_load(struct lxc_conf *conf) { return 0; } static inline int lxc_read_seccomp_config(struct lxc_conf *conf) { return 0; } static inline void lxc_seccomp_free(struct lxc_conf *conf) { free(conf->seccomp); conf->seccomp = NULL; } #endif #endif lxc-3.0.3/src/lxc/conf.h0000644061062106075000000003250413375633353011726 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_CONF_H #define __LXC_CONF_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "compiler.h" #include "config.h" #include "list.h" #include "ringbuf.h" #include "start.h" #include "terminal.h" #if HAVE_SYS_RESOURCE_H #include #endif #if HAVE_SCMP_FILTER_CTX typedef void * scmp_filter_ctx; #endif /* worth moving to configure.ac? */ #define subuidfile "/etc/subuid" #define subgidfile "/etc/subgid" /* * Defines a generic struct to configure the control group. It is up to the * programmer to specify the right subsystem. * @subsystem : the targeted subsystem * @value : the value to set * @version : The version of the cgroup filesystem on which the controller * resides. * * @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 { union { /* information about a specific controller */ struct /* controller */ { int version; 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 { ID_TYPE_UID, ID_TYPE_GID }; /* * Defines a structure to configure kernel parameters at runtime. * @key : the kernel parameters will be configured without the "lxc.sysctl" prefix * @value : the value to set */ struct lxc_sysctl { char *key; char *value; }; /* * Defines a structure to configure proc filesystem at runtime. * @filename : the proc filesystem will be configured without the "lxc.proc" prefix * @value : the value to set */ struct lxc_proc { char *filename; char *value; }; /* * id_map is an id map entry. Form in confile is: * 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. */ struct id_map { enum idtype idtype; unsigned long hostid, nsid, range; }; /* Defines the number of tty configured and contains the * instantiated ptys * @max = number of configured ttys */ struct lxc_tty_info { size_t max; char *dir; char *tty_names; struct lxc_terminal_info *tty; }; /* Defines a structure to store the rootfs location, the * optionals pivot_root, rootfs mount paths * @path : the rootfs source (directory or device) * @mount : where it is mounted * @bev_type : optional backing store type * @options : mount options * @mountflags : the portion of @options that are flags * @data : the portion of @options that are not flags */ struct lxc_rootfs { char *path; char *mount; char *bdev_type; char *options; unsigned long mountflags; char *data; }; /* * Automatic mounts for LXC to perform inside the container */ enum { LXC_AUTO_PROC_RW = 0x001, /* /proc read-write */ LXC_AUTO_PROC_MIXED = 0x002, /* /proc/sys and /proc/sysrq-trigger read-only */ LXC_AUTO_PROC_MASK = 0x003, LXC_AUTO_SYS_RW = 0x004, /* /sys */ LXC_AUTO_SYS_RO = 0x008, /* /sys read-only */ LXC_AUTO_SYS_MIXED = 0x00C, /* /sys read-only and /sys/class/net read-write */ LXC_AUTO_SYS_MASK = 0x00C, LXC_AUTO_CGROUP_RO = 0x010, /* /sys/fs/cgroup (partial mount, read-only) */ LXC_AUTO_CGROUP_RW = 0x020, /* /sys/fs/cgroup (partial mount, read-write) */ LXC_AUTO_CGROUP_MIXED = 0x030, /* /sys/fs/cgroup (partial mount, paths r/o, cgroup r/w) */ LXC_AUTO_CGROUP_FULL_RO = 0x040, /* /sys/fs/cgroup (full mount, read-only) */ LXC_AUTO_CGROUP_FULL_RW = 0x050, /* /sys/fs/cgroup (full mount, read-write) */ LXC_AUTO_CGROUP_FULL_MIXED = 0x060, /* /sys/fs/cgroup (full mount, parent r/o, own r/w) */ /* * These are defined in such a way as to retain binary compatibility * with earlier versions of this code. If the previous mask is applied, * both of these will default back to the _MIXED variants, which is * safe. */ LXC_AUTO_CGROUP_NOSPEC = 0x0B0, /* /sys/fs/cgroup (partial mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FULL_NOSPEC = 0x0E0, /* /sys/fs/cgroup (full mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FORCE = 0x100, /* mount cgroups even when cgroup namespaces are supported */ LXC_AUTO_CGROUP_MASK = 0x1F0, /* all known cgroup options, doe not contain LXC_AUTO_CGROUP_FORCE */ LXC_AUTO_ALL_MASK = 0x1FF, /* all known settings */ }; enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, LXCHOOK_START, LXCHOOK_STOP, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, LXCHOOK_DESTROY, LXCHOOK_START_HOST, NUM_LXC_HOOKS }; extern char *lxchook_names[NUM_LXC_HOOKS]; struct lxc_state_client { int clientfd; lxc_state_t states[MAX_STATE]; }; struct lxc_conf { /* Pointer to the name of the container. Do not free! */ const char *name; bool is_execute; int reboot; signed long personality; struct utsname *utsname; struct { struct lxc_list cgroup; struct lxc_list cgroup2; }; struct { struct lxc_list id_map; /* * Pointer to the idmap entry for the container's root uid in * the id_map list. Do not free! */ const struct id_map *root_nsuid_map; /* * Pointer to the idmap entry for the container's root gid in * the id_map list. Do not free! */ const struct id_map *root_nsgid_map; }; struct lxc_list network; struct { char *fstab; int auto_mounts; struct lxc_list mount_list; }; struct lxc_list caps; struct lxc_list keepcaps; /* /dev/tty devices */ struct lxc_tty_info ttys; /* /dev/console device */ struct lxc_terminal console; /* maximum pty devices allowed by devpts mount */ size_t pty_max; /* set to true when rootfs has been setup */ bool rootfs_setup; struct lxc_rootfs rootfs; bool close_all_fds; struct { unsigned int hooks_version; struct lxc_list hooks[NUM_LXC_HOOKS]; }; char *lsm_aa_profile; unsigned int lsm_aa_allow_incomplete; char *lsm_se_context; bool tmp_umount_proc; char *seccomp; /* filename with the seccomp rules */ #if HAVE_SCMP_FILTER_CTX scmp_filter_ctx seccomp_ctx; #endif int maincmd_fd; unsigned int autodev; /* if 1, mount and fill a /dev at start */ int haltsignal; /* signal used to halt container */ int rebootsignal; /* signal used to reboot container */ int stopsignal; /* signal used to hard stop container */ char *rcfile; /* Copy of the top level rcfile we read */ /* Logfile and loglevel can be set in a container config file. Those * function as defaults. The defaults can be overridden 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 specified in config */ int loglevel; /* loglevel as specified in config (if any) */ int logfd; unsigned int start_auto; unsigned int start_delay; int start_order; struct lxc_list groups; int nbd_idx; /* unshare the mount namespace in the monitor */ unsigned int monitor_unshare; /* list of included files */ struct lxc_list includes; /* config entries which are not "lxc.*" are aliens */ struct lxc_list aliens; /* list of environment variables we'll add to the container when * started */ struct lxc_list environment; /* text representation of the config file */ char *unexpanded_config; size_t unexpanded_len; size_t unexpanded_alloced; /* default command for lxc-execute */ char *execute_cmd; /* init command */ char *init_cmd; /* if running in a new user namespace, the UID/GID that init and COMMAND * should run under when using lxc-execute */ uid_t init_uid; gid_t init_gid; /* indicator if the container will be destroyed on shutdown */ unsigned int ephemeral; /* 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; /* 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; struct { int ns_clone; int ns_keep; char *ns_share[LXC_NS_MAX]; }; /* init working directory */ char *init_cwd; /* A list of clients registered to be informed about a container state. */ struct lxc_list state_clients; /* sysctls */ struct lxc_list sysctls; /* procs */ struct lxc_list procs; }; extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size); #ifdef HAVE_TLS extern thread_local struct lxc_conf *current_config; #else extern struct lxc_conf *current_config; #endif extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]); extern int detect_shared_rootfs(void); extern struct lxc_conf *lxc_conf_init(void); extern void lxc_conf_free(struct lxc_conf *conf); extern int pin_rootfs(const char *rootfs); extern int lxc_map_ids(struct lxc_list *idmap, pid_t pid); extern int lxc_create_tty(const char *name, struct lxc_conf *conf); extern void lxc_delete_tty(struct lxc_tty_info *ttys); 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, int version); extern int lxc_clear_mount_entries(struct lxc_conf *c); extern int lxc_clear_automounts(struct lxc_conf *c); extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); extern int lxc_clear_idmaps(struct lxc_conf *c); extern int lxc_clear_groups(struct lxc_conf *c); extern int lxc_clear_environment(struct lxc_conf *c); extern int lxc_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 lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name, const char *lxcpath); extern int lxc_setup(struct lxc_handler *handler); extern int lxc_setup_parent(struct lxc_handler *handler); 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(const char *path, struct lxc_conf *conf); extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata); extern int parse_propagationopts(const char *mntopts, unsigned long *pflags); extern void tmp_proc_unmount(struct lxc_conf *lxc_conf); extern void remount_all_slave(void); extern void suggest_default_idmap(void); extern FILE *make_anonymous_mount_file(struct lxc_list *mount); extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings); extern unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags); extern int run_script(const char *name, const char *section, const char *script, ...); extern int run_script_argv(const char *name, unsigned int hook_version, const char *section, const char *script, const char *hookname, char **argsin); extern int in_caplist(int cap, struct lxc_list *caps); extern int setup_sysctl_parameters(struct lxc_list *sysctls); extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key); extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid); extern int lxc_clear_procs(struct lxc_conf *c, const char *key); #endif /* __LXC_CONF_H */ lxc-3.0.3/src/lxc/version.h0000644061062106075000000000212013375633375012461 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_VERSION_H #define __LXC_VERSION_H #define LXC_DEVEL 0 #define LXC_VERSION_MAJOR 3 #define LXC_VERSION_MINOR 0 #define LXC_VERSION_MICRO 3 #define LXC_VERSION_ABI "1.4.0" #define LXC_VERSION "3.0.3" #endif lxc-3.0.3/src/lxc/error.h0000644061062106075000000000222513375633353012127 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_ERROR_H #define __LXC_ERROR_H #define LXC_CLONE_ERROR "Failed to clone a new set of namespaces" #define LXC_UNPRIV_EOPNOTSUPP "the requested function %s is not currently supported with unprivileged containers" extern int lxc_error_set_and_log(int pid, int status); #endif lxc-3.0.3/src/lxc/raw_syscalls.h0000644061062106075000000000612013375633353013502 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 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_RAW_SYSCALL_H #define __LXC_RAW_SYSCALL_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include /* * lxc_raw_clone() - create a new process * * - fork() behavior: * This function returns 0 in the child and > 0 in the parent. * * - copy-on-write: * This function does not allocate a new stack and relies on copy-on-write * semantics. * * - supports subset of ClONE_* flags: * lxc_raw_clone() intentionally only supports a subset of the flags available * to the actual system call. Please refer to the implementation what flags * cannot be used. Also, please don't assume that just because a flag isn't * explicitly checked for as being unsupported that it is supported. If in * doubt or not sufficiently familiar with process creation in the kernel and * interactions with libcs this function should be used. * * - no pthread_atfork() handlers: * This function circumvents - as much as this this is possible - any libc * wrappers and thus does not run any pthread_atfork() handlers. Make sure * that this is safe to do in the context you are trying to call this * function. * * - must call lxc_raw_getpid(): * The child must use lxc_raw_getpid() to retrieve its pid. */ extern pid_t lxc_raw_clone(unsigned long flags); /* * lxc_raw_clone_cb() - create a new process * * - non-fork() behavior: * Function does return pid of the child or -1 on error. Pass in a callback * function via the "fn" argument that gets executed in the child process. * The "args" argument is passed to "fn". * * All other comments that apply to lxc_raw_clone() apply to lxc_raw_clone_cb() * as well. */ extern pid_t lxc_raw_clone_cb(int (*fn)(void *), void *args, unsigned long flags); extern int lxc_raw_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags); /* * Because of older glibc's pid cache (up to 2.25) whenever clone() is called * the child must must retrieve it's own pid via lxc_raw_getpid(). */ static inline pid_t lxc_raw_getpid(void) { return (pid_t)syscall(SYS_getpid); } static inline pid_t lxc_raw_gettid(void) { #ifdef __NR_gettid return syscall(__NR_gettid); #else return lxc_raw_getpid(); #endif } #endif /* __LXC_RAW_SYSCALL_H */ lxc-3.0.3/src/lxc/af_unix.h0000644061062106075000000000312113375633353012423 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_AF_UNIX_H #define __LXC_AF_UNIX_H #include /* does not enforce \0-termination */ extern int lxc_abstract_unix_open(const char *path, int type, int flags); extern void lxc_abstract_unix_close(int fd); /* does not enforce \0-termination */ extern int lxc_abstract_unix_connect(const char *path); extern int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size); extern int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size); extern int lxc_abstract_unix_send_credential(int fd, void *data, size_t size); extern int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size); #endif /* __LXC_AF_UNIX_H */ lxc-3.0.3/src/lxc/mainloop.h0000644061062106075000000000321213375633353012611 00000000000000/* * lxc: linux Container library * * (C) Copyright IBM Corp. 2007, 2008 * * Authors: * Daniel Lezcano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LXC_MAINLOOP_H #define __LXC_MAINLOOP_H #include #include "list.h" #define LXC_MAINLOOP_ERROR -1 #define LXC_MAINLOOP_CONTINUE 0 #define LXC_MAINLOOP_CLOSE 1 struct lxc_epoll_descr { int epfd; struct lxc_list handlers; }; typedef int (*lxc_mainloop_callback_t)(int fd, uint32_t event, void *data, struct lxc_epoll_descr *descr); extern int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms); extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, void *data); extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd); extern int lxc_mainloop_open(struct lxc_epoll_descr *descr); extern int lxc_mainloop_close(struct lxc_epoll_descr *descr); #endif lxc-3.0.3/src/lxc/lxccontainer.c0000644061062106075000000035252113375633353013471 00000000000000/* liblxcapi * * Copyright © 2012 Serge Hallyn . * Copyright © 2012 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/netns_ifaddrs.h" #include "af_unix.h" #include "attach.h" #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "criu.h" #include "error.h" #include "initutils.h" #include "log.h" #include "lxc.h" #include "lxccontainer.h" #include "lxclock.h" #include "monitor.h" #include "namespace.h" #include "network.h" #include "parse.h" #include "raw_syscalls.h" #include "start.h" #include "state.h" #include "storage.h" #include "storage/btrfs.h" #include "storage/overlay.h" #include "storage_utils.h" #include "sync.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #include "version.h" /* major()/minor() */ #ifdef MAJOR_IN_MKDEV #include #endif #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif /* Define faccessat() if missing from the C library */ #ifndef HAVE_FACCESSAT static int faccessat(int __fd, const char *__file, int __type, int __flag) { #ifdef __NR_faccessat return syscall(__NR_faccessat, __fd, __file, __type, __flag); #else errno = ENOSYS; return -1; #endif } #endif lxc_log_define(lxccontainer, lxc); static bool do_lxcapi_destroy(struct lxc_container *c); static const char *lxcapi_get_config_path(struct lxc_container *c); #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c) static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); static bool container_destroy(struct lxc_container *c, 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); static bool config_file_exists(const char *lxcpath, const char *cname) { int ret; size_t len; char *fname; /* $lxcpath + '/' + $cname + '/config' + \0 */ len = strlen(lxcpath) + strlen(cname) + 9; fname = alloca(len); ret = snprintf(fname, len, "%s/%s/config", lxcpath, cname); if (ret < 0 || (size_t)ret >= len) return false; return file_exists(fname); } /* A few functions to help detect when a container creation failed. If a * container creation was killed partway through, then trying to actually start * that container could harm the host. We detect this by creating a 'partial' * file under the container directory, and keeping an advisory lock. When * container creation completes, we remove that file. When we load or try to * start a container, if we find that file, without a flock, we remove the * container. */ static int ongoing_create(struct lxc_container *c) { int fd, ret; size_t len; char *path; struct flock lk = {0}; len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return -1; fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) { if (errno != ENOENT) return -1; return 0; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; /* F_OFD_GETLK requires that l_pid be set to 0 otherwise the kernel * will EINVAL us. */ lk.l_pid = 0; ret = fcntl(fd, F_OFD_GETLK, &lk); if (ret < 0 && errno == EINVAL) { ret = flock(fd, LOCK_EX | LOCK_NB); if (ret < 0 && errno == EWOULDBLOCK) ret = 0; } close(fd); /* F_OFD_GETLK will not send us back a pid so don't check it. */ if (ret == 0) /* Create is still ongoing. */ return 1; /* Create completed but partial is still there. */ return 2; } static int create_partial(struct lxc_container *c) { int fd, ret; size_t len; char *path; struct flock lk = {0}; /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return -1; fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0000); if (fd < 0) return -1; lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) { ret = flock(fd, LOCK_EX); if (ret == 0) return fd; } SYSERROR("Failed to lock partial file %s", path); close(fd); return -1; } return fd; } static void remove_partial(struct lxc_container *c, int fd) { int ret; size_t len; char *path; close(fd); /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + strlen(c->name) + 10; path = alloca(len); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) return; ret = unlink(path); if (ret < 0) SYSERROR("Failed to remove partial file %s", path); } /* LOCKING * 1. container_mem_lock(c) protects the struct lxc_container from multiple threads. * 2. container_disk_lock(c) protects the on-disk container data - in particular the * container configuration file. * The container_disk_lock also takes the container_mem_lock. * 3. thread_mutex protects process data (ex: fd table) from multiple threads. * NOTHING mutexes two independent programs with their own struct * lxc_container for the same c->name, between API calls. For instance, * c->config_read(); c->start(); Between those calls, data on disk * could change (which shouldn't bother the caller unless for instance * the rootfs get moved). c->config_read(); update; c->config_write(); * Two such updaters could race. The callers should therefore check their * results. Trying to prevent that would necessarily expose us to deadlocks * due to hung callers. So I prefer to keep the locks only within our own * functions, not across functions. * * If you're going to clone while holding a lxccontainer, increment * c->numthreads (under privlock) before forking. When deleting, * decrement numthreads under privlock, then if it hits 0 you can delete. * Do not ever use a lxccontainer whose numthreads you did not bump. */ static void lxc_container_free(struct lxc_container *c) { if (!c) return; free(c->configfile); c->configfile = NULL; free(c->error_string); c->error_string = NULL; if (c->slock) { lxc_putlock(c->slock); c->slock = NULL; } if (c->privlock) { lxc_putlock(c->privlock); c->privlock = NULL; } free(c->name); c->name = NULL; if (c->lxc_conf) { lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } free(c->config_path); c->config_path = NULL; free(c); } /* Consider the following case: * * |====================================================================| * | freer | racing get()er | * |====================================================================| * | lxc_container_put() | lxc_container_get() | * | \ lxclock(c->privlock) | c->numthreads < 1? (no) | * | \ c->numthreads = 0 | \ lxclock(c->privlock) -> waits | * | \ lxcunlock() | \ | * | \ lxc_container_free() | \ lxclock() returns | * | | \ c->numthreads < 1 -> return 0 | * | \ \ (free stuff) | | * | \ \ sem_destroy(privlock) | | * |_______________________________|____________________________________| * * When the get()er checks numthreads the first time, one of the following * is true: * 1. freer has set numthreads = 0. get() returns 0 * 2. freer is between lxclock and setting numthreads to 0. get()er will * sem_wait on privlock, get lxclock after freer() drops it, then see * numthreads is 0 and exit without touching lxclock again.. * 3. freer has not yet locked privlock. If get()er runs first, then put()er * will see --numthreads = 1 and not call lxc_container_free(). */ int lxc_container_get(struct lxc_container *c) { if (!c) return 0; /* If someone else has already started freeing the container, don't try * to take the lock, which may be invalid. */ if (c->numthreads < 1) return 0; if (container_mem_lock(c)) return 0; /* 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; } int lxc_container_put(struct lxc_container *c) { if (!c) return -1; if (container_mem_lock(c)) return -1; c->numthreads--; if (c->numthreads < 1) { container_mem_unlock(c); lxc_container_free(c); return 1; } container_mem_unlock(c); return 0; } static bool do_lxcapi_is_defined(struct lxc_container *c) { int statret; struct stat statbuf; bool ret = false; if (!c) return false; if (container_mem_lock(c)) return false; if (!c->configfile) goto on_error; statret = stat(c->configfile, &statbuf); if (statret != 0) goto on_error; ret = true; on_error: container_mem_unlock(c); return ret; } #define WRAP_API(rettype, fnname) \ static rettype fnname(struct lxc_container *c) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_1(rettype, fnname, t1) \ static rettype fnname(struct lxc_container *c, t1 a1) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_2(rettype, fnname, t1, t2) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_3(rettype, fnname, t1, t2, t3) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2, a3); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } WRAP_API(bool, lxcapi_is_defined) static const char *do_lxcapi_state(struct lxc_container *c) { lxc_state_t s; if (!c) return NULL; s = lxc_getstate(c->name, c->config_path); return lxc_state2str(s); } WRAP_API(const char *, lxcapi_state) static bool is_stopped(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); return (s == STOPPED); } static bool do_lxcapi_is_running(struct lxc_container *c) { if (!c) return false; return !is_stopped(c); } WRAP_API(bool, lxcapi_is_running) static bool do_lxcapi_freeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_freeze(c->name, c->config_path); if (ret < 0) return false; return true; } WRAP_API(bool, lxcapi_freeze) static bool do_lxcapi_unfreeze(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_unfreeze(c->name, c->config_path); if (ret < 0) return false; return true; } WRAP_API(bool, lxcapi_unfreeze) static int do_lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { if (!c) return -1; return lxc_terminal_getfd(c, ttynum, masterfd); } WRAP_API_2(int, lxcapi_console_getfd, int *, int *) static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape); current_config = NULL; return ret; } static int do_lxcapi_console_log(struct lxc_container *c, struct lxc_console_log *log) { int ret; if (!c) return -EINVAL; ret = lxc_cmd_console_log(c->name, do_lxcapi_get_config_path(c), log); if (ret < 0) { if (ret == -ENODATA) NOTICE("The console log is empty"); else if (ret == -EFAULT) NOTICE("The container does not keep a console log"); else if (ret == -ENOENT) NOTICE("The container does not keep a console log file"); else if (ret == -EIO) NOTICE("Failed to write console log to log file"); else ERROR("Failed to retrieve console log"); } return ret; } WRAP_API_1(int, lxcapi_console_log, struct lxc_console_log *) static pid_t do_lxcapi_init_pid(struct lxc_container *c) { if (!c) return -1; return lxc_cmd_get_init_pid(c->name, c->config_path); } WRAP_API(pid_t, lxcapi_init_pid) static bool load_config_locked(struct lxc_container *c, const char *fname) { if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; if (lxc_config_read(fname, c->lxc_conf, false) != 0) return false; c->lxc_conf->name = c->name; return true; } static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file) { int lret; const char *fname; bool need_disklock = false, ret = false; if (!c) return false; fname = c->configfile; if (alt_file) fname = alt_file; if (!fname) return false; /* If we're reading something other than the container's config, we only * need to lock the in-memory container. If loading the container's * config file, take the disk lock. */ if (strcmp(fname, c->configfile) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; ret = load_config_locked(c, fname); if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_load_config, const char *) static bool do_lxcapi_want_daemonize(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->daemonize = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_daemonize, bool) static bool do_lxcapi_want_close_all_fds(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->lxc_conf->close_all_fds = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_close_all_fds, bool) static bool do_lxcapi_wait(struct lxc_container *c, const char *state, int timeout) { int ret; if (!c) return false; ret = lxc_wait(c->name, state, timeout, c->config_path); return ret == 0; } WRAP_API_2(bool, lxcapi_wait, const char *, int) static bool am_single_threaded(void) { DIR *dir; struct dirent *direntp; int count = 0; dir = opendir("/proc/self/task"); if (!dir) return false; while ((direntp = readdir(dir))) { if (strcmp(direntp->d_name, ".") == 0) continue; if (strcmp(direntp->d_name, "..") == 0) continue; count++; if (count > 1) break; } closedir(dir); return count == 1; } static void push_arg(char ***argp, char *arg, int *nargs) { char *copy; char **argv; copy = must_copy_string(arg); do { argv = realloc(*argp, (*nargs + 2) * sizeof(char *)); } while (!argv); *argp = argv; argv[*nargs] = copy; (*nargs)++; argv[*nargs] = NULL; } static char **split_init_cmd(const char *incmd) { size_t len, retlen; char *copy, *p; char **argv; int nargs = 0; if (!incmd) return NULL; len = strlen(incmd) + 1; copy = alloca(len); retlen = strlcpy(copy, incmd, len); if (retlen >= len) return NULL; do { argv = malloc(sizeof(char *)); } while (!argv); argv[0] = NULL; lxc_iterate_parts(p, copy, " ") push_arg(&argv, p, &nargs); if (nargs == 0) { free(argv); return NULL; } return argv; } static void free_init_cmd(char **argv) { int i = 0; if (!argv) return; while (argv[i]) free(argv[i++]); free(argv); } static int lxc_rcv_status(int state_socket) { int ret; int state = -1; again: /* Receive container state. */ ret = lxc_abstract_unix_rcv_credential(state_socket, &state, sizeof(int)); if (ret <= 0) { if (errno != EINTR) return -1; TRACE("Caught EINTR; retrying"); goto again; } return state; } static bool wait_on_daemonized_start(struct lxc_handler *handler, int pid) { int ret, state; /* Close write end of the socket pair. */ close(handler->state_socket_pair[1]); handler->state_socket_pair[1] = -1; state = lxc_rcv_status(handler->state_socket_pair[0]); /* Close read end of the socket pair. */ close(handler->state_socket_pair[0]); handler->state_socket_pair[0] = -1; /* The first child is going to fork() again and then exits. So we reap * the first child here. */ ret = wait_for_pid(pid); if (ret < 0) DEBUG("Failed waiting on first child %d", pid); else DEBUG("First child %d exited", pid); if (state < 0) { SYSERROR("Failed to receive the container state"); return false; } /* If we receive anything else then running we know that the container * failed to start. */ if (state != RUNNING) { ERROR("Received container state \"%s\" instead of \"RUNNING\"", lxc_state2str(state)); return false; } TRACE("Container is in \"RUNNING\" state"); return true; } static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) { int ret; struct lxc_handler *handler; struct lxc_conf *conf; char *default_args[] = { "/sbin/init", NULL, }; char **init_cmd = NULL; int keepfds[3] = {-1, -1, -1}; /* container does exist */ if (!c) return false; /* If anything fails before we set error_num, we want an error in there. */ c->error_num = 1; /* Container has not been setup. */ if (!c->lxc_conf) return false; ret = ongoing_create(c); if (ret < 0) { ERROR("Failed checking for incomplete container creation"); return false; } else if (ret == 1) { ERROR("Ongoing container creation detected"); return false; } else if (ret == 2) { ERROR("Failed to create container"); do_lxcapi_destroy(c); return false; } if (container_mem_lock(c)) return false; conf = c->lxc_conf; /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, c->daemonize); container_mem_unlock(c); if (!handler) return false; if (!argv) { if (useinit && conf->execute_cmd) argv = init_cmd = split_init_cmd(conf->execute_cmd); else argv = init_cmd = split_init_cmd(conf->init_cmd); } /* ... otherwise use default_args. */ if (!argv) { if (useinit) { ERROR("No valid init detected"); lxc_free_handler(handler); return false; } argv = default_args; } /* I'm not sure what locks we want here.Any? Is liblxc's locking enough * here to protect the on disk container? We don't want to exclude * things like lxc_info while the container is running. */ if (c->daemonize) { bool started; char title[2048]; pid_t pid; pid = fork(); if (pid < 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } /* first parent */ if (pid != 0) { /* Set to NULL because we don't want father unlink * the PID file, child will do the free and unlink. */ c->pidfile = NULL; /* Wait for container to tell us whether it started * successfully. */ started = wait_on_daemonized_start(handler, pid); free_init_cmd(init_cmd); lxc_free_handler(handler); return started; } /* first child */ /* We don't really care if this doesn't print all the * characters. All that it means is that the proctitle will be * ugly. Similarly, we also don't care if setproctitle() fails. */ ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); if (ret > 0) { ret = setproctitle(title); if (ret < 0) INFO("Failed to set process title to %s", title); else INFO("Set process title to %s", title); } /* We fork() a second time to be reparented to init. Like * POSIX's daemon() function we change to "/" and redirect * std{in,out,err} to /dev/null. */ pid = fork(); if (pid < 0) { SYSERROR("Failed to fork first child process"); _exit(EXIT_FAILURE); } /* second parent */ if (pid != 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); _exit(EXIT_SUCCESS); } /* second child */ /* change to / directory */ ret = chdir("/"); if (ret < 0) { SYSERROR("Failed to change to \"/\" directory"); _exit(EXIT_FAILURE); } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, true, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) _exit(EXIT_FAILURE); /* redirect std{in,out,err} to /dev/null */ ret = null_stdfds(); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to /dev/null"); _exit(EXIT_FAILURE); } /* become session leader */ ret = setsid(); if (ret < 0) TRACE("Process %d is already process group leader", lxc_raw_getpid()); } else if (!am_single_threaded()) { ERROR("Cannot start non-daemonized container when threaded"); free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } /* We need to write PID file after daemonize, so we always write the * right PID. */ if (c->pidfile) { int ret, w; char pidstr[INTTYPE_TO_STRLEN(pid_t)]; w = snprintf(pidstr, sizeof(pidstr), "%d", lxc_raw_getpid()); if (w < 0 || (size_t)w >= sizeof(pidstr)) { free_init_cmd(init_cmd); lxc_free_handler(handler); SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile); if (c->daemonize) _exit(EXIT_FAILURE); return false; } ret = lxc_write_to_file(c->pidfile, pidstr, w, false, 0600); if (ret < 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile); if (c->daemonize) _exit(EXIT_FAILURE); return false; } } conf->reboot = REBOOT_NONE; /* Unshare the mount namespace if requested */ if (conf->monitor_unshare) { ret = unshare(CLONE_NEWNS); if (ret < 0) { SYSERROR("Failed to unshare mount namespace"); lxc_free_handler(handler); ret = 1; goto on_error; } ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to make / rslave at startup"); lxc_free_handler(handler); ret = 1; goto on_error; } } reboot: if (conf->reboot == REBOOT_INIT) { /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, c->daemonize); if (!handler) { ret = 1; goto on_error; } } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, c->daemonize, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) { lxc_free_handler(handler); ret = 1; goto on_error; } if (useinit) ret = lxc_execute(c->name, argv, 1, handler, c->config_path, c->daemonize, &c->error_num); else ret = lxc_start(c->name, argv, handler, c->config_path, c->daemonize, &c->error_num); if (conf->reboot == REBOOT_REQ) { INFO("Container requested reboot"); conf->reboot = REBOOT_INIT; goto reboot; } on_error: if (c->pidfile) { unlink(c->pidfile); free(c->pidfile); c->pidfile = NULL; } free_init_cmd(init_cmd); if (c->daemonize && ret != 0) _exit(EXIT_FAILURE); else if (c->daemonize) _exit(EXIT_SUCCESS); if (ret != 0) return false; return true; } static bool lxcapi_start(struct lxc_container *c, int useinit, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_start(c, useinit, argv); current_config = NULL; return ret; } /* Note, there MUST be an ending NULL. */ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) { va_list ap; char **inargs = NULL; bool bret = false; /* container exists */ if (!c) return false; current_config = c->lxc_conf; va_start(ap, useinit); inargs = lxc_va_arg_list_to_argv(ap, 0, 1); va_end(ap); if (!inargs) goto on_error; /* pass NULL if no arguments were supplied */ bret = do_lxcapi_start(c, useinit, *inargs ? inargs : NULL); on_error: if (inargs) { char **arg; for (arg = inargs; *arg; arg++) free(*arg); free(inargs); } current_config = NULL; return bret; } static bool do_lxcapi_stop(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_cmd_stop(c->name, c->config_path); return ret == 0; } WRAP_API(bool, lxcapi_stop) static int do_create_container_dir(const char *path, struct lxc_conf *conf) { int lasterr; size_t len; char *p; int ret = -1; mode_t mask = umask(0002); ret = mkdir(path, 0770); lasterr = errno; umask(mask); errno = lasterr; if (ret) { if (errno != EEXIST) return -1; ret = 0; } len = strlen(path); p = alloca(len + 1); (void)strlcpy(p, path, len + 1); if (!lxc_list_empty(&conf->id_map)) { ret = chown_mapped_root(p, conf); if (ret < 0) ret = -1; } return ret; } /* Create the standard expected container dir. */ static bool create_container_dir(struct lxc_container *c) { int ret; size_t len; char *s; len = strlen(c->config_path) + strlen(c->name) + 2; s = malloc(len); if (!s) return false; ret = snprintf(s, len, "%s/%s", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) { free(s); return false; } ret = do_create_container_dir(s, c->lxc_conf); free(s); return ret == 0; } /* do_storage_create: thin wrapper around storage_create(). Like * storage_create(), it returns a mounted bdev on success, NULL on error. */ static struct lxc_storage *do_storage_create(struct lxc_container *c, const char *type, struct bdev_specs *specs) { int ret; size_t len; char *dest; struct lxc_storage *bdev; /* rootfs.path or lxcpath/lxcname/rootfs */ if (c->lxc_conf->rootfs.path && (access(c->lxc_conf->rootfs.path, F_OK) == 0)) { const char *rpath = c->lxc_conf->rootfs.path; len = strlen(rpath) + 1; dest = alloca(len); ret = snprintf(dest, len, "%s", rpath); } else { const char *lxcpath = do_lxcapi_get_config_path(c); len = strlen(c->name) + strlen(lxcpath) + 9; dest = alloca(len); ret = snprintf(dest, len, "%s/%s/rootfs", lxcpath, c->name); } if (ret < 0 || (size_t)ret >= len) return NULL; bdev = storage_create(dest, type, c->name, specs); if (!bdev) { ERROR("Failed to create \"%s\" storage", type); return NULL; } if (!c->set_config_item(c, "lxc.rootfs.path", bdev->src)) { ERROR("Failed to set \"lxc.rootfs.path = %s\"", bdev->src); return NULL; } /* If we are not root, chown the rootfs dir to root in the target user * namespace. */ ret = geteuid(); if (ret != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) { ret = chown_mapped_root(bdev->dest, c->lxc_conf); if (ret < 0) { ERROR("Error chowning \"%s\" to container root", bdev->dest); suggest_default_idmap(); storage_put(bdev); return NULL; } } return bdev; } static char *lxcbasename(char *path) { char *p; p = path + strlen(path) - 1; while (*p != '/' && p > path) p--; return p; } static bool create_run_template(struct lxc_container *c, char *tpath, bool need_null_stdfds, char *const argv[]) { int ret; pid_t pid; if (!tpath) return true; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task for container creation template"); return false; } if (pid == 0) { /* child */ int i, len; char *namearg, *patharg, *rootfsarg; char **newargv; int nargs = 0; struct lxc_storage *bdev = NULL; struct lxc_conf *conf = c->lxc_conf; uid_t euid; if (need_null_stdfds) { ret = null_stdfds(); if (ret < 0) _exit(EXIT_FAILURE); } bdev = storage_init(c->lxc_conf); if (!bdev) { ERROR("Failed to initialize storage"); _exit(EXIT_FAILURE); } euid = geteuid(); if (euid == 0) { ret = unshare(CLONE_NEWNS); if (ret < 0) { ERROR("Failed to unshare CLONE_NEWNS"); _exit(EXIT_FAILURE); } ret = detect_shared_rootfs(); if (ret == 1) { ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to make \"/\" rslave"); ERROR("Continuing..."); } } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "btrfs") != 0) { if (euid != 0) { ERROR("Unprivileged users can only create " "btrfs and directory-backed containers"); _exit(EXIT_FAILURE); } if (strcmp(bdev->type, "overlay") == 0 || strcmp(bdev->type, "overlayfs") == 0) { /* 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"); _exit(EXIT_FAILURE); } } else { ret = bdev->ops->mount(bdev); if (ret < 0) { ERROR("Failed to mount rootfs"); _exit(EXIT_FAILURE); } } } else { /* TODO come up with a better way here! */ const char *src; free(bdev->dest); src = lxc_storage_get_path(bdev->src, bdev->type); bdev->dest = strdup(src); } /* Create our new array, pre-pend the template name and base * args. */ if (argv) for (nargs = 0; argv[nargs]; nargs++) ; /* template, path, rootfs and name args */ nargs += 4; newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) _exit(EXIT_FAILURE); newargv[0] = lxcbasename(tpath); /* --path */ len = strlen(c->config_path) + strlen(c->name) + strlen("--path=") + 2; patharg = malloc(len); if (!patharg) _exit(EXIT_FAILURE); ret = snprintf(patharg, len, "--path=%s/%s", c->config_path, c->name); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[1] = patharg; /* --name */ len = strlen("--name=") + strlen(c->name) + 1; namearg = malloc(len); if (!namearg) _exit(EXIT_FAILURE); ret = snprintf(namearg, len, "--name=%s", c->name); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[2] = namearg; /* --rootfs */ len = strlen("--rootfs=") + 1 + strlen(bdev->dest); rootfsarg = malloc(len); if (!rootfsarg) _exit(EXIT_FAILURE); ret = snprintf(rootfsarg, len, "--rootfs=%s", bdev->dest); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[3] = rootfsarg; /* add passed-in args */ if (argv) for (i = 4; i < nargs; i++) newargv[i] = argv[i - 4]; /* add trailing NULL */ nargs++; newargv = realloc(newargv, nargs * sizeof(*newargv)); if (!newargv) _exit(EXIT_FAILURE); newargv[nargs - 1] = NULL; /* If we're running the template in a mapped userns, then we * prepend the template command with: lxc-usernsexec <-m map1> * ... <-m mapn> -- and we append "--mapped-uid x", where x is * the mapped uid for our geteuid() */ if (!lxc_list_empty(&conf->id_map)) { int extraargs, hostuid_mapped, hostgid_mapped; char **n2; char txtuid[20], txtgid[20]; struct lxc_list *it; struct id_map *map; int n2args = 1; n2 = malloc(n2args * sizeof(*n2)); if (!n2) _exit(EXIT_FAILURE); newargv[0] = tpath; tpath = "lxc-usernsexec"; n2[0] = "lxc-usernsexec"; lxc_list_for_each(it, &conf->id_map) { map = it->elem; n2args += 2; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); n2[n2args - 2] = "-m"; n2[n2args - 1] = malloc(200); if (!n2[n2args - 1]) _exit(EXIT_FAILURE); ret = snprintf(n2[n2args - 1], 200, "%c:%lu:%lu:%lu", map->idtype == ID_TYPE_UID ? 'u' : 'g', map->nsid, map->hostid, map->range); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } hostuid_mapped = mapped_hostid(geteuid(), conf, ID_TYPE_UID); extraargs = hostuid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); if (hostuid_mapped < 0) { hostuid_mapped = find_unmapped_nsid(conf, ID_TYPE_UID); n2[n2args++] = "-m"; if (hostuid_mapped < 0) { ERROR("Failed to find free uid to map"); _exit(EXIT_FAILURE); } n2[n2args++] = malloc(200); if (!n2[n2args - 1]) { SYSERROR("out of memory"); _exit(EXIT_FAILURE); } ret = snprintf(n2[n2args - 1], 200, "u:%d:%d:1", hostuid_mapped, geteuid()); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } hostgid_mapped = mapped_hostid(getegid(), conf, ID_TYPE_GID); extraargs = hostgid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); if (hostgid_mapped < 0) { hostgid_mapped = find_unmapped_nsid(conf, ID_TYPE_GID); n2[n2args++] = "-m"; if (hostgid_mapped < 0) { ERROR("Failed to find free gid to map"); _exit(EXIT_FAILURE); } n2[n2args++] = malloc(200); if (!n2[n2args - 1]) { SYSERROR("out of memory"); _exit(EXIT_FAILURE); } ret = snprintf(n2[n2args - 1], 200, "g:%d:%d:1", hostgid_mapped, getegid()); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } n2[n2args++] = "--"; for (i = 0; i < nargs; i++) n2[i + n2args] = newargv[i]; n2args += nargs; /* Finally add "--mapped-uid $uid" to tell template what * to chown cached images to. */ n2args += 4; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); /* note n2[n2args-1] is NULL */ n2[n2args - 5] = "--mapped-uid"; ret = snprintf(txtuid, 20, "%d", hostuid_mapped); if (ret < 0 || ret >= 20) { free(newargv); free(n2); _exit(EXIT_FAILURE); } n2[n2args - 4] = txtuid; n2[n2args - 3] = "--mapped-gid"; ret = snprintf(txtgid, 20, "%d", hostgid_mapped); if (ret < 0 || ret >= 20) { free(newargv); free(n2); _exit(EXIT_FAILURE); } n2[n2args - 2] = txtgid; n2[n2args - 1] = NULL; free(newargv); newargv = n2; } execvp(tpath, newargv); SYSERROR("Failed to execute template %s", tpath); _exit(EXIT_FAILURE); } ret = wait_for_pid(pid); if (ret != 0) { ERROR("Failed to create container from template"); return false; } return true; } static bool prepend_lxc_header(char *path, const char *t, char *const argv[]) { long flen; size_t len; char *contents; FILE *f; int ret = -1; #if HAVE_LIBGNUTLS int i; unsigned char md_value[SHA_DIGEST_LENGTH]; char *tpath; #endif f = fopen(path, "r"); if (f == NULL) return false; ret = fseek(f, 0, SEEK_END); if (ret < 0) goto out_error; ret = -1; flen = ftell(f); if (flen < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = -1; contents = malloc(flen + 1); if (!contents) goto out_error; len = fread(contents, 1, flen, f); if (len != flen) goto out_free_contents; contents[flen] = '\0'; ret = fclose(f); f = NULL; if (ret < 0) goto out_free_contents; #if HAVE_LIBGNUTLS tpath = get_template_path(t); if (!tpath) { ERROR("Invalid template \"%s\" specified", t); goto out_free_contents; } ret = sha1sum_file(tpath, md_value); if (ret < 0) { ERROR("Failed to get sha1sum of %s", tpath); free(tpath); goto out_free_contents; } free(tpath); #endif f = fopen(path, "w"); if (f == NULL) { SYSERROR("Reopening config for writing"); free(contents); return false; } fprintf(f, "# Template used to create this container: %s\n", t); if (argv) { fprintf(f, "# Parameters passed to the template:"); while (*argv) { fprintf(f, " %s", *argv); argv++; } fprintf(f, "\n"); } #if HAVE_LIBGNUTLS fprintf(f, "# Template script checksum (SHA-1): "); for (i=0; ilxc_conf) return; lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } #define do_lxcapi_clear_config(c) lxcapi_clear_config(c) /* * lxcapi_create: * create a container with the given parameters. * @c: container to be created. It has the lxcpath, name, and a starting * configuration already set * @t: the template to execute to instantiate the root filesystem and * adjust the configuration. * @bdevtype: backing store type to use. If NULL, dir will be used. * @specs: additional parameters for the backing store, i.e. LVM vg to * use. * * @argv: the arguments to pass to the template, terminated by NULL. If no * arguments, you can just pass NULL. */ static bool do_lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { int partial_fd; mode_t mask; pid_t pid; bool ret = false; char *tpath = NULL; if (!c) return false; if (t) { tpath = get_template_path(t); if (!tpath) { ERROR("Unknown template \"%s\"", t); goto out; } } /* If a template is passed in, and the rootfs already is defined in the * container config and exists, then the caller is trying to create an * existing container. Return an error, but do NOT delete the container. */ if (do_lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0 && tpath) { ERROR("Container \"%s\" already exists in \"%s\"", c->name, c->config_path); goto free_tpath; } if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s", lxc_global_config_value("lxc.default_config")); goto free_tpath; } } if (!create_container_dir(c)) goto free_tpath; /* If both template and rootfs.path are set, template is setup as * rootfs.path. The container is already created if we have a config and * rootfs.path is accessible */ if (!c->lxc_conf->rootfs.path && !tpath) { /* No template passed in and rootfs does not exist. */ if (!c->save_config(c, NULL)) { ERROR("Failed to save initial config for \"%s\"", c->name); goto out; } ret = true; goto out; } /* Rootfs passed into configuration, but does not exist. */ if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) != 0) goto out; if (do_lxcapi_is_defined(c) && c->lxc_conf->rootfs.path && !tpath) { /* Rootfs already existed, user just wanted to save the loaded * configuration. */ if (!c->save_config(c, NULL)) ERROR("Failed to save initial config for \"%s\"", c->name); ret = true; goto out; } /* Mark that this container is being created */ partial_fd = create_partial(c); if (partial_fd < 0) goto out; /* No need to get disk lock bc we have the partial lock. */ mask = umask(0022); /* Create the storage. * Note we can't do this in the same task as we use to execute the * template because of the way zfs works. * After you 'zfs create', zfs mounts the fs only in the initial * namespace. */ pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task for container creation template"); goto out_unlock; } if (pid == 0) { /* child */ struct lxc_storage *bdev = NULL; bdev = do_storage_create(c, bdevtype, specs); if (!bdev) { ERROR("Failed to create %s storage for %s", bdevtype ? bdevtype : "(none)", c->name); _exit(EXIT_FAILURE); } /* Save config file again to store the new rootfs location. */ if (!do_lxcapi_save_config(c, NULL)) { ERROR("Failed to save initial config for %s", c->name); /* Parent task won't see the storage driver in the * config so we delete it. */ bdev->ops->umount(bdev); bdev->ops->destroy(bdev); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } if (wait_for_pid(pid) != 0) goto out_unlock; /* Reload config to get the rootfs. */ lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; if (!load_config_locked(c, c->configfile)) goto out_unlock; if (!create_run_template(c, tpath, !!(flags & LXC_CREATE_QUIET), argv)) goto out_unlock; /* Now clear out the lxc_conf we have, reload from the created * container. */ do_lxcapi_clear_config(c); if (t) { if (!prepend_lxc_header(c->configfile, tpath, argv)) { ERROR("Failed to prepend header to config file"); goto out_unlock; } } ret = load_config_locked(c, c->configfile); out_unlock: umask(mask); remove_partial(c, partial_fd); out: if (!ret) container_destroy(c, NULL); free_tpath: free(tpath); return ret; } static bool lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_create(c, t, bdevtype, specs, flags, argv); current_config = NULL; return ret; } static bool do_lxcapi_reboot(struct lxc_container *c) { int ret; pid_t pid; int rebootsignal = SIGINT; if (!c) return false; if (!do_lxcapi_is_running(c)) return false; pid = do_lxcapi_init_pid(c); if (pid <= 0) return false; if (c->lxc_conf && c->lxc_conf->rebootsignal) rebootsignal = c->lxc_conf->rebootsignal; ret = kill(pid, rebootsignal); if (ret < 0) { WARN("Failed to send signal %d to pid %d", rebootsignal, pid); return false; } return true; } WRAP_API(bool, lxcapi_reboot) static bool do_lxcapi_reboot2(struct lxc_container *c, int timeout) { int killret, ret; pid_t pid; int rebootsignal = SIGINT, state_client_fd = -1; lxc_state_t states[MAX_STATE] = {0}; if (!c) return false; if (!do_lxcapi_is_running(c)) return true; pid = do_lxcapi_init_pid(c); if (pid <= 0) return true; if (c->lxc_conf && c->lxc_conf->rebootsignal) rebootsignal = c->lxc_conf->rebootsignal; /* Add a new state client before sending the shutdown signal so that we * don't miss a state. */ if (timeout != 0) { states[RUNNING] = 2; ret = lxc_cmd_add_state_client(c->name, c->config_path, states, &state_client_fd); if (ret < 0) return false; if (state_client_fd < 0) return false; if (ret == RUNNING) return true; if (ret < MAX_STATE) return false; } /* Send reboot signal to container. */ killret = kill(pid, rebootsignal); if (killret < 0) { if (state_client_fd >= 0) close(state_client_fd); WARN("Failed to send signal %d to pid %d", rebootsignal, pid); return false; } TRACE("Sent signal %d to pid %d", rebootsignal, pid); if (timeout == 0) return true; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); close(state_client_fd); if (ret < 0) return false; TRACE("Received state \"%s\"", lxc_state2str(ret)); if (ret != RUNNING) return false; return true; } WRAP_API_1(bool, lxcapi_reboot2, int) static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) { int killret, ret; pid_t pid; int haltsignal = SIGPWR, state_client_fd = -EBADF; lxc_state_t states[MAX_STATE] = {0}; if (!c) return false; if (!do_lxcapi_is_running(c)) return true; pid = do_lxcapi_init_pid(c); if (pid <= 0) return true; /* Detect whether we should send SIGRTMIN + 3 (e.g. systemd). */ if (c->lxc_conf && c->lxc_conf->haltsignal) haltsignal = c->lxc_conf->haltsignal; else if (task_blocks_signal(pid, (SIGRTMIN + 3))) haltsignal = (SIGRTMIN + 3); /* Add a new state client before sending the shutdown signal so that we * don't miss a state. */ if (timeout != 0) { states[STOPPED] = 1; ret = lxc_cmd_add_state_client(c->name, c->config_path, states, &state_client_fd); if (ret < 0) return false; if (state_client_fd < 0) return false; if (ret == STOPPED) return true; if (ret < MAX_STATE) return false; } /* Send shutdown signal to container. */ killret = kill(pid, haltsignal); if (killret < 0) { if (state_client_fd >= 0) close(state_client_fd); WARN("Failed to send signal %d to pid %d", haltsignal, pid); return false; } TRACE("Sent signal %d to pid %d", haltsignal, pid); if (timeout == 0) return true; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); close(state_client_fd); if (ret < 0) return false; TRACE("Received state \"%s\"", lxc_state2str(ret)); if (ret != STOPPED) return false; return true; } WRAP_API_1(bool, lxcapi_shutdown, int) static bool lxcapi_createl(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, ...) { bool bret = false; char **args = NULL; va_list ap; if (!c) return false; current_config = c->lxc_conf; /* * since we're going to wait for create to finish, I don't think we * need to get a copy of the arguments. */ va_start(ap, flags); args = lxc_va_arg_list_to_argv(ap, 0, 0); va_end(ap); if (!args) { ERROR("Failed to allocate memory"); goto out; } bret = do_lxcapi_create(c, t, bdevtype, specs, flags, args); out: free(args); current_config = NULL; return bret; } static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key) { if (!strcmp(key, "lxc.cgroup")) 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) { int ret = 1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; 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; } WRAP_API_1(bool, lxcapi_clear_config_item, const char *) static inline bool enter_net_ns(struct lxc_container *c) { pid_t pid = do_lxcapi_init_pid(c); if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && (access("/proc/self/ns/user", F_OK) == 0)) if (!switch_to_ns(pid, "user")) return false; return switch_to_ns(pid, "net"); } /* Used by qsort and bsearch functions for comparing names. */ static inline int string_cmp(char **first, char **second) { return strcmp(*first, *second); } /* Used by qsort and bsearch functions for comparing container names. */ static inline int container_cmp(struct lxc_container **first, struct lxc_container **second) { return strcmp((*first)->name, (*second)->name); } static bool add_to_array(char ***names, char *cname, int pos) { char **newnames = realloc(*names, (pos+1) * sizeof(char *)); if (!newnames) { ERROR("Out of memory"); return false; } *names = newnames; newnames[pos] = strdup(cname); if (!newnames[pos]) return false; /* Sort the array as we will use binary search on it. */ qsort(newnames, pos + 1, sizeof(char *), (int (*)(const void *, const void *))string_cmp); return true; } static bool add_to_clist(struct lxc_container ***list, struct lxc_container *c, int pos, bool sort) { struct lxc_container **newlist = realloc(*list, (pos + 1) * sizeof(struct lxc_container *)); if (!newlist) { ERROR("Out of memory"); return false; } *list = newlist; newlist[pos] = c; /* Sort the array as we will use binary search on it. */ if (sort) qsort(newlist, pos + 1, sizeof(struct lxc_container *), (int (*)(const void *, const void *))container_cmp); return true; } static char** get_from_array(char ***names, char *cname, int size) { return (char **)bsearch(&cname, *names, size, sizeof(char *), (int (*)(const void *, const void *))string_cmp); } static bool array_contains(char ***names, char *cname, int size) { if(get_from_array(names, cname, size) != NULL) return true; return false; } static bool remove_from_array(char ***names, char *cname, int size) { char **result = get_from_array(names, cname, size); if (result != NULL) { free(result); return true; } return false; } static char **do_lxcapi_get_interfaces(struct lxc_container *c) { pid_t pid; int i, count = 0, pipefd[2]; char **interfaces = NULL; char interface[IFNAMSIZ]; if (pipe2(pipefd, O_CLOEXEC) < 0) return NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task to get interfaces information"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { /* child */ int ret = 1, nbytes; struct netns_ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to enter network namespace"); goto out; } /* Grab the list of interfaces */ if (netns_getifaddrs(&interfaceArray, -1, &(bool){false})) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { nbytes = lxc_write_nointr(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ); if (nbytes < 0) goto out; count++; } ret = 0; out: if (interfaceArray) netns_freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &interface, IFNAMSIZ) == IFNAMSIZ) { interface[IFNAMSIZ - 1] = '\0'; if (array_contains(&interfaces, interface, count)) continue; if (!add_to_array(&interfaces, interface, count)) ERROR("Failed to add \"%s\" to array", interface); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(interfaces[i]); free(interfaces); interfaces = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (interfaces) interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count); return interfaces; } WRAP_API(char **, lxcapi_get_interfaces) static char **do_lxcapi_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope) { int i, ret; pid_t pid; int pipefd[2]; char address[INET6_ADDRSTRLEN]; int count = 0; char **addresses = NULL; ret = pipe2(pipefd, O_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create pipe"); return NULL; } pid = fork(); if (pid < 0) { SYSERROR("Failed to create new process"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { ssize_t nbytes; char addressOutputBuffer[INET6_ADDRSTRLEN]; int ret = 1; char *address = NULL; void *tempAddrPtr = NULL; struct netns_ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to attach to network namespace"); goto out; } /* Grab the list of interfaces */ if (netns_getifaddrs(&interfaceArray, -1, &(bool){false})) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr; tempIfAddr = tempIfAddr->ifa_next) { if (tempIfAddr->ifa_addr == NULL) continue; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (tempIfAddr->ifa_addr->sa_family == AF_INET) { if (family && strcmp(family, "inet")) continue; tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; } else { if (family && strcmp(family, "inet6")) continue; if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope) continue; tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; } #pragma GCC diagnostic pop if (interface && strcmp(interface, tempIfAddr->ifa_name)) continue; else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) continue; address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, tempAddrPtr, addressOutputBuffer, sizeof(addressOutputBuffer)); if (!address) continue; nbytes = lxc_write_nointr(pipefd[1], address, INET6_ADDRSTRLEN); if (nbytes != INET6_ADDRSTRLEN) { SYSERROR("Failed to send ipv6 address \"%s\"", address); goto out; } count++; } ret = 0; out: if (interfaceArray) netns_freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &address, INET6_ADDRSTRLEN) == INET6_ADDRSTRLEN) { address[INET6_ADDRSTRLEN - 1] = '\0'; if (!add_to_array(&addresses, address, count)) ERROR("PARENT: add_to_array failed"); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(addresses[i]); free(addresses); addresses = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (addresses) addresses = (char **)lxc_append_null_to_array((void **)addresses, count); return addresses; } WRAP_API_3(char **, lxcapi_get_ips, const char *, const char *, int) static int do_lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret = -1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; config = lxc_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; } WRAP_API_3(int, lxcapi_get_config_item, const char *, char *, int) static char* do_lxcapi_get_running_config_item(struct lxc_container *c, const char *key) { char *ret; if (!c || !c->lxc_conf) return NULL; if (container_mem_lock(c)) return NULL; ret = lxc_cmd_get_config_item(c->name, key, do_lxcapi_get_config_path(c)); container_mem_unlock(c); return ret; } WRAP_API_1(char *, lxcapi_get_running_config_item, const char *) static int do_lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret = -1; /* List all config items. */ if (!key) return lxc_list_config_items(retv, inlen); if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; /* 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) == 0) ret = lxc_list_net(c->lxc_conf, key, retv, inlen); else ret = lxc_list_subkeys(c->lxc_conf, key, retv, inlen); container_mem_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_keys, const char *, char *, int) static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file) { int fd, lret; bool ret = false, need_disklock = false; if (!alt_file) alt_file = c->configfile; if (!alt_file) return false; /* If we haven't yet loaded a config, load the stock config. */ if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s " "while saving %s", lxc_global_config_value("lxc.default_config"), c->name); return false; } } if (!create_container_dir(c)) return false; /* If we're writing to the container's config file, take the disk lock. * Otherwise just take the memlock to protect the struct lxc_container * while we're traversing it. */ if (strcmp(c->configfile, alt_file) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; fd = open(alt_file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) goto on_error; lret = write_config(fd, c->lxc_conf); close(fd); if (lret < 0) goto on_error; ret = true; on_error: if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_save_config, const char *) static bool mod_rdep(struct lxc_container *c0, struct lxc_container *c, bool inc) { FILE *f1; struct stat fbuf; void *buf = NULL; char *del = NULL; char path[PATH_MAX]; char newpath[PATH_MAX]; int fd, ret, n = 0, v = 0; bool bret = false; size_t len = 0, bytes = 0; if (container_disk_lock(c0)) return false; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c0->config_path, c0->name); if (ret < 0 || ret > PATH_MAX) goto out; ret = snprintf(newpath, PATH_MAX, "%s\n%s\n", c->config_path, c->name); if (ret < 0 || ret > PATH_MAX) goto out; /* If we find an lxc-snapshot file using the old format only listing the * number of snapshots we will keep using it. */ f1 = fopen(path, "r"); if (f1) { n = fscanf(f1, "%d", &v); fclose(f1); if (n == 1 && v == 0) { ret = remove(path); if (ret < 0) SYSERROR("Failed to remove \"%s\"", path); n = 0; } } if (n == 1) { v += inc ? 1 : -1; f1 = fopen(path, "w"); if (!f1) goto out; if (fprintf(f1, "%d\n", v) < 0) { ERROR("Error writing new snapshots value"); fclose(f1); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else { /* Here we know that we have or can use an lxc-snapshot file * using the new format. */ if (inc) { f1 = fopen(path, "a"); if (!f1) goto out; if (fprintf(f1, "%s", newpath) < 0) { ERROR("Error writing new snapshots entry"); ret = fclose(f1); if (ret != 0) SYSERROR("Error writing to or closing snapshots file"); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else if (!inc) { if ((fd = open(path, O_RDWR | O_CLOEXEC)) < 0) goto out; if (fstat(fd, &fbuf) < 0) { close(fd); goto out; } if (fbuf.st_size != 0) { buf = lxc_strmmap(NULL, fbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { SYSERROR("Failed to create mapping %s", path); close(fd); goto out; } len = strlen(newpath); while ((del = strstr((char *)buf, newpath))) { memmove(del, del + len, strlen(del) - len + 1); bytes += len; } lxc_strmunmap(buf, fbuf.st_size); if (ftruncate(fd, fbuf.st_size - bytes) < 0) { SYSERROR("Failed to truncate file %s", path); close(fd); goto out; } } close(fd); } /* If the lxc-snapshot file is empty, remove it. */ if (stat(path, &fbuf) < 0) goto out; if (!fbuf.st_size) { ret = remove(path); if (ret < 0) SYSERROR("Failed to remove \"%s\"", path); } } bret = true; out: container_disk_unlock(c0); return bret; } void mod_all_rdeps(struct lxc_container *c, bool inc) { struct lxc_container *p; char *lxcpath = NULL, *lxcname = NULL, path[PATH_MAX]; size_t pathlen = 0, namelen = 0; FILE *f; int ret; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) { ERROR("Path name too long"); return; } f = fopen(path, "r"); if (f == NULL) return; while (getline(&lxcpath, &pathlen, f) != -1) { if (getline(&lxcname, &namelen, f) == -1) { ERROR("badly formatted file %s", path); goto out; } remove_trailing_newlines(lxcpath); remove_trailing_newlines(lxcname); if ((p = lxc_container_new(lxcname, lxcpath)) == NULL) { ERROR("Unable to find dependent container %s:%s", lxcpath, lxcname); continue; } if (!mod_rdep(p, c, inc)) ERROR("Failed to update snapshots file for %s:%s", lxcpath, lxcname); lxc_container_put(p); } out: free(lxcpath); free(lxcname); fclose(f); } static bool has_fs_snapshots(struct lxc_container *c) { FILE *f; char path[PATH_MAX]; int ret, v; struct stat fbuf; bool bret = false; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret > PATH_MAX) goto out; /* If the file doesn't exist there are no snapshots. */ if (stat(path, &fbuf) < 0) goto out; v = fbuf.st_size; if (v != 0) { f = fopen(path, "r"); if (!f) goto out; ret = fscanf(f, "%d", &v); fclose(f); /* TODO: Figure out what to do with the return value of fscanf. */ if (ret != 1) INFO("Container uses new lxc-snapshots format %s", path); } bret = v != 0; out: return bret; } static bool has_snapshots(struct lxc_container *c) { char path[PATH_MAX]; struct dirent *direntp; int count=0; DIR *dir; if (!get_snappath_dir(c, path)) return false; dir = opendir(path); if (!dir) return false; while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; count++; break; } closedir(dir); return count > 0; } static bool do_destroy_container(struct lxc_conf *conf) { int ret; if (am_guest_unpriv()) { ret = userns_exec_full(conf, storage_destroy_wrapper, conf, "storage_destroy_wrapper"); if (ret < 0) return false; return true; } return storage_destroy(conf); } static int lxc_rmdir_onedev_wrapper(void *data) { char *arg = (char *) data; return lxc_rmdir_onedev(arg, "snaps"); } static 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; if (!c || !do_lxcapi_is_defined(c)) return false; conf = c->lxc_conf; if (container_disk_lock(c)) return false; if (!is_stopped(c)) { /* We should queue some sort of error - in c->error_string? */ ERROR("container %s is not stopped", c->name); goto out; } if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) { /* Start of environment variable setup for hooks */ if (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, NULL)) { ERROR("Failed to execute clone hook for \"%s\"", c->name); goto out; } } if (current_config && conf == current_config) { current_config = NULL; if (conf->logfd != -1) { close(conf->logfd); conf->logfd = -1; } } if (conf && conf->rootfs.path && conf->rootfs.mount) { if (!do_destroy_container(conf)) { ERROR("Error destroying rootfs for %s", c->name); goto out; } INFO("Destroyed rootfs for %s", c->name); } mod_all_rdeps(c, false); 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_guest_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_guest_unpriv()) ret = userns_exec_full(conf, lxc_rmdir_onedev_wrapper, path, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(path, "snaps"); if (ret < 0) { ERROR("Failed to destroy directory \"%s\" for \"%s\"", path, c->name); goto out; } INFO("Destroyed directory \"%s\" for \"%s\"", path, c->name); bret = true; out: if (path) free(path); container_disk_unlock(c); return bret; } static bool do_lxcapi_destroy(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (has_snapshots(c)) { ERROR("Container %s has snapshots; not removing", c->name); return false; } if (has_fs_snapshots(c)) { ERROR("container %s has snapshots on its rootfs", c->name); return false; } return container_destroy(c, NULL); } WRAP_API(bool, lxcapi_destroy) static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (!lxcapi_snapshot_destroy_all(c)) { ERROR("Error deleting all snapshots"); return false; } return lxcapi_destroy(c); } WRAP_API(bool, lxcapi_destroy_with_snapshots) int lxc_set_config_item_locked(struct lxc_conf *conf, const char *key, const char *v) { int ret; struct lxc_config_t *config; bool bret = true; config = lxc_get_config(key); if (!config) return -EINVAL; ret = config->set(key, v, conf, NULL); if (ret < 0) return -EINVAL; if (lxc_config_value_empty(v)) do_clear_unexp_config_line(conf, key); else bret = do_append_unexp_config_line(conf, key, v); if (!bret) return -ENOMEM; return 0; } static bool do_set_config_item_locked(struct lxc_container *c, const char *key, const char *v) { int ret; if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; ret = lxc_set_config_item_locked(c->lxc_conf, key, v); if (ret < 0) return false; return true; } static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) { bool b = false; if (!c) return false; if (container_mem_lock(c)) return false; b = do_set_config_item_locked(c, key, v); container_mem_unlock(c); return b; } WRAP_API_2(bool, lxcapi_set_config_item, const char *, const char *) static char *lxcapi_config_file_name(struct lxc_container *c) { if (!c || !c->configfile) return NULL; return strdup(c->configfile); } static const char *lxcapi_get_config_path(struct lxc_container *c) { if (!c || !c->config_path) return NULL; return (const char *)(c->config_path); } /* * not for export * Just recalculate the c->configfile based on the * c->config_path, which must be set. * The lxc_container must be locked or not yet public. */ static bool set_config_filename(struct lxc_container *c) { char *newpath; int len, ret; if (!c->config_path) return false; /* $lxc_path + "/" + c->name + "/" + "config" + '\0' */ len = strlen(c->config_path) + strlen(c->name) + strlen("config") + 3; newpath = malloc(len); if (!newpath) return false; ret = snprintf(newpath, len, "%s/%s/config", c->config_path, c->name); if (ret < 0 || ret >= len) { fprintf(stderr, "Error printing out config file name\n"); free(newpath); return false; } free(c->configfile); c->configfile = newpath; return true; } static bool do_lxcapi_set_config_path(struct lxc_container *c, const char *path) { char *p; bool b = false; char *oldpath = NULL; if (!c) return b; if (container_mem_lock(c)) return b; p = strdup(path); if (!p) { ERROR("Out of memory setting new lxc path"); goto err; } b = true; if (c->config_path) oldpath = c->config_path; c->config_path = p; /* Since we've changed the config path, we have to change the * config file name too */ if (!set_config_filename(c)) { ERROR("Out of memory setting new config filename"); b = false; free(c->config_path); c->config_path = oldpath; oldpath = NULL; } err: free(oldpath); container_mem_unlock(c); return b; } WRAP_API_1(bool, lxcapi_set_config_path, const char *) static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) { int ret; struct cgroup_ops *cgroup_ops; if (!c) return false; if (is_stopped(c)) return false; cgroup_ops = cgroup_init(NULL); if (!cgroup_ops) return false; ret = cgroup_ops->set(cgroup_ops, subsys, value, c->name, c->config_path); cgroup_exit(cgroup_ops); return ret == 0; } WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *) static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) { int ret; struct cgroup_ops *cgroup_ops; if (!c) return -1; if (is_stopped(c)) return -1; cgroup_ops = cgroup_init(NULL); if (!cgroup_ops) return -1; ret = cgroup_ops->get(cgroup_ops, subsys, retv, inlen, c->name, c->config_path); cgroup_exit(cgroup_ops); return ret; } WRAP_API_3(int, lxcapi_get_cgroup_item, const char *, char *, int) const char *lxc_get_global_config_item(const char *key) { return lxc_global_config_value(key); } const char *lxc_get_version(void) { return LXC_VERSION; } static int copy_file(const char *old, const char *new) { int in, out; ssize_t len, ret; char buf[8096]; struct stat sbuf; if (file_exists(new)) { ERROR("copy destination %s exists", new); return -1; } ret = stat(old, &sbuf); if (ret < 0) { INFO("Error stat'ing %s", old); return -1; } in = open(old, O_RDONLY); if (in < 0) { SYSERROR("Error opening original file %s", old); return -1; } out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644); if (out < 0) { SYSERROR("Error opening new file %s", new); close(in); return -1; } while (1) { len = lxc_read_nointr(in, buf, 8096); if (len < 0) { SYSERROR("Error reading old file %s", old); goto err; } if (len == 0) break; ret = lxc_write_nointr(out, buf, len); if (ret < len) { /* should we retry? */ SYSERROR("Error: write to new file %s was interrupted", new); goto err; } } close(in); close(out); /* We set mode, but not owner/group. */ ret = chmod(new, sbuf.st_mode); if (ret) { SYSERROR("Error setting mode on %s", new); return -1; } return 0; err: close(in); close(out); return -1; } static int copyhooks(struct lxc_container *oldc, struct lxc_container *c) { int i, len, ret; struct lxc_list *it; char *cpath; len = strlen(oldc->config_path) + strlen(oldc->name) + 3; cpath = alloca(len); ret = snprintf(cpath, len, "%s/%s/", oldc->config_path, oldc->name); if (ret < 0 || ret >= len) return -1; for (i=0; ilxc_conf->hooks[i]) { char *hookname = it->elem; char *fname = strrchr(hookname, '/'); char tmppath[PATH_MAX]; if (!fname) /* relative path - we don't support, but maybe we should */ return 0; if (strncmp(hookname, cpath, len - 1) != 0) { /* this hook is public - ignore */ continue; } /* copy the script, and change the entry in confile */ ret = snprintf(tmppath, PATH_MAX, "%s/%s/%s", c->config_path, c->name, fname+1); if (ret < 0 || ret >= PATH_MAX) return -1; ret = copy_file(it->elem, tmppath); if (ret < 0) return -1; free(it->elem); it->elem = strdup(tmppath); if (!it->elem) { ERROR("out of memory copying hook path"); return -1; } } } if (!clone_update_unexp_hooks(c->lxc_conf, oldc->config_path, c->config_path, oldc->name, c->name)) { ERROR("Error saving new hooks in clone"); return -1; } do_lxcapi_save_config(c, NULL); return 0; } static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c) { char newpath[PATH_MAX]; char *oldpath = oldc->lxc_conf->fstab; int ret; if (!oldpath) return 0; clear_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", false); char *p = strrchr(oldpath, '/'); if (!p) return -1; ret = snprintf(newpath, PATH_MAX, "%s/%s%s", c->config_path, c->name, p); if (ret < 0 || ret >= PATH_MAX) { ERROR("error printing new path for %s", oldpath); return -1; } if (file_exists(newpath)) { ERROR("error: fstab file %s exists", newpath); return -1; } if (copy_file(oldpath, newpath) < 0) { ERROR("error: copying %s to %s", oldpath, newpath); return -1; } free(c->lxc_conf->fstab); c->lxc_conf->fstab = strdup(newpath); if (!c->lxc_conf->fstab) { ERROR("error: allocating pathname"); return -1; } if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", newpath)) { ERROR("error saving new lxctab"); return -1; } return 0; } static void copy_rdepends(struct lxc_container *c, struct lxc_container *c0) { char path0[PATH_MAX], path1[PATH_MAX]; int ret; ret = snprintf(path0, PATH_MAX, "%s/%s/lxc_rdepends", c0->config_path, c0->name); if (ret < 0 || ret >= PATH_MAX) { WARN("Error copying reverse dependencies"); return; } ret = snprintf(path1, PATH_MAX, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) { WARN("Error copying reverse dependencies"); return; } if (copy_file(path0, path1) < 0) { INFO("Error copying reverse dependencies"); return; } } static bool add_rdepends(struct lxc_container *c, struct lxc_container *c0) { int ret; char path[PATH_MAX]; FILE *f; bool bret; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) return false; f = fopen(path, "a"); if (!f) return false; bret = true; /* If anything goes wrong, just return an error. */ if (fprintf(f, "%s\n%s\n", c0->config_path, c0->name) < 0) bret = false; if (fclose(f) != 0) bret = false; return bret; } /* * If the fs natively supports snapshot clones with no penalty, * then default to those even if not requested. * Currently we only do this for btrfs. */ bool should_default_to_snapshot(struct lxc_container *c0, struct lxc_container *c1) { int ret; size_t l0 = strlen(c0->config_path) + strlen(c0->name) + 2; size_t l1 = strlen(c1->config_path) + strlen(c1->name) + 2; char *p0 = alloca(l0 + 1); char *p1 = alloca(l1 + 1); char *rootfs = c0->lxc_conf->rootfs.path; ret = snprintf(p0, l0, "%s/%s", c0->config_path, c0->name); if (ret < 0 || ret >= l0) return false; ret = snprintf(p1, l1, "%s/%s", c1->config_path, c1->name); if (ret < 0 || ret >= l1) return false; if (!is_btrfs_fs(p0) || !is_btrfs_fs(p1)) return false; if (is_btrfs_subvol(rootfs) <= 0) return false; return btrfs_same_fs(p0, p1) == 0; } static int copy_storage(struct lxc_container *c0, struct lxc_container *c, const char *newtype, int flags, const char *bdevdata, uint64_t newsize) { struct lxc_storage *bdev; bool need_rdep; if (should_default_to_snapshot(c0, c)) flags |= LXC_CLONE_SNAPSHOT; bdev = storage_copy(c0, c->name, c->config_path, newtype, flags, bdevdata, newsize, &need_rdep); if (!bdev) { ERROR("Error copying storage."); return -1; } /* Set new rootfs. */ free(c->lxc_conf->rootfs.path); c->lxc_conf->rootfs.path = strdup(bdev->src); storage_put(bdev); if (!c->lxc_conf->rootfs.path) { ERROR("Out of memory while setting storage path."); return -1; } /* 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; } if (flags & LXC_CLONE_SNAPSHOT) copy_rdepends(c, c0); if (need_rdep) { if (!add_rdepends(c, c0)) WARN("Error adding reverse dependency from %s to %s", c->name, c0->name); } mod_all_rdeps(c, true); return 0; } struct clone_update_data { struct lxc_container *c0; struct lxc_container *c1; int flags; char **hookargs; }; static int clone_update_rootfs(struct clone_update_data *data) { struct lxc_container *c0 = data->c0; struct lxc_container *c = data->c1; int flags = data->flags; char **hookargs = data->hookargs; int ret = -1; char path[PATH_MAX]; struct lxc_storage *bdev; FILE *fout; struct lxc_conf *conf = c->lxc_conf; /* update hostname in rootfs */ /* we're going to mount, so run in a clean namespace to simplify cleanup */ if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (setgroups(0, NULL) < 0) WARN("Failed to clear groups"); if (unshare(CLONE_NEWNS) < 0) return -1; bdev = storage_init(c->lxc_conf); if (!bdev) return -1; if (strcmp(bdev->type, "dir") != 0) { if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); storage_put(bdev); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } if (bdev->ops->mount(bdev) < 0) { storage_put(bdev); return -1; } } else { /* TODO come up with a better way */ free(bdev->dest); bdev->dest = strdup(bdev->src); } if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) { /* Start of environment variable setup for hooks */ if (c0->name && setenv("LXC_SRC_NAME", c0->name, 1)) SYSERROR("failed to set environment variable for source container name"); if (setenv("LXC_NAME", c->name, 1)) SYSERROR("failed to set environment variable for container name"); if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) SYSERROR("failed to set environment variable for config path"); if (bdev->dest && setenv("LXC_ROOTFS_MOUNT", bdev->dest, 1)) SYSERROR("failed to set environment variable for rootfs mount"); if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) SYSERROR("failed to set environment variable for rootfs mount"); if (run_lxc_hooks(c->name, "clone", conf, hookargs)) { ERROR("Error executing clone hook for %s", c->name); storage_put(bdev); return -1; } } if (!(flags & LXC_CLONE_KEEPNAME)) { ret = snprintf(path, PATH_MAX, "%s/etc/hostname", bdev->dest); storage_put(bdev); if (ret < 0 || ret >= PATH_MAX) return -1; if (!file_exists(path)) return 0; if (!(fout = fopen(path, "w"))) { SYSERROR("unable to open %s: ignoring", path); return 0; } if (fprintf(fout, "%s", c->name) < 0) { fclose(fout); return -1; } if (fclose(fout) < 0) return -1; } else { storage_put(bdev); } return 0; } static int clone_update_rootfs_wrapper(void *data) { struct clone_update_data *arg = (struct clone_update_data *) data; return clone_update_rootfs(arg); } /* * We want to support: sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \ -p|--lvprefix lvprefix -t|--fstype fstype -B backingstore -s [ implies overlay] -s -B overlay only rootfs gets converted (copied/snapshotted) on clone. */ static int create_file_dirname(char *path, struct lxc_conf *conf) { char *p = strrchr(path, '/'); int ret = -1; if (!p) return -1; *p = '\0'; ret = do_create_container_dir(path, conf); *p = '/'; return ret; } static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { char newpath[PATH_MAX]; int fd, ret; struct clone_update_data data; size_t saved_unexp_len; 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; if (container_mem_lock(c)) return NULL; if (!is_stopped(c)) { ERROR("error: Original container (%s) is running", c->name); goto out; } /* Make sure the container doesn't yet exist. */ if (!newname) newname = c->name; if (!lxcpath) lxcpath = do_lxcapi_get_config_path(c); ret = snprintf(newpath, PATH_MAX, "%s/%s/config", lxcpath, newname); if (ret < 0 || ret >= PATH_MAX) { SYSERROR("clone: failed making config pathname"); goto out; } if (file_exists(newpath)) { ERROR("error: clone: %s exists", newpath); goto out; } ret = create_file_dirname(newpath, c->lxc_conf); if (ret < 0 && errno != EEXIST) { ERROR("Error creating container dir for %s", newpath); goto out; } /* Copy the configuration. Tweak it as needed. */ if (c->lxc_conf->rootfs.path) { origroot = c->lxc_conf->rootfs.path; c->lxc_conf->rootfs.path = NULL; } fd = open(newpath, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) { SYSERROR("Failed to open \"%s\"", newpath); goto out; } saved_unexp_conf = c->lxc_conf->unexpanded_config; saved_unexp_len = c->lxc_conf->unexpanded_len; c->lxc_conf->unexpanded_config = strdup(saved_unexp_conf); if (!c->lxc_conf->unexpanded_config) { close(fd); goto out; } clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", false); write_config(fd, c->lxc_conf); close(fd); c->lxc_conf->rootfs.path = origroot; free(c->lxc_conf->unexpanded_config); c->lxc_conf->unexpanded_config = saved_unexp_conf; saved_unexp_conf = NULL; c->lxc_conf->unexpanded_len = saved_unexp_len; ret = snprintf(newpath, PATH_MAX, "%s/%s/rootfs", lxcpath, newname); if (ret < 0 || ret >= PATH_MAX) { 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_guest_unpriv()) { if (chown_mapped_root(newpath, c->lxc_conf) < 0) { ERROR("Error chowning %s to container root", newpath); goto out; } } c2 = lxc_container_new(newname, lxcpath); if (!c2) { ERROR("clone: failed to create new container (%s %s)", newname, lxcpath); goto out; } /* copy/snapshot rootfs's */ ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); if (ret < 0) goto out; /* update utsname */ if (!(flags & LXC_CLONE_KEEPNAME)) { clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false); clear_unexp_config_line(c2->lxc_conf, "lxc.uts.name", false); if (!do_set_config_item_locked(c2, "lxc.uts.name", newname)) { ERROR("Error setting new hostname"); goto out; } } /* copy hooks */ ret = copyhooks(c, c2); if (ret < 0) { ERROR("error copying hooks"); goto out; } if (copy_fstab(c, c2) < 0) { ERROR("error copying fstab"); goto out; } /* update macaddrs */ if (!(flags & LXC_CLONE_KEEPMACADDR)) { if (!network_new_hwaddrs(c2->lxc_conf)) { ERROR("Error updating mac addresses"); goto out; } } /* Update absolute paths for overlay mount directories. */ if (ovl_update_abs_paths(c2->lxc_conf, c->config_path, c->name, lxcpath, newname) < 0) goto out; /* We've now successfully created c2's storage, so clear it out if we * fail after this. */ storage_copied = 1; if (!c2->save_config(c2, NULL)) goto out; if ((pid = fork()) < 0) { SYSERROR("fork"); goto out; } if (pid > 0) { ret = wait_for_pid(pid); if (ret) goto out; container_mem_unlock(c); return c2; } data.c0 = c; data.c1 = c2; data.flags = flags; data.hookargs = hookargs; if (am_guest_unpriv()) ret = userns_exec_full(c->lxc_conf, clone_update_rootfs_wrapper, &data, "clone_update_rootfs_wrapper"); else ret = clone_update_rootfs(&data); if (ret < 0) _exit(EXIT_FAILURE); container_mem_unlock(c); _exit(EXIT_SUCCESS); out: container_mem_unlock(c); if (c2) { if (!storage_copied) c2->lxc_conf->rootfs.path = NULL; c2->destroy(c2); lxc_container_put(c2); } return NULL; } static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { struct lxc_container * ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_clone(c, newname, lxcpath, flags, bdevtype, bdevdata, newsize, hookargs); current_config = NULL; return ret; } static bool do_lxcapi_rename(struct lxc_container *c, const char *newname) { struct lxc_storage *bdev; struct lxc_container *newc; if (!c || !c->name || !c->config_path || !c->lxc_conf) return false; if (has_fs_snapshots(c) || has_snapshots(c)) { ERROR("Renaming a container with snapshots is not supported"); return false; } bdev = storage_init(c->lxc_conf); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } newc = lxcapi_clone(c, newname, c->config_path, LXC_CLONE_KEEPMACADDR, NULL, bdev->type, 0, NULL); storage_put(bdev); if (!newc) { lxc_container_put(newc); return false; } if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); if (!container_destroy(c, NULL)) { ERROR("Could not destroy existing container %s", c->name); return false; } return true; } WRAP_API_1(bool, lxcapi_rename, const char *) static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process); current_config = NULL; return ret; } static int do_lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]) { lxc_attach_command_t command; pid_t pid; int r; if (!c) return -1; command.program = (char*)program; command.argv = (char**)argv; r = lxc_attach(c->name, c->config_path, lxc_attach_run_command, &command, options, &pid); if (r < 0) { ERROR("ups"); return r; } return lxc_wait_for_pid_status(pid); } static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]) { int ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_attach_run_wait(c, options, program, argv); current_config = NULL; return ret; } static int get_next_index(const char *lxcpath, char *cname) { char *fname; struct stat sb; int i = 0, ret; fname = alloca(strlen(lxcpath) + 20); while (1) { sprintf(fname, "%s/snap%d", lxcpath, i); ret = stat(fname, &sb); if (ret != 0) return i; i++; } } static bool get_snappath_dir(struct lxc_container *c, char *snappath) { int ret; /* * If the old style snapshot path exists, use it * /var/lib/lxc -> /var/lib/lxcsnaps */ ret = snprintf(snappath, PATH_MAX, "%ssnaps", c->config_path); if (ret < 0 || ret >= PATH_MAX) return false; if (dir_exists(snappath)) { ret = snprintf(snappath, PATH_MAX, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) return false; return true; } /* * Use the new style path * /var/lib/lxc -> /var/lib/lxc + c->name + /snaps + \0 */ ret = snprintf(snappath, PATH_MAX, "%s/%s/snaps", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) return false; return true; } static int do_lxcapi_snapshot(struct lxc_container *c, const char *commentfile) { int i, flags, ret; time_t timer; struct tm tm_info; struct lxc_container *c2; char snappath[PATH_MAX], newname[20]; char buffer[25]; FILE *f; if (!c || !lxcapi_is_defined(c)) return -1; if (!storage_can_backup(c->lxc_conf)) { ERROR("%s's backing store cannot be backed up", c->name); ERROR("Your container must use another backing store type"); return -1; } if (!get_snappath_dir(c, snappath)) return -1; i = get_next_index(snappath, c->name); if (mkdir_p(snappath, 0755) < 0) { ERROR("Failed to create snapshot directory %s", snappath); return -1; } ret = snprintf(newname, 20, "snap%d", i); if (ret < 0 || ret >= 20) return -1; /* * We pass LXC_CLONE_SNAPSHOT to make sure that a rdepends file entry is * created in the original container */ flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME | LXC_CLONE_KEEPBDEVTYPE | LXC_CLONE_MAYBE_SNAPSHOT; if (storage_is_dir(c->lxc_conf)) { ERROR("Snapshot of directory-backed container requested"); ERROR("Making a copy-clone. If you do want snapshots, then"); ERROR("please create overlay clone first, snapshot that"); ERROR("and keep the original container pristine"); flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; } c2 = do_lxcapi_clone(c, newname, snappath, flags, NULL, NULL, 0, NULL); if (!c2) { ERROR("Failed to clone of %s:%s", c->config_path, c->name); return -1; } lxc_container_put(c2); /* Now write down the creation time. */ time(&timer); if (!localtime_r(&timer, &tm_info)) { ERROR("Failed to get localtime"); return -1; } strftime(buffer, 25, "%Y:%m:%d %H:%M:%S", &tm_info); char *dfnam = alloca(strlen(snappath) + strlen(newname) + 5); sprintf(dfnam, "%s/%s/ts", snappath, newname); f = fopen(dfnam, "w"); if (!f) { ERROR("Failed to open %s", dfnam); return -1; } if (fprintf(f, "%s", buffer) < 0) { SYSERROR("Writing timestamp"); fclose(f); return -1; } ret = fclose(f); if (ret != 0) { SYSERROR("Writing timestamp"); return -1; } if (commentfile) { /* $p / $name / comment \0 */ int len = strlen(snappath) + strlen(newname) + 10; char *path = alloca(len); sprintf(path, "%s/%s/comment", snappath, newname); return copy_file(commentfile, path) < 0 ? -1 : i; } return i; } WRAP_API_1(int, lxcapi_snapshot, const char *) static void lxcsnap_free(struct lxc_snapshot *s) { free(s->name); free(s->comment_pathname); free(s->timestamp); free(s->lxcpath); } static char *get_snapcomment_path(char* snappath, char *name) { /* $snappath/$name/comment */ int ret, len = strlen(snappath) + strlen(name) + 10; char *s = malloc(len); if (s) { ret = snprintf(s, len, "%s/%s/comment", snappath, name); if (ret < 0 || ret >= len) { free(s); s = NULL; } } return s; } static char *get_timestamp(char* snappath, char *name) { char path[PATH_MAX], *s = NULL; int ret, len; FILE *fin; ret = snprintf(path, PATH_MAX, "%s/%s/ts", snappath, name); if (ret < 0 || ret >= PATH_MAX) return NULL; fin = fopen(path, "r"); if (!fin) return NULL; (void) fseek(fin, 0, SEEK_END); len = ftell(fin); (void) fseek(fin, 0, SEEK_SET); if (len > 0) { s = malloc(len+1); if (s) { s[len] = '\0'; if (fread(s, 1, len, fin) != len) { SYSERROR("reading timestamp"); free(s); s = NULL; } } } fclose(fin); return s; } static int do_lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps) { char snappath[PATH_MAX], path2[PATH_MAX]; int count = 0, ret; struct dirent *direntp; struct lxc_snapshot *snaps =NULL, *nsnaps; DIR *dir; if (!c || !lxcapi_is_defined(c)) return -1; if (!get_snappath_dir(c, snappath)) { ERROR("path name too long"); return -1; } dir = opendir(snappath); if (!dir) { INFO("Failed to open %s - assuming no snapshots", snappath); return 0; } while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; ret = snprintf(path2, PATH_MAX, "%s/%s/config", snappath, direntp->d_name); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out_free; } if (!file_exists(path2)) continue; nsnaps = realloc(snaps, (count + 1)*sizeof(*snaps)); if (!nsnaps) { SYSERROR("Out of memory"); goto out_free; } snaps = nsnaps; snaps[count].free = lxcsnap_free; snaps[count].name = strdup(direntp->d_name); if (!snaps[count].name) goto out_free; snaps[count].lxcpath = strdup(snappath); if (!snaps[count].lxcpath) { free(snaps[count].name); goto out_free; } snaps[count].comment_pathname = get_snapcomment_path(snappath, direntp->d_name); snaps[count].timestamp = get_timestamp(snappath, direntp->d_name); count++; } if (closedir(dir)) WARN("Failed to close directory"); *ret_snaps = snaps; return count; out_free: if (snaps) { int i; for (i=0; iname || !c->config_path) return false; if (has_fs_snapshots(c)) { ERROR("container rootfs has dependent snapshots"); return false; } bdev = storage_init(c->lxc_conf); 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)) { storage_put(bdev); return false; } /* how should we lock this? */ snap = lxc_container_new(snapname, clonelxcpath); if (!snap || !lxcapi_is_defined(snap)) { ERROR("Could not open snapshot %s", snapname); if (snap) lxc_container_put(snap); storage_put(bdev); return false; } if (!strcmp(c->name, newname)) { if (!container_destroy(c, bdev)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); storage_put(bdev); return false; } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0) flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; 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); storage_put(bdev); if (rest && lxcapi_is_defined(rest)) b = true; if (rest) lxc_container_put(rest); lxc_container_put(snap); return b; } WRAP_API_2(bool, lxcapi_snapshot_restore, const char *, const char *) static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath) { struct lxc_container *snap = NULL; bool bret = false; snap = lxc_container_new(snapname, clonelxcpath); if (!snap) { ERROR("Could not find snapshot %s", snapname); goto err; } if (!do_lxcapi_destroy(snap)) { ERROR("Could not destroy snapshot %s", snapname); goto err; } bret = true; err: if (snap) lxc_container_put(snap); return bret; } static bool remove_all_snapshots(const char *path) { DIR *dir; struct dirent *direntp; bool bret = true; dir = opendir(path); if (!dir) { SYSERROR("opendir on snapshot path %s", path); return false; } while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; if (!do_snapshot_destroy(direntp->d_name, path)) { bret = false; continue; } } closedir(dir); if (rmdir(path)) SYSERROR("Error removing directory %s", path); return bret; } static bool do_lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname) { char clonelxcpath[PATH_MAX]; if (!c || !c->name || !c->config_path || !snapname) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return do_snapshot_destroy(snapname, clonelxcpath); } WRAP_API_1(bool, lxcapi_snapshot_destroy, const char *) static bool do_lxcapi_snapshot_destroy_all(struct lxc_container *c) { char clonelxcpath[PATH_MAX]; if (!c || !c->name || !c->config_path) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return remove_all_snapshots(clonelxcpath); } WRAP_API(bool, lxcapi_snapshot_destroy_all) static bool do_lxcapi_may_control(struct lxc_container *c) { if (!c) return false; return lxc_try_cmd(c->name, c->config_path) == 0; } WRAP_API(bool, lxcapi_may_control) static bool do_add_remove_node(pid_t init_pid, const char *path, bool add, struct stat *st) { int ret; char *tmp; pid_t pid; char chrootpath[PATH_MAX]; char *directory_path = NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork()"); return false; } if (pid) { ret = wait_for_pid(pid); if (ret != 0) { ERROR("Failed to create device node"); return false; } return true; } /* prepare the path */ ret = snprintf(chrootpath, PATH_MAX, "/proc/%d/root", init_pid); if (ret < 0 || ret >= PATH_MAX) return false; ret = chroot(chrootpath); if (ret < 0) _exit(EXIT_FAILURE); ret = chdir("/"); if (ret < 0) _exit(EXIT_FAILURE); /* remove path if it exists */ ret = faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW); if(ret == 0) { ret = unlink(path); if (ret < 0) { SYSERROR("Failed to remove \"%s\"", path); _exit(EXIT_FAILURE); } } if (!add) _exit(EXIT_SUCCESS); /* create any missing directories */ tmp = strdup(path); if (!tmp) _exit(EXIT_FAILURE); directory_path = dirname(tmp); ret = mkdir_p(directory_path, 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create path \"%s\"", directory_path); free(tmp); _exit(EXIT_FAILURE); } /* create the device node */ ret = mknod(path, st->st_mode, st->st_rdev); free(tmp); if (ret < 0) { SYSERROR("Failed to create device node at \"%s\"", path); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } static bool add_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path, bool add) { int ret; struct stat st; char value[LXC_MAX_BUFFER]; const char *p; /* make sure container is running */ if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); return false; } /* use src_path if dest_path is NULL otherwise use dest_path */ p = dest_path ? dest_path : src_path; /* make sure we can access p */ if(access(p, F_OK) < 0 || stat(p, &st) < 0) return false; /* continue if path is character device or block device */ if (S_ISCHR(st.st_mode)) ret = snprintf(value, LXC_MAX_BUFFER, "c %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else if (S_ISBLK(st.st_mode)) ret = snprintf(value, LXC_MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else return false; /* check snprintf return code */ if (ret < 0 || ret >= LXC_MAX_BUFFER) return false; if (!do_add_remove_node(do_lxcapi_init_pid(c), p, add, &st)) return false; /* add or remove device to/from cgroup access list */ if (add) { if (!do_lxcapi_set_cgroup_item(c, "devices.allow", value)) { ERROR("set_cgroup_item failed while adding the device node"); return false; } } else { if (!do_lxcapi_set_cgroup_item(c, "devices.deny", value)) { ERROR("set_cgroup_item failed while removing the device node"); return false; } } return true; } static bool do_lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { // cannot mknod if we're not privileged wrt init_user_ns if (am_host_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, true); } WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *) static bool do_lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, false); } WRAP_API_2(bool, lxcapi_remove_device_node, const char *, const char *) static bool do_lxcapi_attach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { pid_t init_pid; int ret = 0; if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } ret = lxc_netdev_isup(ifname); if (ret > 0) { /* netdev of ifname is up. */ ret = lxc_netdev_down(ifname); if (ret) goto err; } init_pid = do_lxcapi_init_pid(c); ret = lxc_netdev_move_by_name(ifname, init_pid, dst_ifname); if (ret) goto err; INFO("Moved network device \"%s\" to network namespace of %d", ifname, init_pid); return true; err: return false; } WRAP_API_2(bool, lxcapi_attach_interface, const char *, const char *) static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { int ret; pid_t pid, pid_outside; /* * TODO - if this is a physical device, then we need am_host_unpriv. * But for other types guest privilege suffices. */ if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } pid_outside = lxc_raw_getpid(); pid = fork(); if (pid < 0) { ERROR("Failed to fork"); return false; } if (pid == 0) { /* child */ pid_t init_pid; init_pid = do_lxcapi_init_pid(c); if (!switch_to_ns(init_pid, "net")) { ERROR("Failed to enter network namespace"); _exit(EXIT_FAILURE); } ret = lxc_netdev_isup(ifname); if (ret < 0) { ERROR("Failed to determine whether network device \"%s\" is up", ifname); _exit(EXIT_FAILURE); } /* netdev of ifname is up. */ if (ret) { ret = lxc_netdev_down(ifname); if (ret) { ERROR("Failed to set network device \"%s\" down", ifname); _exit(EXIT_FAILURE); } } ret = lxc_netdev_move_by_name(ifname, pid_outside, dst_ifname); /* -EINVAL means there is no netdev named as ifname. */ if (ret < 0) { if (ret == -EINVAL) ERROR("Network device \"%s\" not found", ifname); else ERROR("Failed to remove network device \"%s\"", ifname); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } ret = wait_for_pid(pid); if (ret != 0) return false; INFO("Moved network device \"%s\" to network namespace of %d", ifname, pid_outside); return true; } WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *) static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size) { int ret = -1; struct migrate_opts *valid_opts = opts; uint64_t features_to_check = 0; /* If the caller has a bigger (newer) struct migrate_opts, let's make * sure that the stuff on the end is zero, i.e. that they didn't ask us * to do anything special. */ if (size > sizeof(*opts)) { unsigned char *addr; unsigned char *end; addr = (void *)opts + sizeof(*opts); end = (void *)opts + size; for (; addr < end; addr++) if (*addr) return -E2BIG; } /* If the caller has a smaller struct, let's zero out the end for them * so we don't accidentally use bits of it that they didn't know about * to initialize. */ if (size < sizeof(*opts)) { valid_opts = malloc(sizeof(*opts)); if (!valid_opts) return -ENOMEM; memset(valid_opts, 0, sizeof(*opts)); memcpy(valid_opts, opts, size); } switch (cmd) { case MIGRATE_PRE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_pre_dump(c, valid_opts); break; case MIGRATE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_dump(c, valid_opts); break; case MIGRATE_RESTORE: if (do_lxcapi_is_running(c)) { ERROR("container is already running"); goto on_error; } ret = !__criu_restore(c, valid_opts); break; case MIGRATE_FEATURE_CHECK: features_to_check = valid_opts->features_to_check; ret = !__criu_check_feature(&features_to_check); if (ret) { /* Something went wrong. Let's let the caller * know which feature checks failed. */ valid_opts->features_to_check = features_to_check; } break; default: ERROR("invalid migrate command %u", cmd); ret = -EINVAL; } on_error: if (size < sizeof(*opts)) free(valid_opts); return ret; } WRAP_API_3(int, lxcapi_migrate, unsigned int, struct migrate_opts *, unsigned int) static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.stop = stop; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_DUMP, &opts, sizeof(opts)); } WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool) static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_RESTORE, &opts, sizeof(opts)); } WRAP_API_2(bool, lxcapi_restore, char *, bool) static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...) { va_list ap; const char **argv; int ret; if (!c) return -1; current_config = c->lxc_conf; va_start(ap, arg); argv = lxc_va_arg_list_to_argv_const(ap, 1); va_end(ap); if (!argv) { ERROR("Memory allocation error."); ret = -1; goto out; } argv[0] = arg; ret = do_lxcapi_attach_run_wait(c, options, program, (const char * const *)argv); free((void*)argv); out: current_config = NULL; return ret; } struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; size_t len; if (!name) return NULL; c = malloc(sizeof(*c)); if (!c) { fprintf(stderr, "Failed to allocate memory for %s\n", name); return NULL; } memset(c, 0, sizeof(*c)); if (configpath) c->config_path = strdup(configpath); else c->config_path = strdup(lxc_global_config_value("lxc.lxcpath")); if (!c->config_path) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } remove_trailing_slashes(c->config_path); len = strlen(name); c->name = malloc(len + 1); if (!c->name) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } (void)strlcpy(c->name, name, len + 1); c->numthreads = 1; c->slock = lxc_newlock(c->config_path, name); if (!c->slock) { fprintf(stderr, "Failed to create lock for %s\n", name); goto err; } c->privlock = lxc_newlock(NULL, NULL); if (!c->privlock) { fprintf(stderr, "Failed to create private lock for %s\n", name); goto err; } if (!set_config_filename(c)) { fprintf(stderr, "Failed to create config file name for %s\n", name); goto err; } if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) { fprintf(stderr, "Failed to load config for %s\n", name); goto err; } if (ongoing_create(c) == 2) { ERROR("Failed to complete container creation for %s", c->name); container_destroy(c, NULL); lxcapi_clear_config(c); } c->daemonize = true; c->pidfile = NULL; /* Assign the member functions. */ c->is_defined = lxcapi_is_defined; c->state = lxcapi_state; c->is_running = lxcapi_is_running; c->freeze = lxcapi_freeze; c->unfreeze = lxcapi_unfreeze; c->console = lxcapi_console; c->console_getfd = lxcapi_console_getfd; c->init_pid = lxcapi_init_pid; c->load_config = lxcapi_load_config; c->want_daemonize = lxcapi_want_daemonize; c->want_close_all_fds = lxcapi_want_close_all_fds; c->start = lxcapi_start; c->startl = lxcapi_startl; c->stop = lxcapi_stop; c->config_file_name = lxcapi_config_file_name; c->wait = lxcapi_wait; c->set_config_item = lxcapi_set_config_item; c->destroy = lxcapi_destroy; c->destroy_with_snapshots = lxcapi_destroy_with_snapshots; c->rename = lxcapi_rename; c->save_config = lxcapi_save_config; c->get_keys = lxcapi_get_keys; c->create = lxcapi_create; c->createl = lxcapi_createl; c->shutdown = lxcapi_shutdown; c->reboot = lxcapi_reboot; c->reboot2 = lxcapi_reboot2; c->clear_config = lxcapi_clear_config; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; c->get_running_config_item = lxcapi_get_running_config_item; c->get_cgroup_item = lxcapi_get_cgroup_item; c->set_cgroup_item = lxcapi_set_cgroup_item; c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; c->clone = lxcapi_clone; c->get_interfaces = lxcapi_get_interfaces; c->get_ips = lxcapi_get_ips; c->attach = lxcapi_attach; c->attach_run_wait = lxcapi_attach_run_wait; c->attach_run_waitl = lxcapi_attach_run_waitl; c->snapshot = lxcapi_snapshot; c->snapshot_list = lxcapi_snapshot_list; c->snapshot_restore = lxcapi_snapshot_restore; c->snapshot_destroy = lxcapi_snapshot_destroy; c->snapshot_destroy_all = lxcapi_snapshot_destroy_all; c->may_control = lxcapi_may_control; c->add_device_node = lxcapi_add_device_node; c->remove_device_node = lxcapi_remove_device_node; c->attach_interface = lxcapi_attach_interface; c->detach_interface = lxcapi_detach_interface; c->checkpoint = lxcapi_checkpoint; c->restore = lxcapi_restore; c->migrate = lxcapi_migrate; c->console_log = lxcapi_console_log; return c; err: lxc_container_free(c); return NULL; } int lxc_get_wait_states(const char **states) { int i; if (states) for (i=0; i