#!/usr/bin/bash
#
# Copyright: 2021 ZENETYS
# Author: Benoit DOLEZ <bdolez@zenetys.com>
# License: MIT License (http://opensource.org/licenses/MIT)
# Version: 0.1
#

[[ $XDEBUG == 1 ]] && set -x

export LC_ALL=C
shopt -s nullglob
set -o pipefail
set -f

PROGNAME=${0##*/}
USAGE="${PROGNAME} [OPTIONS] ACTION [...]"

function info() {
    local i;
    local d=$(date +%Y-%m-%dT%H:%M:%S.%3N%z);
    local l=info
    [[ ${1:0:2} == -- ]] && l=${1:2} && shift
    for i in "$@"; do
        printf '%s%s%s: %s\n' "${C[$l]}" "${l^^}" "${C[reset]}" "$i" >&2
    done
}

function fatal() {
    local usage=0
    [[ $1 == --usage ]] && usage=1 && shift
    info --${FUNCNAME[0]} "$@"
    (( $usage )) && usage
    trap "" EXIT
    exit 127
}

function error() {
    info --${FUNCNAME[0]} "$@"
}

function warning() {
    info --${FUNCNAME[0]} "$@"
}

function debug() {
    local debug=0

    [[ ${1:0:1} == - ]] && debug=${1:1} && shift
    (( DEBUG < $debug )) && return 0
    info --${FUNCNAME[0]} "$@"
}

function usage() {
  local IFS=$'\t'
  exec >&2
  if [[ $0 == "-bash" ]] ; then return 1 ; fi
  [[ $# -gt 0 ]] && echo "ERROR: $*"
  version
  echo "Usage: $USAGE"
  echo "Options:"
  sed -nr "s/^[[:space:]]*## ([^:]*): /\1\t/p" -- "$0" |
    while read OPT DESC ; do
      printf " %-20s %s\n" "$OPT" "$DESC"
    done
  echo

  echo "<ACTION> is one of :"
  sed -nr "s/^# X-ACTION ([^:]*): /\1\t/p" -- "$0" |
    while read OPT DESC ; do
      printf " %s\n     %s\n" "$OPT" "$DESC"
    done
  echo

  return 0
}

function version() {
  local PROGFILE=$0
  local VERSION=$(sed -n 's/^# Version: //p' $PROGFILE)
  local AUTHOR=$(sed -n 's/^# Author: //p' $PROGFILE)
  local LICENSE=$(sed -n 's/^# License: //p' $PROGFILE)

  echo "${PROGFILE##*/} $VERSION - $AUTHOR - $LICENSE"
}

function lastlog() {
    tail -n 1000 /var/log/messages |fgrep ' rsyslogd: ' |tail -n 30
}

# X-ACTION build <filename.xls>: build config with input filename.xls
function do_build() {
  local cfgdir=$BASEDIR/$(date +%Y%m%d%H%M%S)

  mkdir -p $cfgdir
  debug "Create conf.d to '$cfgdir'"

  # convert xls to csv
  # - lines starting by # are ignored
  # - separator is ',' (coma)
  # - ignore double quotes as field separator

  for file in "$@"; do
    debug -3 "Compiling '$file'"

    if [[ ${file} =~ .*\.xls$ ]]; then
      echo "# $file"
      while read -r; do
        unset "${headers[@]}"; eval "$REPLY"; [[ $row ]] || continue
        if [[ $pattern && $service ]]; then
          debug -3 "CSV LOG : <$hostname> <$program> <$pattern> <$rewrite> <$status> <$continue> <$rewrite_hostname> <$service> <$count> <$timeout> ####"
          debug -1 "logmatch_rule($pattern => $rewrite_hostname:$service = $status)"

      [[ ${hostname:0:4} == CO1- ]] && hostname="${HOSTNAME%-*}-${hostname#*-}"

	  [[ $program ]] && echo "PATTERN:PROGRAM \"$program\""

	  [[ $hostname ]] && echo "PATTERN:HOSTNAME \"$hostname\""

          echo PATTERN \"${pattern//\"/\\\"}\"
          [[ $rewrite ]] && echo REWRITE \"${rewrite//\"/\\\"}\"
          case ${status,,} in
            ack)            status="ACK" ;;
            unknown|2)      status="UNKNOWN" ;;
            warning|1)      status="WARNING" ;;
            ok|0)           status="OK" ;;
            *)              status="CRITICAL" ;;
          esac
          echo "$status"

          [[ $continue ]] && echo "CONTINUE"
          [[ $disabled ]] && echo "DISABLED"
          # FIXME: specific
          [[ ${rewrite_hostname:0:4} == CO1- ]] && rewrite_hostname="${HOSTNAME%-*}-${rewrite_hostname#*-}"
          [[ $rewrite_hostname ]]     && echo "ATTR:NAGIOS_FORCED_HOST \"$rewrite_hostname\""
          [[ $service ]]  && echo "ATTR:NAGIOS_FORCED_SERVICE \"$service\""
          [[ $count ]]    && echo "THRESHOLD:COUNT $count"
          [[ $timeout ]]  && echo "THRESHOLD:TIMEOUT $timeout"
          echo
        fi
      done < <(xls2bash "$file" --detect-headers --ignore-comments | iconv -f cp1252 -t utf8)
    elif [[ $file == *.conf ]] && grep -qE ^PATTERN "$file"; then
      echo "# $file"
      cat "$file"
      echo
    fi
  done > $cfgdir/logmatch.conf

  {
    echo "# /etc/hosts"
    sed 's/#.*//' < /etc/hosts | while read ipaddr hostname rest; do
      [[ -z $ipaddr ]] && continue
      echo "${hostname} $ipaddr ${hostname,,} ${hostname^^} ${rest}"
    done
    echo
    for file in "$@"; do
      [[ $file == *alias*.conf ]] || continue
      echo "# $file"
      cat "$file"
      echo
    done
  } > $cfgdir/aliases.conf

  # validate link to new config
  ln -snf ${cfgdir##*/} ${CFGDIR}/new
}

# X-ACTION apply: apply new config
function do_apply() {
  debug "Apply new logmatch config from ${CFGDIR}/new"

  [[ -L ${CFGDIR}/new ]] ||
    fatal "bad configuration: '${CFGDIR}/new' do not link to configuration"

  [[ -L ${CFGDIR}/previous ]] && rm ${CFGDIR}/previous
  [[ -L ${CFGDIR}/current ]] && mv ${CFGDIR}/current ${CFGDIR}/previous
  mv ${CFGDIR}/new ${CFGDIR}/current

  [[ -n $APPLY_NO_RESTART ]] && return 0

  killall logmatch
  sleep 3
  if ! killall -0 logmatch 2>/dev/null; then
    lastlog
    warning "Rollback to the old logmatch configuration"
    do_revert
    return 1
  fi

  info "New configuration applied and linked to ${CFGDIR}/current"
}

# X-ACTION revert: revert to old config
function do_revert() {
  debug "Revert previous configuration (${CFGDIR}/previous)"

  [[ -L ${CFGDIR}/previous ]] ||
    fatal "can't revert to unknown configuration"

  if [[ -L ${CFGDIR}/current ]]; then
    debug "Disable current ${CFGDIR}/$(readlink ${CFGDIR}/current)"
    [[ -L ${CFGDIR}/old ]] && rm ${CFGDIR}/old
    mv ${CFGDIR}/current ${CFGDIR}/old
    info "Old configuration linked to ${CFGDIR}/old"
  fi

  info "Previous configuration applied and linked to ${CFGDIR}/current"

  mv ${CFGDIR}/previous ${CFGDIR}/current
  killall -0 logmatch 2>/dev/null && killall logmatch
  sleep 3
  if ! killall -0 logmatch 2>/dev/null; then
    lastlog
    fatal "Rollback failed, no active configuration running"
  fi
}

declare VERBOSE=${VERBOSE:-1}
declare BASEDIR=${BASEDIR:-/var/lib/kompot/configs/logmatch}
declare CFGDIR=${BASEDIR}

export PATH=${PATH}:${0%/*}

(( ${#BASH_SOURCE[@]} > 1 )) && return

while (( $# > 0 )); do
  case "$1" in
    ## -h, --help: This help
    -h|--help) usage && exit 0 ;;
    ## -V, --version: Show version
    -V|--version) version && exit 0 ;;
    ## --x-debug: Enable bash debug mode
    --x-debug)    XDEBUG=1 ;;
    ## -v, --verbose: Define verbose level (must be repeat)
    -v|--verbose) ((VERBOSE++)) ;;
    ## -q, --quiet: Set verbose level to 0
    -q|--quiet) ((VERBOSE=0)) ;;
    ## -f, --source: Filename to source, or stdin if last argument
    -f|--source) SOURCE=$2; shift;;
    -*) usage "Unknown parameter '$1'" && exit 1 ;;
    *) ARGS+=( "$1" ) ;;
  esac
  shift
done

[[ $XDEBUG == 1 ]] && set -x

####################################################
## main program starts here
####################################################

ACTION=${ARGS[0]}

case $ACTION in
  # FIXME
  build) do_build "${ARGS[@]:1}" ;;
  apply) do_apply ;;
  revert) do_revert ;;
  *) usage "unknown command '$ACTION'" && exit 1 ;;
esac

exit $?

