#!/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 <${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 } #== 【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权限 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