#!/bin/sh
# Bootstrap system factory-config, failure-config, test-config and sysrepo db.
#
########################################################################
# The system factory-config, failure-config and test-config are derived
# from default settings snippets, from /usr/share/confd/factory.d, and
# some generated snippets, e.g., hostname (based on base MAC address)
# and number of interfaces.
#
# The resulting factory-config is used to create the syrepo db (below)
# {factory} datastore.  Hence, the factory-config file must match the
# the YANG models of the active image.
########################################################################
# NOTE: with the Infix defaults, a br2-external can provide a build-time
#       /etc/factory-config.cfg to override the behavior of this script.
#
#       This applies also for /etc/failure-config.cfg, but we recommend
#       strongly that you instead provide gen-err-custom, see below.
#
# TODO: Look for statically defined factory-config, based on system's
#       product ID, or just custom site-specific factory on /cfg.
########################################################################
STATUS=""

# Log functions
critical()
{
    logger -i -p user.crit -t bootstrap "$1" 2>/dev/null || echo "$1"
}

err()
{
    logger -i -p user.err -t bootstrap "$1" 2>/dev/null || echo "$1"
}

# When logging errors, generating /etc/issue* or /etc/banner (SSH)
. /etc/os-release

# /etc/confdrc controls the behavior or most of the gen-scripts,
# customize in an overlay when using Infix as an br2-external.
RC=/etc/confdrc
if [ "$1" = "-f" ] && [ -f "$2" ]; then
    RC=$2
fi

if [ ! -f "$RC" ]; then
    err "Missing rc file $RC"
    exit 99
fi

# shellcheck disable=SC1090
. "$RC"

# Gather all .json files in $dir, sort them numerically, and use jq
# magic to create a configuration file without duplicates.  Allowing
# overrides of the Infix defaults in a br2-external.
#
#   10-foo.json         -- Static Infix default
#   20-bar.json         -- Generated Infix bar
#   30-config.json      -- By br2-external provided gen-cfg-custom
#   30-foo.json         -- Static br2-external replacing 10-foo.json
#
# Note: to override just the base hostname, used in gen-hostname, set
#       BR2_TARGET_GENERIC_HOSTNAME in your br2-external's defconfig.
#
# shellcheck disable=SC2046,SC2086
collate()
{
    gen=$1; shift
    cfg=$1; shift
    dir=$1

    rm -f "$gen"
    jq -s 'reduce .[] as $item ({}; . * $item)' $(find $dir -name '*.json' | sort) >"$gen"
    chmod 444 "$gen"

    if [ ! -f "$cfg" ]; then
	cp "$gen" "$cfg"
    fi
}

# Report error on console, syslog, and set login banners for getty + ssh
console_error()
{
    critical "$1"

    # shellcheck disable=SC3037
    /bin/echo -e "\n\n\e[31mCRITICAL BOOTSTRAP ERROR\n$1\e[0m\n" > /dev/console

    [ -z "$STATUS" ] || return
    STATUS="CRITICAL ERROR: $1"

    printf "\n$STATUS\n" | tee -a \
			       /etc/banner \
			       /etc/issue \
			       /etc/issue.net \
			       >/dev/null
    return 0
}

gen_factory_cfg()
{
    # Fetch defaults, simplifies sort in collate()
    cp "$FACTORY_DEFAULTS_D"/* "$FACTORY_D/"

    # Create an overlay for /etc/hostname to change the default in an br2-external
    gen-hostname                                            >"$FACTORY_D/20-hostname.json"
    gen-motd                                                >"$FACTORY_D/20-motd.json"
    gen-hardware                                            >"$FACTORY_D/20-hardware.json"
    # shellcheck disable=SC2086
    gen-interfaces $GEN_IFACE_OPTS                          >"$FACTORY_D/20-interfaces.json"
    gen-version                                             >"$FACTORY_D/20-version.json"

    # Optional commands (from an overlay) to run for br2-externals
    [ -x "$(command -v gen-ifs-custom)" ] && gen-ifs-custom >"$FACTORY_D/20-interfaces.json"
    [ -x "$(command -v gen-cfg-custom)" ] && gen-cfg-custom >"$FACTORY_D/30-config.json"

    collate "$FACTORY_GEN" "$FACTORY_CFG" "$FACTORY_D"
}

gen_failure_cfg()
{
    # Fetch defaults, simplifies sort in collate()
    cp "$FAILURE_DEFAULTS_D"/* "$FAILURE_D"

    gen-hostname   "$FAIL_HOSTNAME"                         >"$FAILURE_D/20-hostname.json"
    gen-interfaces                                          >"$FAILURE_D/20-interfaces.json"
    gen-version                                             >"$FAILURE_D/20-version.json"

    # Optional failure/error config to generate (or override) for br2-externals
    [ -x "$(command -v gen-err-custom)" ] && gen-err-custom >"$FAILURE_D/30-error.json"

    collate "$FAILURE_GEN" "$FAILURE_CFG" "$FAILURE_D"
}

gen_test_cfg()
{
    # Fetch defaults, simplifies sort in collate()
    cp "$TEST_DEFAULTS_D"/* "$TEST_D"

    gen-hostname   "$TEST_HOSTNAME"                         >"$TEST_D/20-hostname.json"
    gen-interfaces                                          >"$TEST_D/20-interfaces.json"
    gen-version                                             >"$TEST_D/20-version.json"

    collate "$TEST_GEN" "$TEST_CFG" "$TEST_D"
}

# Both factory-config and failure-config are generated every boot
# regardless if there is a static /etc/factory-config.cfg or not.
gen_factory_cfg
gen_failure_cfg

if [ -f "/mnt/aux/test-mode" ]; then
    gen_test_cfg
    sysrepoctl -c infix-test -e test-mode-enable
fi

if [ -n "$TESTING" ]; then
	echo "Done."
	exit 0
fi
mkdir -p /etc/sysrepo/
if [ -f "$FACTORY_CFG" ]; then
    cp "$FACTORY_CFG" "$INIT_DATA"
else
    cp "$FAILURE_CFG" "$INIT_DATA"
fi
rc=$?

# Ensure 'admin' group users always have access
chgrp wheel "$CFG_PATH_"
chmod g+w   "$CFG_PATH_"

# Ensure factory-config has correct syntax
if ! migrate -cq "$INIT_DATA"; then
    if migrate -iq -b "${INIT_DATA%.*}.bak" "$INIT_DATA"; then
	err "${INIT_DATA}: found and fixed old syntax!"
    fi
fi

if ! sysrepoctl -z "$INIT_DATA"; then
    rc=$?
    err "Failed loading factory-default datastore"
else
    # Clear running-config so we can load/create startup in the next step
    temp=$(mktemp)
    echo "{}" > "$temp"
    sysrepocfg -f json -I"$temp" -d running
    rc=$?
    rm "$temp"
fi

exit $rc
