#!/bin/bash
# Purpose: user interface for configuration management system
################################################################################

set -e
set -E
set -u

# helper functions

ngcpcfg_update_perms() {
  if ${SKIP_UPDATE_PERMS:-false} ; then
    log_debug "skip generation of .ngcpcfg_perms"
    return 0
  fi

  # Ensure that existing hooks are up2date.
  hook_setup "${NGCPCTL_MAIN}/.git/hooks"

  # Must be the first command in every 'ngcpcfg' call as git resets POSIX permissions!
  log_debug 'Generate new .ngcpcfg_perms'
  if [ -x "${NGCPCTL_MAIN}/.git/hooks/pre-commit" ] ; then
    "${NGCPCTL_MAIN}/.git/hooks/pre-commit"
  else
    log_error "Missing pre-commit hook in '${NGCPCTL_MAIN}/.git/hooks/pre-commit', cannot store permissions, aborting to prevent damage"
    exit 1
  fi
}

ngcpcfg_restore_perms() {
  if ${SKIP_RESTORE_PERMS:-false} ; then
    log_debug "skip restore-permissions"
    return 0
  fi
  # Must be the last command in every 'ngcpcfg' call as git resets POSIX permissions!
  trap '' ERR EXIT
  log_debug "Restore permissions from .ngcpcfg_perms as git might reset them"
  if [ ! -x "${HELPER}"/restore-permissions ]; then
    log_error "Missing helper to restore permissions '${HELPER}/restore-permissions'. Exiting."
    exit 1
  fi
  "${HELPER}"/restore-permissions "${NGCPCTL_MAIN}"
}

usage() {
  # make sure to output errors on stderr
  [ "${1:-}" = "1" ] && exec >&2

  printf "%s - Configuration Management System\n\n" "$PN"
  printf "Usage: %s <action> [<opts>]

Actions:
  apply [<msg>]       a short-cut for build-services-commit
  build [<opts>]      generate/update configuration files
  cat [<types>]       print YAML configuration files
  check [<path>]      validate YAML configuration files
  clean [<opts>]      clean /etc/ngcp-config folder configs/templates (see available options)
  commit [<msg>]      commit and record changes (without pushing)
  del [<opts>]        delete YAML option from defined file
  diff [<opts>]       display pending configuration changes
  edit                edit YAML configuration files
  get <key>           print key value from YAML configuration files
  help                display this help screen and exit
  initialise          initialise setup (to be executed only once on setup)
  log [<opts>]        show log of config changes
  patch [<opts>]      create customtt files using patchtt and templates
  services [<opts>]   execute service handlers for modified configuration files
  set [<opts>]        set YAML option in defined file
  show [<id>]         show latest config change (or <id> if specified)
  status [<opts>]     display status of configuration file
  values <key>        obsolete synonym for 'get'
  version             display program version and exit

" "$PN"

  # display only if ngcp-ngcpcfg-ha is available
  if [ -r "${FUNCTIONS}"/ha_features ] ; then
    printf "Actions (High Availability):\n"
    printf "  push [<opts>]       push modifications to other node(s) (shared storage setup only)\n"
    printf "  push-parallel       push modifications to other node(s) in parallel (shared storage setup only)\n"
    printf "  pull                retrieve modifications from shared storage (shared storage setup only)\n"
    printf "\n"
  fi


  if [ -r "${SCRIPTS}"/encrypt ]; then
    printf "Actions (extra):\n"
  fi

  # display only if ngcp-ngcpcfg-locker is available
  if [ -r "${SCRIPTS}"/encrypt ] ; then
    printf "  decrypt             decrypt /etc/ngcp-config.tgz.pgp and restore config files\n"
    printf "  encrypt             encrypt /etc/ngcp-config and all resulting configuration files\n"
  fi

  printf "\nFor further usage information and options see the ngcpcfg(8) man page.\n"
}

version() {
  versinfo=$(dpkg-query -f "\${Version}" -W ngcp-ngcpcfg 2>/dev/null)
  [ -n "${versinfo:-}" ] || versinfo='information not available'
  printf "ngcpcfg, version %s\n" "${versinfo}"
}

# Main code

if [ "$UID" -ne 0 ] ; then
  printf "Error: ngcpcfg requires root permissions. Exiting.\n" >&2
  exit 1
fi

# Notify subprocesses we are running.
export NGCPCFG_RUNNING=1

# Provide invocation name (ngcpcfg vs. ngcp-config) to subprocesses/scripts
NGCPCFG_NAME="$(basename "$0")"
export NGCPCFG_NAME

export NGCPCFG_PID=$$
logger -t ngcpcfg --id="${NGCPCFG_PID}" -- "******************************************************"
logger -t ngcpcfg --id="${NGCPCFG_PID}" -- "Running: $0 $*"

# support for testsuite
FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"
SCRIPTS="${SCRIPTS:-/usr/share/ngcp-ngcpcfg/scripts/}"
HELPER="${HELPER:-/usr/share/ngcp-ngcpcfg/helper/}"
HOOKS="${HOOKS:-/usr/share/ngcp-ngcpcfg/hooks/}"

if ! [ -r "${FUNCTIONS}/main" ] ; then
  printf "Error: %s/main could not be read. Exiting.\n" "${FUNCTIONS}" >&2
  exit 1
fi

if [[ "${1:-}" == "decrypt" ]] ; then
  # do NOT source ${FUNCTIONS}/main but just provide
  # the part we need for executing ngcpcfg itself
  log_debug() {
    if [ -n "${DEBUG:-}" ] ; then
      logger -t ngcpcfg --id="${NGCPCFG_PID}" -- "Debug: $*"
      echo ; echo "DEBUG: $*" ; echo # newlines to avoid messup with cmdline output
    fi
  }
else
  # shellcheck source=./functions/main
  . "${FUNCTIONS}"/main
fi

case ${1:-} in
  decrypt)
    printf "Running 'ngcpcfg decrypt' outside ngcpcfg framework...\n"
    "${SCRIPTS}/decrypt"
    ;;
  cat|\
  check|\
  diff|\
  edit|\
  get|\
  initialise|\
  init-shared|\
  log|\
  show|\
  set|\
  del|\
  status)
    main_action "$@"
    ;;
  values)
    printf "Note: 'ngcpcfg values' is obsolete, use 'ngcpcfg get'\n" >&2
    main_action "$@"
    ;;
  apply|\
  build|\
  clean|\
  commit|\
  encrypt|\
  pull|\
  push|\
  push-parallel|\
  services|\
  patch)
    ngcpcfg_update_perms
    trap 'ngcpcfg_restore_perms' ERR EXIT
    main_action "$@"
    ;;
  --debug)         export DEBUG=1 ;      shift ; "$0" "$@" ;;
  --jobs)          export NGCP_JOBS=0; shift ; "$0" "$@" ;;
  --jobs=*)        export NGCP_JOBS="${1##--jobs=}"; shift ; "$0" "$@" ;;
  --no-db-sync)    export NO_DB_SYNC=1 ; shift ; "$0" "$@" ;;
  --no-validate)   export NO_VALIDATE=1; shift ; "$0" "$@" ;;
  --no-action-failure) export NO_ACTION_FAILURE=1; shift ; "$0" "$@" ;;
  --no-check-origin) export NO_CHECK_ORIGIN=1; shift ; "$0" "$@" ;;
  --validate)   export VALIDATE=1; shift ; "$0" "$@" ;;
  --summary-only) export SUMMARY_ONLY=1; shift ; "$0" "$@" ;;
  -h|--help|help)       usage   ; exit 0;;
  -v|--version|version) version ; exit 0;;
  *)          usage 1; exit 1;;
esac

## END OF FILE #################################################################
