#!/bin/sh
#  /usr/sbin/upgrade
#
#  Copyright: 2008-2016, Guralp Systems Ltd.
#  Author: Laurence Withers <lwithers@guralp.com>
#  License: GPLv3
#
#  This script will upgrade a platinum module using rsync. It finds its
#  configuration from the file ‘/etc/conf.d/upgrade’. After upgrading, it will
#  run the (potentially updated) post upgrade scripts from /usr/lib/upgrade/
#
#  The upgrader will skip certain parts of the filesystem:
#   specials        /dev /mnt /media /proc /sys lost+found /boot/grub
#   variable        /home /tmp /var resolv.conf
#   machine-local   /opt /usr/local *.local
#

if [ "$(id -u)" != "0" ]
then
    echo "You must be root in order to upgrade the system."
    exit 1
fi


. "/etc/system_type"
. "/etc/conf.d/upgrade"

case $# in
0)
    EXCLUDE_LOCAL_CHANGES=" \
        --exclude \*.local \
        --exclude /home \
        --exclude /root \
        --exclude /usr/local \
        --exclude /var \
        --exclude /etc/crontab \
        --exclude /var/spool/cron/tabs \
        --exclude /etc/group \
        --exclude /etc/passwd \
        --exclude /etc/shadow \
        --exclude /etc/dhcpcd.enter-hook \
        --exclude /etc/dhcpcd.exit-hook \
        --exclude /etc/resolv.conf.head \
        --exclude /etc/resolv.conf.tail \
    "
    ;;

1)
    case "$1" in
    --restore-defaults)
        EXCLUDE_LOCAL_CHANGES=" \
            --exclude /home \
            --exclude /root \
            --exclude /usr/local \
            --exclude /var \
        "
        ;;

    --force-factory-settings)
        EXCLUDE_LOCAL_CHANGES="--exclude /var/lib/das-in"
        ;;

    --prerelease)
	RSYNC_MODULE="platinum-prerelease" ;

        EXCLUDE_LOCAL_CHANGES=" \
            --exclude \*.local \
            --exclude /home \
            --exclude /root \
            --exclude /usr/local \
            --exclude /var \
            --exclude /etc/group \
            --exclude /etc/passwd \
            --exclude /etc/shadow \
            --exclude /etc/dhcpcd.enter-hook \
            --exclude /etc/dhcpcd.exit-hook \
            --exclude /etc/resolv.conf.head \
            --exclude /etc/resolv.conf.tail \
        "
        ;;

    --help)
        echo "Usage: upgrade [--restore-defaults | --force-factory-settings | --prerelease]"
        exit 0
        ;;

    *)
        echo "Unknown option."
        exit 1
        ;;
    esac
    ;;

*)
    echo "Usage: upgrade [--restore-defaults | --force-factory-settings | --prerelease]"
    exit 1
    ;;
esac



#
# logging function
#
ulog() {
    MSG="$*"

    echo "${MSG}"
    echo "`isodate -se` -- ${MSG}" >> "/var/log/upgrade.log"
}



#
# report start of upgrade
#
. "/etc/build.version"
ulog "Beginning upgrade with host '${RSYNC_HOST}', module '${RSYNC_MODULE}'. Upgrading from ${BUILD_LABEL} build ${BUILD_VERSION}."


# Nslookup output needs filtering to give us just the IP address
lookup_ip() {
    local name=$1

    nslookup $name 2> /dev/null | awk '
    BEGIN { sawname = 0 }
    $1 == "Name:" { sawname = 1 }
    sawname == 1 && $1 == "Address" && $2 == "1:" { print $3 }
    sawname == 1 && $1 == "Address:" { print $2 }
    '
}

#
# Check DNS lookup of the rsync host
#
ip=$(lookup_ip ${RSYNC_HOST})
if [ -z "$ip" ]
then
    if ping -c 1 8.8.8.8 > /dev/null
    then
	echo "Lookup of ${RSYNC_HOST} failed, trying alternate DNS." 1>&2
	# The server option on nslookup isn't working we have to
	# hack resolve.conf.
	res=/etc/resolv.conf
	bak=${res}.$$
	mv $res $bak
	echo "nameserver 8.8.8.8" > $res
	ip=$(lookup_ip ${RSYNC_HOST})
	mv $bak $res
	if [ -z "$ip" ]
	then
	    echo "External lookup for ${RSYNC_HOST} also failed." 1>&2
	    echo "Check your network configuration." 1>&2
	    if [ "${RSYNC_HOST}" != "rsync.guralp.com" ]
	    then
		echo "Also check upgrade settings in /etc/conf.d/upgrade.local" 1>&2
	    fi
	    echo "Attempting upgrade but there may be problems."
	else
	    ulog "Using IP address $ip for ${RSYNC_HOST}"
	    RSYNC_HOST="$ip"
	fi
    else
	echo "Lookup of ${RSYNC_HOST} failed, and cannot ping 8.8.8.8" 1>&2
	echo "Check your network configuration." 1>&2
	echo "Attempting upgrade but there may be problems."
    fi
fi



#
# cope with parts of the filesystem that are normally mounted read-only
#
RDONLY_MOUNTS=""
case "${BUILD_MACHINE}" in
CMG-DAS)
    RDONLY_MOUNTS="/boot /usr"
    ;;
esac

for MOUNT in ${RDONLY_MOUNTS}
do
    echo " * Remounting ${MOUNT} as read-write"
    mount -o remount,rw "${MOUNT}" || exit 1
done

# We can't remount as read-only later, because if the upgrade script itself is
# overwritten, then bash holds open a temporary writeable file which stops the
# remount from succeeding. Instead, we rely on the system being rebooted, which
# will properly unmount the filesystem.



# clean rsync temporary files (in case a previous upgrade was aborted)
echo " * Cleaning rsync temporary files"
remove-rsync-temp --auto
if [ $? -ne 0 ]
then
    ulog "Failed to clean rsync temporary files; aborting upgrade."
    exit 1
fi



#
# run the rsync process itself
#
eval rsync \
    --super \
    --verbose --stats --human-readable --progress --itemize-changes \
    --recursive --times --links \
    --perms --chmod=ugo+r,+X \
    --delete --delete-delay --force \
    --delay-updates --compress \
    --exclude "/dev" --exclude "lost+found" --exclude "/mnt" --exclude "/media" \
    --exclude "/proc" --exclude "/sys" --exclude "/tmp" --exclude "resolv.conf" \
    --exclude "/boot/grub" --exclude "/run" \
    ${EXCLUDE_LOCAL_CHANGES} \
    ${RSYNC_ADDITIONAL_OPTS} \
    --port "${RSYNC_PORT}" \
    "rsync://${RSYNC_HOST}/${RSYNC_MODULE}/${BUILD_MACHINE}/" "/"
RET=$?

if [ ${RET} -eq 20 ]
then
    echo "Rsync interrupted. Aborting." >&2
    ulog "Upgrade aborted by user."
    exit 20
fi

if [ ${RET} -ne 0 ]
then
    remove-rsync-temp --auto
    ulog "Upgrade failed (rsync exit code ${RET})."
    exit 1
fi

# report the new version
. "/etc/build.version"
ulog "Rsync to ${BUILD_LABEL} build ${BUILD_VERSION} completed, running post-upgrade scripts."



#
# run post upgrade scripts
#
set +e
export POST_UPGRADE_CACHE="/var/cache/post_upgrade"
mkdir -p "${POST_UPGRADE_CACHE}"
for script in `find /usr/lib/upgrade -type f | sort`
do
    [ -e "${POST_UPGRADE_CACHE}/`basename ${script}`" ] && continue
    [ -x "${script}" ] && "${script}"
done

# Sync the files in case the user decides to powercycle
sync


# Show an upgrade complete message
ulog "Upgrade complete. Now at ${BUILD_LABEL} build ${BUILD_VERSION}."

# vim: ts=4:sw=4:expandtab
