#!/bin/bash
# Copyright 2012 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

function unmount_image () {
    # Calling sync before helps ensure the mount isn't busy when you unmount it.
    # Previously observing having disk corruption issues; one possibility is
    # qemu-nbd not flushing dirty pages on disconnect?
    # https://bugs.launchpad.net/diskimage-builder/+bug/1214388
    sync

    # unmount from the chroot
    # Don't use TMP_MOUNT_PATH here, it might not have been set.
    unmount_dir "$TMP_BUILD_DIR/mnt"
    if [ -n "$EXTRA_DETACH" ]; then
        $EXTRA_DETACH
    fi
    if [ -n "$EXTRA_UNMOUNT" ]; then
        $EXTRA_UNMOUNT
    fi
}

function fstrim_image () {
    # A race condition can occur when trying to fstrim immediately after
    # deleting a file resulting in that free space not being reclaimed.
    # Calling sync before fstrim is a workaround for this behaviour.
    # https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg02978.html
    sync

    # Discard all unused bytes
    sudo fstrim "${TMP_BUILD_DIR}/mnt"
}

function trap_cleanup() {
    exitval=$?
    cleanup
    exit $exitval
}

function cleanup () {
    unmount_image
    cleanup_build_dir
    cleanup_image_dir
}

# Helper function to run a command inside the chroot
function run_in_target () {
    cmd="$@"
    # -E to preserve http_proxy
    ORIG_HOME=$HOME
    export HOME=/root
    # Force an empty TMPDIR inside the chroot. There is no need to use an user
    # defined tmp dir which may not exist in the chroot.
    # Bug: #1330290
    # Force the inclusion of a typical set of dirs in PATH, this is needed for guest
    # distros that have path elements not in the host PATH.
    sudo -E chroot $TMP_MOUNT_PATH env -u TMPDIR PATH="\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" sh -c "$cmd"
    export HOME=$ORIG_HOME
}

# Helper function to run a directory of scripts inside the chroot
function run_d_in_target () {
    check_element
    # If we can find a directory of hooks to run in the target filesystem, bind
    # mount it into the target and then execute run-parts in a chroot
    if [ -d ${TMP_HOOKS_PATH}/$1.d ] ; then
      sudo mkdir $TMP_MOUNT_PATH/tmp/in_target.d
      sudo mount --bind ${TMP_HOOKS_PATH} $TMP_MOUNT_PATH/tmp/in_target.d
      sudo mount -o remount,ro,bind ${TMP_HOOKS_PATH} $TMP_MOUNT_PATH/tmp/in_target.d
      check_break before-$1 run_in_target bash
      [ -z "$break_outside_target" ] && in_target_arg="run_in_target" || in_target_arg=
      trap "check_break after-error $in_target_arg ${break_cmd:-bash}" ERR
      run_in_target dib-run-parts /tmp/in_target.d/$1.d
      trap - ERR
      check_break after-$1 run_in_target bash
      sudo umount -f $TMP_MOUNT_PATH/tmp/in_target.d
      if ! timeout 5  sh -c " while ! sudo rmdir $TMP_MOUNT_PATH/tmp/in_target.d; do sleep 1; done"; then
          echo "ERROR: unable to cleanly remove $TMP_MOUNT_PATH/tmp/in_target.d"
          exit 1
      fi
    fi
}

function finalise_base () {
    TARGET_ROOT=$TMP_MOUNT_PATH run_d cleanup
    # If the file has been set immutable, we probably want to keep it
    if lsattr $TMP_MOUNT_PATH/etc/resolv.conf | grep '^....i' >/dev/null ; then
        # We're keeping the contents of resolv.conf set in the elements,
        # so remove the old saved file
        sudo rm -f $TMP_MOUNT_PATH/etc/resolv.conf.ORIG
    else
        # Remove the resolv.conf we created above
        sudo rm -f $TMP_MOUNT_PATH/etc/resolv.conf
        # Move the original back
        if [ -L $TMP_MOUNT_PATH/etc/resolv.conf.ORIG ] || [ -f $TMP_MOUNT_PATH/etc/resolv.conf.ORIG ] ; then
            sudo mv $TMP_MOUNT_PATH/etc/resolv.conf.ORIG $TMP_MOUNT_PATH/etc/resolv.conf
        fi
    fi
    # Cleanup /tmp in the guest, so there is less cruft left there
    if [ -d "$TMP_MOUNT_PATH/tmp" ]; then
        unmount_dir $TMP_MOUNT_PATH/tmp
    fi
    find $TMP_MOUNT_PATH/tmp -maxdepth 1 -mindepth 1 | xargs sudo rm -rf --one-file-system
}

function compress_and_save_image () {
    # Recreate our image to throw away unnecessary data
    test $IMAGE_TYPE != qcow2 && COMPRESS_IMAGE=""
    if [ -n "$QEMU_IMG_OPTIONS" ]; then
        EXTRA_OPTIONS="-o $QEMU_IMG_OPTIONS"
    else
        EXTRA_OPTIONS=""
    fi
    if [ "$IMAGE_TYPE" = "raw" ]; then
        mv $TMP_IMAGE_PATH $1-new
    elif [ "$IMAGE_TYPE" == "vhd" ]; then
        cp $TMP_IMAGE_PATH $1-intermediate
        vhd-util convert -s 0 -t 1 -i $1-intermediate -o $1-intermediate
        vhd-util convert -s 1 -t 2 -i $1-intermediate -o $1-new
        # The previous command creates a .bak file
        rm $1-intermediate.bak
        OUT_IMAGE_PATH=$1-new
    else
        echo "Converting image using qemu-img convert"
        qemu-img convert ${COMPRESS_IMAGE:+-c} -f raw -O $IMAGE_TYPE $EXTRA_OPTIONS $TMP_IMAGE_PATH $1-new
    fi

    OUT_IMAGE_PATH=$1-new
    finish_image $1
}

function do_extra_package_install () {
  # Install any packages that were requested with the -p command line option
  if [ "$INSTALL_PACKAGES" != "" ]; then
    run_in_target install-packages ${INSTALL_PACKAGES[@]}
  fi
}

function copy_elements_lib () {
  sudo mkdir -p $TMP_MOUNT_PATH/lib/diskimage-builder
  sudo cp -t $TMP_MOUNT_PATH/lib/diskimage-builder $_LIB/elements-functions
}

# Dig up the initrd and kernel.
function select_boot_kernel_initrd () {
    TARGET_ROOT=$1
    BOOTDIR=$TARGET_ROOT/boot
    if [ -n "${DIB_BAREMETAL_KERNEL_PATTERN:-}" -a -n "${DIB_BAREMETAL_INITRD_PATTERN:-}" ]; then
        KERNEL=$(basename $(eval ls -1rv "$BOOTDIR/${DIB_BAREMETAL_KERNEL_PATTERN}" | head -1))
        RAMDISK=$(basename $(eval ls -1rv "$BOOTDIR/${DIB_BAREMETAL_INITRD_PATTERN}" | head -1))
    elif [ -f $TARGET_ROOT/etc/redhat-release ]; then

        # Prioritize PAE if present
        KERNEL=$(ls -1rv $BOOTDIR/vmlinuz* | grep PAE | grep -v debug | head -1 || echo "")
        KERNEL=${KERNEL:-$(ls -1rv $BOOTDIR/vmlinuz* | grep -v debug | head -1 || echo "")}
        if [ ! $KERNEL ]; then
           echo "No suitable kernel found."
           exit 1
        fi

        KERNEL=$(basename $KERNEL)
        KERNEL_VERSION=${KERNEL#vmlinuz-}
        RAMDISK=$(basename $(ls $BOOTDIR/initramfs-$KERNEL_VERSION.img) || echo "")
        if [ ! $RAMDISK ]; then
            echo "Can't find an initramfs for the $KERNEL_VERSION version of the kernel."
            exit 1
        fi
    elif [ -f $TARGET_ROOT/etc/debian_version ]; then
        KERNEL=$(basename $(ls -1rv $BOOTDIR/vmlinu*generic 2>/dev/null || ls -1rv $BOOTDIR/vmlinu* | head -1))
        RAMDISK=$(basename $(ls -1rv $BOOTDIR/initrd*generic 2>/dev/null || ls -1rv $BOOTDIR/initrd* | head -1))

        if [ -f $TARGET_ROOT/dib-signed-kernel-version ] ; then
            . $TARGET_ROOT/dib-signed-kernel-version
        fi

        if [ -n "${DIB_SIGNED_KERNEL_VERSION:-}" ]; then
            echo "Using signed kernel $DIB_SIGNED_KERNEL_VERSION"
            KERNEL=$(basename $(ls -1rv $BOOTDIR/vmlinu*generic.efi.signed 2>/dev/null))
        fi
    elif [ -f $TARGET_ROOT/etc/SuSE-release ]; then
        KERNEL=$(basename $(readlink -e $BOOTDIR/vmlinuz))
        RAMDISK=$(basename $(readlink -e $BOOTDIR/initrd))
    elif [[ -f "${TARGET_ROOT}"/etc/gentoo-release ]]; then
        KERNEL="$(basename $(ls -1rv $BOOTDIR/kernel-*-openstack | head -n 1))"
        RAMDISK="$(basename $(ls -1rv $BOOTDIR/initramfs-*-openstack | head -n 1))"
    else
        echo "ERROR: Unable to detect operating system"
        exit 1
    fi
}