#!/bin/bash
# Copyright (c) 2019 SUSE Software Solutions Germany GmbH.  All rights reserved.
#
# This file is part of suse-migration-services.
#
# suse-migration-services is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# suse-migration-services 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 suse-migration-services. If not, see <http://www.gnu.org/licenses/>
#
# Enable shell globbing
set +o noglob

logfile=/var/log/migration_startup.log


function setup_logging {
    rm -f "${logfile}"
    exec 2>>"${logfile}"
    set -x
}

function cleanup {
    # shellcheck disable=SC2181
    if [ ! $? = 0 ];then
        echo "Migration run failed: see ${logfile} for details"
    fi
    umount /mnt &>/dev/null
}

function get_migration_image {
    # """
    # Search for migration ISO file
    # """
    echo /migration-image/*-*Migration.*.iso
}

function get_system_root_device {
    # """
    # Looking for running system root device
    # """
    lsblk -p -n -r -o NAME,MOUNTPOINT | grep -E "/$" | uniq | cut -f1 -d" "
}

function get_boot_options {
    # """
    # Create boot options list for kexec
    # """
    local boot_options
    local host_boot_option
    local migration_iso=$1
    local is_sles16_migration=$2
    boot_options="iso-scan/filename=${migration_iso} root=live:CDLABEL=CDROM rd.live.image"
    if mdadm --detail "$(get_system_root_device)" &>/dev/null; then
        boot_options="${boot_options} rd.auto"
    fi
    for host_boot_option in $(xargs -n1 < /proc/cmdline);do
        case "${host_boot_option}" in
            root=*)
                continue
            ;;
            quiet)
                continue
            ;;
            security=apparmor)
            if ${is_sles16_migration}; then
                boot_options="${boot_options} security=selinux"
                continue
            fi
            ;;
        esac
        boot_options="${boot_options} ${host_boot_option}"
    done
    echo "${boot_options}"
}

function extract_kernel_and_initrd {
    # """
    # Extract kernel and initrd for kexec load operation
    #
    # The method assumes EFI layout of the live image
    # """
    local migration_image=$1
    local boot_data_dir
    if mount -o ro "${migration_image}" /mnt; then
        boot_data_dir=$(mktemp -d -t migration_XXXX)
        if \
            cp /mnt/boot/*/loader/initrd /mnt/boot/*/loader/linux \
            "${boot_data_dir}"
        then
            umount /mnt &>/dev/null
            echo "${boot_data_dir}"
        fi
    fi
}

function setup_wicked_to_NetworkManager_prereqs {
    # """
    # Set up required files for wicked to NetworkManager migration
    #
    # Only applies for SLE16
    # """
    if [ -x "$(command -v wicked)" ]; then
        mkdir -p /var/cache/wicked_config
        wicked show-config > /var/cache/wicked_config/config.xml
    fi

    # Create systemd.link file for VMWARE or Hyper-V virtual interfaces
    for netdev in /sys/class/net/*; do
        dev=$(basename "${netdev}")
        link_file=/etc/systemd/network/70-persistent-net-"${dev}".link
        test -e "${link_file}" && continue

        ! grep -E '^(00:0c:29:|00:50:56:|00:15:5d:)' \
            /sys/class/net/"${dev}"/address >/dev/null && continue
        ID_PATH=$(
            udevadm info -q property --property=ID_PATH \
            /sys/class/net/"${dev}"
        )
        ID_PATH=${ID_PATH#ID_PATH=}
        ID_NET_DRIVER=$(
            udevadm info -q property --property=ID_NET_DRIVER \
            /sys/class/net/"${dev}"
        )
        ID_NET_DRIVER=${ID_NET_DRIVER#ID_NET_DRIVER=}
        if [ -z "$ID_PATH" ] || [ -z "$ID_NET_DRIVER" ]; then
            echo "[ERROR] failed to create systemd.link file for ${dev}"
            exit 1
        fi
        grep -sE '^Name="?'"$dev"'"?$' \
            /etc/systemd/network/*.link && continue
        grep -sE '^\s*[^#].*[ \t,]NAME="'"$dev"'"$' \
            /etc/udev/rules.d/*.rules && continue

        echo "Create $link_file with Path=$ID_PATH Driver=$ID_NET_DRIVER"
        cat > "${link_file}" <<EOT
# This file was created from run_migration
[Match]
Path=$ID_PATH
Driver=$ID_NET_DRIVER

[Link]
NamePolicy=
Name=$dev
EOT

    done
}

function store_snapper_pre_snapshot {
    if [ -x /usr/bin/snapper ]; then
        PRE_SNAPSHOT=$(
            snapper -c root \
                --machine-readable csv list \
                --disable-used-space \
                --columns subvolume,number,type 2>/dev/null| grep '^/,[^,]*,pre' |sed -e '$!d' -e 's|/,\([^,]*\),pre|\1|g'
        )
        if [ "x$PRE_SNAPSHOT" != x ]; then
            echo "${PRE_SNAPSHOT}" > /var/cache/suse_migration_snapper_btrfs_pre_snapshot_number
        fi
    fi
}

# Check on permissions
if (( EUID != 0 )); then
    echo "Migration startup requires root permissions"
    exit 1
fi

# Set signal handler on EXIT
trap cleanup EXIT

# Setup logging
setup_logging

# Lookup installed migration ISO image
migration_iso=$(get_migration_image)
if [ ! -e "${migration_iso}" ];then
    echo "Migration image ISO file not found: ${migration_iso}"
    exit 1
fi

# Helper to identify migration target
is_sles16_migration=false
if [[ "${migration_iso}" == *SLES16*-*Migration*.iso ]]; then
    is_sles16_migration=true
fi

if ${is_sles16_migration}; then
    # Do not start migration for CPUs that are too old
    # SLE16 image panics on CPUs older than x86_64_v2 (i.e. x86_64) (bsc#1249593), so we need to
    # block migration before getting into the image.
    if [ "$(zypper system-architecture)" == "x86_64" ]; then
        echo "CPU Architecture too old. Aborting migration."
        exit 1
    fi

    # store snapper pre snapshot if available
    store_snapper_pre_snapshot

    # Setup wicked to NetworkManager migration files
    setup_wicked_to_NetworkManager_prereqs
fi

# Extract kexec boot data from migration ISO image
boot_dir=$(extract_kernel_and_initrd "${migration_iso}")
if [ ! -e "${boot_dir}" ];then
    echo "Failed to extract boot files"
    exit 1
fi

# Run Migration
kexec \
    --load "${boot_dir}/linux" \
    --initrd "${boot_dir}/initrd" \
    --kexec-file-syscall \
    --command-line "$(get_boot_options "${migration_iso}" "${is_sles16_migration}")"
systemctl kexec
