#!/bin/bash

#==脚本执行中遇到不存在的变量就报错
set -o nounset
#==脚本执行中报错即刻退出
set -o errexit
#==脚本执行只要一个子命令失
set -o pipefail

readonly BRAND_FORMAL_NAME="Cloudwise"
readonly BRAND_PRODUCT_NAME="euspace"
#== 根路径
readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
readonly BRAND_PRODUCT_NAME_LOWER="euspace"
readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="cloudwise"

#== **********************************************************
#==  配置重要文件名称
#== **********************************************************
#== smartagentd
readonly AGENT_PROC="euspace"
readonly AGENT_INSTALLER_VERSION=1.0.0
#== **********************************************************
#==  配置目录
#== **********************************************************
readonly INSTALL_BASE=/opt
#== /opt/cloudwise/omniagent/agents 目录
readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
#== cloudwise/omniagent/agents/agent
readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
#== cloudwise/omniagent/agents/agent/version
readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
#== /opt/cloudwise/omniagent/agents/agent/version
readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
#== /opt/cloudwise/omniagent/agents/agent
readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}

readonly CURRENT_VERSION_LINK=${AGENT_BASE_DIR}/current

#== /opt/cloudwise/omniagent/conf
readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"

readonly AGENT_LIBS_DIR="${INSTALL_DIR}/libs"

#== agent 初始化脚本目录 /opt/cloudwise/omniagent/scripts
readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"

#== agent 初始化脚本目录 /opt/cloudwise/omniagent/bin
readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"

readonly AGENT_AGENTS_DIR="${INSTALL_DIR}/agents"

readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"



readonly PERL_FILE="/usr/bin/perl"

readonly SAFE_RM_FILE="safe-rm"

readonly SAFE_RM_BIN="${AGENT_BIN_DIR}/${SAFE_RM_FILE}"

if [ -f ${PERL_FILE} ] && [ -f "${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}" ]; then
    readonly SAFE_RM_EXEC="${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}"
else
    readonly SAFE_RM_EXEC="rm"
fi

readonly LOG_DIR_NAME=logs
#== /opt/cloudwise/omniagent/logs
readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}

#== /var/log/cloudwise/omniagent/
readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"

#readonly INSTALLER_LOG_SUBDIR="installer"
##== /opt/cloudwise/smartagent/log/installer
#readonly INSTALLER_LOG_DIR=${LOG_DIR}/${INSTALLER_LOG_SUBDIR}

readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"


#== 【0】=
readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"

readonly INIT_SYSTEM_SYSV="SysV"
#== 系统 systemd
readonly INIT_SYSTEM_SYSTEMD="systemd"
#== cw-serveragent
readonly SERVICE_SCRIPT_FILE="cw-oneagent"
#== cw-serveragent.service
readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"

#== 系统文件目录
readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"

readonly EXIT_CODE_OK=0
readonly EXIT_CODE_GENERIC_ERROR=1

readonly EXIT_CODE_SIGNAL_RECEIVED=12
readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17


readonly USER="root"
readonly GROUP="root"
#== 非root模式
readonly PARAM_NON_ROOT_MODE=false

readonly CONTAINER_DEPLOYMENT_STATE_ENTRY=false
#== 是否有 SELinux
readonly ARCH_HAS_SELINUX=true
################################################################################
#	Logs
################################################################################
toLog() {
  if [ -e "${LOG_FILE}" ]; then
    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
  fi
}

toConsoleOnly() {
  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
}

toLogInfo() {
  toLog "[INFO]" "$@"
}

toLogWarning() {
  toLog "[WARN]" "$@"
}

toLogError() {
  toLog "[ERROR]" "$@"
}

toLogAdaptive() {
  local success="${1}"
  shift
  if [ "${success}" -eq 0 ]; then
    toLogInfo "$@"
  else
    toLogError "$@"
  fi
}

toConsoleInfo() {
  toConsoleOnly "$@"
  toLogInfo "$@"
}

toConsoleWarning() {
  toConsoleOnly "Warning:" "$@"
  toLogWarning "$@"
} >&2

toConsoleError() {
  toConsoleOnly "Error:" "$@"
  toLogError "$@"
} >&2

createDirIfNotExistAndSetRights() {
  local dir="${1}"
  local rights="${2}"

  if [ ! -d "${dir}" ]; then
    toLogInfo "Creating directory ${dir} with rights ${rights}"
    local message
    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
      toConsoleWarning "Cannot create ${dir} directory."
      toLogWarning "Error message: ${message}"
      return 1
    fi
  fi

  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
    toConsoleWarning "Cannot change permisions of ${dir} directory to ${rights}."
    toLogWarning "Error message: ${message}"
    return 1
  fi

  return 0
}

#== 创建日志目录及日志文件installation_$$.log
createLogDirsIfMissing() {
  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777

#  for agentLog in ${PLUGIN_LOG_NAMES}; do
#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
#  done

  touch "${LOG_FILE}"
  setRightsForFiles "${LOG_FILE}" 766
}


################################################################################
#	Platform characteristics detection
################################################################################

#== 设置文件可执行权限
setRightsForFiles() {
  local file="${1}"
  local perms="${2}"

  if [ -f "${file}" ]; then
    chmod "${perms}" "${file}"
  elif [ -d "${file}" ]; then
    chmod -R "${perms}" "${file}"
  fi
}

#== 安装类型（f:文件，d:目录）递归设置权限
chmod4FilesRecursively() {
  local dir="${1}"
  local type="${2}"
  local mask="${3}"
  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
}

detectLinuxDistribution() {
  if [ -f /etc/redhat-release ]; then
    cat /etc/redhat-release
  elif [ -f /etc/os-release ]; then
    (
      . /etc/os-release
      local distrib="${NAME}"
      if [ -z "${distrib}" ]; then
        distrib="${ID}"
      fi

      local version="${VERSION_ID}"
      if printf "%s" "${distrib}" | grep -iq "debian"; then
        version="$(cat /etc/debian_version)"
      fi

      printf "%s %s" "${distrib}" "${version}"
    )
  elif [ -f /etc/SuSE-release ]; then
    head -1 /etc/SuSE-release
  elif [ -f /etc/lsb-release ]; then
    (
      . /etc/lsb-release
      printf "%s %s" "${DISTRIB_ID}" "${DISTRIB_RELEASE}"
    )
  elif ls /etc/*release* >/dev/null 2>&1; then
    # Generic fallback
    cat /etc/*release*
  else
    printf "AIX %s" "$(oslevel -s 2>&1)"
  fi
}

checkInitSystem() {
  local version
  if version="$(systemctl --version 2>&1)"; then
    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
    else
      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
      toLogWarning "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
    fi
  else
    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
    if ! version="$(init --version 2>&1)"; then
      if ! version="$(chkconfig --version 2>&1)"; then
        version="$(head -n1 /etc/inittab 2>&1)"
      fi
    fi
  fi

  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
}

setLocationOfInitScripts() {
  toLogInfo "Determining location of init scripts..."

  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSTEMD}" ] || [ "${ARCH_ARCH}" = "AIX" ]; then
    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
  else
    if [ -d "/etc/init.d" ]; then
      readonly INIT_DIR="/etc/init.d"
    elif [ -d "/sbin/init.d" ]; then
      readonly INIT_DIR="/sbin/init.d"
    elif [ -d "/etc/rc.d" ]; then
      readonly INIT_DIR="/etc/rc.d"
    else
      return 1
    fi
  fi

  toLogInfo "Location of init scripts ${INIT_DIR}"
  return 0
}

detectArchitecture() {
  local detected_arch=
  if isAvailable arch; then
    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
  fi

  if [ -z "${detected_arch}" ]; then
    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
  fi

  printf '%s' "${detected_arch}"
}

################################################################################
#	Misc functions
################################################################################

getAgentCtlBinPath() {
  printf "%s" "${AGENT_TOOLS_DIR}/lib64/${AGENT_CTL_BIN}"
}

getOsConfigBinPath() {
  printf "%s" "${AGENT_INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
}

commandErrorWrapper() {
  set +e
  local command="${*}"
  local errorFile="/tmp/smartagent_commanderror_$$"

  ${command} 2>"${errorFile}"
  local returnCode=$?
  if [ ${returnCode} -ne 0 ]; then
    toLogWarning "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
  fi

  ${SAFE_RM_EXEC} -f "${errorFile}"
  set -e
  return ${returnCode}
}

checkRootAccess() {
  toConsoleInfo "Checking root privileges..."

  if [ "$(id -u)" != "0" ]; then
    toConsoleError "NOT OK"
    return 1
  fi

  toConsoleInfo "OK"
  return 0
}

removeIfExists() {
  local pathToRemove="${1}"
  toLogInfo "${SAFE_RM_EXEC} -rf ${pathToRemove}"
  if [ ! -e "${pathToRemove}" ]; then
    toLogInfo "${pathToRemove} does not exist, skipping removal"
    return
  fi

  local output
  if ! output="$(${SAFE_RM_EXEC} -rf "${pathToRemove}" 2>&1)"; then
    toLogWarning "Failed to remove ${pathToRemove}: ${output}"
  fi
}

################################################################################
#	SELinux related functions
################################################################################

executeUsingOsConfigBin() {
  local command="${1}"
  local unit="${2}"
  if [ "${unit}" ]; then
    command="${command}-${unit}"
    unit=""
  fi

  local output=
  output="$("$(getOsConfigBinPath)" "${command}" 2>&1)"
  local exitCode=$?
  toLogAdaptive ${exitCode} "Executed $(getOsConfigBinPath) ${command} ${unit}, exitCode = ${exitCode}, output: ${output}"
  return ${exitCode}
}

executeSystemctlCommand() {
  local command="${1}"
  local unit="${2}"

  if [ "$(id -u)" != 0 ]; then
    executeUsingOsConfigBin "${command}" "${unit}"
    return $?
  fi

  local output=
  #== shellcheck disable=SC2086
  output="$(systemctl "${command}" ${unit} 2>&1)"
  local exitCode=$?

  if [ ${exitCode} -eq 0 ]; then
    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
  else
    toLogError "Failed to execute: systemctl ${command} ${unit}"
    toLogError "Command output: ${output}"
    if [ -n "${unit}" ]; then
      local reachBackNumSeconds=360
      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
    fi
  fi

  return ${exitCode}
} 2>>"${LOG_FILE}"

executeInitScriptCommand() {
  local command=
  local parameters="$*"
  local output=
  local exitCode=

  if isAvailable service; then
    command="service"
    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
  else
    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
  fi
  #shellcheck disable=SC2086
  output="$("${command}" ${parameters} 2>&1)"
  exitCode=$?
  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
  return ${exitCode}
}

signalHandler() {
	end_exit_code=$?
	if [ ${end_exit_code} -eq ${EXIT_CODE_SIGNAL_RECEIVED} ]; then
		return
	fi
	local signal="${1}"
	local callback="${2}"
	if [ ${end_exit_code} -ne 0 ]; then
		toLogWarning "Process Received [Signal: ${signal}] [Code : ${end_exit_code}]"
	fi
	${callback} ${end_exit_code}

	if [ ${end_exit_code} -eq ${EXIT_CODE_OK} ]; then
		exit ${EXIT_CODE_OK}
	fi

	exit ${EXIT_CODE_SIGNAL_RECEIVED}
}

configureSignalHandling() {
  local callback="${1}"
  for signal in HUP INT QUIT ABRT ALRM TERM EXIT; do
    # shellcheck disable=SC2064
    trap "signalHandler '${signal}' '${callback}'" ${signal}
  done

  trap "" PIPE
}

removeSecretsFromString() {
  printf "%s" "$*" | sed 's#\(Api-Token=\)[[:alnum:]_-]\{21\}#\1***#' |
    sed 's#\(TENANT_TOKEN=\)[[:alnum:]]\{16\}#\1***#' |
    sed 's#\(latest/\)[[:alnum:]]\{16\}#\1***#' |
    sed 's#\(PROXY=\)[^[:space:]]*#\1***#'
}

#== waitTime must be divisible by 10
sendSignalToProcessAndWaitForStop() {
  local pidCheckingFunction="${1}"
  local signal="${2}"
  local action="${3}"
  local waitTime="${4}"
  local pidToStop="${5}"

  if ! ${pidCheckingFunction} "${pidToStop}"; then
    toLogInfo "Process with pid ${pidToStop} doesn't exist"
    return
  fi

  toConsoleInfo "Waiting ${waitTime} seconds for process with pid ${pidToStop} to ${action}."
  while [ "${waitTime}" -gt 0 ]; do
    if [ "$((waitTime % 10))" -eq 0 ]; then
      toLogInfo "Sending signal: ${signal} to ${pidToStop}"
      kill -s "${signal}" "${pidToStop}" 2>>"${LOG_FILE}"
    fi

    if ! ${pidCheckingFunction} "${pidToStop}"; then
      return 0
    fi
    sleep 1
    waitTime=$((waitTime - 1))
  done
  return 1
}

testWriteAccessToDir() {
  local errorFile="/tmp/smartagent_commandError_$$"
  local dir="${1}"
  local tmpfilename
  if [ "${ARCH_ARCH}" = "AIX" ]; then
    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
    touch "${tmpfilename}" 2>"${errorFile}"
  else
    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
  fi

  #shellcheck disable=SC2181
  if [ $? -ne 0 ]; then
    toLogInfo "$(cat "${errorFile}")"
    ${SAFE_RM_EXEC} -f "${errorFile}"
    return 1
  fi

  ${SAFE_RM_EXEC} -f "${tmpfilename}" "${errorFile}"
  return 0
}

setPATH() {
  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
  if [ "${PATH}" ]; then
    PATH=${prependToPATH}:${PATH}
  else
    PATH=${prependToPATH}
  fi
}

systemLibDirSanityCheck() {
  local dir="${1}"
  if [ ! -d "${dir}" ]; then
    toLogWarning "Directory: ${dir} does not exist"
    printf ""
    return
  fi

  if ! testWriteAccessToDir "${dir}"; then
    toLogWarning "Detected that ${dir} is not writable"
    dir="${PA_FALLBACK_INSTALLATION_DIR}${dir}"
    createDirIfNotExistAndSetRights "${dir}" 755
  fi

  printf "%s" "${dir}"
}

isNonRootModeEnabled() {
  printf '%s' "${PARAM_NON_ROOT_MODE}" | grep -qE "(true|no_ambient)"
}

runAutostartAddingTool() {
  local prefix="${1}"
  local file="${2}"
  local suffix="${3}"
  local output

  if isNonRootModeEnabled && printf '%s' "${prefix}" | grep -q "chkconfig"; then
    local action="chkconfig-add"
    if printf '%s' "${prefix}" | grep -q -- "--del"; then
      action="chkconfig-del"
    fi
    toLogInfo "Using ${AGENT_OS_CONFIG_BIN} ${action}-${file} to modify autostart"
    output="$("$(getOsConfigBinPath)" "${action}-${file}" 2>&1)"
  else
    local command="${prefix}${file} ${suffix}"
    toLogInfo "Executing ${command}"
    output="$(${command} 2>&1)"
  fi
  local status=$?

  if [ "${output}" ]; then
    toLogAdaptive ${status} "${output}"
  fi

  return ${status}
}

readLinkFromLs() {
  local path="${1}"
  local result="${path}"
  local lsOutput="$(ls -dl "${path}" 2>/dev/null)"
  if printf '%s' "${lsOutput}" | grep -q " -> "; then
    local parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
    if [ "${parsedLinkTarget}" ]; then
      result="${parsedLinkTarget}"
    else
      toLogWarning "Failed to parse ls output '${lsOutput}'"
    fi
  fi
  printf '%s' "${result}"
}

readLink() {
  local args=-e
  local path="${1}"
  if [ "${2}" ]; then
    args="${1}"
    path="${2}"
  fi

  (
    if [ "${ARCH_ARCH}" = "AIX" ]; then
      PATH="${PATH}:/opt/freeware/bin"
    fi

    if isAvailable readlink; then
      #shellcheck disable=SC2086
      readlink ${args} "${path}"
    else
      toLogInfo "readlink command not found, falling back to parsing ls output"
      readLinkFromLs "${path}"
    fi
  )
}

isAvailable() {
  command -v "${1}" >/dev/null 2>&1
}

getValueFromConfigFile() {
  local key="${1}"
  local separator="${2}"
  local configFile="${3}"
  local defaultValue="${4}"

  local value="$(sed -n "s|${key}${separator}||p" "${configFile}" 2>/dev/null)"

  if [ "${value}" ]; then
    printf '%s' "${value}"
  else
    printf '%s' "${defaultValue}"
  fi
}

isAnotherInstallerRunning() {
  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
    return 1
  fi

  local pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
  local nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
    toConsoleWarning "Installer lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
    toConsoleWarning "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
    return 1
  fi

  #shellcheck disable=SC2009
  local foundProcesses="$(ps -e -o "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIRECTORY}"; then
      errorMessage="${errorMessage} (AutoUpdate is in progress)"
    fi

    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
    return 0
  fi

  toConsoleInfo "Lock file exists but corresponding installer process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
  return 0
}

createInstallerLockFile() {
  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
}

removeInstallerLockFile() {
  toLogInfo "Removing installer lock file."
  ${SAFE_RM_EXEC} -f "${INSTALLER_LOCK_FILE}"
}

logBasicStartupInformation() {
  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
  toLogInfo "Shell options: $-"
  toLogInfo "Working dir: $(pwd)"
  toLogInfo "PID: $$"
  toLogInfo "Parent process: $(
    printf '\n'
    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
  )"
  toLogInfo "User id: $(id -u)"
}

mapPidsToName() {
  local pids="${1}"
  local output
  for pid in ${pids}; do
    local name="$(grep 'Name:' "/proc/${pid}/status" 2>/dev/null | awk '{print $2}')"
    output="${output}, ${pid} (${name})"
  done

  printf '%s' "${output}" | cut -c 3-
}

readonly ARCH_ARCH="X86"
readonly ARCH_VERSIONED_LIB_DIR_PREFIX="linux-x86"

arch_checkArchitectureCompatibility() {
  local arch="$(detectArchitecture)"
  if [ "${arch}" = "X86_64" ] || [ "${arch}" = "IA64" ]; then
    arch="X86_64"
  else
    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
  fi

  printf '%s' "${arch}"
  [ "${arch}" = "X86_64" ]
}

arch_local_getLibraryPathFromLdd() {
  local binary="${1}"
  ldd "${binary}" 2>/dev/null | grep libc.so | awk '{ print $3 }'
}

arch_local_detectProcessAgentInstallerDirectories() {
  local useLddOutput="false"

  local systemLib32Prefix
  systemLib32Prefix="$(arch_local_getSystemLibraryPath 32)"
  local exitCode=$?

  if [ ! "${systemLib32Prefix}" ]; then
    if [ "${exitCode}" -eq 0 ]; then
      toLogWarning "This is a 64-bit platform with 32-bit libraries installed, but ${AGENT_INSTALL_ACTION_BIN} failed to determine their location"
      useLddOutput="true"
    else
      toLogInfo "This is a 64-bit platform and 32-bit libraries were not detected"
    fi
  else
    systemLib32Prefix="$(systemLibDirSanityCheck "/${systemLib32Prefix}")"
    if [ ! "${systemLib32Prefix}" ]; then
      useLddOutput="true"
    fi
  fi

  local systemLib64Prefix="$(arch_local_getSystemLibraryPath 64)"
  if [ ! "${systemLib64Prefix}" ]; then
    toLogWarning "This is a 64-bit platform, but ${AGENT_INSTALL_ACTION_BIN} failed to determine location of 64-bit libraries"
    useLddOutput="true"
  else
    systemLib64Prefix="$(systemLibDirSanityCheck "/${systemLib64Prefix}")"
    if [ ! "${systemLib64Prefix}" ]; then
      useLddOutput="true"
    fi
  fi

  if [ "${useLddOutput}" = "true" ]; then
    arch_local_detectProcessAgentDirectoriesBasedOnLdd
  else
    readonly SYSTEM_LIB32="${systemLib32Prefix}"
    readonly SYSTEM_LIB64="${systemLib64Prefix}"
  fi
} 2>>"${LOG_FILE}"

arch_detectProcessAgentInstallerDirectories() {
  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x = "true"x ]; then
    readonly SYSTEM_LIB32="${PA_FALLBACK_INSTALLATION_DIR}/lib32"
    readonly SYSTEM_LIB64="${PA_FALLBACK_INSTALLATION_DIR}/lib64"
    createDirIfNotExistAndSetRights "${SYSTEM_LIB32}" 755
    createDirIfNotExistAndSetRights "${SYSTEM_LIB64}" 755
    return 0
  fi

  arch_local_detectProcessAgentInstallerDirectories
}

arch_getLibMacro() {
  local libMacro=""
  if [ "${SYSTEM_LIB32}" ]; then
    #shellcheck disable=SC2016
    libMacro='/$LIB'
  fi
  printf "%s" "${libMacro}"
}

arch_checkGlibc() {
  local glibcVersion="$(ldd --version | awk 'NR==1{ print $NF }')"

  toLogInfo "Detected glibc version: ${glibcVersion}"

  if [ "$(format_version "${glibcVersion}")" -gt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
    return
  elif [ "$(format_version "${glibcVersion}")" -lt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
    toConsoleError "We can't continue setup. The glibc version: ${glibcVersion} detected on your system isn't supported."
    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}."
    toConsoleError "Stopping installer process..."
    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
  fi

  if ! isAvailable rpm; then
    toLogInfo "RPM not detected - skipping glibc patch version check."
    return
  fi

  local glibcFullVersion="$(arch_runCommandWithTimeout 60 "rpm" "-q" "glibc" | tail -n 1)"
  if [ ! "${glibcFullVersion}" ]; then
    toConsoleError "Could not determine exact glibc version using RPM."
    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
  fi

  # fix for RedHat 5.x with unsufficient glibc 2.5 patch version
  local glibcPatchVersion="$(format_patch "${glibcFullVersion}")"
  if [ "$(format_version "${glibcVersion}")" -eq "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ] && [ "${glibcPatchVersion}" -lt "${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}" ]; then
    toConsoleError "glibc patch version: ${glibcPatchVersion} detected on your system is not supported, setup won't continue."
    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}-${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}."
    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
  fi
}

# 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
arch_runCommandWithTimeout() {
  local commandTimeout="${1}"
  shift
  local resultFile="/tmp/smartagent_commandResult_$$"
  local errorFile="/tmp/smartagent_commandError_$$"
  local loopErrorsFile="/tmp/smartagent_loopErrors_$$"

  toLogInfo "Executing $* with timeout ${commandTimeout} seconds"
  (
    "$@" >${resultFile} 2>${errorFile} &
    child=$!
    while [ "${commandTimeout}" -gt 0 ]; do
      toLogInfo "Time left: ${commandTimeout} s,  pid: ${child}"
      sleep 1
      kill -0 "${child}" 2>/dev/null || exit 0
      commandTimeout=$((commandTimeout - 1))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    toLogInfo "Killing ${child} with SIGTERM"
    kill -s 15 "${child}" && kill -0 "${child}" || exit 0
    sleep 1
    toLogWarning "Killing ${child} with SIGKILL"
    kill -s 9 "${child}"
  ) 2>"${loopErrorsFile}"

  local errorOutput="$(cat "${errorFile}")"
  local commandOutput="$(cat "${resultFile}")"
  local loopErrors="$(cat "${loopErrorsFile}")"
  ${SAFE_RM_EXEC} -f "${errorFile}" "${resultFile}" "${loopErrorsFile}"

  if [ -n "${errorOutput}" ]; then
    toLogInfo "Failed to execute command, error output: ${errorOutput}"
    if [ "${loopErrors}" ]; then
      toLogInfo "Loop errors: ${loopErrors}"
    fi
    printf ""
    return 1
  else
    toLogInfo "Command executed successfully, output: ${commandOutput}"
    if [ "${loopErrors}" ]; then
      toLogInfo "Loop errors: ${loopErrors}"
    fi
    printf '%s' "${commandOutput}"
    return 0
  fi
}

arch_getAccessRights() {
  stat --format='%A' "${1}"
}

arch_preloadTest() {
  if [ "${SYSTEM_LIB32}" ]; then
    runSanityCheckCommand "performLdPreloadSanityCheck" "32"
    local returnCode=$?
    if [ ${returnCode} -ne 0 ]; then
      return ${returnCode}
    fi
  fi

  if [ "${SYSTEM_LIB64}" ]; then
    runSanityCheckCommand "performLdPreloadSanityCheck" "64"
  fi
}

arch_checkEnvironmentConfiguration() {
  if ! stat --format='%t,%T' /dev/null | grep -q "1,3"; then
    toLogInfo "$(stat /dev/null)"
    toConsoleError "Installer detected corruption of '/dev/null': Not a character device"
    return "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
  fi

  return 0
}

arch_moveReplaceTarget() {
  local source="${1}"
  local target="${2}"
  mv -fT "${source}" "${target}"
}

initLog() {
  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
    createLogDirsIfMissing
  fi

  toLogInfo "Uninstaller started."
  toLogInfo "Distribution: $(detectLinuxDistribution)"
  logBasicStartupInformation "${@}"
}

finishUninstaller() {
  removeInstallerLockFile
  if [ -z "$(ls -A ${INSTALL_DIR})" ];then
    ${SAFE_RM_EXEC} -df ${INSTALL_DIR}
    ${SAFE_RM_EXEC} -f ${CURRENT_VERSION_LINK}
  fi
  if [ $1 -eq 0 ]; then
    toConsoleInfo "Uninstaller finished successfully"
  else
    toConsoleInfo "Uninstaller finished failed"
  fi
  exit "${EXIT_CODE_OK}"
}

setLogFilePath() {
    #== - 反安装日志: uninstallation-{timestamp}.log 如: uninstallation-20220601110912.log
    readonly LOG_FILE="${INSTALLER_LOG_DIR}/uninstallation-"$(date -u +"%Y%m%d%H%M%S")".log"
}

removeScriptsFromAutostart() {
  local prefix="${1}"
  local suffix="${2}"

  if ! runAutostartAddingTool "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"; then
    toConsoleError "Failed to remove ${SERVICE_SCRIPT_FILE} from autostart. For details, see ${LOG_FILE}"
  fi
}

removeUnitFromSystemd() {
  local unit=${1}
  if [ ! -e "${SYSTEMD_UNIT_FILES_DIR}/${unit}" ]; then
    toLogInfo "${unit} does not exist and will not be removed from ${INIT_SYSTEM_SYSTEMD} autostart"
    return
  fi

  executeSystemctlCommand disable "${unit}"
  ${SAFE_RM_EXEC} -f "${SYSTEMD_UNIT_FILES_DIR}/${unit}"
}

removeSystemvAutostart() {
  toConsoleInfo "Removing ${BRAND_AGENT_PRODUCT_NAME} from autostart"

  #Order of checking is important
  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
    removeScriptsFromAutostart "/usr/bin/update-rc.d -f " "remove"
  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
    removeScriptsFromAutostart "/usr/sbin/update-rc.d -f " "remove"
  elif [ -x /sbin/chkconfig ]; then #RedHat
    removeScriptsFromAutostart "/sbin/chkconfig --del "
  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
    removeScriptsFromAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
  elif [ "${ARCH_ARCH}" = "AIX" ]; then
    #== 不存在
    arch_removeAutostart
  else
    toConsoleError "Couldn't remove ${BRAND_AGENT_PRODUCT_NAME} from autostart."
  fi
}

removeSystemdAutostart() {
  removeUnitFromSystemd "${SYSTEMD_UNIT_FILE_AGENT}"
  executeSystemctlCommand daemon-reload ""
}

stopAgentService() {
  if [ -s "${AGENT_PID_FILE}" ]; then
    toLogInfo "Stop ${AGENT_PROC}..."
    commandErrorWrapper ${AGENT_BIN_DIR}/agentctl stop
  fi
}

removeSystemvInitScripts() {
  if [ "${ARCH_ARCH}" = "AIX" ]; then
    return
  fi

  toConsoleInfo "Removing ${SERVICE_SCRIPT_FILE} from ${INIT_DIR}"
  ${SAFE_RM_EXEC} -f "${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
}

removeAutostartScripts() {
  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSV}" ]; then
    removeSystemvAutostart
    removeSystemvInitScripts
  else
    removeSystemdAutostart
  fi
}

createUninstallInfoFile() {
  local reason
  if [ "${IS_UPGRADING}"x = "true"x ]; then
    reason="upgrade"
  else
    reason="uninstall"
  fi

  toLogInfo "Creating uninstall.info file: ${reason}"
}

initializeUninstaller() {
  local installerPid=
  local skipRootPrivilegesCheck=

  if [ $# -eq 1 ]; then
    installerPid="${1}"
  fi
  if [ $# -eq 2 ]; then
     installerPid="${1}"
     skipRootPrivilegesCheck="${2}"
  fi

  setPATH

  #setLogFilePath "${installerPid}"
  setLogFilePath

  if [ "${installerPid}" ] && [ "${installerPid}"x != "online"x ]; then
    IS_UPGRADING="true"
  else
    IS_UPGRADING="false"
  fi
  toConsoleInfo "IS_UPGRADING :${IS_UPGRADING}"

  if [ "${skipRootPrivilegesCheck}"x = "true"x ] && [ "${IS_UPGRADING}"x = "true"x ]; then
    toConsoleInfo "Skipping root privileges check"
  else
    if ! checkRootAccess; then
      toConsoleError "You must run uninstaller with root privileges"
      exit "${EXIT_CODE_GENERIC_ERROR}"
    fi
  fi

  initLog "${@}"

  toLogInfo "Launched during upgrade: ${IS_UPGRADING}"
  createUninstallInfoFile

  configureSignalHandling "finishUninstaller"

  if [ "${IS_UPGRADING}"x = "false"x ] && [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
    if isAnotherInstallerRunning; then
      exit "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}"
    fi
    createInstallerLockFile
  fi

  if [ ! -f ${PERL_FILE} ]; then
    toConsoleInfo "/usr/bin/perl file not found so ${SAFE_RM_EXEC} is used."
  fi

  toConsoleInfo "Using ${SAFE_RM_EXEC} to uninstall"
}

getVolumeStorageAgentInstallDir() {
  readLink "$(dirname "${0}")"
}

cleanUpSELinux() {
  if [ ! "${ARCH_HAS_SELINUX}" ] || ! isAvailable semodule; then
    return
  fi

  toConsoleInfo "Removing ${BRAND_FORMAL_NAME} SELinux policies, this may take a while..."
}

cleanUpFilesDuringUninstall() {
	toLogInfo "This is a full uninstaller, cleaning up rest of the files"

  local fileNames="${AGENT_CONF_DIR} ${AGENT_BIN_DIR} ${LOG_DIR} ${AGENT_SCRIPTS_DIR} ${AGENT_LIBS_DIR} ${AGENT_AGENTS_DIR} ${AGENT_RUNTIME_DIR}"
  for fileName in ${fileNames}; do
	  removeIfExists "${fileName}"
  done

  fileNames="uninstall.sh installer.version heartbeat"
  for fileName in ${fileNames}; do
	  removeIfExists "${INSTALL_DIR}/${fileName}"
  done

}

handleRegularUninstaller() {

  checkInitSystem
  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
  if ! setLocationOfInitScripts; then
    toLogWarning "Cannot determine location of init scripts."
  fi

  stopAgentService

  cleanUpFilesDuringUninstall

}

main() {
  initializeUninstaller "$@"
  handleRegularUninstaller "$@"
#  finishUninstaller
}

################################################################################
#
# Script start
#
################################################################################
main "$@"
