#!/bin/sh -x
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

SONIC_VERSION=$(sonic-cfggen -y /etc/sonic/sonic_version.yml -v build_version)
FIRST_BOOT_FILE="/host/image-${SONIC_VERSION}/platform/firsttime"

# In case the unit is migrating from another NOS, save the logs
log_migration() {
    echo $1 >> /host/migration/migration.log
}

# Import files from another NOS's partition onto SONiC
nos_migration_import() {
    [ -f $1 ] && cp $1 $2 || log_migration "ERROR: $1 not found!"
}

# While migrating form another NOS, we need to preserve the MAC addresses
# of eth0 (and eventually switchports).
# Update the eth0 mac and also the EEPROM using ethtool so that subsequent
# reboots use the NOS's mac.
# Input : mgmt_interface.cfg file imported from the previous NOS.
update_mgmt_interface_macaddr() {
    mgmt_config=$1
    if [ ! -f "$mgmt_config" ]; then
        log_migration "ERROR : unable update eth0 MAC : $mgmt_config not found!"
        return
    fi

    # Save the previous NOS's mac
    old_mac=`ip link show eth0 | grep ether | awk '{print $2}'`
    [ -z "$old_mac" ] && log_migration "Unable to retrieve old mac address !" && return

    # Extract, validate and set the eth0's mac address for the current session
    new_mac=$(grep "macaddr" $mgmt_config | awk -F'=' '{print $2}')
    log_migration "Setting eth0 mac as $new_mac."
    if [ `echo $new_mac | egrep "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"` ]; then
        ip link set eth0 down
        ip link set eth0 address $new_mac
        ip link set eth0 up
    else
        log_migration "ERROR: mac imported from NOS is invalid : $new_mac !"
        return
    fi

    # Get the ethtool magic and offset for changing the mac address in the EEPROM
    ethtool_magic=$(grep "ethtool_magic" $mgmt_config | awk -F'=' '{print $2}') 
    ethtool_offset=$(grep "ethtool_offset" $mgmt_config | awk -F'=' '{print $2}') 
    if [ -z "$ethtool_magic" ] || [ -z "$ethtool_offset" ]; then
        log_migration "Unable to retrieve ethtool params ($ethtool_magic,$ethtool_offset)"
        return
    fi

    log_migration "eth0 mac in EEPROM before update:"
    ethtool -e eth0 offset $ethtool_offset length 6 >> /host/migration/migration.log

    # Update the mac address in the EEPROM for subsequent reboots
    # Write only changed octets
    for i in 1 2 3 4 5 6; do
        offset=$(($ethtool_offset+$i-1))
        old_mac_octet="$(echo $old_mac | cut -d":" -f$i)"
        new_mac_octet="$(echo $new_mac | cut -d":" -f$i)"

        if [ "$old_mac_octet" != "$new_mac_octet" ]; then
            ethtool -E eth0 magic $ethtool_magic offset $offset value 0x$new_mac_octet
            if [ $? != 0 ]; then
                log_migration "ERROR: unable to update eth0 EEPROM!"
                log_migration "index $i, magic $ethtool_magic offset $offset, value $new_mac_octet"
                return
            fi
        fi
    done

    log_migration "eth0 mac in EEPROM after update:"
    ethtool -e eth0 offset $ethtool_offset length 6 >> /host/migration/migration.log

    # Update the 70-persistent-net.rules with the new mac for eth0
    log_migration "/etc/udev/rules.d/70-persistent-net.rules : replacing $old_mac with $new_mac for eth0"
    sed -i "/eth0/ s/ATTR{address}==\"$old_mac\"/ATTR{address}==\"$new_mac\"/g" /etc/udev/rules.d/70-persistent-net.rules
}

firsttime_exit() {
    rm -rf $FIRST_BOOT_FILE
    exit 0
}

# Given a string of tuples of the form field=value, extract the value for a field
# In : $string, $field
# Out: $value
value_extract() {
    set -- $string
    for x in "$@"; do
        case "$x" in
            $field=*)
                value="${x#$field=}"
        esac
    done
}

# Set up previous and next reboot cause files accordingly
process_reboot_cause() {
    REBOOT_CAUSE_DIR="/host/reboot-cause"
    REBOOT_CAUSE_FILE="${REBOOT_CAUSE_DIR}/reboot-cause.txt"
    PREVIOUS_REBOOT_CAUSE_FILE="${REBOOT_CAUSE_DIR}/previous-reboot-cause.txt"

    mkdir -p $REBOOT_CAUSE_DIR

    # If this is the first boot after an image install, store that as the
    # previous reboot cause.
    if [ -f $FIRST_BOOT_FILE ]; then
        echo "SONiC image installation" > $PREVIOUS_REBOOT_CAUSE_FILE
    fi

    # If there is an existing REBOOT_CAUSE_FILE, copy that file to
    # PREVIOUS_REBOOT_CAUSE_FILE.
    if [ -f $REBOOT_CAUSE_FILE ]; then
        mv -f $REBOOT_CAUSE_FILE $PREVIOUS_REBOOT_CAUSE_FILE
    else
        echo "Unknown reboot cause" > $PREVIOUS_REBOOT_CAUSE_FILE
    fi

    # Log the previous reboot cause to the syslog
    logger "Previous reboot cause: $(cat $PREVIOUS_REBOOT_CAUSE_FILE)"

    # Set the default cause for the next reboot
    echo "Unexpected reboot" > $REBOOT_CAUSE_FILE
}

program_console_speed()
{
    speed=$(cat /proc/cmdline | grep -Eo 'console=ttyS[0-9]+,[0-9]+' | cut -d "," -f2)
    if [ -z "$speed" ]; then
        CONSOLE_SPEED=9600
    else
        CONSOLE_SPEED=$speed
    fi

    sed -i "s|\-\-keep\-baud .* %I| $CONSOLE_SPEED %I|g" /lib/systemd/system/serial-getty@.service
    systemctl daemon-reload
}

#### Begin Main Body ####

# Set up previous and next reboot cause files
process_reboot_cause

# If the machine.conf is absent, it indicates that the unit booted
# into SONiC from another NOS. Extract the machine.conf from ONIE.
if [ ! -e /host/machine.conf ]; then
    mkdir -p /host/migration

    onie_dev=$(blkid | grep ONIE-BOOT | head -n 1 | awk '{print $1}' |  sed -e 's/:.*$//')
    mkdir -p /mnt/onie-boot
    mount $onie_dev /mnt/onie-boot
    onie_grub_cfg=/mnt/onie-boot/onie/grub/grub-machine.cfg
    
    if [ ! -e $onie_grub_cfg ]; then
        log_migration "$onie_grub_cfg not found"
    else
        . ./$onie_grub_cfg
        grep = $onie_grub_cfg | sed -e 's/onie_//' -e 's/=.*$//' | while read var ; do
            eval val='$'onie_$var
            echo "onie_${var}=${val}" >> /host/machine.conf
        done
    fi

    # Extract the previous NOS's partition that contains the migration artifacts
    set -- $(cat /proc/cmdline)
    for x in "$@"; do
        case "$x" in
            nos-config-part=*)
                nos_val="${x#nos-config-part=}"
                ;;
        esac
    done

    if [ -n "$nos_val" ]; then
        nos_dev=$(findfs $nos_val)
        if [ $? != 0 ]; then
            log_migration "ERROR: nos_dev not found. Check grub parameters"
        fi
    else
        log_migration "ERROR: nos_val not found. Check grub parameters"
    fi

    if [ -n "$nos_dev" ]; then
        # Mount the previous NOS's partition
        NOS_DIR=/mnt/nos_migration
        MG_GZFILE=$NOS_DIR/minigraph.xml.gz.base64.txt
        MG_FILE=$NOS_DIR/minigraph.xml
        ACL_GZFILE=$NOS_DIR/acl.json.gz.base64.txt
        ACL_FILE=$NOS_DIR/acl.json
        SNMP_FILE=$NOS_DIR/snmp.yml
        mkdir -p $NOS_DIR
        mount $nos_dev $NOS_DIR
        mkdir -p /host/fast-reboot

        # decode & unzip minigraph.xml.gz.base64.txt
        [ -f $MG_GZFILE ] && /usr/bin/base64 -d $MG_GZFILE | /bin/gunzip > $MG_FILE
        [ -f $ACL_GZFILE ] && /usr/bin/base64 -d $ACL_GZFILE | /bin/gunzip > $ACL_FILE

        # Copy relevant files
        nos_migration_import $NOS_DIR/mgmt_interface.cfg /host/migration
        nos_migration_import $MG_FILE /host/migration
        nos_migration_import $ACL_FILE /host/migration
        nos_migration_import $SNMP_FILE /host/migration
        nos_migration_import $NOS_DIR/arp.json /host/fast-reboot
        nos_migration_import $NOS_DIR/fdb.json /host/fast-reboot
        nos_migration_import $NOS_DIR/default_routes.json /host/fast-reboot

        umount $NOS_DIR
        rmdir $NOS_DIR
    fi

    update_mgmt_interface_macaddr /host/migration/mgmt_interface.cfg

    migration="TRUE"
    umount /mnt/onie-boot
fi

. /host/machine.conf

program_console_speed

if [ -f $FIRST_BOOT_FILE ]; then

    echo "First boot detected. Performing first boot tasks..."

    if [ -n "$aboot_platform" ]; then
        platform=$aboot_platform
    elif [ -n "$onie_platform" ]; then
        platform=$onie_platform
    else
        echo "Unknown SONiC platform"
        firsttime_exit
    fi

    # Try to take old configuration saved during installation
    # and create a flag in /tmp/ to let updategraph service know
    if [ -d /host/old_config ]; then
        mv -f /host/old_config /etc/sonic/
        rm -rf /etc/sonic/old_config/old_config
        touch /tmp/pending_config_migration
    elif [ -f /host/minigraph.xml ]; then
        mkdir -p /etc/sonic/old_config
        mv /host/minigraph.xml /etc/sonic/old_config/
        touch /tmp/pending_config_migration
    elif [ -n "$migration" ] && [ -f /host/migration/minigraph.xml ];  then
        mkdir -p /etc/sonic/old_config
        mv /host/migration/minigraph.xml /etc/sonic/old_config/
        [ -f /host/migration/acl.json ] && mv /host/migration/acl.json /etc/sonic/old_config/
        [ -f /host/migration/snmp.yml ] && mv /host/migration/snmp.yml /etc/sonic/old_config/
        touch /tmp/pending_config_migration
        [ -f /etc/sonic/updategraph.conf ] && sed -i -e "s/enabled=false/enabled=true/g" /etc/sonic/updategraph.conf
    else
        touch /tmp/pending_config_initialization
    fi

    if [ -d /host/image-$SONIC_VERSION/platform/$platform ]; then
        dpkg -i /host/image-$SONIC_VERSION/platform/$platform/*.deb
    fi

    # If the unit booted into SONiC from another NOS's grub,
    # we now install a grub for SONiC.
    if [ -n "$onie_platform" ] && [ -n "$migration" ]; then

        grub_bin=$(ls /host/image-$SONIC_VERSION/platform/x86_64-grub/grub-pc-bin*.deb 2> /dev/null)
        if [ -z "$grub_bin" ]; then
            log_migration "Unable to locate grub package !"
            firsttime_exit
        fi

        dpkg -i $grub_bin > /dev/null 2>&1
        if [ $? != 0 ]; then
            log_migration "Unable to install grub package !"
            firsttime_exit
        fi

        # Determine the block device to install grub
        sonic_dev=$(blkid | grep SONiC-OS | head -n 1 | awk '{print $1}' |  sed -e 's/[0-9]:.*$//')
        if [ -z "$sonic_dev" ]; then
            log_migration "Unable to determine sonic partition !"
            firsttime_exit
        fi

        grub-install --boot-directory=/host --recheck $sonic_dev 2>/dev/null
        if [ $? != 0 ]; then
            log_migration "grub install failed !"
            firsttime_exit
        fi

        # The SONiC "raw" build mode has already generated a proto grub.cfg
        # as part of the migration. Platform specific constants need to be
        # retrieved from installer.conf (if present) and assigned.
        . /usr/share/sonic/device/$platform/installer.conf

        if [ ! -z "$CONSOLE_PORT" ]; then
            field="\-\-port"
            string=$(grep $field /host/grub.cfg)
            value_extract $string $field
            console_port=$value
            if [ ! -z "$console_port" ] && [ "$console_port" != "$CONSOLE_PORT" ]; then
                sed -i -e "s/\-\-port=$console_port/\-\-port=$CONSOLE_PORT/g" /host/grub.cfg
            fi
            log_migration "grub.cfg console port=$console_port & installer.conf CONSOLE_PORT=$CONSOLE_PORT"
        fi

        if [ ! -z "$CONSOLE_DEV" ]; then
            field="console"
            string=$(grep $field /host/grub.cfg)
            value_extract $string $field
            console_dev_name=$(echo $value | sed -e "s/^.*=//" -e "s/,.*//")
            console_dev="${console_dev_name#ttyS}"
            if [ "$console_dev" != "$CONSOLE_DEV" ]; then
                sed -i -e "s/console=ttyS$console_dev/console=ttyS$CONSOLE_DEV/g" /host/grub.cfg
            fi
            log_migration "grub.cfg console dev=$console_dev & installer.conf CONSOLE_DEV=$CONSOLE_DEV"
        fi

        if [ ! -z "$VAR_LOG_SIZE" ]; then
            field="var_log_size"
            string=$(grep $field /host/grub.cfg)
            value_extract $string $field
            var_log_size=$value
            if [ ! -z "$var_log_size" ] && [ "$var_log_size" != "$VAR_LOG_SIZE" ]; then
                sed -i -e "s/var_log_size=$var_log_size/var_log_size=$VAR_LOG_SIZE/g" /host/grub.cfg
            fi
            log_migration "grub.cfg var_log_size=$var_log_size & installer.conf VAR_LOG_SIZE=$VAR_LOG_SIZE"
        fi

        # Set the root based on the label
        sonic_root=$(blkid | grep SONiC-OS | head -n 1 | awk '{print $1}' |  sed -e 's/:.*$//')
        sonic_root=$(echo "$sonic_root" | sed 's/\//\\\//g')
        sed -i -e "s/%%SONIC_ROOT%%/$sonic_root/g" /host/grub.cfg

        # Add the Diag and ONIE entries
        mount $onie_dev /mnt/onie-boot
        . /mnt/onie-boot/onie/grub.d/50_onie_grub >> /host/grub.cfg
        umount /mnt/onie-boot

        # Initialize the SONiC's grub config
        mv /host/grub.cfg /host/grub/grub.cfg
    fi

    firsttime_exit
fi

exit 0