#!/bin/bash
# Purpose: detect modified files in config tree and execute
#          any defined service modifications
################################################################################

set -e
set -u

# support testsuite

FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"

CLEANUP_FILES=()

# load modules

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

. "${FUNCTIONS}"/main

# functions

exec_wrapper() {
  if ${DRYRUN} ; then
    log_info "TEST MODE: Would execute action for ${line}"
    return 0
  fi

  log_info "Executing action for ${line}"
  if [[ -x "${line}" ]]; then
    log_debug "${line}"
    if ! "${line}" ; then
      log_warn "${line} returned with error code, continuing anyway."
    fi
  elif [[ -r "${line}" ]]; then
    log_debug "bash ${line}"
    if ! bash "${line}" ; then
      log_warn "${line} returned with error code, continuing anyway."
    fi
  else
    log_error "Error: ${line} could not be read."
    exit 1
  fi
}

# get rid of "./" and "//" in file names
normalize_files() {
  NORMALIZED_FILES="$(mktemp)"
  log_debug "NORMALIZED_FILES = ${NORMALIZED_FILES}"

  CLEANUP_FILES+=("${NORMALIZED_FILES}")

  while read -r line ; do
    # shellcheck disable=SC2001
    echo "${line}" | sed -e 's_\./_/_g ; s_//_/_g' >> "${NORMALIZED_FILES}"
  done < "${TMPFILE}"
}

# restart sysctl services before the rest (see TT#58703)
# restart monit services before the rest (see MT#9971)
# restart HA just after monit (see MT#17163)
# restart other services in alphabetical order
sort_service_list() {
  SORTED_LIST="$(mktemp)"
  log_debug "SORTED_LIST = ${SORTED_LIST}"

  CLEANUP_FILES+=("${SORTED_LIST}")

  for dir in sysctl.d monit ha.d; do
    grep "${SERVICES_POOL_BASE}"/etc/${dir}/'.*services' \
      "${NORMALIZED_FILES}" >> "${SORTED_LIST}" || true
  done

  # Sort loads the entire contents before outputting, and we are appending
  # instead of truncating, so this should be safe.
  # shellcheck disable=SC2094
  sort "${NORMALIZED_FILES}" "${SORTED_LIST}" | uniq -u \
    >> "${SORTED_LIST}" || true
}

execute() {
  log_info "Clearing out enqueued services actions"
  ngcp-service queue-clear
  log_info "Starting enqueued services actions mode"
  ngcp-service queue-start

  while read -r line ; do
    exec_wrapper "${line}"
  done < "${SORTED_LIST}"

  log_info "Executing enqueued services actions"
  ngcp-service --queue=default,policy-rc.d queue-show
  ngcp-service --queue=default,policy-rc.d queue-run
}

services_sync_state() {
  log_info "Synchronizing current with expected services state"
  ngcp-service sync-state
}

systemd_daemon_reload_preset() {
  if [[ -d "/run/systemd/system" ]]; then
    log_info "Reloading systemd daemon and preset all services"

    log_debug "systemd needs daemon-reload so the unit files loaded are in sync"
    log_debug "Running: systemctl daemon-reload 2>&1 || true"
    systemctl daemon-reload 2>&1 || true

    log_debug "systemd needs preset-all to enable/disabe services (to start them on boot)"
    log_debug "Running: rm -rf /etc/systemd/system/*.wants/ || true"
    rm -rf /etc/systemd/system/*.wants/ || true
    log_debug "Running: systemctl preset-all 2>&1 || true"
    systemctl preset-all 2>&1 || true
  fi
}

is_absolute_path() {
  local dir="$1"

  if [[ ! "${dir}" =~ ^/ ]]; then
    log_error "${dir} is not an absolute path"
    return 1
  fi

  return 0
}

is_non_git_folder() {
  local dir="$1"

  if [[ ! -d "${dir}/.git" ]]; then
    log_info "${dir} has no support of .services"
    return 1
  fi

  return 0
}

generate_list_to_process() {
  local dir="$1"

  if ${FORCE_ALL_SERVICES} ; then
    log_debug "calling function find_all_services()"
    find_all_services "${dir}"
  else
    log_debug "calling function find_all_changed_services()"
    find_all_changed_services "${dir}"
  fi
}

find_all_services() {
  local dir="$1"

  while read -r file ; do
    if [[ ! -r "${file}" ]]; then
      log_warn "Cannot read file '${file}'"
    fi
    log_debug "Storing ${file} in '${TMPFILE}'"
    echo "${file}" >> "${TMPFILE}"
  done < <(find "${SERVICES_POOL_BASE}/${dir}" -name '*.services' | sort -u)
}

find_all_changed_services() {
  local dir="$1"

  log_debug "${FUNCNAME[0]}(): Working in ${dir}"
  pushd "${dir}" >/dev/null

  for file in $(git status -uall --porcelain | sed 's/^...//') ; do
    if [[ -r "${file}" ]] && [[ -r "${SERVICES_POOL_BASE}/${dir}/${file}".services ]]; then
      log_debug "Storing ${SERVICES_POOL_BASE}/${dir}/${file}.services in '${TMPFILE}'"
      echo "${SERVICES_POOL_BASE}/${dir}/${file}".services
    elif [[ -r "${file}" ]] && [[ -r "${SERVICES_POOL_BASE}/${dir}/$(dirname "${file}")"/ngcpcfg.services ]]; then
      log_debug "Storing ${SERVICES_POOL_BASE}/${dir}/$(dirname "$file")/ngcpcfg.services in '${TMPFILE}'"
      echo "${SERVICES_POOL_BASE}/${dir}/$(dirname "$file")/ngcpcfg.services"
    fi
  done | sort -u >> "${TMPFILE}"

  popd >/dev/null
}

# main script

RUNNING_FILE="${RUN_DIR}/ngcpcfg-services.running"
DRYRUN='false'
FORCE_ALL_SERVICES='false'

if [[ "${1:-}" == "test" ]] || [[ "${1:-}" == "--dry-run" ]]; then
  DRYRUN='true'
elif [[ "${1:-}" == "--force-all-services" ]]; then
  FORCE_ALL_SERVICES='true'
fi
log_debug "DRYRUN = ${DRYRUN}"
log_debug "FORCE_ALL_SERVICES = ${FORCE_ALL_SERVICES}"

if ! ${DRYRUN}; then
  log_debug "creating current execution filesystem trail"
  trap 'rm -f "${RUNNING_FILE}"' EXIT ERR
  echo $$ >"${RUNNING_FILE}"
fi

log_debug "systemd_daemon_reload_preset function"
systemd_daemon_reload_preset

TMPFILE="$(mktemp)"
log_debug "TMPFILE = ${TMPFILE}"

CLEANUP_FILES+=("${TMPFILE}")

for dir in ${CONFIG_POOL} ; do
  is_absolute_path "${dir}" || continue
  is_non_git_folder "${dir}" || continue

  generate_list_to_process "${dir}"
done

if [[ -s "${TMPFILE}" ]]; then
  log_debug "normalize_files function"
  normalize_files

  log_debug "sort_service_list function"
  sort_service_list

  log_debug "execute function"
  execute
else
  log_debug "No services file(s) reported - no explicit service state changed."
fi

log_debug "services_sync_state function"
services_sync_state

if [[ -n "${DEBUG:-}" ]]; then
  log_debug "Not removing temporary files"
else
  rm -f "${CLEANUP_FILES[@]}"
fi
## END OF FILE #################################################################
