#!/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 () { DIB_BLOCK_DEVICE_SCRIPT=$(which dib-block-device) sudo -E ${DIB_BLOCK_DEVICE_SCRIPT} \ --phase=umount \ --build-dir="${TMP_BUILD_DIR}" 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 -u VIRTUAL_ENV 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 # Copy in dib-run-parts to run inside chroot. Note, in the # future, we might like to use a diffrent dib-run-parts for # running inside the chroot that doesn't rely on bash. For now # they're the same. Note also this gets cleaned up with the dir # delete below. sudo cp ${DIB_RUN_PARTS} ${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 # NOTE: this is the dib-run-parts copied into the chroot by the # dib-run-parts element. run_in_target /tmp/in_target.d/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 # Truncate /var/log files in preparation for first boot sudo find ${TMP_MOUNT_PATH}/var/log -type f -exec cp /dev/null '{}' \; # also /root logs sudo find ${TMP_MOUNT_PATH}/root -name \*.log -type f -delete } 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" == "tgz" ]; then gzip -9 < $IMAGE_NAME.tar > $1-new rm $IMAGE_NAME.tar 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 }