# ----------------------------------------------------------------------------- # # Copyright (c) 2014 - OpenSLX GmbH # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # # Common functions for chrooting # # ----------------------------------------------------------------------------- CHROOT_TEMPDIR="${ROOT_DIR}/remote/chroot.tmp" CHROOT_MOUNTDIR="${CHROOT_TEMPDIR}/rootmount" CHROOT_BINDDIR="${CHROOT_TEMPDIR}/rootbind" CHROOT_LOWERDIR="/" CHROOT_BINDMOUNTS="/dev /proc /sys /run" # Helper function to setup the directory structure chroot_prepare_dirs() { # first check if CHROOT_TEMPDIR exists if [ -d "${CHROOT_TEMPDIR}" ]; then # try to umount and rmdir CHROOT_MOUNTDIR umount "${CHROOT_MOUNTDIR}" 2>/dev/null rmdir "${CHROOT_MOUNTDIR}" || perror "Could not remove '${CHROOT_MOUNTDIR}', meaning it has stuff in it. Aborting..." # try to umount and rmdir CHROOT_BINDDIR umount "${CHROOT_BINDDIR}" 2>/dev/null rmdir "${CHROOT_BINDDIR}" || perror "Could not remove '${CHROOT_BINDDIR}', meaning it has stuff in it. Aborting..." # try to rmdir CHROOT_TEMPDIR rmdir "${CHROOT_TEMPDIR}" || perror "Could not remove '${CHROOT_TEMPDIR}', meaning it has stuff in it. Aborting..." fi mkdir -p "${CHROOT_TEMPDIR}" || perror "Could not create base directory for mount directories $CHROOT_TEMPDIR." for DIR in "${CHROOT_BINDDIR}" "${CHROOT_MOUNTDIR}"; do mkdir -p "${DIR}" || perror "Could not create directory for mount directory $DIR." done } # Helper to mount the overlay structure: # - bind mount system / to CHROOT_BINDDIR and make it read-only # - make an overlay from CHROOT_LOWERDIR CHROOT_UPPERDIR # - bind mount additional pseudo-fs (as given in CHROOT_BINDMOUNTS) chroot_prepare_mounts() { # first mount / on CHROOT_BINDDIR and remount read-only mount -o bind "${CHROOT_LOWERDIR}" "${CHROOT_BINDDIR}" || perror "Could not bind-mount '$CHROOT_LOWERDIR' to '$CHROOT_BINDDIR'." mount -o remount,ro,bind "${CHROOT_BINDDIR}" || perror "Could not remount '$CHROOT_BINDDIR' read-only." # check that it really is read-only [ "x$(mount | grep -E "^/ on ${CHROOT_BINDDIR}" | grep -v '\(.*ro.*\)')" != "x" ] && perror "'${CHROOT_BINDDIR}' is not read-only! Aborting..." # safe, go on to make the overlay mount -t overlayfs overlayfs -o lowerdir="${CHROOT_BINDDIR}",upperdir="${CHROOT_UPPERDIR}" "${CHROOT_MOUNTDIR}" \ || perror "Could not mount (overlayfs) $CHROOT_LOWERDIR, $CHROOT_UPPERDIR to $CHROOT_BINDDIR." # mount pseudo-filesystems for DIR in $CHROOT_BINDMOUNTS; do mount -o bind "${DIR}" "${CHROOT_MOUNTDIR}/${DIR}" \ || perror "Could not bind mount '$DIR' into '$CHROOT_MOUNTDIR/$DIR'." done } # Helper to generate the mighty autoexec.bat chroot_gen_autoexec() { # create the script to be automatically executed. cat >"${CHROOT_MOUNTDIR}/autoexec.bat"<<-EOF #!/bin/bash ####################################################### # # # Warning! # # # # This file is only meant to be executed within # # the specially chrooted mltk building environment. # # # # Do NOT execute it if you are not sure what you do, # # it may be very harmful if being run in a normal # # system environment! # # # ####################################################### echo "chroot started successfully." EOF # dump the piped input to it cat >> "${CHROOT_MOUNTDIR}/autoexec.bat" # make it executable chmod +x "${CHROOT_MOUNTDIR}/autoexec.bat" || perror "Failed to make '${CHROOT_MOUNTDIR}/autoexec.bat' exeutable." } chroot_handle_whiteouts() { local WHITEOUT_LIST="${CHROOT_UPPERDIR}/overlay.whiteout.list" rm -f -- "$WHITEOUT_LIST=" #mkdir -p "$(dirname "$WHITEOUT_LIST")" || perror "Could not create $(dirname "$WHITEOUT_LIST")" pdebug "Searching for overlayfs-whiteouts ..." for WHITEOUT in $(find "$CHROOT_UPPERDIR" -lname "(overlay-whiteout)"); do pdebug "Whiteout found: $WHITEOUT" echo "/./${WHITEOUT#$CHROOT_UPPERDIR}" >> "$WHITEOUT_LIST" rm -f -- "$WHITEOUT" || perror "Could not delete whiteout $WHITEOUT!" done pinfo "Whiteout list dumped to '${CHROOT_UPPERDIR}/overlay.whiteout.list'" } ############################################################################### # # MAIN FUNCTION # # Main function to be called from the outside # Usage: # chroot_run < # # Example: # chroot_run /tmp/chroot_build <<-EOF # echo "This will be executed inside the chroot" # EOF # # It will run: # - chroot_prepare # - chroot $CHROOT_TEMPDIR/rootmount # - executes $CHROOT_TEMPDIR/rootmount/autoexec.bat # - chroot_cleanup chroot_run() { # check args [ $# -eq 1 ] || perror "'chroot_run' requires exactly 1 parameter. Given $#. Use 'chroot_run '" local CHROOT_UPPERDIR="$1" mkdir -p "$1" # first prepare the dir structure chroot_prepare_dirs || perror "'chroot_prepare_dirs' failed with $?." chroot_prepare_mounts || perror "'chroot_prepare_mounts' failed with $?." # generate the code to be executed when chroot'ing chroot_gen_autoexec || perror "'chroot_gen_autoexec' failed with $?." # do the chroot exec 0>&8 # This redirection is used for debugging within a chroot chroot --userspec root:root "${CHROOT_MOUNTDIR}" /autoexec.bat local RET=$? if [ "$RET" -eq 0 ]; then pinfo "chroot executed '${CHROOT_MOUNTDIR}/autoexec.bat' succeeded." else perror "Failed to run '$CHROOT_MOUNTDIR/autoexec.bat' inside the chroot to '$CHROOT_MOUNTDIR' with error code: $RET" fi # handle whiteouts chroot_handle_whiteouts || perror "'chroot_handle_whiteouts' failed with error code: $?" # finally cleanup all the mounting stuff we did previously chroot_cleanup_mounts || perror "'chroot_cleanup' failed with $?." } ############################################################################### # # CLEANUP FUNCTIONS # # Helper to check if the given path is mounted chroot_check_mount_point() { [ "$#" -eq 1 ] || perror "'chroot_check_mount_point' called with $# arguements, only 1 accepted." local MOUNT="$1" if [ "x$(mount | grep "$(readlink -f $MOUNT)")" != "x" ]; then # still mounted pdebug "'$MOUNT' is mounted!" return 1 else pdebug "'$MOUNT' is not mounted." return 0 fi } # Helper to umount the given path chroot_umount() { [ "$#" -eq 1 ] || perror "'chroot_umount' called with $# arguments, only 1 accepted." local MOUNT="$1" # check if MOUNT is mounted if ! chroot_check_mount_point "${MOUNT}"; then # still mounted if umount -l "${MOUNT}"; then pdebug "Successfully umounted '${MOUNT}'." else pwarning "Could not umount '${MOUNT}'! Trying again..." # now it gets ugly for i in `seq 1 5`; do umount -l "${MOUNT}" && return 0 done perror "Could not umount '${MOUNT}' after 5 tries! This shouldn't happen. Check your scripts." fi else pdebug "'${MOUNT}' is not mounted." fi # better be safe than sorry chroot_check_mount_point "$MOUNT" || perror "'$MOUNT' is still mounted, exiting before something bad happens..." } # Helper to cleanup the temporary mounts chroot_cleanup_mounts() { if [[ "$(mount | grep -c $CHROOT_TEMPDIR)" -gt 0 ]]; then # No point in unmounting then... for DIR in $CHROOT_BINDMOUNTS; do chroot_umount "${CHROOT_MOUNTDIR}/${DIR}" done chroot_umount "${CHROOT_MOUNTDIR}" chroot_umount "${CHROOT_BINDDIR}" else pinfo "Nothing chroot-related is mounted - exiting." fi }