#!/bin/sh

################################################################################
#
# Copyright (C) 2020-2021 NVIDIA Corporation. ALL RIGHTS RESERVED.
# Copyright (C) 2014,2015,2019 Cumulus Networks Inc. All rights reserved
#
################################################################################

# This script installs platform specific config files from
# /usr/share/cumulus-platform/<vendor>/<model> into /etc on the target
# file system.
#
# This script uses the facilities from ucf(1) to manage local
# configuration file changes made by the user.
#
# This script is called in the following contexts:
#
# - initramfs: during the first boot after the current platform type
#   is determined.  ucf(1) uses debconf so we have to be careful about
#   which debconf to use when we have no TTY.
#
# - dpkg upgrades: when installing a new version of the cl-platform-config package.

initramfs="${DPKG_RUNNING_VERSION:-yes}"
if [ "$initramfs" = "yes" ] ; then
    # The legacy initramfs calls this script every boot.  Exit if
    # called before.
    [ -e /var/cache/cumulus/platform-config ] && exit 0

    # We have no controlling TTY in initramfs context
    export DEBIAN_FRONTEND=noninteractive
fi

# check if an element is contained in a list
# $1 - element to look for
# $2 - the list
# return 1 if element is in the list; 0 otherwise
contains () {
    element=$1
    list=$2
    for item in $list; do
        if [ "$item" = "$element" ]; then
            echo 1
            return
        fi
    done
    echo 0
}

user_config_files="/etc/cumulus/acl/policy.d/00control_plane.rules
                   /etc/cumulus/acl/policy.d/99control_plane_catch_all.rules
                   /etc/cumulus/ports.conf
                   /etc/mlx/datapath/tcam_profile.conf"

# Use ucf and ucfr to install and register the configuration file with
# the system.
# $1 - source file to install
# $2 - destination file
install_file()
{
    UCF_CMD="ucf --debconf-ok --three-way"
    [ "$quiet" = "y" ] && REDIR="> /dev/null 2>&1"

    # If dpkg was called with --force-confold (or confnew) or apt was
    # called with -o Dpkg::Options::="--force-confold" (or confnew) for
    # noninteractive installation when a configuration file has been
    # changed by both the user and the new version of the package, then
    # set the environment variable used by ucf to cause it to do a similar
    # noninteractive installation in that type of situation.
    is_user_config_file=$(contains "$2" "$user_config_files")
    if [ $is_user_config_file = 1 ]; then
        if echo "$DPKG_FORCE" | grep -q confold ; then
            eval UCF_FORCE_CONFFOLD=yes $UCF_CMD "$1" "$2" $REDIR
	elif echo "$DPKG_FORCE" | grep -q confnew ; then
	    eval UCF_FORCE_CONFFNEW=yes $UCF_CMD "$1" "$2" $REDIR
        else
	    eval $UCF_CMD "$1" "$2" $REDIR
	fi
    else
        eval UCF_FORCE_CONFFNEW=yes $UCF_CMD "$1" "$2" $REDIR
    fi

    ucfr cumulus-platform "$2"
}

base_commondir="/usr/share/cumulus-platform/common"
[ -d "$base_commondir/etc" ] || {
    echo "Error: Unable to find common platform configuration directory: $base_commondir/etc"
    exit 1
}

# Create a confdir using the common files as base, overlaying the
# platform specific files on top.

# clean up for temporary directory
tmp_cleanup()
{
    rm -rf $tmpdir
}

# install the files for $platform by combining the common and
# platform-specific directories.
# [-c] - copy only directory contents, not directory itself
# $1 - source tree (relative to $base_commondir and/or $platformdir)
# $2 - destination tree (rooted path)
copy_with_default()
{
	contentsonly=''
	case $1 in
	-c)
		contentsonly='/*'
		shift
		;;
	*)
		;;
	esac

    src=$1
    dst=$2

    if ! [ -d "$dst" ]; then
        mkdir -p "$dst"
    fi

    # copy the default files
    if [ -d "$base_commondir/$src" ]; then
        cp -a "$base_commondir/$src"$contentsonly "$dst"
    fi

    # override with any platform-specific files
    if [ -d "$platformdir/$src" ]; then
        cp -a "$platformdir/$src"$contentsonly "$dst"
    fi
}

# deinstall the files for $platform by removing the platform-specific
# files and installing the common ones (where applicable)
# [-c] - copy only directory contents, not directory itself
# $1 - source tree (relative to $base_commondir and/or $platformdir)
# $2 - destination tree (rooted path)
restore_default()
{
	contentsonly=''
	case $1 in
	-c)
		contentsonly='/*'
		shift
		;;
	*)
		;;
	esac

    src=$1
    dst=$2

    if ! [ -d "$dst" ]; then
        mkdir -p "$dst"
    fi

    # remove any platform-specific files
    find $platformdir$contentsonly/$src -type f -printf "%P\n" | \
        while read f; do
            if test -f "$commondir/$src/$f"; then
                cp -a "$commondir/$src/$f" "/$dst/$f"
            else
                rm -f "/$dst/$f"
            fi
        done
}

# recursively install from $src into $dst, registering any files
# copied using ucf
# $1 - source tree (rooted path)
# $2 - destination tree (rooted path)
recursive_ucf_register()
{
    src=$1
    dst=$2

     find "$src" -printf "%P\n" | while read f; do
        if [ -d "$src/$f" ] ; then
            [ -d "$dst/$f" ] || mkdir -p "$dst/$f"
        elif [ -f "$src/$f" ] ; then
            install_file "$src/$f" "$dst/$f"
        else
            echo "Error: Can't handle file $src/$f"
            exit 1
        fi
    done
}

# recursively deinstall from $src from $dst, unregistering any files
# copied using ucf
# $1 - source tree (rooted path)
# $2 - destination tree (rooted path)
recursive_ucf_deregister()
{
    src=$1
    dst=$2

     find "$src" -printf "%P\n" | while read f; do
        if [ -d "$src/$f" ] ; then
            # NOTE: no obviously-sane way to remove directories
            true
        elif [ -f "$src/$f" ] ; then
        	ucf --purge "$dst/$f"
        	rm -f "$dst/$f"
        else
            echo "Error: Can't handle file $src/$f"
            exit 1
        fi
    done
}

# install all files needed for a particular platform
# $1 - the platform string to install
install_platform()
{
	platform=$1
	vendor="${platform%%,*}"
	model="${platform#*,}"
	platformdir="/usr/share/cumulus-platform/${vendor}/${model}"

    copy_with_default -c etc/cumulus/acl/policy.d /usr/share/cumulus/acl/default

    copy_with_default lib /lib

    copy_with_default etc $tmpdir
    recursive_ucf_register "$tmpdir/etc" /etc

    echo "$platform" > /var/cache/cumulus/oldplatform
}

# uninstall all files installed by a particular platform
# needed for systems where the platform can change (i.e. simx)
# $1 - the platform string to uninstall
uninstall_platform()
{
	platform=$1
	vendor="${platform%%,*}"
	model="${platform#*,}"
	platformdir="/usr/share/cumulus-platform/${vendor}/${model}"

    restore_default -c etc/cumulus/acl/policy.d /usr/share/cumulus/acl/default

    restore_default lib /lib

	recursive_ucf_deregister etc /etc
    restore_default etc $tmpdir
    recursive_ucf_register "$tmpdir/etc" /etc

	rm /var/cache/cumulus/oldplatform
}

tmpdir=$(mktemp -d) || {
    echo "Error: Unable to create temp directory."
    exit 1
}

trap tmp_cleanup EXIT QUIT

case $1 in
install|'')
	install_platform "$(platform-detect)"
	;;
uninstall)
	if [ -f "/var/cache/cumulus/oldplatform" ]; then
		uninstall_platform "$(cat /var/cache/cumulus/oldplatform)"
	fi
	;;
esac

if [ "$initramfs" = "yes" ] ; then
    mkdir -p /var/cache/cumulus
    touch /var/cache/cumulus/platform-config
fi
