#!/bin/bash
#== AIX（Advanced Interactive eXecutive）是IBM基于AT&T Unix System V开发的一套类UNIX操作系统，运行在IBM专有的Power系列芯片设计的小型机硬件系统之上
#== 以【#== 】开头的注释会在打包时删除
#DEBUG== 以【#DEBUG== 】开头的注释会在调试打包时保留，正式打包时删除


#==脚本执行中遇到不存在的变量就报错
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="cloudwise"

readonly AIX_DF_SPECIFIC_FLAG=
readonly CONF_LD_PRELOAD=

PARAM_DISABLE_SYSTEM_LOGS_ACCESS=

#== 安装包版本项
readonly AGENT_BUILD_DATE=25.09.2020
readonly AGENT_INSTALLER_VERSION=1.2.0
readonly AGENT_BUILD_DATE_INFO=""
readonly AGENT_BUILD_TAG=""
readonly AGENT_BUILD_UPLOADER=""
#== 安装包版本项

#== **********************************************************
#==  配置重要文件名称
#== **********************************************************
#== CW-ServerAgent
readonly AGENT_PROC="euspace"
#== **********************************************************
#==  配置目录
#== **********************************************************
readonly INSTALL_BASE=/opt
#== /opt/cloudwise/oneagent/agents 目录
readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
#== cloudwise/oneagent/agents/chaosd
readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
#== cloudwise/oneagent/agents/chaosd/version
readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
#== /opt/cloudwise/oneagent/agents/chaosd/version
readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
#== /opt/cloudwise/oneagent/agents/chaosd
readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}

#== /opt/cloudwise/cwserveragent/conf
readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/scripts
readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/runtime
readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/bin
readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/lib
#== readonly AGENT_LIB_DIR="${INSTALL_DIR}/lib"


readonly AGENTS_BASE_DIR="${INSTALL_DIR}/agents"

readonly META_BASE_DIR="${INSTALL_DIR}/meta"

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

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


#== server-agent.ini
#== hostId文件
readonly INSTALLER_CONF_FILE_NAME="installation.conf"
#== 部署在容器内的状态信息
readonly INSTALLER_CONF_FILE="${AGENT_CONF_DIR}/${INSTALLER_CONF_FILE_NAME}"

#== 【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/"

#== 【0】=【退出码】执行成功
readonly EXIT_CODE_OK=0
readonly EXIT_CODE_GENERIC_ERROR=1
readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
#== 【0】=无效参数
readonly EXIT_CODE_INVALID_PARAM=8
readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9

#== 【退出码】接收信号
readonly EXIT_CODE_SIGNAL_RECEIVED=12
readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
readonly EXIT_CODE_CORRUPTED_PACKAGE=16
#== 【退出码】管理系统环境变量配置
readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
readonly EXIT_CODE_OS_NOT_SUPPORTED=19

readonly EXTERNAL_TAR_SIZE=233820160
readonly ARTIFACTS_SIZE=953271934
#**********************************************************
# Logs
#**********************************************************
toLogFile() {
  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() {
  toLogFile "[INFO]" "$@"
}

toLogWarn() {
  toLogFile "[WARN]" "$@"
}

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

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

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

toConsoleWarn() {
  toConsoleOnly "Warn:" "$@"
  toLogWarn "$@"
} >&2

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

#== 【0】【3】【4】=创建不存在的目录，并设置权限
createDirIfNotExistAndSetRights() {
  local dir="${1}"
  local rights="${2}"

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

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

  return 0
}

createCurrentVersionSymlink() {
	toLogInfo "Creating symlink to current version..."

	local currentVersionLink="${AGENT_BASE_DIR}/current"
	local tempLink="${currentVersionLink}.tmp"

	if ! commandErrorWrapper ln -s "${AGENT_INSTALLER_VERSION}" "${tempLink}"; then
		toLogError "Failed to create current version link"
		return
	fi

	if ! commandErrorWrapper arch_moveReplaceTarget "${tempLink}" "${currentVersionLink}"; then
		toLogError "Failed to set up current version link"
		commandErrorWrapper rm -f "${tempLink}"
		return
	fi

	toLogInfo "Current version link created: ${currentVersionLink} -> ${AGENT_INSTALLER_VERSION}"
}

#== 【0】=创建日志目录及日志文件installation_$$.log
createLogDirsIfMissing() {
  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
  #== agents根目录
  createDirIfNotExistAndSetRights "${AGENT_BASE_DIR}" 1775
  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
#**********************************************************

#== 【5】=获取系统发型版本
getOsReleasePath() {
	local osReleasePath="/etc/os-release"
	if [ ! -f "${osReleasePath}" ]; then
		osReleasePath="/usr/lib/os-release"
	fi

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

parseOsReleaseFile() {
	local osReleasePath="$(getOsReleasePath)"

	#shellcheck disable=SC1090
	. "${osReleasePath}"
	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)"
	elif printf '%s' "${distrib}" | grep -iq "fedora" && [ "${VARIANT_ID-}" = "coreos" ]; then
		distrib="${distrib} CoreOS"
	fi

	printf '%s %s' "${distrib}" "${version}"
}

#== 【5】=获取系统发型版本
detectLinuxDistribution() {
	if [ -f /etc/oracle-release ]; then
		cat /etc/oracle-release
	elif [ -f /etc/fedora-release ]; then
		if [ -f "$(getOsReleasePath)" ]; then
			(
				parseOsReleaseFile
			)
		else
			cat /etc/fedora-release
		fi
	elif [ -f /etc/redhat-release ]; then
		cat /etc/redhat-release
	elif [ -f "$(getOsReleasePath)" ]; then
		(
			parseOsReleaseFile
		)
	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
}

#== 【3】=检查系统init (INIT_SYSTEM、INIT_SYSTEM_VERSION)
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}
      toLogWarn "${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)"
}

#== 【3】=设置系统初始化脚本目录
setLocationOfScripts() {
  toLogInfo "Determining location of scripts..."

  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSTEMD}"x ] || [ "${ARCH_ARCH}"x = "AIX"x ]; then
    #== /opt/cloudwise/cwserveragent/scripts
    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 scripts ${INIT_DIR}"
  return 0
}

#== 【0】=检查系统结构（X86_64\IA64\X86）
detectArchitecture() {
  local detected_arch=
  if isAvailable arch; then
    #== arch指令主要用于显示当前主机的硬件结构类型，查询结果与uname一致，我们可以看到它输出的结果有：i386、i486、mips、alpha等
    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
#**********************************************************

#== 【1】【4】【5】【6】=获取 agent (64\32)位数lib目录(""或lib64)
getBinariesFolderByBitness() {
  local bitness="${1}"
  if [ "${bitness}" -eq 32 ]; then
    bitness=""
  fi
  printf 'lib%s' "${bitness}"
}

#== 【1】【2】【5】【6】=获取 agent tools/lib64/smartagentctl 路径
#== getAgentCtlBinPath

#== 【1】【4】【5】【6】=获取 agent 64位数lib路径（lib64/installaction）
#== getAgentInstallActionPath

#== 【5】【6】=获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
getOsConfigBinPath() {
  printf "%s" "${INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
}

#== 【5】=设置agent进程可用
setProcessAgentEnabled() {
  local enabled="${1}"
  toLogInfo "Setting process agent enabled: ${enabled}..."
  local changeStatus=
  #== 调用 agent 64位数lib路径（lib64/installaction）执行指令 --set-process-agent-enabled
  changeStatus=$("$(getAgentInstallActionPath)" --set-process-agent-enabled "${enabled}" 2>&1)
  toLogAdaptive $? "Process agent enable(${enabled}) status: ${changeStatus}"
}

#== 【1】【4】【5】【6】=获取命令执行错误信息，并写日志
commandErrorWrapper() {
  local command="${*}"
  local errorFile="/tmp/smartagent_commanderror_$$"

  ${command} 2>"${errorFile}"
  local returnCode=$?

  if [ ${returnCode} -ne 0 ]; then
    toLogWarn "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
  fi

  rm -f "${errorFile}"

  return ${returnCode}
}

#== 【3】=是否独立的namespace
isNamespaceIsolated() {
  local pid="${1}"
  local namespace="${2}"
  local initNamespaceId
  local processNamespaceId
  initNamespaceId="$(readlink "/proc/1/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
  processNamespaceId="$(readlink "/proc/${pid}/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"

  if [ ! "${initNamespaceId}" ] || [ ! "${processNamespaceId}" ]; then
    toLogInfo "Link to /proc/*/ns/${namespace} does not exist"
    printf 'error'
    return
  fi

  if [ "${initNamespaceId}"x != "${processNamespaceId}"x ]; then
    printf 'true'
  else
    printf 'false'
  fi
}

#== 【0】=检查是否root
checkRootAccess() {
  toConsoleInfo "Checking root privileges..."

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

  toConsoleInfo "OK"
  return 0
}

#== 【3】=删除存在的路径
removeIfExists() {
  local pathToRemove="${1}"
  if [ ! -e "${pathToRemove}" ]; then
    toLogInfo "${pathToRemove} does not exist, skipping removal"
    return
  fi

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

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

#== 【5】【6】=执行 systemctl 命令
executeSystemctlCommand() {
  local command="${1}"
  local unit="${2}"

  if [ "$(id -u)" != 0 ]; then
    #== 执行使用 os config bin
    executeUsingOsConfigBin "${command}" "${unit}"
    return $?
  fi

  local output=
  #== shellcheck disable=SC2086
  #== 执行 systemctl 命令
  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}"

#== 【6】=运行初始化命令(通过 service 方式 或 直接运行可执行命令）
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
  output="$("${command}" "${parameters}" 2>&1)"
  exitCode=$?
  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
  return ${exitCode}
}

#== 【0】=信号捕获，并执行回调函数
signalHandler() {
  local signal="${1}"
  local callback="${2}"
  toLogWarn "process received signal: ${signal}"
  ${callback}
  exit ${EXIT_CODE_SIGNAL_RECEIVED}
}

#== 【0】=配置信号捕获和回调
configureSignalHandling() {
  local callback="${1}"
  for signal in HUP INT QUIT ABRT ALRM TERM; do
    # shellcheck disable=SC2064
    trap "signalHandler '${signal}' '${callback}'" ${signal}
  done

  trap "" PIPE
}

#== 【0】删除权限
removeSecretsFromString() {
  printf "%s" "$*" | sed 's#\(LICENSE=\)[[:alnum:]]\{16\}#\1***#'
}

#== 【3】【5】=检查目录写权限
testWriteAccessToDir() {
  local errorFile="/tmp/smartagent_commandError_$$"
  local dir="${1}"
  local tmpfilename
  if [ "${ARCH_ARCH}"x = "AIX"x ]; 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}")"
    rm -f "${errorFile}"
    return 1
  fi

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

#== 【0】=设置PATH
setPATH() {
  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
  if [ "${PATH}" ]; then
    PATH=${prependToPATH}:${PATH}
  else
    PATH=${prependToPATH}
  fi
}

#== 【5】【6】=是否非root权限
isNonRootModeEnabled() {
  #== 调用 agent 64位数lib路径（lib64/installaction）执行指令 --get-drop-root-privileges
  local output
  output="$(getValueFromConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "${LEGACY_AGENT_CONF_FILE}" "${PARAM_NON_ROOT_MODE}")"
  printf '%s' "${output}" | grep -qE "(true|no_ambient)"
}

#== 【5】=自动启动工具
runAutostartAddingTool() {
  local prefix="${1}"
  local file="${2}"
  local suffix="${3}"
  local output

  local command="${prefix} ${file} ${suffix}"
  toLogInfo "Executing ${command}"
  output="$(${command} 2>&1)"
  local status=$?
  if [ "${output}" ]; then
    toLogAdaptive ${status} "${output}"
  fi

  return ${status}
}

#== 【0】【5】=查找软链接真实地址（通过ls命令）
readLinkFromLs() {
  local path="${1}"
  local result="${path}"
  local lsOutput
  local parsedLinkTarget
  lsOutput="$(ls -dl "${path}" 2>/dev/null)"
  if printf '%s' "${lsOutput}" | grep -q " -> "; then
    parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
    if [ "${parsedLinkTarget}" ]; then
      result="${parsedLinkTarget}"
    else
      toLogWarn "Failed to parse ls output '${lsOutput}'"
    fi
  fi
  printf '%s' "${result}"
}

#== 【0】【5】=查找软链接真实地址（通过readlink 或 ls 命令）
readLink() {
  local args=-e
  local path="${1}"
  if [ "${2}" ]; then
    args="${1}"
    path="${2}"
  fi

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

    #== 通过 readlink 命令查找地址
    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
  )
}

#== 【0】【3】【4】【5】【6】=检查命令是否有效/是否存在
isAvailable() {
  command -v "${1}" >/dev/null 2>&1
}

#== 【0】【1】【3】【5】=读取配置文件配置参数信息
getValueFromConfigFile() {
  local key="${1}"
  local configFile="${2}"
  local defaultValue="${3}"
  local value
  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"

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

#== 【1】【5】=删除配置文件中配置
removeValueFromConfigFile() {
  local key="${1}"
  local configFile="${2}"
  local output
  if ! output="$(cp -p "${configFile}" "${configFile}.tmp" 2>&1)"; then
    toLogWarn "Unable to initialize ${configFile}.tmp file using source file, privileges and ownership will not be preserved: ${output}"
  fi

  if sed "/^${key}/d" "${configFile}" >"${configFile}.tmp"; then
    mv -f "${configFile}.tmp" "${configFile}"
  else
    toLogWarn "Failed to remove ${key} from ${configFile}"
    rm -f "${configFile}.tmp"
  fi
}

#== 写配置到配置文件信息
writeParamToConfigFile() {
  local key="${1}"
  local newValue="${2}"
  local configFile="${3}"
  local value
  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
  toConsoleInfo "#DEBUG== writeParamToConfigFile --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${configFile}"
  if [ "${value}" ]; then
    sed -i "s|^${key}=.*|${key}=${newValue}|" "${configFile}" 2>/dev/null
  else
    echo "${key}=${newValue}" >>"${configFile}" 2>/dev/null
  fi
}

writeContentToConfigFile() {
  local newValue="${1}"
  local configFile="${2}"
  toConsoleInfo "#DEBUG== writeContentToConfigFile --> edit ${newValue}, configFile: ${configFile}"

  echo "${newValue}" > "${configFile}" 2>/dev/null
}

#== 修改scripts/smartagent脚本配置信息
editScriptFileParam() {
  local key="${1}"
  local newValue="${2}"
  local file="${3}"
  local value
  value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
  toConsoleInfo "#DEBUG== editScriptFileParam --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
  if [ "${value}" ]; then
    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
  fi
}

#== 【0】=是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
isAnotherInstallationRunning() {
  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
    return 1
  fi
  local pidFromLockFile
  local nameFromLockFile
  pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
  nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
    toConsoleWarn "Installation lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
    toConsoleWarn "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
    return 1
  fi

  #== shellcheck disable=SC2009
  #== 获取正在执行安装的进程信息
  local foundProcesses
  foundProcesses="$(pgrep -f "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_DIR}"; then
      errorMessage="${errorMessage} (AutoUpdate is in progress)"
    fi

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

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

#== 【0】=创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
createInstallationLockFile() {
  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
}

#== 【0】【6】删除安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
removeInstallationLockFile() {
  toLogInfo "Removing installation lock file."
  rm -f "${INSTALLER_LOCK_FILE}"
}

#== 【0】基础启动日志信息
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)"
}

#== 解压文件编码
readonly UNPACK_BINARY=base64
#== 解压文件编码参数
readonly UNPACK_BINARY_ARGS="-di"

readonly ARCH_ARCH="X86"

#== 【0】=检查系统指令集（X86_64\IA64\X86）
arch_checkArchitectureCompatibility() {
  #== 【0】=检查系统结构（X86_64）
  local arch
  arch="$(detectArchitecture)"
  if [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "IA64"x ]; 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}"x = "X86_64"x ] || [ "${arch}"x = "AARCH64"x ]
}

#== 【5】=获取 lib 目录
arch_getLibMacro() {
  local libMacro=""
  if [ "${SYSTEM_LIB32}" ]; then
    #== shellcheck disable=SC2016
    libMacro="/${LIB}"
  fi
  printf "%s" "${libMacro}"
}
#== 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
#== 【3】【5】=执行命令超时配置

#== 【0】【3】=获取文件权限信息
arch_getAccessRights() {
  stat --format='%A' "${1}"
}

#== 【4】=替换目录
arch_moveReplaceTarget() {
  local source="${1}"
  local target="${2}"
  mv -fT "${source}" "${target}"
}


#== xz 压缩包文件名 Cloudwise-SmartAgent
readonly INTERNAL_TAR_FILE_NAME=${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}.tar.xz

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



#== 【临时目录】安装临时目录
readonly TMP_DIR=${INSTALL_DIR}_install_$$
#== 【临时目录】解压缓存目录
readonly UNPACK_CACHE=${BASE_INSTALL_DIR}/unpack_cache
#== 【临时目录】从sh文件读取压缩文件 tarfile_$$.base64
readonly EXTERNAL_TAR_FILE=${BASE_INSTALL_DIR}/tarfile_$$.base64
#== 【临时目录】xz 压缩包文件
readonly INTERNAL_TAR_FILE=${INSTALL_DIR}/${INTERNAL_TAR_FILE_NAME}

#== 【0】=
readonly INSTALLER_FILE=${0}
#== 旧配置文件
readonly LEGACY_AGENT_CONF_FILE="${AGENT_CONF_DIR}/${BRAND_PRODUCT_NAME_LOWER}.conf"

#== 【0】=
readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50

#== 【0】=
readonly HELP_URL=""

readonly CONF_FIELD_NM_DATA_SERVER="DataServer"
readonly CONF_FIELD_NM_CONFIG_SERVER="ConfigServer"
readonly CONF_FIELD_NM_LICENSE="License"
readonly CONF_FIELD_NM_JSON_CONF="JSON_CONF"
readonly CONF_FIELD_NM_USER="User"
readonly CONF_FIELD_NM_GROUP="Group"
readonly CONF_FIELD_NM_DATA_STORAGE="DataStorage"
readonly CONF_FIELD_NM_NON_ROOT_MODE="NonRootMode"


#== 配置默认用户权限
readonly BASE_OMNI_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}
readonly BASE_OMNI_INSTALL_CONF_DIR=${BASE_OMNI_INSTALL_DIR}/conf/installation.conf
BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${BASE_OMNI_INSTALL_CONF_DIR}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE}")"

#== Those are read from params section appended to installer by the server
#== 【0】=【参数】数据服务地址
PARAM_DATA_SERVER=
#== 【0】=【参数】配置服务地址
PARAM_CONFIG_SERVER=
#== 【0】=【参数】license
PARAM_LICENSE=

PARAM_JSON_CONF=

#== 【0】=【参数】安装路径
PARAM_INSTALL_DIR=${INSTALL_DIR}
#== 【0】=【参数】用户
PARAM_USER=
#== 【0】=【参数】用户组
PARAM_GROUP=
#== 【0】=【参数】非root模式
PARAM_NON_ROOT_MODE=true

PARAM_USER_LOGIN=false

#== 【0】=【参数】不允许root回退
PARAM_DISABLE_ROOT_FALLBACK=false
#== 【0】=【参数】数据存储目录
PARAM_DATA_STORAGE=
#== 【0】=【参数】通过容器部署
#== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER=false
#== 【0】=【参数】跳过SELinux策略安装
#== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER=false
#== 【0】=【参数】是否使用解压缓存
PARAM_INTERNAL_USE_UNPACK_CACHE=false
#== 【0】=【参数】是否不使用 dump
#== PARAM_INTERNAL_DISABLE_DUMPPROC=
#== 【0】=【参数】是否跳过非root检查
PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=false
#== 【0】【1】=【参数】额外的配置
#== PARAM_INTERNAL_PASS_THROUGH_SETTERS=
#== 【0】=检查是否降级安装
SKIP_DOWNGRADE_CHECK=false

SKIP_PRIVILEGES_CHECK=false

#== 自定义字符串参数
PARAM_TEST=
#== 自定义字BOOL参数
PARAM_BOOL=

#== 【0】=常规日志
initializeLog() {
  toConsoleInfo "Installation started, version ${AGENT_INSTALLER_VERSION}, build date: ${AGENT_BUILD_DATE}, PID $$."
  toLogInfo "Started from: ${INSTALLER_FILE}"

  if [ -f /proc/version ]; then
    toLogInfo "System version: $(cat /proc/version)"
  else
    toLogInfo "System version: $(uname -a)"
  fi

  toLogInfo "Path: ${PATH}"
  toLogInfo "INSTALL_DIR: ${INSTALL_DIR}"
  toLogInfo "Resolved installation path: $(readLink -e "${INSTALL_DIR}" 2>/dev/null)"
  logBasicStartupInformation "${@}"
}

#**********************************************************
# Signing related stuff
#**********************************************************
#== 【0】【4】=通过占位分割读取位置
locateDelimiter() {
  #== 占位符
  local delimiter="${1}"
  #== 从文件结尾读取行数
  local linesToReadFromEnd="${2}"
  local linesCount
  local offset
  if [ "${linesToReadFromEnd}" ]; then
    #== 文件总行数n（实际行数=n+1）
    linesCount="$(wc -l "${INSTALLER_FILE}" | awk '{print $1}')"
    #== 从后往前读取【linesToReadFromEnd】行
    offset="$(tail -n"${linesToReadFromEnd}" "${INSTALLER_FILE}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
    if [ -n "${offset}" ]; then
      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
    fi
  else
    #== 读取占位符所在行（实际行数=n+1）
    awk '/^'"${delimiter}"'/ { print NR; exit }' "${INSTALLER_FILE}"
  fi
}

#== 【0】=从指定行范围读取配置参数
readParam() {
  local paramName="${1}"
  local paramsSectionBeggining="${2}"
  local paramsSectionEnd="${3}"

  sed -n "${paramsSectionBeggining},${paramsSectionEnd} s/^${paramName}=//p" "${INSTALLER_FILE}"
}

#== 【0】=读取以【----PARAMETERS】开始到 【----PARAMETERS--】之间的参数
readParamsSection() {
  local sectionName="----PARAMETERS"
  local begin
  local end
  begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
  end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})

  if [ -z "${begin}" ] || [ -z "${end}" ]; then
    return
  fi
  #== 从指定行范围读取配置参数
  local value
  if value="$(readParam PARAM_DATA_SERVER "${begin}" "${end}")"; then
    PARAM_DATA_SERVER="${value}"
  fi
  if value="$(readParam PARAM_CONFIG_SERVER "${begin}" "${end}")"; then
    PARAM_CONFIG_SERVER="${value}"
  fi
  if value="$(readParam PARAM_LICENSE "${begin}" "${end}")"; then
    PARAM_LICENSE="${value}"
  fi

  if value="$(readParam PARAM_JSON_CONF "${begin}" "${end}")"; then
    PARAM_JSON_CONF="${value}"
  fi

  if value="$(readParam PARAM_USER "${begin}" "${end}")"; then
    PARAM_USER="${value}"
  fi
  if value="$(readParam PARAM_GROUP "${begin}" "${end}")"; then
    PARAM_GROUP="${value}"
  fi
  if value="$(readParam PARAM_NON_ROOT_MODE "${begin}" "${end}")"; then
    if value="$(getBoolParam "${value}")"; then
      PARAM_NON_ROOT_MODE="${value}"
    fi
  fi

  if value="$(readParam PARAM_USER_LOGIN "${begin}" "${end}")"; then
    if value="$(getBoolParam "${value}")"; then
      PARAM_USER_LOGIN="${value}"
    fi
  fi
  toConsoleInfo "#DEBUG== install sh params, DATA_SERVER: ${PARAM_DATA_SERVER}, PARAM_CONFIG_SERVER: ${PARAM_CONFIG_SERVER}, PARAM_LICENSE:${PARAM_LICENSE}"
  toConsoleInfo "#DEBUG== install sh params, PARAM_USER: ${PARAM_USER}, PARAM_GROUP: ${PARAM_GROUP}, PARAM_USER_LOGIN: ${PARAM_USER_LOGIN}, PARAM_NON_ROOT_MODE:${PARAM_NON_ROOT_MODE}"
}

#== 【0】【6】=清空安装临时文件
#== ${INSTALL_DIR}_install_$$
#== EXTERNAL_TAR_FILE=${INSTALL_DIR}/tarfile_$$.base64
#== ${INSTALL_DIR}/Dynatrace-OneAgent.tar.xz
#== ${INSTALL_DIR}/xzdec
#== /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
cleanInstallationTemporaryFiles() {
  toLogInfo "Cleaning installation temporary files"

  rm -f "${EXTERNAL_TAR_FILE}" "${INTERNAL_TAR_FILE}" "${INSTALL_DIR}/xzdec"
  rm -Rf "${TMP_DIR}"

  local keepInstallationLockFile="${1}"
  if [ -z "${keepInstallationLockFile}" ]; then
    removeInstallationLockFile
  fi
}

#== 完成安装后清理临时目录
finishInstallation() {
  if [ $# -eq 2 ]; then
    cleanInstallationTemporaryFiles "${2}"
  else
    cleanInstallationTemporaryFiles ""
  fi
  toLogInfo "Installation finished, PID $$, exit code: ${1}."
  changeWorkingDir "${CURR_PATH}"
  if [ "${CONF_LD_PRELOAD}"x = "true"x ]; then
    exec /bin/bash && exit 0
  fi
  exit "${1}"
}

#**********************************************************
# Create folders, copy files, set rights
#**********************************************************

#== 【3】=创建临时目录
prepareTempFolder() {
  #== 删除存在的路径
  removeIfExists "${TMP_DIR}"

  toLogInfo "Creating temporary folder $TMP_DIR"
  createDirIfNotExistAndSetRights "${TMP_DIR}" 755
}

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

  if [ -e "${file}" ]; then
    chmod "${perms}" "${file}"
  fi
}

setRightsForDir() {
  local dir="${1}"
  local perms="${2}"

  if [ -d "${dir}" ]; then
    chmod -R "${perms}" "${dir}"
  fi
}

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

#== 【4】=移动目录到指定位置
moveFolderToDestination() {
  local source="${1}"
  local destination="${2}"
  local fullDestination
  fullDestination="${destination}/$(basename "${source}")"
  local output

  toLogInfo "Moving ${source} to ${destination}"
  if [ ! -e "${fullDestination}" ]; then
    if output="$(mv -f "${source}" "${destination}" 2>&1)"; then
      toLogInfo "Moving Successfully."
      return
    fi
    toLogWarn "Failed to move ${source} to ${destination}: ${output}, attempting to copy"
  else
    toLogInfo "${fullDestination} already exists, attempting to copy"
  fi

  if ! output="$(cp -Rfp "${source}" "${destination}" 2>&1)"; then
    toLogError "Failed to copy ${source} to ${destination}: ${output}"
  fi
}

#== 【4】=将 bin 下版本内容移动到安装目录
installVersionedContent() {
  toLogInfo "Installing versioned content..."
  createDirIfNotExistAndSetRights "${AGENT_BIN_DIR}" 755

  local sourceDir="${TMP_DIR}/bin"
  if [ ! -d "${AGENT_BIN_DIR}" ]; then
    moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
    return
  fi

  toLogInfo "Directory ${AGENT_BIN_DIR} already exist, repairing the directory"
  rm -rf "${AGENT_BIN_DIR}"
  moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
}

#== 【4】=创建当前版本软连接

#== 【4】=删除存在的目录
listAndRemoveDirectoryIfExists() {
  local directory="${1}"
  if [ -d "${directory}" ]; then
    toLogInfo "${directory} exists, removing it."
    toLogInfo "Contents: $(ls -lR "${directory}")"
    rm -rf "${directory}"
  fi
}

#== 【4】=临时目录中conf 移动到 agent 安装目录中
setupConfFolder() {
  toLogInfo "Setup conf folder..."
  #== 移动目录到指定位置
  moveFolderToDestination "${TMP_DIR}/conf" "${INSTALL_DIR}"

  chmod 755 "${AGENT_CONF_DIR}"

  toLogInfo "Setup conf done."
}

#== 【4】=给距离当前目录至少 ${mindepth} 个子目录的所有文件设置权限
chmodFilesWithMindepth() {
  local dir="${1}"
  local mindepth="${2}"
  local mask="${3}"

  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
    #== 安装类型（f:文件，d:目录）递归设置权限
    chmod4FilesRecursively "${1}" f "${3}"
  else
    #== 查找深度距离当前目录至少 ${mindepth} 个子目录的所有文件
    find "${dir}" -mindepth "${mindepth}" -print0 | xargs -r -0 chmod "${mask}"
  fi
}

#== 【4】=配置其他文件（cwserveragent.service、installer.version）
setupMiscFiles() {
  toLogInfo "Setup misc files..."
  #== 创建 cwserveragent.service
  createSystemdUnitFile
  #== echo "${AGENT_INSTALLER_VERSION}" >"${INSTALL_DIR}/installer.version"
  mv -f "${TMP_DIR}/installer.version" "${INSTALL_DIR}/"
  toLogInfo "Setup misc done."
}

#== 【4】=创建 cwserveragent.service
createSystemdUnitFile() {
  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR}"
  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
    return
  fi
  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
  cat <<EOF >${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}
[Unit]
Description=${BRAND_AGENT_PRODUCT_NAME}
After=network-online.target
Wants=network-online.target

[Service]
User=root
ExecStart=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} start
ExecStop=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} stop
Type=forking
#Restart=always
KillMode=process
TimeoutSec=240

[Install]
WantedBy=multi-user.target
EOF

  setRightsForFiles "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 755
  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
    cp -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" ${INIT_DIR}
    toLogInfo "Copy scripts ${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT} to ${INIT_DIR} done."
  fi
  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR} done."
}

setupAll() {
#  moveFolderToDestination "${TMP_DIR}/package_dir/*" "${INSTALL_DIR}"
#ls  ${TMP_DIR}/package_dir/*

  #== 移动所有文件到目录
  cp -Rfp ${TMP_DIR}/package_dir/* "${INSTALL_DIR}"
  local installVersionFile="${INSTALL_DIR}/installer.version"
  echo ${AGENT_INSTALLER_VERSION} > ${installVersionFile}
#  mv ${INSTALL_DIR}/package_dir/* ${INSTALL_DIR}
#     echo "${TMP_DIR}/*" "${INSTALL_DIR}/"
}

#== 【4】=配置 lib、conf、bin、cwserveragent.service、installer.version、plugins
setupOptDir() {
  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
  #== 临时目录中conf 移动到 agent 安装目录中
  setupConfFolder
  #== 将 bin 下版本内容移动到安装目录
  installVersionedContent
  #== 配置其他文件（cwserveragent.service、installer.version）
  setupMiscFiles
}

#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
copyScriptsToDirectory() {
  local scriptLocation="${1}"
  #== scripts/cwserveragent
  local scriptFile="${TMP_DIR}/scripts/${SERVICE_SCRIPT_FILE}"

  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."

  local output
  if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
    toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
    return
  fi

  setRightsForFiles "${scriptLocation}/${SERVICE_SCRIPT_FILE}" 755
  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."

  #== scripts/uninstall.sh
#  local scriptLocation="${1}"
#    #== scripts/cwserveragent
#    local scriptFile="${TMP_DIR}/scripts/uninstall.sh"
#
#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
#
#    local output
#    if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
#      toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
#      return
#    fi
#    setRightsForFiles "${scriptLocation}/uninstall.sh" 755
#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
}

#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
copyScripts() {
  toLogInfo "Copy scripts..."
  #== 创建目录 /opt/cloudwise/cwserveragent/scripts
  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
  #== 创建目录 /opt/cloudwise/{product}/agents
  createDirIfNotExistAndSetRights "${AGENTS_BASE_DIR}" 775
  #== 创建目录 /opt/cloudwise/{product}/meta
  createDirIfNotExistAndSetRights "${META_BASE_DIR}" 775
  #== 创建目录 /opt/cloudwise/{product}/runtime
  createDirIfNotExistAndSetRights "${AGENT_RUNTIME_DIR}" 775

  #== scripts/uninstall.sh
  local uninstallScript="${TMP_DIR}/scripts/uninstall.sh"
  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} begin."
  local output
  if ! output="$(cp "${uninstallScript}" "${INSTALL_DIR}/uninstall.sh" 2>&1)"; then
    toLogError "Failed to copy ${uninstallScript} to ${INSTALL_DIR}/uninstall.sh, output: ${output}"
  else
    setRightsForFiles "${INSTALL_DIR}/uninstall.sh" 755
  fi
  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} done."

  #== 复制临时目录 scripts/ 到安装目录 /opt/cloudwise/cwserveragent/scripts
  copyScriptsToDirectory "${AGENT_SCRIPTS_DIR}"
  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
    copyScriptsToDirectory "${INIT_DIR}"
  fi
  toLogInfo "Copy scripts done."
}

#== 【4】=调用 agent 64位数lib路径（lib64/installaction）执行指令 --create-cluster-timestamp-file
createFirstClusterTimestampFile() {
  toLogInfo "Creating firstClusterTimestamp file"
  #== 调用 agent 64位数lib路径（lib64/installaction）执行指令 --create-cluster-timestamp-file
  "$(getAgentInstallActionPath)" "--create-cluster-timestamp-file" >>"${LOG_FILE}" 2>&1
}

#== 【4】=配置数据存储目录
setupDataStorageDir() {
  toLogInfo "Setup datastorage dir..."
  local dataStorage
  dataStorage="$(getValueFromConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}" "${DATA_STORAGE_DIR}")"
  if [ "${dataStorage}"x = "${LOG_DIR}"x ]; then
    toLogInfo "Detected legacy data storage setting, changing it to the new default location"
    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${LEGACY_AGENT_CONF_FILE}"
    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${INSTALLER_CONF_FILE}"
  fi

  if [ "${PARAM_DATA_STORAGE}" ]; then
    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}"
    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${INSTALLER_CONF_FILE}"
  fi

  toLogInfo "Setup datastorage dir done."
}

#**********************************************************
# Processing command line parameters
#**********************************************************
#== 【0】=help
displayHelp() {
  printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] "
  #== printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] [DATA_SERVER=https://server_address:server_port] [CONFIG_SERVER=configService] [LICENSE=license] [INSTALL_DIR=install_path]"
  local beginStr="Usage: "
  local pad="${#beginStr}"

  printf "\n\n"

  pad=25
  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."

}

printParamMessage() {
  local paramNm="${1}"
  local paramValue="${2}"
  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
}

printOtherParamMessage() {
  local param="${1}"
  toConsoleInfo "---> Parameter ${param}"
}

#== 【0】=大小写转化，并返回判断结果
istrcmp() {
  local s1
  local s2
  s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
  s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
  [ "${s1}"x = "${s2}"x ]
}

#== 【0】=
isParamTrue() {
  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
}
#== 【1】【5】=
isParamFalse() {
  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
}

#== 【1】【5】=
invertBoolValue() {
  local valueToInvert="${1}"
  if isParamFalse "${valueToInvert}"; then
    printf '%s' "true"
  else
    printf '%s' "false"
  fi
}

#== shellcheck disable=SC2003
#== 【0】=获取参数值
getParamValue() {
  local paramName="${1}"
  local input="${2}"
  local paramNameLength="${#paramName}"
  paramNameLength=$((paramNameLength + 1))
  local partParam
  partParam="$(expr substr "${input}" 1 ${paramNameLength})"
#  partParam=$(substr "${input}" 1 ${paramNameLength})
  if ! istrcmp "${partParam}" "${paramName}="; then
    return 1
  fi

  local valueSeparator=$((paramNameLength + 1))
  local value
  value="$(expr substr "${input}" ${valueSeparator} 1000)"
#  value=$(substr "${input}" ${valueSeparator} 1000)
  if [ -z "${value}" ]; then
    return 1
  fi

  printf '%s' "${value}"
  return 0
}

#== 【0】=获取bool参数
readBoolParam() {
  local value
  value="$(getParamValue "${1}" "${2}")"

  if value="$(getBoolParam "${value}")"; then
    printf "%s" "${value}"
    return 0
  fi

  return 1
}

#== 【0】=获取bool参数
getBoolParam() {
  local value="${1}"
  if [ "${value}" ]; then
    if isParamFalse "${value}"; then
      printf "false"
      return 0
    fi
    if isParamTrue "${value}"; then
      printf "true"
      return 0
    fi
  fi

  return 1
}

#== 【0】=校验格式是否规范[1、大于4个字符且不能"cw."开头，2、不能超过100字符，3、只能包含字母数字字符，连字符，下划线和点。]
validateParameter() {
  local name="${1}"
  local value="${2}"
  if [ "$(printf '%s' "${value}" | cut -c -3)"x = "cw."x ]; then
    toConsoleError "${name} must not begin with 'cw.'"
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if [ "${#value}" -gt 100 ]; then
    toConsoleError "Maximum allowed length of ${name} is 100."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if printf '%s' "${value}" | grep -q "[^[:alnum:]._-]"; then
    toConsoleError "${name} can only contain alphanumeric characters, hyphen, underscore and dot."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}

#== 【0】=验证用户和用户组是否匹配
validateUserAndGroupParameters() {
  local user="${1}"
  local group="${2}"
  local permittedNameRegex='^[[:alnum:]._][[:alnum:]._-]{2,31}$'

  if [ ! "${group}" ]; then
    group="${user}"
  fi

  if ! printf '%s' "${user}" | grep -qE "${permittedNameRegex}"; then
    toConsoleError "USER can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if ! printf '%s' "${group}" | grep -qE "${permittedNameRegex}"; then
    toConsoleError "GROUP can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  #== 校验用户/用户组信息
  validateUserPrimaryGroup "${user}" "${group}"
}

#== 【0】【3】【5】=获取系统权限信息
getSystemEntityInfo() {
  local database="${1}"
  local valueToCheck="${2}"

  #== 校验命令getent是否存在
  if ! isAvailable "getent"; then
    toLogInfo "Command getent is not available"
    return 2
  fi

  #== 查看系统权限
  local output
  output="$(getent "${database}" "${valueToCheck}" 2>&1)"
  local returnCode=$?

  if [ "${returnCode}" != 0 ]; then
    if [ "${returnCode}" != 2 ]; then
      toLogWarn "Failed to check ${valueToCheck} in ${database} database, message: ${output}, code: ${returnCode}"
    fi
    return 1
  elif [ ! "${output}" ]; then
    toLogWarn "Failed to get user information: getent returned no output"
  fi

  printf '%s' "${output}"
  return 0
}

#== 【0】【3】=查看用户/用户组信息
isEntityPassedById() {
  local database="${1}"
  local name="${2}"

  local output
  output="$(getSystemEntityInfo "${database}" "${name}")"
  local returnCode=$?

  if [ ${returnCode} -ne 0 ]; then
    if [ ${returnCode} -eq 2 ]; then
      toLogInfo "Installer will not be able to verify whether entity was passed by name or by ID"
    fi
    return 1
  fi

  local nameFromDatabase
  nameFromDatabase="$(printf '%s' "${output}" | cut -d: -f1)"

  if [ "${nameFromDatabase}"x = "${name}"x ]; then
    return 1
  fi

  toLogWarn "Name from config and from ${database} system database do not match"
  toLogWarn "Config: ${name}, database: ${nameFromDatabase}"
  return 0
}

#== 【0】【3】【5】=用户是否存在
userExistsInSystem() {
  local user="${1}"

  getSystemEntityInfo "passwd" "${user}" >/dev/null
  local returnCode=$?

  if [ ${returnCode} -ne 2 ]; then
    return ${returnCode}
  fi

  toLogInfo "Trying to determine user existence using 'id' command"
  id "${user}" >/dev/null 2>&1
}

#== 【5】= 查看group
groupExistsInSystem() {
  local group="${1}"

  getSystemEntityInfo "group" "${group}" >/dev/null
  local returnCode=$?

  if [ ${returnCode} -ne 2 ]; then
    return ${returnCode}
  fi

  toLogInfo "Installer will not be able to determine group existence"
  return 1
}

#== 【3】=用户是否存在
validateUserExistence() {
  local user="${1}"
  if ! userExistsInSystem "${user}"; then
    toConsoleError "User named '${user}' configured for ${BRAND_PRODUCT_NAME} does not exist. Installation aborted."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}

#== 【0】【3】=检查用户/用户组信息
checkIfEntityWasNotPassedById() {
  local database="${1}"
  local valueToCheck="${2}"
  local valueTypeToLog="user"

  if [ "${database}"x = "group"x ]; then
    valueTypeToLog="group"
  fi

  if isEntityPassedById "${database}" "${valueToCheck}"; then
    toConsoleError "\"${valueToCheck}\" is not a ${valueTypeToLog} name but its ID. Installation aborted."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}

#== 【0】【3】=用户权限下用户组ID
getUserPrimaryGroupIdForComparison() {
  local user="${1}"

  local userPrimaryGroupId
  userPrimaryGroupId="$(getSystemEntityInfo "passwd" "${user}")"
  local returnCode=$?

  if [ ${returnCode} -ne 2 ]; then
    printf '%s' "${userPrimaryGroupId}" | cut -d: -f4
    return ${returnCode}
  fi

  toLogInfo "Returning user primary group name instead of its id"
  id -gn "${user}"
}

#== 【0】【3】=获取用户组信息
getGroupIdForComparison() {
  local group="${1}"

  local groupId
  groupId="$(getSystemEntityInfo "group" "${group}")"
  local returnCode=$?

  if [ ${returnCode} -ne 2 ]; then
    printf '%s' "${groupId}" | cut -d: -f3
    return ${returnCode}
  fi

  toLogInfo "Returning group name instead of its id"
  printf '%s' "${group}"
}

#== 【0】【3】=校验用户/用户组信息
validateUserPrimaryGroup() {
  local user="${1}"
  local group="${2}"

  if ! userExistsInSystem "${user}"; then
    return
  fi

  checkIfEntityWasNotPassedById "passwd" "${user}"
  checkIfEntityWasNotPassedById "group" "${group}"

  #== 获取用户组ID
  local groupId
  groupId="$(getGroupIdForComparison "${group}")"
  #== 获取用户下用户组ID
  local userPrimaryGroupId
  userPrimaryGroupId="$(getUserPrimaryGroupIdForComparison "${user}")"

  toConsoleInfo "#DEBUG== group \"${group}\" id: \"${groupId}\", user \"${user}\" primaryGroupId: \"${userPrimaryGroupId}\""
  if [ "${userPrimaryGroupId}"x != "${groupId}"x ]; then
    toConsoleError "User named \"${user}\" does not have group named \"${group}\" as its primary group. Installation aborted."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}

#== 【3】=检查用户/用户组是否存在和匹配
checkUserAndGroupFromConfig() {
  local configUser
  local configGroup
  configUser="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  configGroup="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"

  toLogInfo "Checking validity of user account '${configUser}:${configGroup}'"

  if [ "${PARAM_UPGRADE}"x = "yes"x ]; then
    validateUserExistence "${configUser}"
  fi

  #== 校验用户/用户组信息
  validateUserPrimaryGroup "${configUser}" "${configGroup}"
}

#== 【0】=解析命令行中参数
parseCommandLineParameters() {
  local dataServerIsEmpty=true
  local configServerIsEmpty=true
  while [ $# -gt 0 ]; do
    local param="${1}"
    local value=

    if value=$(getParamValue DATA_SERVER "${param}"); then
      PARAM_DATA_SERVER="${value}"
      dataServerIsEmpty=false
      printParamMessage "DATA_SERVER" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue CONFIG_SERVER "${param}"); then
      PARAM_CONFIG_SERVER="${value}"
      configServerIsEmpty=false
      printParamMessage "CONFIG_SERVER" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue LICENSE "${param}"); then
      PARAM_LICENSE="${value}"
      printParamMessage "LICENSE" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue JSON_CONF "${param}"); then
      PARAM_JSON_CONF="${value}"
      printParamMessage "JSON_CONF" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue INSTALL_DIR "${param}"); then
      PARAM_INSTALL_DIR="${value}"
      printParamMessage "INSTALL_DIR" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue DATA_STORAGE "${param}"); then
      PARAM_DATA_STORAGE="${value}"
      printParamMessage "DATA_STORAGE" "${value}"
      shift
      continue
    fi

    if value=$(readBoolParam DISABLE_SYSTEM_LOGS_ACCESS "${param}"); then
      PARAM_DISABLE_SYSTEM_LOGS_ACCESS="${value}"
      printParamMessage "DISABLE_SYSTEM_LOGS_ACCESS" "${value}"
      shift
      continue
    fi

    if value=$(getParamValue INTERNAL_OVERRIDE_CHECKS "${param}"); then
      if printf '%s' "${value}" | grep -wq "privileges"; then
        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=true
      fi

      if printf '%s' "${value}" | grep -wq "downgrade"; then
        SKIP_DOWNGRADE_CHECK=true
      fi
      printParamMessage "INTERNAL_OVERRIDE_CHECKS" "${value}"

      shift
      continue
    fi

    if value=$(readBoolParam INTERNAL_USE_UNPACK_CACHE "${param}"); then
      PARAM_INTERNAL_USE_UNPACK_CACHE="${value}"
      printParamMessage "INTERNAL_USE_UNPACK_CACHE" "${value}"
      shift
      continue
    fi

    if [ "${ARCH_ARCH}"x != "AIX"x ]; then
      if value=$(getParamValue USER "${param}"); then
        PARAM_USER="${value}"
        printParamMessage "USER" "${value}"
        shift
        continue
      fi

      if value=$(getParamValue GROUP "${param}"); then
        PARAM_GROUP="${value}"
        printParamMessage "GROUP" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam NON_ROOT_MODE "${param}"); then
        PARAM_NON_ROOT_MODE="${value}"
        printParamMessage "NON_ROOT_MODE" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam USER_LOGIN "${param}"); then
        PARAM_USER_LOGIN="${value}"
        printParamMessage "USER_LOGIN" "${value}"
        shift
        continue
      fi

      #== 自定义参数获取
      if value=$(getParamValue TEST "${param}"); then
        PARAM_TEST="${value}"
        printParamMessage "TEST" "${value}"
        shift
        continue
      fi

      #== 自定义bool参数
      if value=$(readBoolParam BOOL "${param}"); then
        PARAM_BOOL="${value}"
        printParamMessage "BOOL" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam DISABLE_ROOT_FALLBACK "${param}"); then
        PARAM_DISABLE_ROOT_FALLBACK="${value}"
        printParamMessage "DISABLE_ROOT_FALLBACK" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK "${param}"); then
        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK="${value}"
        printParamMessage "NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam INTERNAL_SKIP_SELINUX_POLICY_INSTALLER "${param}"); then
        #== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER="${value}"
        printParamMessage "INTERNAL_SKIP_SELINUX_POLICY_INSTALLER" "${value}"
        shift
        continue
      fi

      if value=$(readBoolParam DOCKER_ENABLED "${param}"); then
        #== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER="${value}"
        printParamMessage "DOCKER_ENABLED" "${value}"
        shift
        continue
      fi
    fi

    if [ "${ARCH_ARCH}"x != "AIX"x ] && [ "${ARCH_ARCH}"x != "S390"x ]; then
      if value=$(readBoolParam INTERNAL_DISABLE_DUMPPROC "${param}"); then
        #== PARAM_INTERNAL_DISABLE_DUMPPROC="${value}"
        printParamMessage "INTERNAL_DISABLE_DUMPPROC" "${value}"
        shift
        continue
      fi
    fi

    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
      displayHelp
      finishInstallation "${EXIT_CODE_OK}"
    fi

    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
      finishInstallation "${EXIT_CODE_OK}"
    fi
    
    printOtherParamMessage ${param}
    shift
  done

  if [ "${configServerIsEmpty}"x = "true"x ] && [ "${dataServerIsEmpty}"x != "true"x ]; then
    PARAM_CONFIG_SERVER="${PARAM_DATA_SERVER}"
  fi
}

#== 【0】=校验PARAM_INSTALL_DIR是否规范【1、不能包含空格，2、不能在根目录，3、安装路径必须是绝对的】
validateInstallPathParameter() {
  if printf '%s' "${PARAM_INSTALL_DIR}" | grep -q "[[:space:]]"; then
    toConsoleError "Installation path must not contain spaces."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if [ "${PARAM_INSTALL_DIR}"x = "/"x ]; then
    toConsoleError "Installation path must not point to the filesystem root directory."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if [ "$(printf '%s' "${PARAM_INSTALL_DIR}" | cut -c 1)"x != "/"x ]; then
    toConsoleError "Installation path must be absolute."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}

#== 【0】=校验PARAM_DATA_STORAGE是否规范【1、不能包含空格，2、不能在根目录，3、安装路径必须是绝对的，4、数据目录不能放在安装目录下】
validateDataStorageParameter() {
  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "[[:space:]]"; then
    toConsoleError "Data storage path must not contain spaces."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if [ "${PARAM_DATA_STORAGE}"x = "/"x ]; then
    toConsoleError "Data storage path must not point to the filesystem root directory."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if [ "$(printf '%s' "${PARAM_DATA_STORAGE}" | cut -c 1)"x != "/"x ]; then
    toConsoleError "Data storage path must be absolute."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi

  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "^${INSTALL_DIR}"; then
    toConsoleError "Data storage path must not be located within ${INSTALL_DIR}."
    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
  fi
}


#**********************************************************
# Config files
#**********************************************************

#== 【3】【5】=格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
formatSize() {
  local sizeInKiB="${1}"
  local formattedSize

  for symbol in "KiB" "MiB" "GiB" "TiB"; do
    if printf '%s' "${sizeInKiB}" | awk '$1 >= 1024 { exit 1; }'; then
      formattedSize="${sizeInKiB} ${symbol}"
      break
    fi
    sizeInKiB="$(printf '%s' "${sizeInKiB}" | awk '{ print $1 / 1024 }')"
  done

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

#== 【3】【5】=
cropSizeValue() {
  local size="${1}"
  local value
  local unit
  value="$(printf '%s' "${size}" | cut -d' ' -f1)"
  unit="$(printf '%s' "${size}" | cut -d' ' -f2)"
  printf '%.2f %s' "${value}" "${unit}"
}

#== 【3】【5】=检查目录可用空间（单位：k）
checkFreeSpace() {
  local path="${1}"
  local requiredSpaceInKiB="${2}"
  local dfOutput
  local baseFilesystem
  local freeSpace
  local formattedRequiredSpace
  local formattedFreeSpace

  #== shellcheck disable=SC2086
  dfOutput="$(df -P ${AIX_DF_SPECIFIC_FLAG} "${path}" | tail -n +2)"
  #== 文件系统目录
  baseFilesystem="$(printf "%s" "${dfOutput}" | awk '{ print $NF }')"
  #== 剩余空间（单位：k）
  freeSpace="$(printf "%s" "${dfOutput}" | awk '{ print $4 }')"

  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
  formattedRequiredSpace="$(formatSize "${requiredSpaceInKiB}")"
  toLogInfo "Filesystem with ${path} is mounted under ${baseFilesystem}. Space required: ${formattedRequiredSpace}."

  if [ ! "${freeSpace}" ]; then
    printf 'Cannot determine amount of free space on %s (filesystem mounted under %s)' "${path}" "${baseFilesystem}"
    return 1
  fi

  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
  formattedFreeSpace="$(formatSize "${freeSpace}")"
  toLogInfo "Available free space: ${formattedFreeSpace}"

  if [ "${freeSpace}" -lt "${requiredSpaceInKiB}" ]; then
    printf 'Not enough free space on %s (filesystem mounted under %s). ' "${path}" "${baseFilesystem}"
    printf 'Required: %s, available: %s' "$(cropSizeValue "${formattedRequiredSpace}")" "$(cropSizeValue "${formattedFreeSpace}")"
    return 2
  fi

  printf 'Free space is sufficient'
  return 0
}

#== 【3】=检查文件系统类型

#== 【3】=检查安装目录空间
checkInstallPathFreeSpace() {
  local externalTarSize=${EXTERNAL_TAR_SIZE}
  local artifactsSize=${ARTIFACTS_SIZE}
  #== use 10% additional margin
  local requiredSpace=$((externalTarSize + artifactsSize * 11 / 10))
  #== convert to kibibytes
  requiredSpace=$((requiredSpace / 1024))

  toConsoleInfo "Checking free space in ${INSTALL_DIR}"

  local message
  #== 检查目录可用空间（单位：k）
  message="$(checkFreeSpace "${INSTALL_DIR}" "${requiredSpace}")"
  case $? in
  0) toLogInfo "${message}" ;;
  1) toConsoleWarn "${message}. Installation may be incomplete." ;;
  2)
    toConsoleError "${message}"
    finishInstallation "${EXIT_CODE_NOT_ENOUGH_SPACE}"
    ;;
  esac
}

#== 【3】=获取指定路径的文件系统信息
getFilesystemInfo() {
  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
    mount | grep " ${1} "
  else
    grep " ${1} " /proc/self/mounts
  fi
}

#== 【3】=检查目录指定权限信息
checkAccessRightsTo() {
  local dir="${1}"
  toLogInfo "Checking access to ${dir}..."
  #== 获取文件权限信息
  local accessRights
  accessRights="$(arch_getAccessRights "${dir}" | cut -c 2-4)"
  if ! printf '%s' "${accessRights}" | grep -q rwx; then
    toConsoleError "Insufficient permissions on ${dir}: '${accessRights}'."
    toLogInfo "$(ls -dl "${dir}" 2>&1)"
    finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
  fi

  local dfResult
  local filesystem
  local filesystemInfo
  dfResult="$(df -P "${dir}")"
  #== 获取目录文件系统路径
  filesystem="$(printf '%s' "${dfResult}" | tail -1 | awk '{ print $NF }')"
  #== 获取指定路径的文件系统信息
  filesystemInfo="$(getFilesystemInfo "${filesystem}")"
  if ! printf '%s' "${filesystemInfo}" | grep -qw rw; then
    toLogWarn "df-based check determined filesystem mounted under ${filesystem} as readonly, trying fallback."
    toLogWarn "Filesystem access rights: '${filesystemInfo}'"
    toLogWarn "df returned: ${dfResult}"

    if ! testWriteAccessToDir "${dir}"; then
      toConsoleError "readonly filesystem mounted under ${filesystem}"
      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
    fi
  fi

  toLogInfo "Rights on directory ${dir} are sufficient"
} 2>>"${LOG_FILE}"

#== 【3】=目录是否写权限
checkIfInstallationPathIsWriteable() {
  for dir in "${@}"; do
    if [ -d "${dir}" ]; then
      checkAccessRightsTo "${dir}"
      break
    fi
  done
}

#== 【3】=检查目录权限
checkAccessRightsToDirs() {
  checkIfInstallationPathIsWriteable "${INSTALL_DIR}" "${BASE_INSTALL_DIR}" "${INSTALL_BASE}" /

  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
    checkAccessRightsTo "${INIT_DIR}"
  fi
}


#== 【0】=检查系统是 LINUX、AIX及指令集（X86_64\IA64\X86）
checkSystemCompatibility() {
  local expectedPlatform="LINUX"
  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
    expectedPlatform="AIX"
  fi

  #== 获取系统名称
  local platform
  platform="$(uname | sed -e 's/_.*//' | sed -e 's/\///' | tr '[:lower:]' '[:upper:]')"
  if [ "${platform}"x != "${expectedPlatform}"x ]; then
    printf "Cannot determine platform or platform not supported: <%s>" "${platform}"
    return 1
  fi

  #== 检查系统指令集（X86_64\IA64\X86）
  local detectedArchitecture
  if ! detectedArchitecture="$(arch_checkArchitectureCompatibility)"; then
    printf "Cannot determine architecture or architecture not supported: <%s>" "${detectedArchitecture}"
    return 1
  fi

  printf 'Detected platform: %s' "${platform}"
  if [ "${detectedArchitecture}" ]; then
    printf ' arch: %s' "${detectedArchitecture}"
  fi

  return 0
}

#== 【4】=从sh文件读取压缩文件 tarfile_$$.base64
separateExternalTar() {
  toLogInfo "Determining begin of tar archive..."
  local tarBegin
  local tarEnd
  #== 压缩文件开始行数，占位符为【#################ENDOFSCRIPTMARK############】
  tarBegin="$(locateDelimiter "#################ENDOFSCRIPTMARK############" "")"
  tarBegin=$((tarBegin + 1))
  #== 压缩文件结束行数，占位符为【----SIGNED-INSTALLER】
  tarEnd="$(locateDelimiter "----SIGNED-INSTALLER" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})"
  toLogInfo "tarBegin=${tarBegin} tarEnd=${tarEnd}"

  if [ ! "${tarEnd}" ]; then
    toConsoleError "S/MIME signature is missing, installation package is corrupted."
    finishInstallation "${EXIT_CODE_CORRUPTED_PACKAGE}"
  fi

  local tarLength=$((tarEnd - tarBegin))
  toLogInfo "tarLength=${tarLength}"
  #== 从sh文件读取压缩文件 tarfile_$$.base64
  tail -n +"${tarBegin}" "${INSTALLER_FILE}" 2>/dev/null | head -${tarLength} >"${EXTERNAL_TAR_FILE}"
}

#== 【4】【5】=进入目录
changeWorkingDir() {
  if ! cd "${1}"; then
    toLogError "Failed to change working directory to ${1}"
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi
}

#== shellcheck disable=SC2181
#== 【4】=解压文件（1、通过base64转码后，执行tar解压；2、通过xzdec解压xz文件后在执行tar解压）
unpackArchiveWithoutCache() {
  #== 解压文件编码
  local base64Binary="${UNPACK_BINARY}"
  #== 解压文件编码参数
  local base64BinaryArgs="${UNPACK_BINARY_ARGS}"
  #== 使用xzdec命令可以进行liblzma为基础的xz文件解压缩
  local xzBinary="${INSTALL_DIR}/xzdec"
  #== 进入目录
  changeWorkingDir "${INSTALL_DIR}"

  if ! isAvailable tar; then
    toConsoleError "tar binary not found. Setup can't continue"
    finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
  fi

  local totalLines
  if ! isAvailable "${base64Binary}"; then
    toLogInfo "${base64Binary} not found. Falling back to openssl decode"
    if ! isAvailable openssl; then
      toConsoleError "Neither ${base64Binary} nor openssl can be found. Setup can't continue"
      finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
    fi
    #== 如果没有 base64 命令，则使用 openssl 解压文件
    base64Binary="openssl"
    base64BinaryArgs="enc -base64 -d -in"

    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
      #truncate the first and the last one line due to specific format of uuencode on aix
      totalLines="$(wc -l "${EXTERNAL_TAR_FILE}" | awk '{print $1}')"
      head -$((totalLines - 1)) "${EXTERNAL_TAR_FILE}" 2>/dev/null | tail +2 >"${EXTERNAL_TAR_FILE}.$$"
      mv -f "${EXTERNAL_TAR_FILE}.$$" "${EXTERNAL_TAR_FILE}"
    fi
  fi

  {
    #== base64 转编码后，执行 tar 解压
    "${base64Binary}" "${base64BinaryArgs}" "${EXTERNAL_TAR_FILE}" | tar -x -p -f -
  } 2>>"${LOG_FILE}"

  #== base64 转编码 或 tar解压失败
  if [ $? -gt 0 ]; then
    toConsoleError "Archive is corrupted or memory allocation failed. Installation aborted."
    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
  fi

  #== 进入目录
  changeWorkingDir "${TMP_DIR}"
  {
    #== 使用xzdec命令解压后，执行 tar 解压
    "${xzBinary}" "${INTERNAL_TAR_FILE}" | tar -x -p -f -
  } 2>>"${LOG_FILE}"

  if [ $? -gt 0 ]; then
    toConsoleError "XZ compressed archive is corrupted or memory allocation failed. Installation aborted."
    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
  fi
  #== 进入目录
  changeWorkingDir "${INSTALL_DIR}"
}

#== 【4】=解压文件（1、通过base64转码后，执行tar解压；2、通过xzdec解压xz文件后在执行tar解压）
unpackArchive() {
  toConsoleInfo "Unpacking. This may take a while..."

  #== 【参数】是否使用解压缓存
  if [ "${PARAM_INTERNAL_USE_UNPACK_CACHE}"x = "true"x ]; then
    if [ -d "${UNPACK_CACHE}" ]; then
      toLogInfo "Unpack cache will be used."
      cp -Rp "${UNPACK_CACHE}"/* "${TMP_DIR}"
    else
      toLogInfo "Unpack cache does not exist."
      mkdir -p "${UNPACK_CACHE}"
      #== 解压文件（1、通过base64转码后，执行tar解压；2、通过xzdec解压xz文件后在执行tar解压）
      unpackArchiveWithoutCache
      cp -Rp "${TMP_DIR}"/* "${UNPACK_CACHE}"
    fi
  else
    toLogInfo "Unpacking without cache"
    #== 解压文件（1、通过base64转码后，执行tar解压；2、通过xzdec解压xz文件后在执行tar解压）
    unpackArchiveWithoutCache
  fi

  toConsoleInfo "Unpacking complete."
}

#== 【3】=检查容器部署、运行情况、配置参数
#isDeployedInsideOpenVZContainer
#isProcessRunningInContainer
#isDeployedViaContainer


#== Checking if libc is new enough
#== 【3】=格式化version
format_version() {
  printf '%s' "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
}

#== 【3】=格式化路径
format_patch() {
  printf '%s' "$@" | tail -n 1 | awk -F- '{ printf("%d\n", $3); }'
}

#== 【3】=检查是否降级安装
checkIfDowngrade() {
  if [ "${SKIP_DOWNGRADE_CHECK}"x = "true"x ]; then
    toConsoleInfo "Skipped downgrade check"
    return
  fi

  local installVersionFile="${INSTALL_DIR}/installer.version"
  if [ ! -f "${installVersionFile}" ]; then
    toLogWarn "Could not perform downgrade check, ${installVersionFile} file is missing"
    return
  fi

  local oldVersion
  oldVersion="$(cat "${installVersionFile}")"
  if [ "$(format_version "${AGENT_INSTALLER_VERSION}")" -eq "$(format_version "${oldVersion}")" ]; then
    toConsoleError "${BRAND_PRODUCT_NAME} is already installed, please uninstall the old version using [${INSTALL_DIR}/uninstall.sh]."
#    toLogError "Attempted downgrade from ${oldVersion} to ${AGENT_INSTALLER_VERSION}"
    finishInstallation "${EXIT_CODE_UNSUPPORTED_DOWNGRADE}"
  fi
}

#== 【3】=检查是否已经安装
checkIfAlreadyInstalled() {
  if [ -f "${INSTALL_DIR}/uninstall.sh" ] && [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ] ; then
#    toConsoleError "Agent already installed. Uninstalling previous version."
#    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"

    checkIfDowngrade
    PARAM_UPGRADE="yes"
  else
#  		if [ -f "${SIF_AGENT_INSTALL_PATH}/lib64/${AGENT_BIN}" ]; then
#  			sif_toConsoleError "Upgrade is not possible because uninstall script is missing"
#  			finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
#  		fi
      local fileNum
      fileNum=`ls ${INSTALL_DIR} | wc -l`
      if [ "${fileNum}" -gt 1 ];then
         toConsoleError "${INSTALL_DIR} is not empty. Please clean or change the installation directory manually."
         finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
      fi
  fi



}

#**********************************************************
# Init related functions
#**********************************************************

#== clears dependencies in LSB init script
#== 【5】=清除LSB init脚本中的依赖项
clearDependenciesInLSBInit() {
  local file="${1}"
  toConsoleInfo "Clearing dependencies in file ${file}"
  awk '
		BEGIN {
			req_start_found=0;
			req_stop_found=0;
			REQ_START="# Required-Start:";
			REQ_STOP="# Required-Stop:";
			PATTERN_REQ_START="^" REQ_START;
			PATTERN_REQ_STOP="^" REQ_STOP;
		}
		{
			if ($0 ~ PATTERN_REQ_START && req_start_found == 0) {
				print REQ_START;
				req_start_found++;
			} else if ($0 ~ PATTERN_REQ_STOP && req_stop_found == 0) {
				print REQ_STOP;
				req_stop_found++;
			} else
				print $0
		}' "${file}" >"${file}.tmp" && mv -f "${file}.tmp" "${file}"

  chmod +x "${file}"
}

#== 【5】=指定目录添加自动启动脚本
addScriptToSystemvAutostart() {
  local prefix="${1}"
  local file="${2}"
  local suffix="${3}"

  toLogInfo "Adding ${file} to autostart"
  #== 自动启动工具
  if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
    toLogWarn "Failed to add ${file} script to autostart. Trying without dependencies..."
    #== 清除LSB init脚本中的依赖项
    clearDependenciesInLSBInit "${INIT_DIR}/${file}"
    if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
      toConsoleError "Cannot add ${file} to autostart. For details, see: ${LOG_FILE}"
    fi
  fi
}

#== 【5】=指定目录添加自动启动脚本
addScriptsToAutostart() {
  local prefix="${1}"
  local suffix="${2}"
  #== 指定目录添加自动启动脚本
  addScriptToSystemvAutostart "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"
}

#== 【5】=设置自动启动
#setupSystemvAutostart() {
#  toLogInfo "Adding ${BRAND_AGENT_PRODUCT_NAME} to autostart..."
#
#  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
#    #== 指定目录添加自动启动脚本
#    addScriptsToAutostart "/usr/bin/update-rc.d " "defaults"
#  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
#    addScriptsToAutostart "/usr/sbin/update-rc.d " "defaults"
#  elif [ -x /sbin/chkconfig ]; then #RedHat
#    addScriptsToAutostart "/sbin/chkconfig --add "
#  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
#    addScriptsToAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
#  elif [ "${ARCH_ARCH}"x = "AIX"x ]; then
#    #== 【不存在】
#    arch_setAutostart
#  else
#    toConsoleError "Couldn't add ${BRAND_AGENT_PRODUCT_NAME} to autostart. Please adjust and add it manually."
#  fi
#}

#== 【5】=设置 cwserveragent.service 中用户
setServiceScriptUser() {
  if ! isNonRootModeEnabled; then
    return
  fi
  #== 读取用户名（调用 agent 64位数lib路径（lib64/installaction）执行指令 --get-user）
  local user
  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  #== 修改 cwserveragent.service 中用户信息
  sed -i "s/User=.*/User=${user}/g" "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
}

#== 【5】=配置自动启动 cwserveragent.service
setupSystemdAutostart() {
  mv -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" "${SYSTEMD_UNIT_FILES_DIR}/"
  setRightsForFiles "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 644

  #== 设置 cwserveragent.service 中用户
  setServiceScriptUser

  if isAvailable restorecon; then
    restorecon "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
  fi

  #== 执行 systemctl 命令配置允许开机启动 cwserveragent.service
  executeSystemctlCommand enable "${SYSTEMD_UNIT_FILE_AGENT}"
  #== 执行 systemctl 命令重新加载模块
  executeSystemctlCommand daemon-reload ""
}

#== 【6】=运行service 脚本
execIntoServiceScript() {
  toConsoleInfo "${SERVICE_SCRIPT_FILE} will be started via exec()"
  #== 清除安装临时文件
  cleanInstallationTemporaryFiles
  toLogInfo "Installation finished, PID $$."

  local command="exec ${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} exec"
  toLogInfo "Executing: ${command}"
  #== 执行 cwserveragent exec
  ${command}

  toLogError "Could not execute: ${command}"
  finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
}

#== 【6】=运行agent
runAgents() {
  toConsoleInfo "Starting agents..."

  toLogInfo "Using ${INIT_SYSTEM} to start the agent"
  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
    #== 运行初始化命令(通过 service start 方式 或 直接运行可执行命令）
    executeInitScriptCommand start "true"
  else
    #== 执行 systemctl start 命令
    executeSystemctlCommand start "${SYSTEMD_UNIT_FILE_AGENT}"
  fi

  if [ $? -eq 0 ]; then
    toConsoleInfo "${SERVICE_SCRIPT_FILE} service started"
  else
    toConsoleError "Failed to start service: ${SERVICE_SCRIPT_FILE}, it is possible that your init system is not functioning properly. For details, see: ${LOG_FILE}"
  fi
}

#== 【5】=配置自动启动
#setupAutostart() {
#  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
#    #== 设置自动启动
#    setupSystemvAutostart
#  else
#    #== 配置自动启动 cwserveragent.service
#    setupSystemdAutostart
#  fi
#}

#**********************************************************
# Process agent related functions
#**********************************************************

#== 【5】=创建 agent 状态文件
createAgentStateFile() {
  local path="${1}"
  local agentStateContents="RUNNING"

  toLogInfo "Writing ${agentStateContents} to ${path} file"
  {
    printf "%s" "${agentStateContents}" >"${path}.tmp"
    mv -f "${path}.tmp" "${path}"
  } 2>>"${LOG_FILE}"
}

#== 【0】=检查全路径是否可读权限（目录最深100层）
checkIfPathIsGloballyReadable() {
  local path="${1}"
  local sourcePath="${path}"
  local maxDepth=100
  while [ "${path}"x != "/"x ]; do
    #== 【0】=获取文件权限信息
    local accessRights
    accessRights="$(arch_getAccessRights "${path}")"
    if ! printf '%s' "${accessRights}" | cut -c 8-10 | grep -qE "r.[xt]"; then
      toConsoleError "Insufficient access rights (${accessRights}) on: ${path}"
      toConsoleError "${sourcePath} path must be globally readable (r-x permissions for others)."
      toConsoleError "Please adjust the permissions and then retry the installation."
      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
    fi

    path="$(dirname "${path}")"

    maxDepth=$((maxDepth - 1))
    if [ "${maxDepth}" -eq 0 ]; then
      toLogWarn "Unable to verify access rights on ${path}"
      return
    fi
  done
}

#== 【0】=创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
createSymlinkToInstallLocation() {
  #== INSTALL_DIR 不能是软链接地址
  if [ -L "${INSTALL_DIR}" ] && [ ! -e "${INSTALL_DIR}" ]; then
    toConsoleError "Detected that ${INSTALL_DIR} is a dangling symlink, please remove it and then retry the installation"
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi

  #== PARAM_INSTALL_DIR 和 INSTALL_DIR 都不能是空地址
  if [ "${PARAM_INSTALL_DIR}"x = "${INSTALL_DIR}"x ] || [ -z "${PARAM_INSTALL_DIR}" ]; then
    return
  fi

  #== INSTALL_DIR 是软连接，并获取 PARAM_INSTALL_DIR 和 INSTALL_DIR 的真实地址
  if [ -L "${INSTALL_DIR}" ] && [ "$(readLink -m "${PARAM_INSTALL_DIR}")"x = "$(readLink -m "${INSTALL_DIR}")"x ]; then
    return
  fi

  #== 目录存在，表示未卸载
  if [ -e "${INSTALL_DIR}" ]; then
    toConsoleError "Leftovers from previous agent installation detected"
    toConsoleError "If you wish to use INSTALL_DIR parameter then perform a cleanup by following these steps:"
    toConsoleError "1. Uninstall the agent"
    toConsoleError "2. Restart all applications that have Deep Monitoring enabled (host restart is fine as well)"
    toConsoleError "3. Remove ${INSTALL_DIR}"
    toConsoleError "and then retry the installation."
    toConsoleError "#DEBUG==For further information please visit ${HELP_URL}/command-line-install"
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi

  #== 创建不存在的目录
  createDirIfNotExistAndSetRights "${PARAM_INSTALL_DIR}" 1775
  #== 检查全路径是否可读权限（目录最深100层）
  checkIfPathIsGloballyReadable "${PARAM_INSTALL_DIR}"
  #== 创建不存在的目录 /opt/cloudwise
  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" 755

  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR
  local lnOutput
  if lnOutput="$(ln -fs "${PARAM_INSTALL_DIR}" "${INSTALL_DIR}" 2>&1)"; then
    toConsoleInfo "Symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR} created"
  else
    toConsoleError "Failed to create symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR}, aborting installation: ${lnOutput}"
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi
}

#== 【5】=修改文件系统用户和用户组
changeFilesOwnership() {
  local user
  local group
  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"

  toLogInfo "Changing ownership of files to root:${group}"
  #== /opt/cloudwise/cwserveragent 目录下用户和用户组
  commandErrorWrapper chown "${user}:${group}" "${AGENT_BASE_DIR}"
  commandErrorWrapper chown -R "${user}:${group}" "${INSTALL_DIR}"
  toLogInfo "Recursively changing group ownership of ${AGENT_CONF_DIR} to ${group}"

}

#== 【5】=文件用户权限检查
fileCapabilitiesCompatibilityCheck() {
  #== 获取配置用户
  local user
  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  #== 获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
  if output="$("$(getOsConfigBinPath)" file-capabilities-compatibility-check "${user}" 2>&1)"; then
    return 0
  fi

  if [ "${PARAM_NON_ROOT_MODE}" ]; then
    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
    toLogWarn "Capabilities test output: ${output}"
  else
    toConsoleInfo "Non-privileged mode was not enabled, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
    toLogInfo "Capabilities test output: ${output}"
  fi
  #== 设置删除root权限
  "$(getAgentInstallActionPath)" "--set-drop-root-privileges" "false" >>"${LOG_FILE}" 2>&1
  return 1
}


#**********************************************************
# User and group related functions
#**********************************************************

#== 【5】=添加用户组
addGroup() {
  local group="${1}"

  if groupExistsInSystem "${group}"; then
    toLogInfo "Group '${group}' already exists"
    return 0
  fi

  local errorMessage
  errorMessage="$(groupadd "${group}" 2>&1)"
  local returnCode=$?

  case ${returnCode} in
  0) toLogInfo "Group '${group}' successfully created" ;;
  9) toLogInfo "Group '${group}' already exists" ;;
  *)
    toLogError "Error occured while adding '${group}' group, return code: ${returnCode}, message ${errorMessage}"
    return 1
    ;;
  esac
  return 0
}

#== 【5】=添加用户
addUser() {
  local user="${1}"
  local group="${2}"
  local groupCreated="${3}"
  local errorMessage
  local mod="/bin/false"
  if [ "${PARAM_USER_LOGIN}"x == "true"x ]; then
    mod="/bin/bash"
  fi
  if userExistsInSystem "${user}"; then
    toLogInfo "User '${user}' already exists."
    #-- 权限更新
#    errorMessage="$(usermod -s "${mod}" "${user}" 2>&1)"
    return 0
  fi

  if [ "${groupCreated}" -eq 0 ]; then
    errorMessage="$(useradd -r --shell "${mod}" -g "${group}" "${user}" 2>&1)"
  else
    errorMessage="$(useradd -r --shell "${mod}" "${user}" 2>&1)"
  fi

  local returnCode=$?
  if [ ${returnCode} -ne 0 ]; then
    toConsoleError "Failed to create user '${user}'"
    toLogError "Error occured while adding '${user}' user, return value: ${returnCode}, error message: ${errorMessage}."
    return 1
  fi

  toConsoleInfo "User '${user}' added successfully."
  return 0
}

#== 【5】=添加用户和用户组信息
addUserAndGroup() {
  local user="${1}"
  local group="${2}"
  #== 添加用户组
  addGroup "${group}"
  #== 添加用户
  addUser "${user}" "${group}" $?
}

#== 【5】=配置用户和用户组
handleUser() {
  toLogInfo "Processing user and group..."
  local user
  local group
  #== 读取用户名（调用 agent 64位数lib路径（lib64/installaction）执行指令 --get-user）
  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  #== 读取用户组（调用 agent 64位数lib路径（lib64/installaction）执行指令 --get-group）
  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
  #== 添加用户和用户组信息
  addUserAndGroup "${user}" "${group}"
}

#== 【4】=获取agent preLoad 安装路径，并校验是否可执行
checkCompatibilityWithInstallActionBinary() {
  local output
  #== 获取agent preLoad 安装路径，并校验是否可执行
  output="$("$(getAgentInstallActionPathPreInstallation)" "--sanity-check" 2>&1)"
  local exitCode=$?
  toLogAdaptive ${exitCode} "Compatibility check exit code = ${exitCode}, output = ${output}"
  if [ ${exitCode} -ne 0 ] || [ "${output}"x != "SUCCESS"x ]; then
    toConsoleError "System compatibility check failed, this may be caused by a problem with glibc, dynamic loader or incompatible operating system version."
    toConsoleError "Detected version: $(detectLinuxDistribution)"
    toConsoleError "For a list of supported distributions and versions, see: ${HELP_URL}"
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi
}

#== 【4】=【部署】解压文件
extractFiles() {
  umask 000
  toConsoleInfo "Extracting..."
  #== 从sh文件读取压缩文件 tarfile_$$.base64
  separateExternalTar
  #== 解压文件（1、通过base64转码后，执行tar解压；2、通过xzdec解压xz文件后在执行tar解压）
  unpackArchive
  umask 022
}

#== 【3】=检查发现初始化系统信息（INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
detectInitSystem() {
  #== 获取初始化系统信息 (INIT_SYSTEM、INIT_SYSTEM_VERSION)
  checkInitSystem
  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
  #== 设置系统初始化脚本目录
  if ! setLocationOfScripts; then
    toConsoleError "Cannot determine location of init scripts."
    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
  fi
}

#== 【4】=【旧配置】读取旧配置参数
readLegacySetting() {
  local paramName="${1}"
  local value
  value="$(sed -n "s|^${paramName}=||p" "${LEGACY_AGENT_CONF_FILE}" | tr -d '\r' 2>/dev/null)"
  if [ "${value}" ]; then
    local valueToPrint="${value}"
    if [ "${paramName}"x = "${CONF_FIELD_NM_LICENSE}"x ]; then
      valueToPrint="***"
    fi
    toLogInfo "Read legacy value: ${paramName} = ${valueToPrint}"
  fi

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

#== 【4】=【旧配置】迁移旧配置文件
migrateLegacySettingsFromAgentConf() {
  toLogInfo "Looking for legacy config to migrate."
  #== 旧配置文件
  if [ ! -f "${LEGACY_AGENT_CONF_FILE}" ]; then
    toLogInfo "Unable to read agent config file, skipping legacy config migration"
    return
  fi

  if [ ! "${PARAM_CONFIG_SERVER}" ]; then
    local configServerValue
    configServerValue="$(readLegacySetting ${CONF_FIELD_NM_CONFIG_SERVER})"
    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_CONFIG_SERVER}: ${PARAM_CONFIG_SERVER}"
    if [ "${configServerValue}"x = "http://localhost:8020"x ]; then
      toLogInfo "Param 'server' has default value set, skipping it"
    else
      PARAM_CONFIG_SERVER="${configServerValue}"
    fi
  fi

  if [ ! "${PARAM_DATA_SERVER}" ]; then
    PARAM_DATA_SERVER="$(readLegacySetting ${CONF_FIELD_NM_DATA_SERVER})"
    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_DATA_SERVER}: ${PARAM_DATA_SERVER}"
  fi

  if [ ! "${PARAM_LICENSE}" ]; then
    PARAM_LICENSE="$(readLegacySetting ${CONF_FIELD_NM_LICENSE})"
    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_LICENSE}: ${PARAM_LICENSE}"
  fi

  if [ ! "${PARAM_JSON_CONF}" ]; then
    PARAM_JSON_CONF="$(readLegacySetting ${CONF_FIELD_NM_JSON_CONF})"
    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_JSON_CONF}: ${PARAM_JSON_CONF}"
  fi

  if [ ! "${PARAM_USER}" ]; then
    PARAM_USER="$(readLegacySetting ${CONF_FIELD_NM_USER})"
    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_USER}: ${PARAM_USER}"

    if [ ! "${PARAM_GROUP}" ]; then
      PARAM_GROUP="$(readLegacySetting ${CONF_FIELD_NM_GROUP})"
      toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_GROUP}: ${PARAM_GROUP}"
    fi
  fi
}
listProcesses() {
  local errorMessage="${1}"
  local includeRegex="${2}"

  local excludeRegex=" grep"
  if [ "${3}" ]; then
    excludeRegex="grep|${3}"
  fi

  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E \"${includeRegex}\" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
  local output
  if ! output="$(pgrep -f "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
    toLogError "Failed to get ${errorMessage}, output: ${output}"
    return 1
  fi

  local foundProcesses
  foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
  if [ ! "${foundProcesses}" ]; then
    return 1
  fi

  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
  return 0
}

#== 版本&帮助信息
preParams() {
  if [ $# -gt 0 ]; then
    if [ "${1}"x = "-h"x ] || [ "${1}"x = "--help"x ]; then
        displayHelp
        exit "${EXIT_CODE_OK}"
    fi

    if [ "${1}"x = "-v"x ] || [ "${1}"x = "--version"x ]; then
       pad=20
       printf "%-${pad}s%s\n" "Version:" "${AGENT_INSTALLER_VERSION}"
       printf "%-${pad}s%s\n" "Commit:" "${AGENT_BUILD_TAG}"
       printf "%-${pad}s%s\n" "Build At(UTC):" "${AGENT_BUILD_DATE_INFO}"
       printf "%-${pad}s%s\n" "Uploader:" "${AGENT_BUILD_UPLOADER}"
       exit "${EXIT_CODE_OK}"
    fi
  fi
}
#**********************************************************
# Main script functions
#**********************************************************

#== 【0】=初始化安装环境
initializeInstallation() {
  preParams "$@"
  #== 设置PATH
  setPATH
  #== 配置umask权限
  local initialUmask
  initialUmask="$(umask)"
  umask 022

  #== 读取配置参数
#  readParamsSection
  #== 解析命令行参数
  parseCommandLineParameters "$@"
  #== 校验配置参数

  #== 是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
  if isAnotherInstallationRunning; then
    #== 结束安装操作
    finishInstallation "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}" "keep_lock_file"
  fi

  #== 创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
  createInstallationLockFile
  #== 配置是否更新
  PARAM_UPGRADE="no"

  #== 获取并校验系统兼容性【LINUX/AIX  X86_64/IA64】
  local platformDetectionString
  if ! platformDetectionString="$(checkSystemCompatibility)"; then
    toConsoleError "${platformDetectionString}"
    finishInstallation "${EXIT_CODE_OS_NOT_SUPPORTED}"
  fi

  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
  createSymlinkToInstallLocation
  #== 创建日志目录及日志文件installation_$$.log
  createLogDirsIfMissing
  #== 配置信号捕获，执行清空安装临时目录
  configureSignalHandling "cleanInstallationTemporaryFiles"
  #== 常规日志
  initializeLog "${@}"

  toLogInfo "Initial umask: ${initialUmask}"
  toConsoleInfo "${platformDetectionString}"
  toLogInfo "Distribution: $(detectLinuxDistribution)"
}

#== 【3】=安装前检查
preInstallationChecks() {
  #== 检查发现初始化系统信息（INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
  detectInitSystem

  #== 检查目录权限
  checkAccessRightsToDirs
  #== 创建临时目录
  prepareTempFolder
  #== 检查是否已经安装
  checkIfAlreadyInstalled

  if [ ! "${PARAM_USER}" ] && [ "${ARCH_ARCH}"x != "AIX"x ] ; then
    #== 配置文件中配置用户和用户组是否存在
    checkUserAndGroupFromConfig
  fi

  #== 检查安装目录空间
  checkInstallPathFreeSpace

}

uninstallAgent() {
	toConsoleInfo "Agent already installed. Uninstalling previous version."
#	printf '%s' "upgrade" >"${UNINSTALL_INFO_FILE_LEGACY_PATH}"

	#shellcheck disable=SC2086
	"${INSTALL_DIR}/uninstall.sh" $$ ${SKIP_PRIVILEGES_CHECK} 2>>"${LOG_FILE}"

	local uninstallExitCode=$?
	if [ ${uninstallExitCode} -gt 0 ]; then
		toConsoleError "Error during uninstalling, code: ${uninstallExitCode}. Installation aborted."
		finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
	fi

	toConsoleInfo "Agent uninstalled."
}

#== 【4】=部署文件
deployFiles() {
  if [ "${PARAM_UPGRADE}" = "yes" ]; then
    uninstallAgent
  fi
  #== 【部署】解压文件
  extractFiles
  setupAll
  createCurrentVersionSymlink
}
enableRootDropping() {
  local output=
  if ! isAvailable setcap; then
    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. Set NON_ROOT_MODE=false."
    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${LEGACY_AGENT_CONF_FILE}"
    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${INSTALLER_CONF_FILE}"
    #== editScriptFileParam "readonly PARAM_NON_ROOT_MODE" "false" "${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE}"
    return
  fi

  #== 设置文件权限 (https://www.cnblogs.com/xzongblogs/p/14106481.html)
  #== CAP_DAC_OVERRIDE:绕过文件的读，写，和执行权限检查。
  #== CAP_FOWNER:对于通常要求进程的文件系统 UID 与文件的 UID 匹配的操作，绕过权限检查 (比如，chmod(2)，utime(2))，除了那些包含在 CAP_DAC_OVERRIDE 和 CAP_DAC_READ_SEARCH 中的操作
  #== CAP_IPC_LOCK:允许锁定共享内存片段
  #== CAP_SYS_PTRACE:允许跟踪任何进程
  #== CAP_SYS_ADMIN:访问特权 perf 事件信息
  #== CAP_SYS_RESOURCE:忽略资源限制
  #== CAP_NET_ADMIN:允许执行多种网络有关的操作
  commandErrorWrapper setcap cap_setgid,cap_setuid,cap_dac_override,cap_fowner,cap_chown,cap_ipc_lock,cap_sys_ptrace,cap_sys_admin,cap_sys_resource,cap_net_admin+ep "${AGENT_BIN_DIR}/${AGENT_PROC}"
  local setCapCwServerAgentExitCode=$?
  if [ ${setCapCwServerAgentExitCode} -eq 0 ] ; then
    toConsoleInfo "Set file capabilities [${AGENT_PROC}]"
  else
    toConsoleWarn "Failed to enable non-privileged mode. Exit Code : ${setCapCwServerAgentExitCode} . For details, see: ${LOG_FILE}"
    toLogWarn "Set file capabilities output: ${output}"
  fi
}

#== 【5】=配置安装
configureInstallation() {
  #== 配置参数（param、installation.conf）
  #== 自定义修改配置, sed ,echo ...  参考 applyAgentSettings
  if [ "${PARAM_TEST}" ]; then
    toConsoleOnly "Applying test value is: ${PARAM_TEST}"
  fi
  #== 获取bool值参数
  if [ "${PARAM_BOOL}" ]; then
    toConsoleOnly "Applying bool value is: ${PARAM_BOOL}"
  fi


  if [ "${ARCH_ARCH}"x != "AIX"x ]; then
    #== 配置用户和用户组
    if handleUser; then
      #== 修改文件系统用户和用户组
      changeFilesOwnership
    fi
    #== 设置root权限
    enableRootDropping
  fi
  #== 系统配置（策略配置、dump proc、agent 进程配置、自动启动）
#  setupAutostart
}

#== 【6】=验证安装状态、运行启动脚本
postInstallationSteps() {
  #== 运行agent
#  runAgents
  finishInstallation "${EXIT_CODE_OK}"
}

readonly CURR_PATH="$(pwd)"
main() {
  #== 【0】=初始化安装环境
  initializeInstallation "$@"
  #== 【3】=安装前检查
  preInstallationChecks
  #== 【4】=部署文件
  deployFiles
  #== 【5】=配置安装
  configureInstallation
  #== 【6】=验证安装状态、运行启动脚本
  postInstallationSteps

}

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

################################################
############# DO NOT REMOVE THIS ###############
#### DO NOT ADD ANYTHING BELOW THIS COMMENT ####
################################################
#################ENDOFSCRIPTMARK################

----SIGNED-INSTALLER
