#!/usr/bin/env bash

set -Eeuo pipefail
umask 077

readonly EXPECTED_HOSTNAME='sqjcmqtt01'
readonly EXPECTED_IP='10.2.203.108'
readonly EMQX_RPM='/opt/zhct-deploy/emqx01-offline/rpms/emqx-5.8.9-el8-amd64.rpm'
readonly EMQX_RPM_SHA='e4cf9f507cbe32c87c4e7496b943e2d836cf1c1e4e36bc70ed67c8370fbe8484'
readonly ACTIVATION_SCRIPT='/root/emqx01-q141-activate-and-verify.sh'
readonly ACTIVATION_SCRIPT_SHA='fc41df10984406d306a81e42d317d4dd7e5c971f4959ae9e1b778632da872844'
readonly ACL_FILE='/etc/emqx/acl.conf'
readonly CONFIG='/etc/emqx/emqx.conf'
readonly COOKIE='/etc/zhct/secrets/emqx.cluster-cookie'
readonly COOKIE_TRANSFER='/root/emqx.cluster-cookie.transfer'
readonly AUDIT_LOG='/root/q141-restore-rpm-acl-and-retry.log'
readonly TARGET_PORT_RE=':(1883|8083|18083|4370|5370|8883|8084)([[:space:]]|$)'

q141_wrapper_success=0
q141_acl_installed=0
q141_temp_dir=''

exec > >(tee "${AUDIT_LOG}") 2>&1

q141_wrapper_finish() {
  q141_wrapper_rc=$?
  trap - EXIT
  set +e

  if test -n "${q141_temp_dir}"; then
    case "${q141_temp_dir}" in
      /var/tmp/q141-emqx-rpm-acl.*)
        rm -rf -- "${q141_temp_dir}"
        ;;
      *)
        printf 'UNSAFE_TEMP_CLEANUP_TARGET=%s\n' "${q141_temp_dir}"
        q141_wrapper_rc=1
        ;;
    esac
  fi

  if test "${q141_wrapper_success}" -ne 1 &&
    test "${q141_acl_installed}" -eq 1; then
    printf '%s\n' 'Q141_ACL_WRAPPER_ROLLBACK_BEGIN'
    rm -f "${ACL_FILE}"
    q141_wrapper_rollback_ok=1
    test ! -e "${ACL_FILE}" || q141_wrapper_rollback_ok=0
    test ! -e "${CONFIG}" || q141_wrapper_rollback_ok=0
    test ! -e "${COOKIE}" || q141_wrapper_rollback_ok=0
    test ! -e "${COOKIE_TRANSFER}" || q141_wrapper_rollback_ok=0
    printf '%s\n' 'ROLLBACK_MUTATED_FILES_ABSENT_CHECKED'
    for q141_rollback_unit in emqx.service mosquitto.service; do
      q141_rollback_enabled=$(
        systemctl is-enabled "${q141_rollback_unit}" 2>&1 || true
      )
      q141_rollback_active=$(
        systemctl is-active "${q141_rollback_unit}" 2>&1 || true
      )
      printf 'ROLLBACK_UNIT=%s enabled=%s active=%s\n' \
        "${q141_rollback_unit}" \
        "${q141_rollback_enabled}" \
        "${q141_rollback_active}"
      test "${q141_rollback_enabled}" = 'disabled' ||
        q141_wrapper_rollback_ok=0
      test "${q141_rollback_active}" = 'inactive' ||
        q141_wrapper_rollback_ok=0
    done
    if pgrep -x beam.smp >/dev/null || pgrep -x mosquitto >/dev/null; then
      printf '%s\n' 'ROLLBACK_TARGET_PROCESSES_UNEXPECTED'
      q141_wrapper_rollback_ok=0
    else
      printf '%s\n' 'ROLLBACK_TARGET_PROCESSES_ZERO'
    fi
    if ss -lntupH | grep -E "${TARGET_PORT_RE}" >/dev/null; then
      printf '%s\n' 'ROLLBACK_TARGET_LISTENERS_UNEXPECTED'
      q141_wrapper_rollback_ok=0
    else
      printf '%s\n' 'ROLLBACK_TARGET_LISTENERS_ZERO'
    fi
    for q141_runtime_dir in /var/lib/emqx /var/log/emqx; do
      if test ! -d "${q141_runtime_dir}"; then
        printf 'ROLLBACK_RUNTIME_DIRECTORY_MISSING=%s\n' "${q141_runtime_dir}"
        q141_wrapper_rollback_ok=0
        continue
      fi
      if find "${q141_runtime_dir}" -mindepth 1 -print -quit 2>/dev/null |
        grep -q .; then
        printf 'ROLLBACK_RUNTIME_DIRECTORY_NOT_EMPTY=%s\n' "${q141_runtime_dir}"
        q141_wrapper_rollback_ok=0
      else
        printf 'ROLLBACK_RUNTIME_DIRECTORY_EMPTY=%s\n' "${q141_runtime_dir}"
      fi
    done
    if test "${q141_wrapper_rollback_ok}" -eq 1; then
      printf '%s\n' 'Q141_ACL_WRAPPER_ROLLBACK_STATUS=PASS_SEALED_STATE_RESTORED'
    else
      printf '%s\n' 'Q141_ACL_WRAPPER_ROLLBACK_STATUS=FAIL_MANUAL_REVIEW_REQUIRED'
    fi
  elif test "${q141_wrapper_success}" -eq 1; then
    printf '%s\n' 'Q141_ACL_WRAPPER_ROLLBACK_STATUS=NOT_REQUIRED'
  else
    printf '%s\n' 'Q141_ACL_WRAPPER_ROLLBACK_STATUS=NOT_REQUIRED_NO_MUTATION'
  fi

  printf '%s\n' 'Q141_RESTORE_RPM_ACL_AND_RETRY_END'
  printf 'Q141_RESTORE_RPM_ACL_AND_RETRY_RC=%s\n' "${q141_wrapper_rc}"
  exit "${q141_wrapper_rc}"
}
trap q141_wrapper_finish EXIT

printf '%s\n' 'Q141_RESTORE_RPM_ACL_AND_RETRY_BEGIN'

test "${EUID}" -eq 0
hostname -s | grep -Fx "${EXPECTED_HOSTNAME}"
ip -br -4 addr show ens192
ip -br -4 addr show ens192 | grep -F "${EXPECTED_IP}/24"
exec 8>/run/lock/zhct-emqx-q141-acl-repair.lock
flock -n 8

printf '%s\n' '=== REQUIRED COMMANDS ==='
for q141_command in cpio flock getent md5sum rpm rpm2cpio sha256sum ss systemctl; do
  command -v "${q141_command}" >/dev/null
  printf 'COMMAND_PRESENT=%s\n' "${q141_command}"
done

printf '%s\n' '=== ROLLBACK STATE RECONFIRM ==='
printf '%s  %s\n' "${ACTIVATION_SCRIPT_SHA}" "${ACTIVATION_SCRIPT}" |
  sha256sum -c -
test ! -e "${ACL_FILE}"
test ! -e "${CONFIG}"
test ! -e "${COOKIE}"
test ! -e "${COOKIE_TRANSFER}"
for q141_unit in emqx.service mosquitto.service; do
  q141_enabled=$(systemctl is-enabled "${q141_unit}" 2>&1 || true)
  q141_active=$(systemctl is-active "${q141_unit}" 2>&1 || true)
  printf '%s enabled=%s active=%s\n' \
    "${q141_unit}" "${q141_enabled}" "${q141_active}"
  test "${q141_enabled}" = 'disabled'
  test "${q141_active}" = 'inactive'
done
if pgrep -x beam.smp >/dev/null || pgrep -x mosquitto >/dev/null; then
  printf '%s\n' 'TARGET_PROCESS_UNEXPECTED'
  exit 1
fi
if ss -lntupH | grep -E "${TARGET_PORT_RE}"; then
  printf '%s\n' 'TARGET_LISTENER_UNEXPECTED'
  exit 1
fi
if find /var/lib/emqx /var/log/emqx -mindepth 1 -print -quit 2>/dev/null |
  grep -q .; then
  printf '%s\n' 'RUNTIME_STATE_UNEXPECTED'
  exit 1
fi
printf '%s\n' 'FAILED_ATTEMPT_ROLLBACK_STATE_PASS'

printf '%s\n' '=== PINNED OFFLINE RPM ==='
test -f "${EMQX_RPM}"
printf '%s  %s\n' "${EMQX_RPM_SHA}" "${EMQX_RPM}" | sha256sum -c -
rpm -qp --qf 'RPM_NEVRA=%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' \
  "${EMQX_RPM}" |
  grep -Fx 'RPM_NEVRA=emqx-5.8.9-1.el8.x86_64'
printf '%s\n' 'Q141_ACL_SOURCE=PINNED_OFFLINE_EMQX_RPM'

q141_rpm_acl_record=$(rpm -q --dump emqx |
  awk '$1 == "/etc/emqx/acl.conf" {print; found=1} END {exit !found}')
read -r \
  q141_rpm_path \
  q141_rpm_size \
  q141_rpm_mtime \
  q141_rpm_digest \
  q141_rpm_mode \
  q141_rpm_owner \
  q141_rpm_group \
  q141_rpm_config \
  q141_rpm_doc \
  q141_rpm_rdev \
  q141_rpm_link \
  <<<"${q141_rpm_acl_record}"
test "${q141_rpm_path}" = "${ACL_FILE}"
test "${q141_rpm_size}" -gt 0
test -n "${q141_rpm_digest}"
test -n "${q141_rpm_owner}"
test -n "${q141_rpm_group}"
getent passwd "${q141_rpm_owner}" >/dev/null
getent group "${q141_rpm_group}" >/dev/null

q141_temp_dir=$(mktemp -d /var/tmp/q141-emqx-rpm-acl.XXXXXX)
(
  cd "${q141_temp_dir}"
  rpm2cpio "${EMQX_RPM}" | cpio -idm --quiet './etc/emqx/acl.conf'
)
q141_extracted_acl="${q141_temp_dir}/etc/emqx/acl.conf"
test -f "${q141_extracted_acl}"
test "$(stat -c '%s' "${q141_extracted_acl}")" = "${q141_rpm_size}"

case "${#q141_rpm_digest}" in
  64)
    q141_extracted_digest=$(sha256sum "${q141_extracted_acl}" | awk '{print $1}')
    q141_digest_algorithm='SHA256'
    ;;
  32)
    q141_extracted_digest=$(md5sum "${q141_extracted_acl}" | awk '{print $1}')
    q141_digest_algorithm='MD5'
    ;;
  *)
    printf 'UNSUPPORTED_RPM_FILE_DIGEST_LENGTH=%s\n' "${#q141_rpm_digest}"
    exit 1
    ;;
esac
test "${q141_extracted_digest}" = "${q141_rpm_digest}"
q141_install_mode="${q141_rpm_mode: -4}"
case "${q141_install_mode}" in
  0600|0640|0644) ;;
  *)
    printf 'UNSUPPORTED_RPM_ACL_MODE=%s\n' "${q141_install_mode}"
    exit 1
    ;;
esac
q141_stat_mode="${q141_install_mode#0}"
printf 'RPM_ACL_SIZE=%s\n' "${q141_rpm_size}"
printf 'RPM_ACL_DIGEST_ALGORITHM=%s\n' "${q141_digest_algorithm}"
printf 'RPM_ACL_DIGEST=%s\n' "${q141_rpm_digest}"
printf 'RPM_ACL_OWNER_MODE=%s:%s:%s\n' \
  "${q141_rpm_owner}" "${q141_rpm_group}" "${q141_stat_mode}"
printf '%s\n' 'RPM_ACL_EXTRACT_VERIFY_PASS'

printf '%s\n' '=== RESTORE RPM ACL ==='
install \
  -m "${q141_install_mode}" \
  -o "${q141_rpm_owner}" \
  -g "${q141_rpm_group}" \
  "${q141_extracted_acl}" \
  "${ACL_FILE}"
q141_acl_installed=1
test "$(stat -c '%U:%G:%a:%s' "${ACL_FILE}")" = \
  "${q141_rpm_owner}:${q141_rpm_group}:${q141_stat_mode}:${q141_rpm_size}"
case "${q141_digest_algorithm}" in
  SHA256)
    test "$(sha256sum "${ACL_FILE}" | awk '{print $1}')" = "${q141_rpm_digest}"
    ;;
  MD5)
    test "$(md5sum "${ACL_FILE}" | awk '{print $1}')" = "${q141_rpm_digest}"
    ;;
esac
printf '%s\n' 'RPM_ACL_RESTORE_PASS'

printf '%s\n' '=== RETRY Q141 ACTIVATION ==='
"${ACTIVATION_SCRIPT}"

q141_wrapper_success=1
printf '%s\n' 'Q141_RPM_ACL_RESTORE_AND_ACTIVATION_PASS'
