#!/bin/bash
# shellcheck disable=SC2034,SC2155,SC2046,SC2001
#########################################################################
# File Name: system_init_v2.sh
# Author: LookBack
# Email: admin#dwhd.org
# Version: 0.0.3
# Created Time: 2025-08-08
# Description: Further optimized version of system_init_optimized.sh
#              focusing on robustness, correctness and maintainability.
#########################################################################

set -Eeuo pipefail

# 基本错误陷阱：提供更好的诊断信息（避免污染标准输出）
trap on_error ERR

# ======================================================================
# 目录（概要）
#   1) 命令行与全局变量
#   2) 通用工具
#   3) Docker Compose 辅助函数
#   4) 软件仓库与包管理器
#   5) 系统软件与工具
#   6) 外观（MOTD/PS1）
#   7) SSH（基础配置）
#   8) 内核、时间与 IPv6
#   9) 防火墙
#  10) Sysctl 与性能调优
#  11) Docker
#  12) 安全与加固（SSH 加固、fail2ban、auditd、FIPS）
#  13) NUMA/RPS 网络调优
#  14) 监控与杂项
#  15) 主入口
# ======================================================================

# 函数导航索引
#   - 命令行与全局变量
#       printHelp, checkRoot, checkOSinfo, pkgmgr, arch_id, is_ubuntu/is_rocky/is_alma/is_centos/is_rhel
#   - 通用工具
#       log/log_* 系列, try, run_pkg, is_debug, log_level_to_num, log_should_print, safe_source
#       disableRHSMOnNonRHEL, enableCRBOrPowerTools, configureAlmaLinuxAliyunRepos
#       efiVendorDir, updateEFIGrubCfg, ubuntu_codename, is_cn
#       pm_install/pm_groupinstall/pm_clean/pm_makecache, has_kernel_exclude, pm_install_no_excludes
#   - GitHub 辅助函数
#       gh_candidates_for, gh_download, gh_api
#   - Docker 辅助函数
#       detectComposeArch, getLatestComposeVersion, currentComposeVersion, versionLT
#       ensureComposeShimIfNeeded, installOrUpdateDockerCompose
#       buildDockerMirrorCandidates, selectFastDockerMirrors
#       mergeDockerMirrorsIntoDaemon, mergeDockerExtraFields, addrPoolsCsvToJson
#   - 软件仓库与包管理器
#       configureAptSources, configureYumDnf, addExtraRepos
#       disableCentOS8StaleRepos, pickFastRepoForCentOS8, writeCentOS8VaultRepo
#   - 系统软件与工具
#       updateSystemAndInstallTools
#   - 外观（MOTD/PS1）
#       addMotd, setPS1, addBINscript
#   - SSH
#       setSSH, hardenSSH
#   - 内核、时间与 IPv6
#       changeKernel, installKernelFallbackEl7, installKernelFallbackEl8
#       syncTime, disableIPV6
#   - 防火墙
#       setFirewall, setFirewallUbuntu, ensureIptablesAllowPort, setupIptablesPersist, applyFirewallRateLimit
#   - Sysctl 与性能调优
#       setSYSCTL, adaptiveSysctl
#   - Docker
#       dockerINSTALL
#   - 安全与加固
#       tuneLimits, tuneJournald, optimizeServices, enableTunedPerformance
#       configureTHP, enableFail2ban, enableAuditd, enableFIPSMode
#   - NUMA/RPS 网络调优
#       ensureDevToolchain, generateCPUMask, configureNUMANetRPS
#   - 监控与杂项
#       installZabbixAgent, addSpeedtest, changZHCN, fixLocaleWarnings
#   - 编排与计划
#       stage_desc, stage_execute, define_stages_for_subcommand
#       print_plan_table, print_plan_json, run_orchestrated
#       print_summary_table, print_summary_json
#   - 主入口
#       main

# === 章节：命令行与全局变量 ===
# --- 全局变量 ---
OS=""
RELEASE=""
BIT=""
SERVER_LOCATION=""
readonly GIT_REPO_URL="http://git.dwhd.org/lookback/CentOS_INIT/-/raw/master"

# 功能开关（可被命令行参数覆盖）
NETWORK_TYPE="public"
INSTALL_DOCKER="true"
INSTALL_ZABBIX="true"
DO_ADAPTIVE_SYSCTL="true"
DO_HARDEN_SSH="true"
DO_FIREWALL_LIMIT="true"
TUNED_PROFILE="throughput-performance"
DO_THP="true"
DO_FAIL2BAN="true"
DO_AUDITD="true"
DO_NUMA_RPS="true"
ENABLE_FIPS="false"
ZBX_SERVER_IP="180.150.154.122"
INSECURE_REG_CSV=""
EXEC_OPTS_CSV=""
STORAGE_DRIVER=""
DATA_ROOT=""
DEFAULT_ADDR_POOLS_CSV=""
MIRROR_PROBE_TIMEOUT="3"
MIRROR_MAX_JOBS="6"
MIRROR_CANDIDATES_CSV=""
# 内核安装开关
DO_KERNEL_ML="true"
# Pyenv 安装开关
INSTALL_PYENV="true"
# pyenv 的默认 Python 版本（为空则不自动安装）。若通过环境变量提供则优先生效。
PYENV_PY_VERSION="${PYENV_PY_VERSION:-}"
# Docker Compose 安装模式：auto | plugin | standalone
COMPOSE_INSTALL_MODE="${COMPOSE_INSTALL_MODE:-auto}"
# 实际生效的 Compose 模式/版本（运行时填充）
COMPOSE_MODE_EFFECTIVE=""
COMPOSE_VERSION_EFFECTIVE=""
# GitHub mirrors for CN/HK
GH_USE_MIRRORS="${GH_USE_MIRRORS:-auto}"
GH_TIMEOUT="${GH_TIMEOUT:-12}"

# 运行时标志
DRY_RUN="false"
NO_REBOOT="false"
LOG_FILE=""
SUBCOMMAND="all"

printHelp() {
    cat << 'EOF'
Usage: system_init_2.sh [options]

Network:
  -p, --private                  私网模式（默认公网）

Install:
  -D, --nodocker                 跳过安装 Docker
  -Z, --nozabbix                 跳过安装 Zabbix Agent

Tuning/Security toggles:
  -A, --noadaptive               关闭按内存自适应 sysctl
  -S, --nohardenssh              跳过 SSH 加固
  -F, --nofwlimit                跳过 SSH 防火墙限速
  -T, --nothp                    不修改透明大页（THP）
  -B, --nofail2ban               不启用 fail2ban
  -U, --noauditd                 不启用 auditd
  -N, --nonuma                   跳过 NUMA/RPS 优化
  -t <profile>, --tuned=<profile>
                                 指定 tuned profile（默认 throughput-performance）
  -f, --fips                     启用 FIPS（需重启，限制加密算法）
  -z <ip>, --zabbix=<ip>         指定 Zabbix Server IP（默认 180.150.154.122）
  -i <csv>, --insecure=<csv>    追加 Docker insecure-registries（逗号分隔）
  -e <csv>, --execopts=<csv>    追加 Docker exec-opts（逗号分隔，如 native.cgroupdriver=systemd）
  -s <drv>, --storage=<drv>     设置 Docker storage-driver（如 overlay2）
  -d <dir>, --dataroot=<dir>    设置 Docker data-root（如 /var/lib/docker）
  -a <csv>, --addrpools=<csv>    设置 default-address-pools（格式 base/16:24,base2/16:24）
  -w <sec>, --mirror-timeout=<sec>
                                  选择镜像时的探测超时（秒，默认 3）
  -j <num>, --mirror-jobs=<num>   并发探测任务数（默认 6）
  -m <csv>, --mirrors=<csv>       自定义候选镜像（逗号分隔）

Misc:
  -n, --dry-run                  演练模式，仅打印将执行的步骤
  -L <file>, --log-file=<file>   指定日志文件（默认 /var/log/system_init_2.log）
  -R, --no-reboot                运行完成后不重启
  -K, --nokernelml               不安装 kernel-ml（EL7/8 回退逻辑也将跳过）
  -g <level>, --log-level=<level> 设置日志级别(DEBUG/INFO/WARN/ERROR/IMPORTANT，默认 INFO)
  -v, --verbose                  等同于 --log-level=DEBUG
  -c, --continue-on-error        阶段失败继续执行（默认遇错退出）
  -J, --plan-json                以 JSON 输出计划（通常与 --dry-run 一起使用）
  -h, --help                     显示此帮助

子命令（可选，默认 all）：
  all        执行完整初始化流程（不含 speedtest）
  docker     仅安装/配置 Docker（含 docker-compose）
  firewall   仅设置防火墙与持久化
  pyenv      仅安装 pyenv（若设置 PYENV_PY_VERSION 则同时安装该版本 Python）
  speedtest  仅安装 LibreSpeed CLI 网络测速工具
EOF
}

# === 章节：通用工具函数 ===

# === 第1层：基础设施层 ===
# --- 1.1 错误处理 ---

on_error() {
    local exit_code=$?
    local line_no=${BASH_LINENO[0]:-unknown}
    # 在子 shell/后台环境中跳过冗余日志，避免污染命令替换的输出
    if [[ ${BASH_SUBSHELL:-0} -gt 0 ]]; then
        return
    fi
    # 直接输出到标准错误，避免与标准输出混淆
    printf '[%s] [ERROR] Script error at line %s, exit %s.\n' "$(date '+%F %T')" "${line_no}" "${exit_code}" >&2
}

# --- 1.2 日志系统 ---

log_level_to_num() {
    case "${1^^}" in
        DEBUG) echo 10 ;;
        INFO) echo 20 ;;
        OK | SUCCESS) echo 22 ;;
        NOTE) echo 25 ;;
        WARN | WARNING) echo 30 ;;
        ERROR | FAIL) echo 40 ;;
        IMPORTANT | ALERT) echo 50 ;;
        *) echo 20 ;;
    esac
}

log_should_print() {
    local level_num=0 cur=0
    level_num=$(log_level_to_num "$1")
    cur=$(log_level_to_num "${LOG_LEVEL}")
    [[ "$level_num" -ge "$cur" ]]
}

log() {
    local level msg ts plain code="" PLAIN
    level="${1:-INFO}"
    shift || true
    msg="${*:-}"
    log_should_print "$level" || return 0
    ts="$(date '+%F %T')"
    plain="[${ts}] [${level^^}] ${msg}"
    PLAIN='\033[0m'
    case "${level^^}" in
        DEBUG) code='\033[0;37m' ;; # gray
        INFO) code='\033[0;32m' ;;
        OK | SUCCESS) code='\033[0;32m' ;;
        NOTE) code='\033[0;36m' ;;
        WARN | WARNING) code='\033[0;33m' ;;
        ERROR | FAIL) code='\033[0;31m' ;;
        IMPORTANT | ALERT) code='\033[45;37;1m' ;;
        *) code='' ;;
    esac
    # Send WARN/ERROR/IMPORTANT to stderr; others to stdout
    case "${level^^}" in
        WARN | WARNING | ERROR | FAIL | IMPORTANT | ALERT)
            if [[ -n "$code" ]]; then
                >&2 echo -e "${code}${plain}${PLAIN}"
            else
                >&2 echo "$plain"
            fi
            ;;
        *)
            if [[ -n "$code" ]]; then
                echo -e "${code}${plain}${PLAIN}"
            else
                echo "$plain"
            fi
            ;;
    esac
}

log_info() { log INFO "$@"; }

log_warn() { log WARN "$@"; }

log_note() { log NOTE "$@"; }

log_ok() { log OK "$@"; }

log_error() { log ERROR "$@"; }

log_important() { log IMPORTANT "$@"; }

log_debug() { log DEBUG "$@"; }

# --- 1.3 基础工具函数 ---

try() { "$@" > /dev/null 2>&1 || true; }

is_debug() {
    local cur
    cur=$(log_level_to_num "${LOG_LEVEL}")
    [[ "$cur" -le 10 ]]
}

run_pkg() {
    if is_debug; then
        "$@"
    else
        "$@" > /dev/null 2>&1
    fi
}

kv_set() {
    # kv_set <file> <key> <value> [sep='=']
    local file="$1" key="$2" val="$3" sep="${4:-=}"
    [[ -f "$file" ]] || touch "$file" 2> /dev/null || true
    # 转义分隔符/键名以适配正则
    local esc_key="" esc_sep=""
    esc_key=$(printf '%s' "$key" | sed -e 's/[\^$*+.?()[{}|]/\\&/g')
    esc_sep=$(printf '%s' "$sep" | sed -e 's/[\^$*+.?()[{}|]/\\&/g')
    # 删除该键的所有现有定义（含注释/空白变体），避免重复
    sed -ri "/^[[:space:]]*#?[[:space:]]*${esc_key}[[:space:]]*${esc_sep}.*$/d" "$file" 2> /dev/null || true
    # 追加唯一一条定义
    printf '%s %s %s\n' "$key" "$sep" "$val" >> "$file" || true
}

ini_set() {
    # ini_set <file> <key> <value>
    kv_set "$1" "$2" "$3" '='
}

json_merge_jq() {
    # json_merge_jq <file> <jq-filter>
    local file="$1" filter="$2" tmp
    tmp="${file}.tmp.$$"
    if command -v jq > /dev/null 2>&1; then
        jq -M "$filter" "$file" > "$tmp" 2> /dev/null && mv -f "$tmp" "$file" && return 0
    fi
    rm -f "$tmp" 2> /dev/null || true
    json_merge_simple "$@"
}

json_merge_simple() {
    # json_merge_simple <file> <jq-like-filter>
    # 简易实现：若有 python3，用 python3 处理；否则保底写回原文件不变（返回 1）
    local file="$1" filter="$2" tmp
    tmp="${file}.tmp.$$"
    if command -v python3 > /dev/null 2>&1; then
        python3 - "$file" "$filter" > "$tmp" 2> /dev/null << 'PY'
import json, sys
fp, flt = sys.argv[1], sys.argv[2]
try:
    data=json.load(open(fp,'r'))
except Exception:
    data={}
# 仅支持最常见的合并表达式形如: .key = value | .k2 = value2
# 这里实现一个极简解析器：按 '|', '=' 分割，仅支持顶层 key 赋值
for expr in flt.split('|'):
    expr = expr.strip()
    if not expr.startswith('.') or '=' not in expr:
        continue
    left,right = expr.split('=',1)
    key = left.strip()[1:].strip()
    try:
        val = json.loads(right.strip())
    except Exception:
        # 作为字符串
        val = right.strip().strip('"')
    data[key]=val
json.dump(data, sys.stdout, separators=(',',':'))
PY
        if [ -s "$tmp" ]; then
            mv -f "$tmp" "$file"
            return 0
        fi
    fi
    rm -f "$tmp" 2> /dev/null || true
    return 1
}

arr_contains() { # arr_contains <array_name> <value>
    local -n __arr="$1"
    local __v="$2"
    local x
    for x in "${__arr[@]}"; do [[ "$x" == "$__v" ]] && return 0; done
    return 1
}

arr_add_unique() { # arr_add_unique <array_name> <values...>
    # 保存原始数组名，避免将 nameref 名字传递给下游造成循环引用
    local __arr_name="$1"
    local -n __arr="$1"
    shift
    local v
    for v in "$@"; do
        if arr_contains "${__arr_name}" "$v"; then
            :
        else
            __arr+=("$v")
        fi
    done
}

arr_remove() { # arr_remove <array_name> <values...>
    local -n __arr="$1"
    shift
    local -A del=()
    local v
    for v in "$@"; do del["$v"]=1; done
    local -a tmp=()
    for v in "${__arr[@]}"; do [[ -n "${del[$v]-}" ]] || tmp+=("$v"); done
    __arr=("${tmp[@]}")
}

safe_source() {
    local target="${1:-}"
    if [[ -r "$target" ]]; then
        # Predefine possibly referenced variables to avoid "unbound variable"
        export USER_LS_COLORS="${USER_LS_COLORS-}"
        set +u
        # shellcheck source=/dev/null
        . "$target"
        set -u
    fi
}


# === 第2层：系统检测与环境判断层 ===

# --- 2.1 系统信息检测 ---

checkRoot() {
    if [[ "$(id -u)" -ne 0 ]]; then
        log_error "Please use the root user to run this script."
        exit 1
    fi
}

checkOSinfo() {
    log_info "Checking System Information....."
    if [[ -f /etc/os-release ]]; then
        . /etc/os-release
        case "${ID,,}" in
            centos | rhel | rocky | almalinux)
                OS="${ID,,}"
                RELEASE="$(echo "${VERSION_ID}" | cut -d. -f1)"
                ;;
            ubuntu)
                OS=$ID
                RELEASE="$(echo "${VERSION_ID}" | cut -d. -f1)"
                ;;
            *)
                log_error "Unsupported operating system: ${NAME}"
                exit 1
                ;;
        esac
    elif [[ -f /etc/centos-release ]]; then
        OS="CentOS"
        RELEASE="$(rpm -q --qf '%{VERSION}' centos-release)"
    elif [[ -f /etc/redhat-release ]]; then
        OS="RedHat"
        RELEASE="$(rpm -q --qf '%{VERSION}' redhat-release)"
    else
        log_error "Cannot determine the operating system."
        exit 1
    fi

    BIT="$(getconf LONG_BIT)"
    
    # Initialize architecture variable (used by is_arch_x86_64, is_arch_aarch64)
    ARCH="$(arch_id)"

    # Determine server location (best-effort) using ISO country codes
    local cc
    cc=$(curl -m 4 -fsSL https://ifconfig.co/country-iso 2> /dev/null || true)
    cc="$(printf '%s' "$cc" | tr -d '\r\n\t ')"
    if [[ -z "$cc" ]]; then
        cc=$(curl -m 4 -fsSL https://ipapi.co/country/ 2> /dev/null || true)
        cc="$(printf '%s' "$cc" | tr -d '\r\n\t ')"
    fi
    if [[ -z "$cc" ]]; then
        # Fallback to previous heuristic
        local ip_info
        ip_info=$(curl -m 4 -Lks curlip.me || true)
        if [[ -n "${ip_info}" ]]; then
            if grep -q "中国" <<< "${ip_info}"; then
                if grep -q "香港" <<< "${ip_info}"; then
                    SERVER_LOCATION="HK"
                else
                    SERVER_LOCATION="CN"
                fi
            else
                SERVER_LOCATION="Other"
            fi
        else
            SERVER_LOCATION="Other"
        fi
    else
        case "$cc" in
            CN) SERVER_LOCATION="CN" ;;
            HK) SERVER_LOCATION="HK" ;;
            *) SERVER_LOCATION="Other" ;;
        esac
    fi
    
    # Export variables for use by helper functions
    export OS RELEASE BIT ARCH SERVER_LOCATION
}

pkgmgr() {
    if [[ "${OS,,}" == "ubuntu" ]]; then
        echo apt-get
        return
    fi
    if [[ "${RELEASE}" -ge 8 ]]; then
        echo dnf
    else
        echo yum
    fi
}

arch_id() {
    local m
    m="$(uname -m)"
    case "$m" in
        x86_64 | amd64) echo "x86_64" ;;
        aarch64 | arm64) echo "aarch64" ;;
        *) echo "$m" ;;
    esac
}

is_arch_x86_64() { [[ "$ARCH" == "x86_64" ]]; }

is_arch_aarch64() { [[ "$ARCH" == "aarch64" ]]; }

is_ubuntu() { [[ "${OS,,}" == "ubuntu" ]]; }

is_rocky() { [[ "${OS,,}" == "rocky" ]]; }

is_alma() { [[ "${OS,,}" == "almalinux" ]]; }

is_centos() { [[ "${OS,,}" == "centos" ]]; }

is_rhel() { [[ "${OS,,}" == "rhel" ]]; }

ubuntu_codename() {
    if command -v lsb_release > /dev/null 2>&1; then lsb_release -cs; else
        . /etc/os-release 2> /dev/null || true
        echo "${VERSION_CODENAME:-focal}"
    fi
}

efiVendorDir() {
    local d
    for d in "/boot/efi/EFI/rocky" "/boot/efi/EFI/centos" "/boot/efi/EFI/redhat" "/boot/efi/EFI/almalinux"; do
        [[ -d "$d" ]] && {
            echo "$d"
            return 0
        }
    done
    echo ""
}

updateEFIGrubCfg() {
    local d
    d="$(efiVendorDir)"
    [[ -n "$d" ]] && grub2-mkconfig -o "$d/grub.cfg" > /dev/null 2>&1 || true
}

# --- 2.2 网络位置判断 ---

is_cn() { [[ "$SERVER_LOCATION" == "CN" || "$SERVER_LOCATION" == "HK" ]]; }

get_first_ipv4() {
    local public_ip=""
    local private_ip=""
    
    # Try to get public IP from multiple sources
    # Priority: ip-api.com > ipinfo.io > ifconfig.me
    if command -v curl >/dev/null 2>&1; then
        # Try ip-api.com (fast, no rate limit for personal use)
        public_ip=$(timeout 3 curl -fsSL --max-time 3 'http://ip-api.com/line/?fields=query' 2>/dev/null | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
        
        # Fallback to ipinfo.io
        if [ -z "$public_ip" ]; then
            public_ip=$(timeout 3 curl -fsSL --max-time 3 'https://ipinfo.io/ip' 2>/dev/null | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
        fi
        
        # Fallback to ifconfig.me
        if [ -z "$public_ip" ]; then
            public_ip=$(timeout 3 curl -fsSL --max-time 3 'https://ifconfig.me' 2>/dev/null | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
        fi
    fi
    
    # Get private IP as fallback
    if command -v ip >/dev/null 2>&1; then
        private_ip=$(ip -4 -o addr show up scope global 2>/dev/null | awk '{print $4}' | sed 's#/.*##' | head -n1)
    fi
    if [ -z "$private_ip" ]; then
        private_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
    fi
    if [ -z "$private_ip" ] && command -v ip >/dev/null 2>&1; then
        private_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '/src/ {print $7; exit}')
    fi
    
    # Use public IP if available, otherwise use private IP
    if [ -n "$public_ip" ]; then
        echo "${public_ip}"
    else
        echo "${private_ip}"
    fi
}

# --- 2.3 包管理器判断 ---

pm_is_apt() { [[ "$(pkgmgr)" == apt-get ]]; }

pm_is_dnf() { command -v dnf > /dev/null 2>&1; }

pm_is_yum() { command -v yum > /dev/null 2>&1; }

# === 第3层：包管理器封装层 ===

# --- 3.1 包管理器通用接口 ---

pm__parse_common_flags() {
    PM_FLAG_NO_EXCLUDES=false
    PM_FLAG_ENABLE_REPOS=""
    PM_FLAG_DISABLE_REPOS=""
    PM_FLAG_NOBEST=false
    PM_FLAG_ALLOWERASING=false
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --no-excludes)
                PM_FLAG_NO_EXCLUDES=true
                shift
                ;;
            --enable)
                PM_FLAG_ENABLE_REPOS="$2"
                shift 2
                ;;
            --disable)
                PM_FLAG_DISABLE_REPOS="$2"
                shift 2
                ;;
            --nobest)
                PM_FLAG_NOBEST=true
                shift
                ;;
            --allowerasing)
                PM_FLAG_ALLOWERASING=true
                shift
                ;;
            --)
                shift
                break
                ;;
            *) break ;;
        esac
    done
    PM_ARGS_REST=("$@")
}

pm_install() {
    pm__parse_common_flags "$@"
    set -- "${PM_ARGS_REST[@]}"
    if pm_is_apt; then
        NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
            run_pkg apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew "$@" || true
        return
    fi
    # dnf/yum path
    local cmd="dnf"
    pm_is_dnf || cmd="yum"
    local -a extra=()
    $PM_FLAG_NO_EXCLUDES && extra+=(--disableexcludes=all)
    [[ -n "$PM_FLAG_ENABLE_REPOS" ]] && extra+=(--enablerepo="$PM_FLAG_ENABLE_REPOS")
    [[ -n "$PM_FLAG_DISABLE_REPOS" ]] && extra+=(--disablerepo="$PM_FLAG_DISABLE_REPOS")
    $PM_FLAG_NOBEST && extra+=(--nobest)
    $PM_FLAG_ALLOWERASING && extra+=(--allowerasing)
    # EL8+ 常见需要 allowerasing；若未显式指定，则按版本启用
    if [[ "${RELEASE:-0}" -ge 8 && "$cmd" == dnf && "$PM_FLAG_ALLOWERASING" == false ]]; then
        extra+=(--allowerasing)
    fi
    run_pkg "$cmd" -y install "${extra[@]}" "$@" || true
}

pm_groupinstall() {
    pm__parse_common_flags "$@"
    set -- "${PM_ARGS_REST[@]}"
    if pm_is_apt; then
        # apt 无 groupinstall：提供常用组的等价包映射
        # 示例："Development Tools" → build-essential gcc make pkg-config
        local group="$1"
        shift || true
        case "$group" in
            *Development*Tools*)
                NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
                    run_pkg apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew \
                    build-essential gcc make pkg-config >/dev/null 2>&1 || true
                ;;
            *)
                # 回退：按剩余参数安装
                [[ "$group" != "" ]] && set -- "$group" "$@"
                NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
                    run_pkg apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew "$@" >/dev/null 2>&1 || true
                ;;
        esac
        return
    fi
    local cmd="dnf"
    pm_is_dnf || cmd="yum"
    local -a extra=()
    $PM_FLAG_NO_EXCLUDES && extra+=(--disableexcludes=all)
    [[ -n "$PM_FLAG_ENABLE_REPOS" ]] && extra+=(--enablerepo="$PM_FLAG_ENABLE_REPOS")
    [[ -n "$PM_FLAG_DISABLE_REPOS" ]] && extra+=(--disablerepo="$PM_FLAG_DISABLE_REPOS")
    $PM_FLAG_NOBEST && extra+=(--nobest)
    $PM_FLAG_ALLOWERASING && extra+=(--allowerasing)
    run_pkg "$cmd" -y groupinstall "${extra[@]}" "$@" || true
}

pm_clean() {
    if pm_is_apt; then
        apt-get clean > /dev/null 2>&1 || true
        return
    fi
    local cmd="dnf"
    pm_is_dnf || cmd="yum"
    run_pkg "$cmd" clean all || true
}

pm_makecache() {
    if pm_is_apt; then
        DEBIAN_FRONTEND=noninteractive apt-get update -y > /dev/null 2>&1 || true
        return
    fi
    local cmd="dnf"
    pm_is_dnf || cmd="yum"
    run_pkg "$cmd" makecache || true
}

has_kernel_exclude() {
    local found=1
    if grep -qiE '^\s*exclude\s*=.*kernel' /etc/yum.conf 2> /dev/null; then
        found=0
    fi
    if grep -qiE '^\s*exclude\s*=.*kernel' /etc/dnf/dnf.conf 2> /dev/null; then
        found=0
    fi
    if grep -RqiE '^\s*exclude\s*=.*kernel' /etc/yum.repos.d 2> /dev/null; then
        found=0
    fi
    return $found
}

pm_install_no_excludes() {
    local pm
    pm=$(pkgmgr)
    if [[ "$pm" == "apt-get" ]]; then
        NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
            run_pkg apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew "$@" >/dev/null 2>&1 || true
        return 0
    fi
    # yum/dnf path
    if has_kernel_exclude; then
        # Only raise visibility if we are installing kernel headers/devel
        case " $* " in
            *"kernel-headers"* | *"kernel-devel"*)
                log_warn "Detected exclude=kernel*; installing with --disableexcludes=all: $*"
                ;;
            *)
                log_debug "Installing with --disableexcludes=all: $*"
                ;;
        esac
    else
        log_debug "Installing: $*"
    fi
    $pm -y --disableexcludes=all install "$@" > /dev/null 2>&1 || return 1
    return 0
}


# === 第4层：外部资源下载辅助层 ===

# --- 4.1 GitHub 辅助函数 ---

gh_candidates_for() {
    # 为某个 GitHub 相关 URL 构建按优先级排序的候选地址（含加速镜像）
    local original="$1"
    local host
    host="${original#*://}"
    host="${host%%/*}"
    local -a list=()
    if [[ "$host" == "api.github.com" ]]; then
        if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
            list+=("https://ghproxy.com/${original}")
            list+=("${original}")
        else
            list+=("${original}")
            list+=("https://ghproxy.com/${original}")
        fi
    else
        # 处理 github.com / raw.githubusercontent.com
        local xget_url fastgit
        xget_url="${original/https:\/\/github.com/https:\/\/xget.xi-xu.me\/gh}"
        xget_url="${xget_url/https:\/\/raw.githubusercontent.com/https:\/\/xget.xi-xu.me\/gh}"
        fastgit="${original/https:\/\/github.com/https:\/\/download.fastgit.org}"
        
        if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
            # 国内节点：优先使用 xget.xi-xu.me
            list+=("${xget_url}")
            list+=("https://ghproxy.com/${original}")
            list+=("${fastgit}")
            list+=("${original}")
        else
            # 国际节点：使用 GitHub 原地址
            list+=("${original}")
            list+=("https://ghproxy.com/${original}")
            list+=("${fastgit}")
        fi
    fi
    printf '%s\n' "${list[@]}"
}

gh_download() {
    # gh_download <官方URL> <目标文件>：按候选镜像依次下载，成功即返回
    local url="$1" dest="$2"
    local tmp
    tmp="${dest}.tmp.$$"
    rm -f "$tmp" 2> /dev/null || true
    local u
    while IFS= read -r u; do
        if curl -m "${GH_TIMEOUT:-12}" -fLkS "$u" -o "$tmp" 2> /dev/null; then
            mv -f "$tmp" "$dest"
            return 0
        fi
    done < <(gh_candidates_for "$url")
    rm -f "$tmp" 2> /dev/null || true
    return 1
}

gh_api() {
    # gh_api <path> (e.g. /repos/docker/compose/releases/latest)
    local path="$1"
    local base="https://api.github.com"
    local u
    for u in "https://ghproxy.com/${base}${path}" "${base}${path}"; do
        if curl -m "${GH_TIMEOUT:-12}" -fsSL "$u" 2> /dev/null; then
            return 0
        fi
    done
    return 1
}

# === 第5层：系统服务管理层 ===

# --- 5.1 服务管理函数 ---

enable_now() {
    local svc="$1"
    systemctl enable --now "$svc" > /dev/null 2>&1 || true
}

reload_sshd() {
    # 统一处理，优先验证配置；不同发行版分别执行
    local cfg=/etc/ssh/sshd_config
    local crypto_policy_cfg="/etc/crypto-policies/back-ends/opensshserver.config"
    local crypto_backup=""
    
    if command -v sshd > /dev/null 2>&1; then
        # 检查配置，捕获错误信息
        local test_output
        local test_rc
        test_output=$(sshd -t -f "$cfg" 2>&1) || test_rc=$?
        test_rc=${test_rc:-0}
        
        if [[ $test_rc -ne 0 ]]; then
            # Configuration validation failed
            log_error "═══════════════════════════════════════════════════════════"
            log_error "⚠️  SSH 配置验证失败！"
            log_error "═══════════════════════════════════════════════════════════"
            log_error ""
            log_error "错误详情:"
            echo "$test_output" | while IFS= read -r line; do
                log_error "  $line"
            done
            log_error ""
            
            # 如果是 crypto-policies 相关错误，尝试修复
            if echo "$test_output" | grep -q "crypto-policies.*GSSAPIKexAlgorithms"; then
                log_warn "检测到 crypto-policies GSSAPIKexAlgorithms 问题，尝试修复..."
                
                if [[ -f "$crypto_policy_cfg" ]]; then
                    # 备份原文件
                    crypto_backup="${crypto_policy_cfg}.bak.$(date +%Y%m%d_%H%M%S)"
                    cp -f "$crypto_policy_cfg" "$crypto_backup" 2>/dev/null || true
                    log_note "备份 crypto-policies 配置: $crypto_backup"
                    
                    # 注释掉 GSSAPIKexAlgorithms 行
                    sed -i 's/^GSSAPIKexAlgorithms/#GSSAPIKexAlgorithms/g' "$crypto_policy_cfg" 2>/dev/null || true
                    
                    # 再次验证
                    test_output=$(sshd -t -f "$cfg" 2>&1) || test_rc=$?
                    test_rc=${test_rc:-0}
                    
                    if [[ $test_rc -eq 0 ]]; then
                        log_ok "crypto-policies 问题已修复，SSH 配置验证通过"
                    fi
                fi
            fi
            
            # 最终验证结果
            if [[ $test_rc -ne 0 ]]; then
                # Still failing after auto-fix attempt - display comprehensive error message
                cat >&2 << 'EOF_SSH_ERROR'

═══════════════════════════════════════════════════════════
🚨 严重警告：SSH 配置验证失败！
═══════════════════════════════════════════════════════════

⚠️  当前 SSH 配置存在错误，如果重启服务器：
  ❌ SSH 服务可能无法启动
  ❌ 您将失去远程访问权限
  ❌ 服务器将无法远程管理

📋 必须执行以下步骤才能安全重启：

  1. 检查 SSH 配置错误
     /usr/sbin/sshd -t -f /etc/ssh/sshd_config

  2. 根据错误信息修复配置文件
     vi /etc/ssh/sshd_config

  3. 如果是 crypto-policies 问题
     vi /etc/crypto-policies/back-ends/opensshserver.config
     # 注释掉或删除 GSSAPIKexAlgorithms 行

  4. 重新验证配置
     /usr/sbin/sshd -t

  5. 验证通过后重启 SSH 服务
     systemctl restart sshd

  6. 确认 SSH 服务正常后才能重启服务器
     systemctl status sshd
     ss -tlnp | grep :22
EOF_SSH_ERROR
                
                # 显示备份文件位置
                if [[ -f "$crypto_backup" ]]; then
                    cat >&2 << EOF_BACKUP

📁 备份文件位置：
  • Crypto 配置备份: $crypto_backup

🔄 恢复备份命令：
  cp -f "$crypto_backup" /etc/crypto-policies/back-ends/opensshserver.config
EOF_BACKUP
                fi
                
                cat >&2 << 'EOF_WARNING'

═══════════════════════════════════════════════════════════
🛑 请勿重启服务器！请先修复 SSH 配置问题！
═══════════════════════════════════════════════════════════

EOF_WARNING
                
                # Do NOT reload SSH if validation fails
                return 1
            fi
        fi
    fi

    # Validation passed or sshd not available, proceed with reload
    if is_ubuntu; then
        systemctl reload ssh > /dev/null 2>&1 || systemctl restart ssh > /dev/null 2>&1 || true
        return
    fi
    case "$RELEASE" in
        6)
            service sshd reload > /dev/null 2>&1 || service sshd restart > /dev/null 2>&1 || true
            ;;
        7 | 8 | 9 | 10)
            systemctl reload sshd > /dev/null 2>&1 || systemctl restart sshd > /dev/null 2>&1 || true
            ;;
        *)
            systemctl reload sshd > /dev/null 2>&1 || systemctl restart sshd > /dev/null 2>&1 || true
                ;;
        esac
}

# === 第6层：仓库配置层 ===

# --- 6.1 RHEL/CentOS 仓库配置辅助函数 ---

disableRHSMOnNonRHEL() {
    if [[ "${OS,,}" != "rhel" ]]; then
        log_warn "Disabling subscription-manager plugin on non-RHEL..."
        mkdir -p /etc/yum/pluginconf.d /etc/dnf/plugins
        printf "[main]\nenabled=0\n" > /etc/yum/pluginconf.d/subscription-manager.conf || true
        printf "[main]\nenabled=0\n" > /etc/dnf/plugins/subscription-manager.conf || true
    fi
}

enableCRBOrPowerTools() {
    local arch
    arch="$(arch_id)"
    if [[ "$RELEASE" -eq 8 ]]; then
        if is_rocky || is_alma || [[ "${OS,,}" == "centos" ]]; then
            dnf config-manager --set-enabled powertools > /dev/null 2>&1 \
                || dnf config-manager --set-enabled PowerTools > /dev/null 2>&1 || true
        elif [[ "${OS,,}" == "rhel" ]]; then
            # RHEL 8: try various CodeReady repo ids
            subscription-manager repos --enable "codeready-builder-for-rhel-8-${arch}-rpms" > /dev/null 2>&1 \
                || subscription-manager repos --enable "codeready-builder-for-rhel-8-rhui-rpms" > /dev/null 2>&1 || true
        fi
    elif [[ "$RELEASE" -eq 9 ]]; then
        if is_rocky || is_alma || [[ "${OS,,}" == "centos" ]]; then
            dnf config-manager --set-enabled crb > /dev/null 2>&1 || true
        elif [[ "${OS,,}" == "rhel" ]]; then
            subscription-manager repos --enable "codeready-builder-for-rhel-9-${arch}-rpms" > /dev/null 2>&1 \
                || subscription-manager repos --enable "codeready-builder-for-rhel-9-rhui-rpms" > /dev/null 2>&1 || true
        fi
    fi
}

configureAlmaLinuxAliyunRepos() {
    is_alma || return 0
    is_cn || return 0
    local arch
    arch="$(arch_id)"
    # Disable default almalinux repo ids if present (best-effort)
    dnf config-manager --set-disabled baseos appstream extras > /dev/null 2>&1 || true
    cat > /etc/yum.repos.d/almalinux-aliyun.repo << EOF
[baseos]
name=AlmaLinux-
baseurl=https://mirrors.aliyun.com/almalinux/\$releasever/BaseOS/\$basearch/os/
enabled=1
gpgcheck=0

[appstream]
name=AlmaLinux-AppStream
baseurl=https://mirrors.aliyun.com/almalinux/\$releasever/AppStream/\$basearch/os/
enabled=1
gpgcheck=0

[extras]
name=AlmaLinux-Extras
baseurl=https://mirrors.aliyun.com/almalinux/\$releasever/extras/\$basearch/os/
enabled=1
gpgcheck=0
EOF
}


disableCentOS8StaleRepos() {
    # Disable/move legacy CentOS 8 repo files to avoid EOL mirrorlist/baseurl抢优先级
    local d="/etc/yum.repos.d"
    mkdir -p "$d"
    for f in "$d"/*.repo; do
        [[ -e "$f" ]] || continue
        # keep our target file name; others move aside
        case "$(basename -- "$f")" in
            CentOS-Base.repo) continue ;;
        esac
        if grep -qiE 'CentOS|mirrorlist=.*centos|BaseOS|AppStream|PowerTools|extras' "$f"; then
            mv -f "$f" "$f.disabled.$(date +%s)" 2> /dev/null || true
        fi
    done
}

pickFastRepoForCentOS8() {
    local arch basearch url_rack url_ali t_rack t_ali chosen
    arch="$(uname -m)"
    case "$arch" in
        x86_64 | amd64) basearch="x86_64" ;;
        aarch64 | arm64) basearch="aarch64" ;;
        *) basearch="x86_64" ;;
    esac
    url_rack="https://mirror.rackspace.com/centos-vault/8.5.2111/BaseOS/${basearch}/os/repodata/repomd.xml"
    url_ali="https://mirrors.aliyun.com/centos-vault/8.5.2111/BaseOS/${basearch}/os/repodata/repomd.xml"

    t_rack=$(curl -m 5 -s -o /dev/null -w '%{time_total}' "$url_rack" 2> /dev/null || echo 9)
    t_ali=$(curl -m 5 -s -o /dev/null -w '%{time_total}' "$url_ali" 2> /dev/null || echo 9)

    # If both failed, fallback to aliyun
    if [[ "$t_rack" == 9 && "$t_ali" == 9 ]]; then
        echo aliyun
        return 0
    fi

    # If one failed, choose the other
    if [[ "$t_rack" == 9 ]]; then
        echo aliyun
        return 0
    fi
    if [[ "$t_ali" == 9 ]]; then
        echo rackspace
        return 0
    fi

    # Compare floats using awk
    if awk -v a="$t_rack" -v b="$t_ali" 'BEGIN{exit (a<b)?0:1}'; then
        chosen=rackspace
    else
        chosen=aliyun
    fi
    echo "$chosen"
}

writeCentOS8VaultRepo() {
    local origin="${1:-rackspace}"
    local base=""
    case "$origin" in
        rackspace) base="https://mirror.rackspace.com/centos-vault/8.5.2111" ;;
        aliyun | *) base="https://mirrors.aliyun.com/centos-vault/8.5.2111" ;;
    esac
    cat > /etc/yum.repos.d/CentOS-Base.repo << EOF
[base]
name=CentOS-8.5.2111 - BaseOS
baseurl=${base}/BaseOS/\$basearch/os/
enabled=1
gpgcheck=0

[appstream]
name=CentOS-8.5.2111 - AppStream
baseurl=${base}/AppStream/\$basearch/os/
enabled=1
gpgcheck=0

[extras]
name=CentOS-8.5.2111 - Extras
baseurl=${base}/extras/\$basearch/os/
enabled=1
gpgcheck=0
EOF
}

# --- 6.2 仓库配置主函数 ---

configureAptSources() {
    log_info "Configuring APT repositories..."
    local codename
    codename="$(ubuntu_codename)"
    if is_cn; then
        log_warn "Using Aliyun mirrors for Ubuntu ${codename}..."
        cat > /etc/apt/sources.list << EOF
deb https://mirrors.aliyun.com/ubuntu/ ${codename} main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ ${codename}-updates main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ ${codename}-backports main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ ${codename}-security main restricted universe multiverse
EOF
    else
        log_warn "Keeping official Ubuntu mirrors (${codename})..."
        [[ -s /etc/apt/sources.list ]] || {
            cat > /etc/apt/sources.list << EOF
deb http://archive.ubuntu.com/ubuntu/ ${codename} main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ ${codename}-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ ${codename}-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu ${codename}-security main restricted universe multiverse
EOF
        }
    fi
    apt-get update -y || true
}

configureYumDnf() {
    if is_ubuntu; then
        configureAptSources
        return 0
    fi
    log_info "Configuring YUM/DNF repositories..."
    local pm
    pm=$(pkgmgr)

    # Enable fastestmirror features globally
    if [[ "$RELEASE" -eq 7 ]]; then
        yum -y install yum-plugin-fastestmirror > /dev/null 2>&1 || true
        mkdir -p /etc/yum/pluginconf.d
        [[ -f /etc/yum/pluginconf.d/fastestmirror.conf ]] || echo -e "[main]\n" > /etc/yum/pluginconf.d/fastestmirror.conf
        kv_set /etc/yum/pluginconf.d/fastestmirror.conf enabled 1 '='
    else
        mkdir -p /etc/dnf
        [[ -f /etc/dnf/dnf.conf ]] || echo -e "[main]\n" > /etc/dnf/dnf.conf
        kv_set /etc/dnf/dnf.conf fastestmirror True '='
        # Tune parallel downloads by memory to avoid OOM on small hosts
        local __mem_gb __dl
        __mem_gb=$(detectMemoryGiB 2>/dev/null || echo 4)
        __dl=10
        if [[ -n "$__mem_gb" ]]; then
            if (( __mem_gb <= 1 )); then __dl=2; elif (( __mem_gb <= 2 )); then __dl=3; elif (( __mem_gb <= 4 )); then __dl=5; else __dl=10; fi
        fi
        kv_set /etc/dnf/dnf.conf max_parallel_downloads "$__dl" '='
    fi

    # Mirror selection by location
    if is_cn; then
        log_warn "Using Aliyun mirrors for CN server."
        case "$RELEASE" in
            7)
                curl -fsSL -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
                sed -i "/mirrors.aliyuncs.com/d" /etc/yum.repos.d/CentOS-Base.repo
                ;;
            8)
                if is_rocky || is_alma; then
                    # Rocky/Alma 8: 保持官方仓库，仅启用加速与插件，避免错误切换到 CentOS vault
                    $pm -y install --allowerasing dnf-plugins-core > /dev/null 2>&1 || true
                    enableCRBOrPowerTools
                    # AlmaLinux 额外提供阿里源文件加速（不强行替换官方 mirrorlist）
                    configureAlmaLinuxAliyunRepos
                else
                    disableCentOS8StaleRepos
                    curl -fsSL -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
                    # install plugins AFTER repos are configured; allow erasing to resolve EVR conflicts
                    $pm -y install --allowerasing dnf-plugins-core > /dev/null 2>&1 || true
                fi
                ;;
            9)
                if [[ "${OS,,}" == "centos" ]]; then
                    # 兼容不同大小写命名的 CentOS Stream repo 文件；若不存在则跳过避免 sed 报错
                    shopt -s nullglob
                    stream_repo_files=(/etc/yum.repos.d/CentOS-Stream*.repo /etc/yum.repos.d/centos-stream*.repo)
                    if ((${#stream_repo_files[@]} > 0)); then
                        sed -e 's!^#baseurl=!baseurl=!g' \
                            -e 's!^mirrorlist=!#mirrorlist=!g' \
                            -e 's!mirror.stream.centos.org!mirrors.aliyun.com!g' \
                            -i "${stream_repo_files[@]}" || true
                    else
                        log_warn "No CentOS Stream repo files found; skipping mirror rewrite for EL9 CentOS."
                    fi
                    shopt -u nullglob
                else
                    enableCRBOrPowerTools
                    # Alma/Rocky 默认 mirrorlist 已较快，必要时可自定义 .repo 另行接入
                fi
                ;;
            10)
                # CentOS 10: tentatively switch to mirrors.aliyun.com if mirrorlist issues
                if ls /etc/yum.repos.d/centos-stream*.repo > /dev/null 2>&1; then
                    sed -e 's!^#baseurl=!baseurl=!g' \
                        -e 's!^mirrorlist=!#mirrorlist=!g' \
                        -e 's!mirror.stream.centos.org!mirrors.aliyun.com!g' \
                        -i /etc/yum.repos.d/centos-stream*.repo || true
                fi
                ;;
        esac
    else
        log_warn "Using official/default mirrors for global server."
        case "$RELEASE" in
            7)
                # Use project-provided CentOS-7 repo file as stable base for EOL vault access
                curl -Lks "${GIT_REPO_URL}/Kickstart/repo-mirrorslist/CentOS-7-x86_64-Base.repo" > /etc/yum.repos.d/CentOS-Base.repo
                ;;
            8)
                if is_rocky || is_alma; then
                    # Rocky/Alma 8: 保持官方 mirrorlist，不切换到 CentOS 8 vault
                    $pm -y install --allowerasing dnf-plugins-core > /dev/null 2>&1 || true
                    dnf config-manager --set-enabled powertools > /dev/null 2>&1 \
                        || dnf config-manager --set-enabled PowerTools > /dev/null 2>&1 || true
                else
                    # CentOS 8 official mirrors EOL. Prefer fastest between Rackspace and Aliyun vaults.
                    disableCentOS8StaleRepos
                    local chosen
                    chosen="$(pickFastRepoForCentOS8)"
                    log_note "CentOS 8 vault mirror selected: ${chosen}"
                    writeCentOS8VaultRepo "$chosen"
                    # install plugins AFTER repos are configured; allow erasing to resolve EVR conflicts
                    $pm -y install --allowerasing dnf-plugins-core > /dev/null 2>&1 || true
                fi
                ;;
            9)
                # Rocky/Alma/CentOS Stream 9: 默认保留官方 mirrorlist
                :
                ;;
            10)
                # CentOS 10: keep official mirrorlist; if later issues are found, fallback handled above for CN/HK
                :
                ;;
        esac
    fi

    $pm clean all || true
    $pm makecache || true

    # Install EPEL for systems that don't have it yet
    # Note: EL9+ are handled in addExtraRepos with RPM URL installation
    if ! rpm -q epel-release > /dev/null 2>&1; then
        # Only CentOS/RHEL 6/7/8 can use 'dnf/yum install epel-release'
        if [[ "$RELEASE" =~ ^[678]$ ]]; then
    log_warn "Installing EPEL repository..."
    $pm install -y epel-release || true
        fi
    fi
    
    # Configure EPEL to use domestic mirrors for CN users
    if rpm -q epel-release > /dev/null 2>&1 && { [[ "$SERVER_LOCATION" == "CN" ]] || is_cn; }; then
        log_info "Configuring EPEL to use Aliyun mirrors..."
        
        # CentOS 7
        if [[ "$RELEASE" -eq 7 ]]; then
            # Backup original repo file
            [[ -f /etc/yum.repos.d/epel.repo ]] && cp -f /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.bak
            
            # Download Aliyun EPEL repo file or modify existing
            if curl -fsSL -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo; then
                log_ok "EPEL configured to use Aliyun mirror (CentOS 7)"
            else
                # Fallback: modify existing repo file
                if [[ -f /etc/yum.repos.d/epel.repo ]]; then
                    sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel.repo
                    sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                    sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                    log_ok "EPEL baseurl modified to use Aliyun mirror (CentOS 7)"
                fi
            fi
        fi
        
        # CentOS 8
        if [[ "$RELEASE" -eq 8 ]]; then
            # Backup original repo files
            [[ -f /etc/yum.repos.d/epel.repo ]] && cp -f /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.bak
            [[ -f /etc/yum.repos.d/epel-modular.repo ]] && cp -f /etc/yum.repos.d/epel-modular.repo /etc/yum.repos.d/epel-modular.repo.bak
            
            # Modify EPEL repo files
            if [[ -f /etc/yum.repos.d/epel.repo ]]; then
                sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel.repo
                sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
            fi
            
            if [[ -f /etc/yum.repos.d/epel-modular.repo ]]; then
                sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel-modular.repo
                sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel-modular.repo
                sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel-modular.repo
            fi
            
            log_ok "EPEL configured to use Aliyun mirror (CentOS 8)"
        fi
        
        # CentOS 6 (legacy support)
    if [[ "$RELEASE" -eq 6 ]]; then
            [[ -f /etc/yum.repos.d/epel.repo ]] && cp -f /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.bak
            
            if curl -fsSL -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-6.repo; then
                log_ok "EPEL configured to use Aliyun mirror (CentOS 6)"
            else
                if [[ -f /etc/yum.repos.d/epel.repo ]]; then
                    sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel.repo
                    sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                    sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                fi
            fi
        fi
    fi

    addExtraRepos
}

addExtraRepos() {
    case "$RELEASE" in
        7)
            log_warn "Adding ELRepo for CentOS 7....."
            rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org || true
            yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm || true
            ;;
        8)
            if is_rocky || is_alma; then
                log_warn "Rocky/Alma 8 detected: enabling PowerTools (powertools) and ELRepo....."
                enableCRBOrPowerTools
                rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org || true
                if ! rpm -q elrepo-release > /dev/null 2>&1; then
                    dnf install -y https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm || true
                fi
            else
                log_warn "Adding ELRepo for CentOS 8....."
                if ! rpm -q elrepo-release > /dev/null 2>&1; then
                    dnf install -y https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm || true
                fi
            fi
            ;;
        9)
            if is_rocky || is_alma; then
                log_warn "Rocky/Alma 9 detected: enabling CRB and ELRepo....."
                enableCRBOrPowerTools
                rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org || true
                if ! rpm -q elrepo-release > /dev/null 2>&1; then
                    dnf install -y https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm || true
                fi
            else
                log_warn "Adding EPEL and ELRepo for CentOS Stream 9....."
                
                # Enable CRB (CodeReady Builder) first - required for many EPEL packages
                enableCRBOrPowerTools
                
                # Install EPEL - CentOS Stream 9 needs direct RPM installation
                if ! rpm -q epel-release > /dev/null 2>&1; then
                    log_info "Installing EPEL repository..."
                    
                    # Use domestic mirror for CN users, official mirror for international users
                    if is_cn; then
                        # Domestic mirrors (China): Aliyun > Tsinghua > Official
                        log_info "Using domestic EPEL mirrors for faster installation..."
                        dnf install -y https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm || {
                            log_warn "Aliyun mirror failed, trying Tsinghua mirror..."
                            dnf install -y https://mirrors.tuna.tsinghua.edu.cn/epel/epel-release-latest-9.noarch.rpm || {
                                log_warn "Tsinghua mirror failed, trying official mirror..."
                                dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm || true
                            }
                        }
                        # Also install epel-next with domestic mirror
                        dnf install -y https://mirrors.aliyun.com/epel/epel-next-release-latest-9.noarch.rpm 2>/dev/null || \
                        dnf install -y https://mirrors.tuna.tsinghua.edu.cn/epel/epel-next-release-latest-9.noarch.rpm 2>/dev/null || \
                        dnf install -y https://dl.fedoraproject.org/pub/epel/epel-next-release-latest-9.noarch.rpm 2>/dev/null || true
                        
                        # Configure EPEL to use domestic mirrors after installation
                        if rpm -q epel-release > /dev/null 2>&1; then
                            log_info "Configuring EPEL to use Aliyun mirrors..."
                            
                            # Backup original repo files
                            [[ -f /etc/yum.repos.d/epel.repo ]] && cp -f /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.bak
                            [[ -f /etc/yum.repos.d/epel-next.repo ]] && cp -f /etc/yum.repos.d/epel-next.repo /etc/yum.repos.d/epel-next.repo.bak
                            
                            # Replace EPEL baseurl with Aliyun mirror
                            if [[ -f /etc/yum.repos.d/epel.repo ]]; then
                                sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel.repo
                                sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                                sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel.repo
                            fi
                            
                            # Replace EPEL-Next baseurl with Aliyun mirror
                            if [[ -f /etc/yum.repos.d/epel-next.repo ]]; then
                                sed -i 's|^metalink=|#metalink=|g' /etc/yum.repos.d/epel-next.repo
                                sed -i 's|^#baseurl=https\?://download\.fedoraproject\.org/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel-next.repo
                                sed -i 's|^#baseurl=https\?://download\.example/pub|baseurl=https://mirrors.aliyun.com|g' /etc/yum.repos.d/epel-next.repo
                            fi
                            
                            log_ok "EPEL repositories configured to use Aliyun mirrors"
                        fi
                    else
                        # International: use official mirror
                        dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm || {
                            log_warn "Official mirror failed, trying Aliyun mirror..."
                            dnf install -y https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm || true
                        }
                        # Also install epel-next
                        dnf install -y https://dl.fedoraproject.org/pub/epel/epel-next-release-latest-9.noarch.rpm 2>/dev/null || true
                    fi
                fi
                
                # Try to install REMI (skip if EPEL installation failed)
                if rpm -q epel-release > /dev/null 2>&1; then
                    dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm 2>/dev/null || {
                        log_warn "REMI installation failed (expected if EPEL dependency issue), skipping..."
                    }
                fi
                
                # Install ELRepo
                rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org || true
                if ! rpm -q elrepo-release > /dev/null 2>&1; then
                dnf install -y https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm || true
                fi
            fi
            ;;
        10)
            log_warn "Adding ELRepo for EL10....."
            # EL10 上部分系统策略可能拒绝导入 GPG 公钥；失败则降级为 --nogpgcheck
            rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org > /dev/null 2>&1 || true
            if ! rpm -q elrepo-release > /dev/null 2>&1; then
                dnf install -y --nogpgcheck https://www.elrepo.org/elrepo-release-10.el10.elrepo.noarch.rpm || true
            fi
            # 为避免后续元数据/包验证失败，显式关闭 elrepo 的 gpg 检查（仅 EL10）
            if [[ -f /etc/yum.repos.d/elrepo.repo ]]; then
                kv_set /etc/yum.repos.d/elrepo.repo gpgcheck 0 '='
                kv_set /etc/yum.repos.d/elrepo.repo repo_gpgcheck 0 '='
            fi
            # 确保内核仓库启用
            dnf config-manager --set-enabled elrepo-kernel > /dev/null 2>&1 || true
            ;;
    esac
}


# === 第7层：Docker 完整模块 ===

# --- 7.1 Docker 仓库配置 ---

getDockerRepoUrlRhelFamily() {
    local base_cn="http://mirrors.aliyun.com/docker-ce/linux"
    local base_off="https://download.docker.com/linux"
    local base
    if is_cn; then base="$base_cn"; else base="$base_off"; fi
    local candidates=()
    if is_alma; then
        candidates+=("${base}/almalinux/docker-ce.repo")
    fi
    candidates+=("${base}/centos/docker-ce.repo")
    local url
    for url in "${candidates[@]}"; do
        if curl -I -k -m 6 -s -o /dev/null -w '%{http_code}' "$url" | grep -qE '^(200|3..)'; then
            echo "$url"
            return 0
        fi
    done
    # Fallback
    echo "${base}/centos/docker-ce.repo"
}

# --- 7.2 Docker 镜像源配置 ---

buildDockerMirrorCandidates() {
    # 输出按区域优先级排列的候选镜像（以空格分隔），允许通过 CLI 覆盖
    if [[ -n "$MIRROR_CANDIDATES_CSV" ]]; then
        echo "$MIRROR_CANDIDATES_CSV" | tr ',' ' '
        return 0
    fi
    local cn_list="https://registry.cn-hangzhou.aliyuncs.com https://mirror.ccs.tencentyun.com https://docker.mirrors.ustc.edu.cn https://hub-mirror.c.163.com https://registry.docker-cn.com http://hub.dtops.co"
    local global_list="https://registry-1.docker.io https://mirror.gcr.io https://dockerhub.timeweb.cloud https://dockerhub.pullmirror.com"
    if is_cn; then
        echo "$cn_list $global_list"
    else
        echo "$global_list $cn_list"
    fi
}

selectFastDockerMirrors() {
    # Concurrently probe candidates and return JSON array of up to 5 fastest responsive mirrors
    local candidates url tmp lines json="[" count=0
    candidates="$(buildDockerMirrorCandidates)"
    tmp=$(mktemp)
    local -a pids=()
    local max_jobs
    max_jobs=${MIRROR_MAX_JOBS:-6}
    for url in $candidates; do
        (
            # Probe /v2/ endpoint; many registries return 200 or 401
            local code time
            read -r code time < <(curl -m "${MIRROR_PROBE_TIMEOUT:-3}" -s -o /dev/null -w '%{http_code} %{time_total}' "$url/v2/" 2> /dev/null || echo "000 9")
            if [[ "$code" =~ ^(200|401)$ ]]; then
                printf "%s %s\n" "$time" "$url" >> "$tmp"
            fi
        ) &
        pids+=($!)
        if ((${#pids[@]} >= max_jobs)); then
            wait "${pids[0]}" 2> /dev/null || true
            pids=(${pids[@]:1})
        fi
    done
    # Wait remaining
    for pid in "${pids[@]}"; do wait "$pid" 2> /dev/null || true; done

    if [[ -s "$tmp" ]]; then
        while IFS= read -r line; do
            url="${line#* }"
            [[ $count -gt 0 ]] && json+=" ,"
            json+="\"$url\""
            count=$((count + 1))
            [[ $count -ge 5 ]] && break
        done < <(sort -n "$tmp")
    fi
    rm -f "$tmp" 2> /dev/null || true
    json+="]"
    # Fallback if no mirror responded: pick a conservative default set
    if [[ "$json" == "[]" ]]; then
        json='["https://registry-1.docker.io","https://mirror.gcr.io"]'
    fi
    echo "$json"
}

mergeDockerMirrorsIntoDaemon() {
    local daemon_file="$1" mirrors_json="$2"
    [[ -f "$daemon_file" ]] || return 1
    # 若 JSON 非法，写入最小骨架
    if command -v jq > /dev/null 2>&1; then
        jq . "$daemon_file" > /dev/null 2>&1 || echo '{"registry-mirrors":[]}' > "$daemon_file"
        # 使用追加去重策略（仅在 jq 可用时）
        json_merge_jq "$daemon_file" '."registry-mirrors" = ((."registry-mirrors" // []) + '"$mirrors_json"' | unique)'
        return 0
    fi
    # 无 jq：直接覆盖顶层键，保证语义正确性
    json_merge_jq "$daemon_file" '."registry-mirrors" = '"$mirrors_json"'' || true
}

mergeDockerExtraFields() {
    # Merge insecure-registries and exec-opts into daemon.json
    local daemon_file="$1"
    local insecure_csv="$2"
    local execopts_csv="$3"

    [[ -f "$daemon_file" ]] || return 0

    # Prepare JSON arrays from CSV
    local insecure_json="" execopts_json=""
    if [[ -n "$insecure_csv" ]]; then
        insecure_json=$(printf '%s' "$insecure_csv" | awk -F',' '{printf("["); for(i=1;i<=NF;i++){gsub(/^\s+|\s+$/,"",$i); printf("\"%s\"%s", $i, (i<NF?",":""))} printf("]") }')
    fi
    if [[ -n "$execopts_csv" ]]; then
        execopts_json=$(printf '%s' "$execopts_csv" | awk -F',' '{printf("["); for(i=1;i<=NF;i++){gsub(/^\s+|\s+$/,"",$i); printf("\"%s\"%s", $i, (i<NF?",":""))} printf("]") }')
    fi

    # 使用 json_merge_jq 做顶层赋值；若 jq 可用可选择去重追加，否则赋值覆盖
    if command -v jq > /dev/null 2>&1; then
        jq . "$daemon_file" > /dev/null 2>&1 || echo '{}' > "$daemon_file"
        [[ -n "$insecure_json" ]] && json_merge_jq "$daemon_file" '."insecure-registries" = ((."insecure-registries" // []) + '"$insecure_json"' | unique)'
        [[ -n "$execopts_json" ]] && json_merge_jq "$daemon_file" '."exec-opts" = ((."exec-opts" // []) + '"$execopts_json"' | unique)'
    else
        [[ -n "$insecure_json" ]] && json_merge_jq "$daemon_file" '."insecure-registries" = '"$insecure_json"''
        [[ -n "$execopts_json" ]] && json_merge_jq "$daemon_file" '."exec-opts" = '"$execopts_json"''
    fi
    [[ -n "$STORAGE_DRIVER" ]] && json_merge_jq "$daemon_file" '."storage-driver" = "'"$STORAGE_DRIVER"'"'
    [[ -n "$DATA_ROOT" ]] && json_merge_jq "$daemon_file" '."data-root" = "'"$DATA_ROOT"'"'
    if [[ -n "$DEFAULT_ADDR_POOLS_CSV" ]]; then
        local pools_json
        pools_json=$(addrPoolsCsvToJson "$DEFAULT_ADDR_POOLS_CSV")
        json_merge_jq "$daemon_file" '."default-address-pools" = '"$pools_json"''
    fi
}

# --- 7.3 Docker Compose 安装 ---

installOrUpdateDockerCompose() {
    # 策略：
    # - 在支持插件的情况下优先使用 Docker Compose 插件（或按 COMPOSE_INSTALL_MODE=plugin 指定）
    # - 否则按架构下载独立二进制，并进行校验；失败则使用仓库内置保底版本
    # - Prefer plugin when COMPOSE_INSTALL_MODE=plugin, or auto and plugin is available (esp. on aarch64)
    # - Otherwise install standalone binary by arch
    # - Provide arch-aware fallback from repo for x86_64/aarch64 with checksum verification best-effort
    if command -v docker > /dev/null 2>&1; then
        if docker compose version > /dev/null 2>&1; then
            if [[ "${COMPOSE_INSTALL_MODE}" != "standalone" ]]; then
                ensureComposeShimIfNeeded
                COMPOSE_MODE_EFFECTIVE="plugin"
                COMPOSE_VERSION_EFFECTIVE="$(docker compose version 2> /dev/null | sed -n 's/.*version\s\(v[0-9.]*\).*/\1/p' | head -n1)"
                log_note "Docker Compose plugin detected (${COMPOSE_VERSION_EFFECTIVE}); shim ensured."
                return 0
            fi
        fi
    fi

    log_warn "Checking docker-compose latest version....."
    local latest curr arch asset url1 url2 url3 tmpdest fallback_repo_url
    arch="$(detectComposeArch)"
    latest="$(getLatestComposeVersion)"
    curr="$(currentComposeVersion)"

    # If current is same as latest and docker-compose exists, skip
    if [[ -n "$curr" && -n "$latest" && "$curr" == "$latest" && -x /usr/local/bin/docker-compose ]]; then
        log_note "docker-compose is up-to-date: ${curr}"
        return 0
    fi

    asset="docker-compose-linux-${arch}"

    # 主下载地址（国内节点优先使用 xget.xi-xu.me）
    if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
        url1="https://xget.xi-xu.me/gh/docker/compose/releases/download/${latest}/${asset}"
        url2="https://ghproxy.com/https://github.com/docker/compose/releases/download/${latest}/${asset}"
        url3="https://download.fastgit.org/docker/compose/releases/download/${latest}/${asset}"
    else
        url1="https://github.com/docker/compose/releases/download/${latest}/${asset}"
        url2="https://ghproxy.com/https://github.com/docker/compose/releases/download/${latest}/${asset}"
        url3="https://download.fastgit.org/docker/compose/releases/download/${latest}/${asset}"
    fi

    tmpdest="/usr/local/bin/.docker-compose.tmp"
    rm -f "$tmpdest" 2> /dev/null || true

    # 多镜像尝试并校验 SHA256（尽力而为）
    local download_ok=false verified=false checksums_url="https://github.com/docker/compose/releases/download/${latest}/checksums.txt" expected_sum actual_sum

    for u in "$url1" "$url2" "$url3"; do
        if curl -m 25 -fLkS "$u" -o "$tmpdest" 2> /dev/null; then
            download_ok=true
            # 尽力而为的校验（必要时通过镜像获取 checksums）
            if gh_download "$checksums_url" "$tmpdest.sha256s"; then
                expected_sum=$(grep -E "\s${asset}$" "$tmpdest.sha256s" | awk '{print $1}' | head -n1 || true)
                if [[ -n "$expected_sum" ]] && command -v sha256sum > /dev/null 2>&1; then
                    actual_sum=$(sha256sum "$tmpdest" | awk '{print $1}')
                    if [[ "$expected_sum" == "$actual_sum" ]]; then
                        verified=true
                        log_debug "docker-compose checksum verified (${arch})"
                    else
                        log_error "docker-compose checksum mismatch (${arch}); trying next source"
                        download_ok=false
                        verified=false
                        rm -f "$tmpdest" > /dev/null 2>&1 || true
                        continue
                    fi
                fi
            fi
            break
        fi
    done

    if [[ "$download_ok" == true ]]; then
        mv -f "$tmpdest" /usr/local/bin/docker-compose
        chmod +x /usr/local/bin/docker-compose || true
        COMPOSE_MODE_EFFECTIVE="standalone"
        COMPOSE_VERSION_EFFECTIVE="${latest}"
        log_ok "Installed docker-compose ${latest} (${arch})"
    else
        # 回退：使用仓库内置的保底二进制
        case "$arch" in
            x86_64 | amd64)
                fallback_repo_url="${GIT_REPO_URL}/Docker/docker-comepose/2.37.2/docker-compose-linux-x86_64"
                ;;
            aarch64 | arm64)
                fallback_repo_url="${GIT_REPO_URL}/Docker/docker-comepose/2.37.2/docker-compose-linux-aarch64"
                ;;
            *)
                fallback_repo_url=""
            ;;
    esac
        if [[ -n "$fallback_repo_url" ]] && curl -m 25 -fLkS "$fallback_repo_url" -o "$tmpdest" 2> /dev/null; then
            mv -f "$tmpdest" /usr/local/bin/docker-compose
            chmod +x /usr/local/bin/docker-compose || true
            COMPOSE_MODE_EFFECTIVE="standalone"
            COMPOSE_VERSION_EFFECTIVE="v2.37.2"
            log_warn "Used bundled docker-compose fallback v2.37.2 (${arch})"
        else
            log_error "Failed to download docker-compose for arch ${arch}."
        fi
    fi

    ensureComposeShimIfNeeded
}

# --- 7.4 Docker 网络支持 ---

ensureNetfilterModulesForDocker() {
    # Best-effort load of kernel modules needed for Docker networking
    # EL10: prefer nftables path; legacy iptable_* modules may not exist
    local mods_common=(nf_tables nfnetlink nf_nat nf_conntrack)
    local mods_legacy=(ip_tables iptable_nat iptable_filter iptable_mangle)
    local mods_extra=(br_netfilter xt_addrtype)
    local m
    for m in "${mods_common[@]}"; do modprobe "$m" > /dev/null 2>&1 || true; done
    if [[ "$RELEASE" -le 9 ]]; then
        for m in "${mods_legacy[@]}"; do modprobe "$m" > /dev/null 2>&1 || true; done
    fi
    for m in "${mods_extra[@]}"; do modprobe "$m" > /dev/null 2>&1 || true; done
    # Ensure sysctl to pass bridged traffic (if present)
    sysctl -w net.bridge.bridge-nf-call-iptables=1 > /dev/null 2>&1 || true
    sysctl -w net.bridge.bridge-nf-call-ip6tables=1 > /dev/null 2>&1 || true
}

# --- 7.5 Docker 主安装流程 ---

dockerINSTALL() {
    log_info "Installing Docker....."
    local pm
    pm=$(pkgmgr)

    if is_ubuntu; then
        log_warn "Configuring Docker APT repository (Ubuntu)....."
        apt-get update -y || true
        apt-get install -y ca-certificates curl gnupg || true
        install -m 0755 -d /etc/apt/keyrings
        local codename base_url arch
        codename="$(ubuntu_codename)"
        arch="$(dpkg --print-architecture)"
        if is_cn; then
            base_url="https://mirrors.aliyun.com/docker-ce/linux/ubuntu"
            # Prefer mirror GPG first; fallback to official
            curl -fsSL ${base_url}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
                || curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg || true
        else
            base_url="https://download.docker.com/linux/ubuntu"
            curl -fsSL ${base_url}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg || true
        fi
        chmod a+r /etc/apt/keyrings/docker.gpg || true
        echo "deb [arch=${arch} signed-by=/etc/apt/keyrings/docker.gpg] ${base_url} ${codename} stable" > /etc/apt/sources.list.d/docker.list
        apt-get update -y || true
        DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || true

        # Configure daemon.json for Ubuntu
        mkdir -p /etc/docker
        if is_cn; then
            if [[ ! -f /etc/docker/daemon.json ]]; then
                cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://registry.cn-hangzhou.aliyuncs.com",
    "https://mirror.ccs.tencentyun.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://registry.docker-cn.com",
    "http://hub.dtops.co"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "live-restore": true,
  "max-concurrent-downloads": 10
}
EOF
            else
                cp -a /etc/docker/daemon.json{,.bak.$(date +%s)} || true
                local cn_mirrors_ubuntu='["https://registry.cn-hangzhou.aliyuncs.com","https://mirror.ccs.tencentyun.com","https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com","https://registry.docker-cn.com","http://hub.dtops.co"]'
                mergeDockerMirrorsIntoDaemon /etc/docker/daemon.json "$cn_mirrors_ubuntu" || true
                mergeDockerExtraFields /etc/docker/daemon.json "$INSECURE_REG_CSV" "$EXEC_OPTS_CSV" || true
            fi
        else
            local mirrors_json
            mirrors_json=$(selectFastDockerMirrors)
            if [[ ! -f /etc/docker/daemon.json ]]; then
                cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ${mirrors_json},
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "live-restore": true,
  "max-concurrent-downloads": 10
}
EOF
            else
                cp -a /etc/docker/daemon.json{,.bak.$(date +%s)} || true
                mergeDockerMirrorsIntoDaemon /etc/docker/daemon.json "${mirrors_json}" || true
                mergeDockerExtraFields /etc/docker/daemon.json "$INSECURE_REG_CSV" "$EXEC_OPTS_CSV" || true
            fi
        fi
        DID_DOCKER_DAEMON_CFG=true
    else
        # Ensure repo management plugin for RHEL-family
        if [[ "$pm" == dnf ]]; then
            dnf -y install dnf-plugins-core || true
            if [[ "$SERVER_LOCATION" == CN ]]; then
                dnf config-manager --add-repo "$(getDockerRepoUrlRhelFamily)" || true
            else
                dnf config-manager --add-repo "$(getDockerRepoUrlRhelFamily)" || true
            fi
        else
            yum -y install yum-utils || true
            if [[ "$SERVER_LOCATION" == CN ]]; then
                yum-config-manager --add-repo "$(getDockerRepoUrlRhelFamily)" || true
            else
                yum-config-manager --add-repo "$(getDockerRepoUrlRhelFamily)" || true
            fi
        fi
        $pm install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || true
    fi

    # Always ensure docker-compose is updated to the latest version (with fallbacks)
    installOrUpdateDockerCompose

    mkdir -p /etc/docker

    # Configure daemon.json conservatively: enable log rotation, live-restore, and mirror for CN
    if [[ "${DID_DOCKER_DAEMON_CFG:-false}" == "true" ]]; then
        : # Ubuntu 已在上面完成 daemon.json 配置/合并
    elif [[ "$SERVER_LOCATION" == CN ]]; then
        if [[ ! -f /etc/docker/daemon.json ]]; then
            cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://registry.cn-hangzhou.aliyuncs.com",
    "https://mirror.ccs.tencentyun.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://registry.docker-cn.com",
    "http://hub.dtops.co"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "live-restore": true,
  "max-concurrent-downloads": 10
}
EOF
        else
            cp -a /etc/docker/daemon.json{,.bak.$(date +%s)} || true
            local cn_mirrors='["https://registry.cn-hangzhou.aliyuncs.com","https://mirror.ccs.tencentyun.com","https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com","https://registry.docker-cn.com","http://hub.dtops.co"]'
            mergeDockerMirrorsIntoDaemon /etc/docker/daemon.json "$cn_mirrors" || true
            mergeDockerExtraFields /etc/docker/daemon.json "$INSECURE_REG_CSV" "$EXEC_OPTS_CSV" || true
        fi
    else
        local mirrors_json
        mirrors_json=$(selectFastDockerMirrors)
        if [[ ! -f /etc/docker/daemon.json ]]; then
            cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ${mirrors_json},
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "live-restore": true,
  "max-concurrent-downloads": 10
}
EOF
        else
            cp -a /etc/docker/daemon.json{,.bak.$(date +%s)} || true
            mergeDockerMirrorsIntoDaemon /etc/docker/daemon.json "${mirrors_json}" || true
            mergeDockerExtraFields /etc/docker/daemon.json "$INSECURE_REG_CSV" "$EXEC_OPTS_CSV" || true
        fi
    fi

    # Raise docker service file descriptor limits via systemd drop-in
    mkdir -p /etc/systemd/system/docker.service.d
    cat > /etc/systemd/system/docker.service.d/override.conf << 'EOF'
[Service]
LimitNOFILE=1048576
TasksMax=infinity
EOF

    # Ensure services are enabled on boot and started
    systemctl daemon-reload || true
    if systemctl list-unit-files | grep -q '^containerd\.service'; then
        systemctl enable containerd.service || true
    fi
    if systemctl list-unit-files | grep -q '^docker\.socket'; then
        systemctl enable docker.socket || true
    fi
    systemctl enable docker.service || true
    # EL8+ ensure kernel extra modules present (xt_addrtype, br_netfilter, etc.)
    if [[ "$RELEASE" -ge 8 ]]; then
        dnf -y install "kernel-modules-extra-$(uname -r)" > /dev/null 2>&1 || true
    fi
    # EL10: ensure iptables-nft userspace present
    if [[ "$RELEASE" -eq 10 ]]; then
        dnf -y install iptables iptables-nft > /dev/null 2>&1 || true
    fi
    # Load required netfilter modules for Docker (nftables backend compatible)
    ensureNetfilterModulesForDocker || true
    # Validate daemon.json before (re)starting to prevent flapping
    if [[ -f /etc/docker/daemon.json ]]; then
        if command -v jq > /dev/null 2>&1; then
            if ! jq . /etc/docker/daemon.json > /dev/null 2>&1; then
                log_warn "daemon.json 非法，写入最小可用配置以恢复 Docker 启动……"
                echo '{}' > /etc/docker/daemon.json
                json_merge_jq /etc/docker/daemon.json '."registry-mirrors" = ["https://registry-1.docker.io"] | ."log-driver" = "json-file" | ."log-opts" = {"max-size":"100m","max-file":"3"} | ."live-restore" = true | ."max-concurrent-downloads" = 10'
            fi
        fi
    fi
    systemctl restart docker.service || systemctl start docker.service || true
    #systemctl status docker --no-pager || true
}


# === 第8层：系统优化与配置层 ===

# --- 8.1 内核和系统工具 ---

changeKernel() {
    log_info "Changing system kernel........."
    if is_ubuntu; then
        log_warn "Skipping kernel changes on Ubuntu."
        return 0
    fi
    if [[ "${DO_KERNEL_ML}" != "true" ]]; then
        log_warn "Kernel-ml installation disabled by flag. Skipping."
        return 0
    fi
    case "$RELEASE" in
        7)
            #yum -y remove kernel-headers kernel-tools kernel-tools-libs || true
            curl -Lks "${GIT_REPO_URL}/Kickstart/repo-mirrorslist/elrepo-7.repo?ref_type=heads" > /etc/yum.repos.d/elrepo.repo || true
            yum clean all && yum makecache || true
            # Install kernel-ml with headers/devel/modules together; disable excludes to avoid exclude=kernel*
            yum --enablerepo=elrepo-kernel --disableexcludes=all install -y kernel-ml || true
            # If kernel-ml not installed, try fallback RPM (x86_64 only)
            if ! rpm -q kernel-ml > /dev/null 2>&1 && is_arch_x86_64; then
                installKernelFallbackEl7 || log_error "Fallback kernel-ml install failed on EL7"
            fi
            grub2-mkconfig -o /boot/grub2/grub.cfg || true
            updateEFIGrubCfg
            grub2-set-default 0 || true
            ;;
        8 | 9)
            #dnf -y remove kernel-headers kernel-tools kernel-tools-libs || true
            dnf clean all && dnf makecache || true
            # Install kernel-ml with headers/devel/modules together; disable excludes to avoid exclude=kernel*
            dnf --enablerepo=elrepo-kernel --disableexcludes=all install -y kernel-ml || true
            # If kernel-ml not installed on EL8、9, try fallback RPM (x86_64 only)
            if [[ "$RELEASE" -eq 8 ]] && ! rpm -q kernel-ml > /dev/null 2>&1 && is_arch_x86_64; then
                installKernelFallbackEl8 || log_error "Fallback kernel-ml install failed on EL8"
            fi
            grub2-mkconfig -o /boot/grub2/grub.cfg || true
            updateEFIGrubCfg
            grubby --set-default-index=0 || true
            ;;
        10)
            log_warn "EL10 detected: enabling elrepo-kernel and installing kernel-ml..."
            dnf clean all && dnf makecache || true
            # 先检测 kernel-ml 是否可用，不可用则尝试 kernel-lt；均不可用则跳过
            if dnf -q --disablerepo='*' --enablerepo='elrepo-kernel' list --available kernel-ml > /dev/null 2>&1; then
                # Install perf stacks alongside kernel-ml when available (EL10)
                dnf --enablerepo=elrepo-kernel --disableexcludes=all install -y kernel-ml || true
            elif dnf -q --disablerepo='*' --enablerepo='elrepo-kernel' list --available kernel-lt > /dev/null 2>&1; then
                log_warn "kernel-ml 不可用，尝试安装 kernel-lt..."
                # Install perf stacks alongside kernel-ml when available (EL10)
                dnf --enablerepo=elrepo-kernel --disableexcludes=all install -y kernel-lt || true
            else
                log_warn "elrepo-kernel 仓库中未发现 kernel-ml/kernel-lt（架构或同步未就绪）；保留系统默认内核。"
            fi
            grub2-mkconfig -o /boot/grub2/grub.cfg || true
            updateEFIGrubCfg
            ;;
    esac
    # Append exclude=kernel* only on EL7/EL8 to avoid filtering headers on EL9/EL10
    if [[ "$RELEASE" -eq 7 || "$RELEASE" -eq 8 ]]; then
        grep -q '^exclude=kernel\*' /etc/yum.conf || echo 'exclude=kernel*' >> /etc/yum.conf
    fi
}

updateSystemAndInstallTools() {
    log_info "Updating system and installing common tools..."
    local pm
    pm=$(pkgmgr)

    if is_ubuntu; then
        apt-get update -y >/dev/null 2>&1 || true
        log_warn "Installing most used software (Ubuntu)....."
        local ubuntu_packages=()
        build_ubuntu_packages ubuntu_packages
        local apt_rc=0
        DEBIAN_FRONTEND=noninteractive apt-get install -y "${ubuntu_packages[@]}" >/dev/null 2>&1 || apt_rc=$?
        if [[ $apt_rc -ne 0 ]]; then
            log_warn "Batch install (Ubuntu) failed (rc=$apt_rc); retrying per-package best-effort..."
            for pkg in "${ubuntu_packages[@]}"; do
                DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg" >/dev/null 2>&1 \
                    || log_info "Skip missing package (Ubuntu): $pkg"
            done
        fi

        # Ensure tree is installed (best-effort), even if previous install succeeded/changed
        DEBIAN_FRONTEND=noninteractive apt-get install -y tree >/dev/null 2>&1 || true

        enable_now irqbalance
        return 0
    fi

    log_warn "Installing Development Tools....."
    if [[ "$RELEASE" -eq 6 ]]; then
        yum -y groupinstall "Development tools" "Server Platform Development" || true
    else
        $pm -y groupinstall "Development Tools" || true
    fi

    log_warn "Installing most used software....."
    local packages=()
    build_common_packages packages

    # On EL8, prefer --allowerasing to avoid dnf-plugins-core/yum-utils EVR pinning conflicts
    # 批量安装：若因个别包缺失导致失败，则回退为逐包安装，避免整批失败
    local install_rc=0
    if [[ "$RELEASE" -eq 8 ]]; then
        $pm -y install --allowerasing "${packages[@]}" || install_rc=$?
    else
        $pm -y install "${packages[@]}" || install_rc=$?
    fi
    if [[ $install_rc -ne 0 ]]; then
        log_warn "Batch install failed (rc=$install_rc); retrying per-package best-effort..."
        for pkg in "${packages[@]}"; do
            if [[ "$RELEASE" -eq 8 ]]; then
                $pm -y install --allowerasing "$pkg" > /dev/null 2>&1 \
                    || log_info "Skip missing package: $pkg"
            else
                $pm -y install "$pkg" > /dev/null 2>&1 || log_info "Skip missing package: $pkg"
            fi
        done
    fi

    # load profile safely to avoid unbound variable errors from /etc/profile.d/*
    safe_source /etc/profile || true

    # Enable irqbalance if available
    enable_now irqbalance

    # 在所有 RHEL 家族系统上确保安装 tree（尽力而为）
    $pm -y install tree > /dev/null 2>&1 || true

    # 确保编辑器可用（RHEL 家族：优先安装 vim-enhanced；若无则回退 vim 或 vim-minimal）
    if [[ "${OS,,}" != "ubuntu" ]]; then
        $pm -y install vim-enhanced > /dev/null 2>&1 || $pm -y install vim > /dev/null 2>&1 || true
        if ! command -v vim > /dev/null 2>&1; then
            $pm -y install vim-minimal > /dev/null 2>&1 || true
        fi
    fi
}

ensureDevToolchain() {
    log_info "Ensuring development toolchain (gcc/make/headers)....."
    local pm
    pm=$(pkgmgr)
    if is_ubuntu; then
        run_pkg apt-get update -y || true
        NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
            run_pkg apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew \
            build-essential gcc make pkg-config || true
        # Ubuntu/Debian 使用 linux-headers-* 而非 kernel-headers-*
        if ! NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
            run_pkg apt-get install -y linux-headers-"$(uname -r)"; then
            NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
                run_pkg apt-get install -y linux-headers-generic \
                || NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive \
                    run_pkg apt-get install -y linux-headers-virtual || true
        fi
        return 0
    fi
    case "$RELEASE" in
        7 | 8 | 9 | 10)
            $pm -y groupinstall "Development Tools" || true
            ;;
        *)
            $pm -y install gcc gcc-c++ make || true
            ;;
    esac
}

# --- 8.10 MOTD和系统信息 ---

addMotd() {
    log_info "Setting MOTD....."
    cat << 'EOF' > /etc/motd
                                                                                                      
                                                                                                      
DDDDDDDDDDDDD       TTTTTTTTTTTTTTTTTTTTTTT     OOOOOOOOO     PPPPPPPPPPPPPPPPP      SSSSSSSSSSSSSSS 
D::::::::::::DDD    T:::::::::::::::::::::T   OO:::::::::OO   P::::::::::::::::P   SS:::::::::::::::S
D:::::::::::::::DD  T:::::::::::::::::::::T OO:::::::::::::OO P::::::PPPPPP:::::P S:::::SSSSSS::::::S
DDD:::::DDDDD:::::D T:::::TT:::::::TT:::::TO:::::::OOO:::::::OPP:::::P     P:::::PS:::::S     SSSSSSS
  D:::::D    D:::::DTTTTTT  T:::::T  TTTTTTO::::::O   O::::::O  P::::P     P:::::PS:::::S            
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P::::P     P:::::PS:::::S            
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P::::PPPPPP:::::P  S::::SSSS         
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P:::::::::::::PP    SS::::::SSSSS    
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P::::PPPPPPPPP        SSS::::::::SS  
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P::::P                   SSSSSS::::S 
  D:::::D     D:::::D       T:::::T        O:::::O     O:::::O  P::::P                        S:::::S
  D:::::D    D:::::D        T:::::T        O::::::O   O::::::O  P::::P                        S:::::S
DDD:::::DDDDD:::::D       TT:::::::TT      O:::::::OOO:::::::OPP::::::PP          SSSSSSS     S:::::S
D:::::::::::::::DD        T:::::::::T       OO:::::::::::::OO P::::::::P          S::::::SSSSSS:::::S
D::::::::::::DDD          T:::::::::T         OO:::::::::OO   P::::::::P          S:::::::::::::::SS 
DDDDDDDDDDDDD             TTTTTTTTTTT           OOOOOOOOO     PPPPPPPPPP           SSSSSSSSSSSSSSS   
                                                                                                      
                                                                                                      
EOF
}

__stage_systeminfo() { command -v systeminfo > /dev/null 2>&1 && systeminfo all || true; }

# --- 8.11 FIPS模式 ---

enableFIPSMode() {
    log_important "Enabling FIPS mode (requires reboot)....."
    local pm
    pm=$(pkgmgr)
    if [[ "$RELEASE" -ge 8 ]]; then
        $pm -y install dracut-fips > /dev/null 2>&1 || true
        grubby --update-kernel=ALL --args=fips=1 > /dev/null 2>&1 || true
        dracut -f > /dev/null 2>&1 || true
        update-crypto-policies --set FIPS > /dev/null 2>&1 || true
    else
        $pm -y install dracut-fips > /dev/null 2>&1 || true
        grubby --update-kernel=ALL --args=fips=1 > /dev/null 2>&1 || true
        dracut -f > /dev/null 2>&1 || true
    fi
}


# === 第9层：应用安装层 ===

# --- 9.1 Pyenv 安装 ---

installPyenv() {
    log_info "Installing pyenv and common build deps....."

    # Configure CN-optimized mirrors early for pyenv/Python downloads
    if is_cn; then
        export PYENV_GITHUB_URL="https://xget.xi-xu.me/gh/pyenv/pyenv.git"
        
        # Try Python mirrors in priority order: Huawei Cloud > npmmirror > jedore
        local python_mirror=""
        local mirror_candidates=(
            "https://registry.npmmirror.com/-/binary/python/"
            "https://mirrors.huaweicloud.com/python/"
            "https://jedore.netlify.app/tools/python-mirrors/"
        )
        
        for mirror in "${mirror_candidates[@]}"; do
            # Test mirror availability with a lightweight HEAD request
            if timeout 3 curl -fsSL -I --max-time 3 "${mirror}" > /dev/null 2>&1; then
                python_mirror="$mirror"
                log_info "Using Python mirror: $mirror"
                break
            fi
        done
        
        # Use the first available mirror, or default to Huawei Cloud
        if [[ -n "$python_mirror" ]]; then
            export PYTHON_BUILD_MIRROR_URL="$python_mirror"
        else
            log_warn "No Python mirror reachable, using default: npmmirror"
            export PYTHON_BUILD_MIRROR_URL="https://registry.npmmirror.com/-/binary/python/"
        fi
        
        export PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM=1
        export PIP_INDEX_URL="https://mirrors.aliyun.com/pypi/simple/"
        export PIP_TRUSTED_HOST="mirrors.aliyun.com"
    fi

    if is_ubuntu; then
        # Ubuntu deps
        apt-get update -y || true
        DEBIAN_FRONTEND=noninteractive apt-get install -y \
            build-essential curl git ca-certificates tar gzip libssl-dev zlib1g-dev \
            libbz2-dev libreadline-dev libsqlite3-dev llvm \
            libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
            libffi-dev liblzma-dev wget || true
    else
        # RHEL/CentOS deps
        local pm pm_inst
        pm=$(pkgmgr)
        pm_inst="$pm"
        if [[ "$pm" == "yum" || "$pm" == "dnf" ]]; then
            pm_inst="$pm --disableexcludes=all"
        fi
        $pm_inst -y install gcc gcc-c++ make git curl wget tar gzip openssl-devel bzip2 bzip2-devel \
            zlib-devel readline-devel sqlite-devel xz xz-devel libffi-devel libuuid-devel \
            tk-devel ncurses-devel findutils which ca-certificates patch || true
    fi

    # Install pyenv if not present
    if ! command -v pyenv > /dev/null 2>&1; then
        # Remove existing incomplete installation
        if [[ -d ~/.pyenv && ! -f ~/.pyenv/bin/pyenv ]]; then
            log_warn "Found incomplete pyenv installation, removing..."
            rm -rf ~/.pyenv
        fi
        
        local pyenv_installed=false
        
        if [[ -n "${PYENV_GITHUB_URL:-}" ]]; then
            log_info "Cloning pyenv from ${PYENV_GITHUB_URL}..."
            if git clone --depth 1 "$PYENV_GITHUB_URL" ~/.pyenv 2>/dev/null; then
                pyenv_installed=true
            else
                log_warn "Primary mirror failed, trying Gitee mirror..."
                if git clone --depth 1 "https://gitee.com/mirrors/pyenv.git" ~/.pyenv 2>/dev/null; then
                    pyenv_installed=true
                fi
            fi
        else
            if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
                log_info "Cloning pyenv from xget.xi-xu.me (China mirror)..."
                if git clone --depth 1 https://xget.xi-xu.me/gh/pyenv/pyenv.git ~/.pyenv 2>/dev/null; then
                    pyenv_installed=true
                else
                    log_warn "xget.xi-xu.me mirror failed, trying Gitee..."
                    if git clone --depth 1 https://gitee.com/mirrors/pyenv.git ~/.pyenv 2>/dev/null; then
                        pyenv_installed=true
                    fi
                fi
            else
                log_info "Cloning pyenv from GitHub..."
                if git clone --depth 1 https://github.com/pyenv/pyenv.git ~/.pyenv 2>/dev/null; then
                    pyenv_installed=true
                fi
            fi
        fi
        
        # Verify installation
        if [[ "$pyenv_installed" == "false" ]] || [[ ! -f ~/.pyenv/bin/pyenv ]]; then
            log_error "Failed to clone pyenv from all mirrors"
            log_error "Please check your network connection and try:"
            log_error "  # China mirror (recommended):"
            log_error "  git clone https://xget.xi-xu.me/gh/pyenv/pyenv.git ~/.pyenv"
            log_error "  # Or:"
            log_error "  git clone https://gitee.com/mirrors/pyenv.git ~/.pyenv"
            log_error "  # International:"
            log_error "  git clone https://github.com/pyenv/pyenv.git ~/.pyenv"
            return 1
        fi
        
        log_ok "pyenv cloned successfully"
    fi

    # Install pyenv plugins for enhanced functionality
    log_info "Installing pyenv plugins..."
    
    # pyenv-update: Provides 'pyenv update' command
    if [[ ! -d ~/.pyenv/plugins/pyenv-update ]]; then
        if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
            git clone --depth 1 https://xget.xi-xu.me/gh/pyenv/pyenv-update.git ~/.pyenv/plugins/pyenv-update 2>/dev/null || \
            git clone --depth 1 https://gitee.com/mirrors/pyenv-update.git ~/.pyenv/plugins/pyenv-update 2>/dev/null || \
            git clone --depth 1 https://github.com/pyenv/pyenv-update.git ~/.pyenv/plugins/pyenv-update 2>/dev/null || true
        else
            git clone --depth 1 https://github.com/pyenv/pyenv-update.git ~/.pyenv/plugins/pyenv-update 2>/dev/null || true
        fi
        [[ -d ~/.pyenv/plugins/pyenv-update ]] && log_ok "  ✓ pyenv-update installed" || log_warn "  ✗ pyenv-update installation failed"
    fi
    
    # pyenv-doctor: Provides 'pyenv doctor' command for diagnosing issues
    if [[ ! -d ~/.pyenv/plugins/pyenv-doctor ]]; then
        if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
            git clone --depth 1 https://xget.xi-xu.me/gh/pyenv/pyenv-doctor.git ~/.pyenv/plugins/pyenv-doctor 2>/dev/null || \
            git clone --depth 1 https://gitee.com/mirrors/pyenv-doctor.git ~/.pyenv/plugins/pyenv-doctor 2>/dev/null || \
            git clone --depth 1 https://github.com/pyenv/pyenv-doctor.git ~/.pyenv/plugins/pyenv-doctor 2>/dev/null || true
        else
            git clone --depth 1 https://github.com/pyenv/pyenv-doctor.git ~/.pyenv/plugins/pyenv-doctor 2>/dev/null || true
        fi
        [[ -d ~/.pyenv/plugins/pyenv-doctor ]] && log_ok "  ✓ pyenv-doctor installed" || log_warn "  ✗ pyenv-doctor installation failed"
    fi
    
    # pyenv-virtualenv: Better virtualenv integration
    if [[ ! -d ~/.pyenv/plugins/pyenv-virtualenv ]]; then
        if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
            git clone --depth 1 https://xget.xi-xu.me/gh/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 2>/dev/null || \
            git clone --depth 1 https://gitee.com/mirrors/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 2>/dev/null || \
            git clone --depth 1 https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 2>/dev/null || true
        else
            git clone --depth 1 https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 2>/dev/null || true
        fi
        [[ -d ~/.pyenv/plugins/pyenv-virtualenv ]] && log_ok "  ✓ pyenv-virtualenv installed" || log_warn "  ✗ pyenv-virtualenv installation failed"
    fi

    # Shell profile integration
    if ! grep -q 'PYENV_ROOT=~/.pyenv' /etc/profile.d/pyenv.sh 2> /dev/null; then
        cat > /etc/profile.d/pyenv.sh << 'EOF'
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
if command -v pyenv >/dev/null 2>&1; then
  eval "$(pyenv init -)"
  # Enable pyenv-virtualenv if available
  if command -v pyenv-virtualenv-init >/dev/null 2>&1; then
    eval "$(pyenv virtualenv-init -)"
  fi
fi
EOF
        chmod +x /etc/profile.d/pyenv.sh || true
    fi

    # Pre-create shims and cache dirs
    mkdir -p ~/.pyenv/shims ~/.pyenv/versions ~/.pyenv/cache || true

    # Configure pip mirror for CN users
    if [[ -n "${PIP_INDEX_URL:-}" ]]; then
        mkdir -p ~/.pip ~/.config/pip
        cat > ~/.pip/pip.conf << EOF
[global]
index-url = ${PIP_INDEX_URL}
trusted-host = ${PIP_TRUSTED_HOST}
timeout = 120
[install]
trusted-host = ${PIP_TRUSTED_HOST}
EOF
        # Also create in ~/.config/pip for newer pip versions
        cp ~/.pip/pip.conf ~/.config/pip/pip.conf 2>/dev/null || true
        log_note "Pip mirror configured for faster downloads"
    fi

    log_note "pyenv installed. Re-login or source /etc/profile.d/pyenv.sh to use."

    # Auto-install Python only when PYENV_PY_VERSION is set
    if [[ -n "${PYENV_PY_VERSION}" ]]; then
        # Support comma-separated multiple versions (e.g., "3.11.7,3.12.2,3.13.0")
        local -a versions=()
        IFS=',' read -ra versions <<< "${PYENV_PY_VERSION}"
        
        log_info "Python versions to install: ${versions[*]}"
        
        local installed_count=0
        local failed_versions=()
        
        for target_ver in "${versions[@]}"; do
            # Trim whitespace
            target_ver=$(echo "$target_ver" | xargs)
            
            # Check if version is already installed
            if [[ -x "$HOME/.pyenv/versions/$target_ver/bin/python3" ]]; then
                log_ok "Python ${target_ver} is already installed"
                installed_count=$((installed_count + 1))
                continue
            fi
            
            log_info "Installing Python ${target_ver} via pyenv..."
            
            # Check if version is available
            if ! pyenv install --list 2>/dev/null | grep -q "^  ${target_ver}$"; then
                log_warn "Python ${target_ver} not found in pyenv list. Trying anyway..."
            fi

        if ! is_ubuntu && [[ "$RELEASE" -eq 7 ]]; then
            # EL7: ensure SCL repos, newer toolchain & OpenSSL 1.1 for building Python 3.13.x
            yum --disableexcludes=all -y install epel-release yum-utils || true
            if ! yum repolist all 2> /dev/null | grep -qi sclo; then
                    if is_cn; then
                        # Use Aliyun SCL mirror for CN users
                        log_info "Configuring Aliyun SCL mirror for CentOS 7..."
                        cat > /etc/yum.repos.d/CentOS-SCLo.repo << 'SCLEOF'
[centos-sclo-sclo]
name=CentOS-7 - SCLo sclo
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/sclo/
gpgcheck=0
enabled=1

[centos-sclo-rh]
name=CentOS-7 - SCLo rh
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/rh/
gpgcheck=0
enabled=1
SCLEOF
                    else
                curl -Lks "${GIT_REPO_URL}/Kickstart/repo-mirrorslist/CentOS-SCLo-scl.repo" -o /etc/yum.repos.d/CentOS-SCLo-scl.repo || true
                curl -Lks "${GIT_REPO_URL}/Kickstart/repo-mirrorslist/CentOS-SCLo-scl-rh.repo" -o /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo || true
            fi
                fi
                yum clean all && yum makecache fast || yum makecache || true
                yum --disableexcludes=all -y install devtoolset-11-toolchain devtoolset-11-gcc devtoolset-11-gcc-c++ || \
                    yum --disableexcludes=all -y install devtoolset-10-toolchain devtoolset-10-gcc devtoolset-10-gcc-c++ || true
            yum --disableexcludes=all -y install openssl11 openssl11-devel libuuid-devel || true
                
            bash -lc 'source /opt/rh/devtoolset-11/enable 2>/dev/null || source /opt/rh/devtoolset-10/enable 2>/dev/null || true; \
                      PYENV_ROOT="$HOME/.pyenv"; export PYENV_ROOT PATH="$PYENV_ROOT/bin:$PATH"; \
                      eval "$(pyenv init -)"; \
                          export CPPFLAGS="-I/usr/include/openssl11" LDFLAGS="-L/usr/lib64/openssl11 -Wl,-rpath,/usr/lib64/openssl11" PKG_CONFIG_PATH="/usr/lib64/openssl11/pkgconfig"; \
                          CFLAGS="-O2" MAKE_OPTS="-j$(nproc 2>/dev/null || echo 2)" pyenv install -v '"$target_ver"' && pyenv global '"$target_ver"'' || true
        else
                # Modern systems (EL8+, Ubuntu)
            PYENV_ROOT="$HOME/.pyenv"
                export PYENV_ROOT PATH="$PYENV_ROOT/bin:$PATH"
            eval "$(pyenv init -)" 2> /dev/null || true
                
                # Ensure required tools are available
                command -v tar > /dev/null 2>&1 || {
                    (command -v dnf && dnf -y install tar gzip wget) || \
                    (command -v yum && yum -y install tar gzip wget) || \
                    (command -v apt-get && DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get install -y tar gzip wget)
                } > /dev/null 2>&1 || true
                
                # Build with optimizations
                export CFLAGS="-O2"
                export MAKE_OPTS="-j$(nproc 2>/dev/null || echo 2)"
                log_info "Building Python ${target_ver} (may take a few minutes)..."
                #pyenv install -v "$target_ver" && pyenv global "$target_ver" || {
                pyenv install "$target_ver" && pyenv global "$target_ver" || {
                    log_warn "Build failed, retrying with basic options..."
                    unset CFLAGS MAKE_OPTS
                    #pyenv install -v "$target_ver" && pyenv global "$target_ver" || true
                    pyenv install "$target_ver" && pyenv global "$target_ver" || true
                }
            fi

            # Update shims
            pyenv rehash 2>/dev/null || true

            # Upgrade pip and install commonly used packages
            if [[ -x "$HOME/.pyenv/versions/$target_ver/bin/python3" ]] || command -v python3 > /dev/null 2>&1; then
                log_info "Upgrading pip and installing essential packages..."
                python3 -m pip install --upgrade pip setuptools wheel --timeout 120 || true
                
                # Ensure pip mirror is configured
            if is_cn; then
                python3 -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ || true
                python3 -m pip config set install.trusted-host mirrors.aliyun.com || true
                    python3 -m pip config set global.timeout 120 || true
            fi
                
                # Install useful packages
                python3 -m pip install --timeout 120 virtualenv ipython requests 2>/dev/null || true
        fi

            # Report result for this version
        if [[ -x "$HOME/.pyenv/versions/$target_ver/bin/python3" ]]; then
                local py_ver=$(~/.pyenv/versions/$target_ver/bin/python3 --version 2>&1 || echo "unknown")
                log_ok "Python ${target_ver} installed successfully: ${py_ver}"
                installed_count=$((installed_count + 1))
            else
                log_error "Python ${target_ver} installation failed."
                failed_versions+=("$target_ver")
                
                # Try to show build log tail
                local build_log="$HOME/.pyenv/sources/${target_ver}/build.log"
                [[ ! -f "$build_log" ]] && build_log="$HOME/.pyenv/sources/${target_ver}.*/build.log"
                if ls $build_log 2>/dev/null | head -1 | xargs -I{} test -f "{}"; then
                    local actual_log=$(ls $build_log 2>/dev/null | head -1)
                    log_error "Last 15 lines of build log:"
                    tail -15 "$actual_log" 2>/dev/null | while IFS= read -r line; do
                        log_error "  | $line"
                    done
                fi
                
                log_error ""
                log_error "Troubleshooting steps:"
                log_error "  1. Check build dependencies:"
                if is_ubuntu; then
                    log_error "     sudo apt-get install -y build-essential libssl-dev zlib1g-dev \\"
                    log_error "       libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \\"
                    log_error "       libncurses5-dev libncursesw5-dev xz-utils tk-dev \\"
                    log_error "       libffi-dev liblzma-dev python3-openssl git"
                else
                    log_error "     sudo yum groupinstall -y 'Development Tools'"
                    log_error "     sudo yum install -y openssl-devel bzip2-devel libffi-devel \\"
                    log_error "       readline-devel sqlite-devel xz-devel tk-devel"
                fi
                log_error ""
                log_error "  2. Review full build log:"
                log_error "     find ~/.pyenv/sources -name 'build.log' -type f"
                log_error ""
                log_error "  3. Try a stable version (e.g., 3.11.7 or 3.12.2):"
                log_error "     export PYENV_PY_VERSION=3.11.7"
                log_error "     pyenv install \$PYENV_PY_VERSION"
                log_error ""
                log_error "  4. Manual verbose install to see errors:"
                log_error "     pyenv install -v ${target_ver}"
                log_error ""
                is_cn && {
                    log_error "  5. Check network/mirror access:"
                    log_error "     curl -I https://registry.npmmirror.com/-/binary/python/${target_ver}/Python-${target_ver}.tar.xz"
                }
            fi
        done  # End of versions loop
        
        # Summary report
        log_info "═══════════════════════════════════════════════════════════"
        log_info "Python Installation Summary:"
        log_info "  Total versions requested: ${#versions[@]}"
        log_info "  Successfully installed: ${installed_count}"
        log_info "  Failed: ${#failed_versions[@]}"
        
        if [[ ${#failed_versions[@]} -gt 0 ]]; then
            log_warn "  Failed versions: ${failed_versions[*]}"
        fi
        
        # Set global Python version
        if [[ ${installed_count} -gt 0 ]]; then
            local global_ver=""
            
            # Use PYENV_GLOBAL_VERSION if set and installed
            if [[ -n "${PYENV_GLOBAL_VERSION}" ]]; then
                if [[ -x "$HOME/.pyenv/versions/${PYENV_GLOBAL_VERSION}/bin/python3" ]]; then
                    global_ver="${PYENV_GLOBAL_VERSION}"
                    log_info "  Setting global version to: ${global_ver} (from PYENV_GLOBAL_VERSION)"
                else
                    log_warn "  PYENV_GLOBAL_VERSION=${PYENV_GLOBAL_VERSION} not installed, using first installed version"
                fi
            fi
            
            # Fallback: use first successfully installed version
            if [[ -z "$global_ver" ]]; then
                for ver in "${versions[@]}"; do
                    ver=$(echo "$ver" | xargs)
                    if [[ -x "$HOME/.pyenv/versions/$ver/bin/python3" ]]; then
                        global_ver="$ver"
                        log_info "  Setting global version to: ${global_ver} (first installed)"
                        break
                    fi
                done
            fi
            
            # Set global version
            if [[ -n "$global_ver" ]]; then
                pyenv global "$global_ver" 2>/dev/null || true
                log_ok "Global Python version set to: ${global_ver}"
            fi
            
            log_info "  Run 'pyenv versions' to see all installed versions"
            log_info "  Run 'pyenv global <version>' to change default version"
            log_info "  Run 'pyenv local <version>' to set project-specific version"
        fi
        log_info "═══════════════════════════════════════════════════════════"
    else
        log_warn "PYENV_PY_VERSION not set. Skipping Python auto-install."
        log_info "To install Python versions:"
        log_info "  Single version:   export PYENV_PY_VERSION=3.13.7"
        log_info "  Multiple versions: export PYENV_PY_VERSION=\"3.13.7,3.13.8,3.14.0\""
        log_info "  Set global default: export PYENV_GLOBAL_VERSION=3.13.7"
        log_info "Then run: source /etc/profile.d/pyenv.sh && pyenv install \$PYENV_PY_VERSION"
    fi
}


enableFail2ban() {
    log_info "Enabling fail2ban for sshd....."
    local pm
    pm=$(pkgmgr)
    $pm -y install fail2ban > /dev/null 2>&1 || true
    mkdir -p /etc/fail2ban
    cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 15m
maxretry = 6
backend = auto

[sshd]
enabled = true
mode = aggressive
EOF

    # Prefer systemd backend on EL8/9
    if [[ "$RELEASE" -ge 8 ]]; then
        kv_set /etc/fail2ban/jail.local backend systemd '='
    fi
    enable_now fail2ban
    systemctl status fail2ban --no-pager > /dev/null 2>&1 || true
}



# === 章节：NUMA/RPS 网络调优 ===

generateCPUMask() {
    # Generate cpumask string for given number of CPUs (or detected nproc)
    local n cpus hex digits groups result chunk
    n="${1:-}"
    if [[ -z "$n" ]]; then
        n=$(nproc 2> /dev/null || echo 1)
    fi
    ((n < 1)) && n=1
    # Number of hex digits needed
    digits=$(((n + 3) / 4))
    # Build hex string of 'f' repeated digits times
    hex=""
    while ((${#hex} < digits)); do hex+="ffffffff"; done
    hex=${hex:0:digits}
    # Split into groups of 8 from right to left, join with commas (omit leading zeros group)
    result=""
    while ((${#hex} > 8)); do
        chunk=${hex: -8}
        result=",${chunk}${result}"
        hex=${hex:0:${#hex}-8}
    done
    result="${hex}${result}"
    # Remove leading zeros before comma if present
    result=${result#,}
    echo "$result"
}


# === 章节：监控与杂项 ===
installZabbixAgent() {
    log_info "Installing Zabbix Agent....."
    if is_ubuntu; then
        # Ubuntu: 先尝试官方仓库安装；若无则添加 Zabbix 源，按主版本与发行代号回退（noble→jammy）
        run_pkg apt-get update -y >/dev/null 2>&1 || true
        NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent >/dev/null 2>&1 || true
        if dpkg -s zabbix-agent >/dev/null 2>&1; then
            mkdir -p /etc/zabbix >/dev/null 2>&1 || true
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            enable_now zabbix-agent
            return 0
        fi
        local codename
        codename=$(lsb_release -sc 2> /dev/null || (
            . /etc/os-release 2> /dev/null
            echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}"
        ))
        mkdir -p /usr/share/keyrings /etc/apt/sources.list.d
        if [[ ! -s /usr/share/keyrings/zabbix-official.gpg ]]; then
            curl -fsSL https://repo.zabbix.com/zabbix-official-repo.key | gpg --dearmor > /usr/share/keyrings/zabbix-official.gpg 2> /dev/null || true
        fi
        local installed=false
        local -a majors=(7.0)
        if is_arch_aarch64; then majors=(7.0 6.0); fi
        local -a code_candidates=("${codename}" jammy)
        local maj ucodename apt_arch zbx_base_path
        apt_arch="$(dpkg --print-architecture 2>/dev/null || echo arm64)"
        for maj in "${majors[@]}"; do
            for ucodename in "${code_candidates[@]}"; do
                if is_arch_aarch64; then
                    zbx_base_path="ubuntu-arm64"
                else
                    zbx_base_path="ubuntu"
                fi
                echo "deb [arch=${apt_arch} signed-by=/usr/share/keyrings/zabbix-official.gpg] https://repo.zabbix.com/zabbix/${maj}/${zbx_base_path} ${ucodename} main" > /etc/apt/sources.list.d/zabbix.list
                run_pkg apt-get update -y >/dev/null 2>&1 || true
            NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent || true
            if dpkg -s zabbix-agent > /dev/null 2>&1; then
                installed=true
                    break
            fi
            NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent2 || true
            if dpkg -s zabbix-agent2 > /dev/null 2>&1; then
                installed=true
                    break
            fi
            done
            $installed && break || true
        done
        if ! $installed; then
            log_warn "未能在 Ubuntu (${codename}) 上安装 zabbix-agent/agent2（官方源与 Zabbix 源均不可用）。跳过配置。"
            return 0
        fi
        if dpkg -s zabbix-agent2 > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agent2.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf Hostname "$(hostname)"
            enable_now zabbix-agent2
        elif dpkg -s zabbix-agent > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            enable_now zabbix-agent
        fi
    else
        # RHEL 家族：使用官方 Zabbix 源（架构感知 base），避免使用 CentOS release 探测器
        # 安装成功后如为 7.0，屏蔽 EPEL 源中的 zabbix 包，避免被错误升级/覆盖

        maskEpelZabbix() {
            log_info "Masking zabbix* from EPEL repositories....."
            # 1) 用 config-manager 对所有以 epel 开头的 repo id 设置 exclude/excludepkgs
            local -a epel_ids=()
            if command -v dnf >/dev/null 2>&1; then
                # 取第一列 repo-id，筛选 epel*（包含 epel-next、epel-modular 等）
                while read -r rid _; do
                    [[ -n "$rid" && "$rid" == epel* ]] && epel_ids+=("$rid")
                done < <(dnf repolist --all 2>/dev/null | awk 'NF>0 {print $1,$0}')
                local rid
                for rid in "${epel_ids[@]}"; do
                    dnf config-manager --save --setopt="${rid}.excludepkgs=zabbix*" >/dev/null 2>&1 || true
                    dnf config-manager --save --setopt="${rid}.exclude=zabbix*" >/dev/null 2>&1 || true
                done
            fi
            if command -v yum >/dev/null 2>&1 && command -v yum-config-manager >/dev/null 2>&1; then
                epel_ids=()
                while read -r rid _; do
                    [[ -n "$rid" && "$rid" == epel* ]] && epel_ids+=("$rid")
                done < <(yum repolist all 2>/dev/null | awk 'NF>0 {print $1,$0}')
                local rid2
                for rid2 in "${epel_ids[@]}"; do
                    yum-config-manager --save --setopt="${rid2}.exclude=zabbix*" >/devnull 2>&1 || true
                    yum-config-manager --save --setopt="${rid2}.excludepkgs=zabbix*" >/dev/null 2>&1 || true
                done
            fi
            # 2) 兜底：重写 /etc/yum.repos.d/epel*.repo，在 [epel*] 节内确保 excludepkgs 含 zabbix*
            shopt -s nullglob
            local f
            for f in /etc/yum.repos.d/epel*.repo; do
                [ -f "$f" ] || continue
                awk '
                    BEGIN{ins=0}
                    {
                        line=$0
                        if ($0 ~ /^\[/) {
                            if (sect ~ /^epel/) {
                                if (!seen) { print "excludepkgs=zabbix*" }
                            }
                            sect=$0; sub(/^\[/, "", sect); sub(/\].*$/, "", sect); seen=0
                        }
                        if (sect ~ /^epel/ && $0 ~ /^\s*excludepkgs\s*=/) {
                            if ($0 !~ /zabbix\*/) { sub(/$/, " zabbix*", $0) }
                            seen=1
                        }
                        print $0
                    }
                    END{
                        if (sect ~ /^epel/ && !seen) { print "excludepkgs=zabbix*" }
                    }
                ' "$f" >"$f.tmp" 2>/dev/null && mv -f "$f.tmp" "$f" || true
            done
            shopt -u nullglob
        }
        local basearch
        basearch="$(arch_id)"
        case "$basearch" in
            aarch64 | x86_64) : ;;
            *) basearch="x86_64" ;;
        esac

        # Default 7.0 LTS; on aarch64 若不支持则回退 6.0
        local -a zbx_majors=()
        if is_arch_aarch64; then
            zbx_majors=(${ZBX_MAJOR_VERSION:-7.0} 6.0)
        else
            zbx_majors=(${ZBX_MAJOR_VERSION:-7.0})
        fi

        local zrepo_file="/etc/yum.repos.d/zabbix.repo"
        local installed=false
        local installed_from_zabbix_repo=false
        local installed_major=""
        run_pkg dnf -y install dnf-plugins-core || true

        local maj
        for maj in "${zbx_majors[@]}"; do
            # Choose vendor path per OS (rocky/almalinux/rhel)
            local vendor_candidates=()
            if is_rocky; then
                vendor_candidates=(rocky rhel)
            elif is_alma; then
                # Zabbix 官方 AlmaLinux 路径使用 alma 而非 almalinux
                vendor_candidates=(alma rhel)
            else
                vendor_candidates=(rhel)
            fi
            local vendor
            for vendor in "${vendor_candidates[@]}"; do
                # 兼容性处理：历史上可能使用过 "almalinux"，Zabbix 官方实际为 "alma"
                vendor_path="$vendor"
                if [[ "$vendor_path" == "almalinux" ]]; then vendor_path="alma"; fi
                cat > "$zrepo_file" << EOF
[zabbix]
name=Zabbix ${maj} for ${vendor} ${RELEASE} - ${basearch}
baseurl=https://repo.zabbix.com/zabbix/${maj}/${vendor_path}/${RELEASE}/${basearch}/
enabled=1
gpgcheck=0
EOF
                run_pkg dnf -y clean all || true
                run_pkg dnf -y makecache || true
                log_info "Trying Zabbix ${maj} (${vendor}/${RELEASE}/${basearch}) zabbix-agent (zabbix repo only, epel disabled)..."
                if run_pkg dnf -y --disablerepo='epel*' --enablerepo='zabbix' --nobest --skip-broken install zabbix-agent zabbix-sender; then
                    installed=true
                    installed_from_zabbix_repo=true
                    installed_major="$maj"
                    log_ok "Installed Zabbix Agent (${maj}) for ${vendor}/${RELEASE}/${basearch}"
                    break
                fi
                log_info "Trying Zabbix ${maj} (${vendor}/${RELEASE}/${basearch}) zabbix-agent2 (zabbix repo only, epel disabled)..."
                if run_pkg dnf -y --disablerepo='epel*' --enablerepo='zabbix' --nobest --skip-broken install zabbix-agent2; then
                    installed=true
                    installed_from_zabbix_repo=true
                    installed_major="$maj"
                    log_ok "Installed Zabbix Agent2 (${maj}) for ${vendor}/${RELEASE}/${basearch}"
                    break
                fi
                log_warn "Zabbix ${maj} not available for ${vendor}/${RELEASE}/${basearch}, trying next vendor..."
            done
            # If 7.0 全部供应商不可用，且当前考虑 6.0，则尝试 EPEL 6.0 兜底（禁用 zabbix 源）
            if ! $installed && [[ "$maj" == "6.0" ]]; then
                log_info "Zabbix ${maj} fallback via EPEL (zabbix repo disabled)..."
                # agent 与 sender 同源安装避免文件冲突，允许 allowerasing 解决已有包替换
                if run_pkg dnf -y --disablerepo='zabbix' --enablerepo='epel*' --nobest --skip-broken --allowerasing install zabbix-agent zabbix-sender; then
                    installed=true
                    log_ok "Installed Zabbix Agent (${maj}) from EPEL"
                elif run_pkg dnf -y --disablerepo='zabbix' --enablerepo='epel*' --nobest --skip-broken --allowerasing install zabbix-agent2; then
                    installed=true
                    log_ok "Installed Zabbix Agent2 (${maj}) from EPEL"
                else
                    log_warn "EPEL fallback for Zabbix ${maj} failed; will try next major if any."
                fi
            fi
            # If installed for any vendor of this major version, stop trying other majors
            if $installed; then
                break
            fi
        done

        if ! $installed; then
            log_warn "zabbix-agent not installed; no supported version found for ${RELEASE}/${basearch}. Skipping configuration."
            return 0
        fi
        # 若来自官方 Zabbix 源且为 7.0，则屏蔽 EPEL 源中的 zabbix 包
        if $installed_from_zabbix_repo && [[ "$installed_major" == "7.0" ]]; then
            maskEpelZabbix || true
        fi
        # Configure whichever installed
        if rpm -q zabbix-agent2 > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agent2.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf Hostname "$(hostname)"
            cat >> /etc/zabbix/zabbix_agentd.conf << EOF
UserParameter=authorized_keys,sudo /usr/bin/cksum /root/.ssh/authorized_keys|awk '{print \$1}'
UserParameter=iptables_lins,/usr/bin/sudo iptables -S |md5sum|awk '{print \$1}'
UserParameter=iptables_file,/usr/bin/sudo /usr/bin/cksum /etc/sysconfig/iptables|awk '{print \$1}'
EOF
            mkdir -p /etc/zabbix/zabbix_agent2.d || true
            gh_download "${GIT_REPO_URL}/Zabbix/disktcp.conf" /etc/zabbix/zabbix_agent2.d/disktcp.conf || curl -fsSL "${GIT_REPO_URL}/Zabbix/disktcp.conf" -o /etc/zabbix/zabbix_agent2.d/disktcp.conf || true
            # 校验配置以避免解析错误（例如误下成 HTML）。应包含以 UserParameter= 开头的行
            if ! grep -q '^UserParameter=' /etc/zabbix/zabbix_agent2.d/disktcp.conf 2> /dev/null; then
                log_warn "Downloaded disktcp.conf seems invalid; removing to avoid agent parse error."
                rm -f /etc/zabbix/zabbix_agent2.d/disktcp.conf || true
            fi
            enable_now zabbix-agent2
        elif rpm -q zabbix-agent > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            cat >> /etc/zabbix/zabbix_agentd.conf << EOF
UserParameter=authorized_keys,sudo /usr/bin/cksum /root/.ssh/authorized_keys|awk '{print \$1}'
UserParameter=iptables_lins,/usr/bin/sudo iptables -S |md5sum|awk '{print \$1}'
UserParameter=iptables_file,/usr/bin/sudo /usr/bin/cksum /etc/sysconfig/iptables|awk '{print \$1}'
EOF
            mkdir -p /etc/zabbix/zabbix_agentd.d || true
            gh_download "${GIT_REPO_URL}/Zabbix/disktcp.conf" /etc/zabbix/zabbix_agentd.d/disktcp.conf || curl -fsSL "${GIT_REPO_URL}/Zabbix/disktcp.conf" -o /etc/zabbix/zabbix_agentd.d/disktcp.conf || true
            if ! grep -q '^UserParameter=' /etc/zabbix/zabbix_agentd.d/disktcp.conf 2> /dev/null; then
                log_warn "Downloaded disktcp.conf seems invalid; removing to avoid agent parse error."
                rm -f /etc/zabbix/zabbix_agentd.d/disktcp.conf || true
            fi
            enable_now zabbix-agent
        fi
    fi
}

addSpeedtest() {
    log_info "Installing LibreSpeed CLI....."
    
    # Detect architecture
    local arch=""
    case "$(uname -m)" in
        x86_64|amd64) arch="amd64" ;;
        aarch64|arm64) arch="arm64" ;;
        armv7l|armhf) arch="armv7" ;;
        i386|i686) arch="386" ;;
        *) 
            log_warn "Unsupported architecture: $(uname -m), skipping LibreSpeed CLI installation"
            return 0
            ;;
    esac
    
    # Detect OS
    local os_type=""
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        case "$ID" in
            ubuntu|debian|centos|rhel|rocky|alma|fedora|opensuse*|arch)
                os_type="linux"
                ;;
            *)
                os_type="linux"
                ;;
        esac
    else
        os_type="linux"
    fi
    
    # Download latest release from GitHub
    local base_url=""
    local binary_name="librespeed-cli_${os_type}_${arch}"
    local download_url=""
    local target="/usr/local/bin/librespeed-cli"
    
    # Use xget.xi-xu.me mirror for CN users, GitHub for international users
    if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
        base_url="https://xget.xi-xu.me/gh/librespeed/speedtest-cli/releases/latest/download"
        log_info "Downloading LibreSpeed CLI for ${os_type}/${arch} from xget.xi-xu.me (China mirror)..."
    else
        base_url="https://github.com/librespeed/speedtest-cli/releases/latest/download"
        log_info "Downloading LibreSpeed CLI for ${os_type}/${arch} from GitHub..."
    fi
    
    download_url="${base_url}/${binary_name}"
    
    # Try different download methods
    if command -v curl > /dev/null 2>&1; then
        curl -fsSL -o "${target}" "${download_url}" 2>/dev/null || {
            log_error "Failed to download LibreSpeed CLI from ${base_url}"
            return 1
        }
    elif command -v wget > /dev/null 2>&1; then
        wget -q -O "${target}" "${download_url}" 2>/dev/null || {
            log_error "Failed to download LibreSpeed CLI from ${base_url}"
            return 1
        }
    else
        log_error "Neither curl nor wget is available"
        return 1
    fi
    
    # Make executable
    chmod +x "${target}" || {
        log_error "Failed to make LibreSpeed CLI executable"
        return 1
    }
    
    # Create symlink for backward compatibility
    ln -sf "${target}" /usr/local/bin/speedtest 2>/dev/null || true
    
    # Verify installation
    if "${target}" --version > /dev/null 2>&1; then
        local version=$("${target}" --version 2>&1 | head -1)
        log_ok "LibreSpeed CLI installed successfully: ${version}"
        log_info "Usage: librespeed-cli --help"
        log_info "Symlink: speedtest -> librespeed-cli"
    else
        log_error "LibreSpeed CLI installation verification failed"
        return 1
    fi
}

# 低内存预处理：为内存较小的机器创建交换分区并降低 DNF 并发，避免 OOM 杀死 dnf
preflightLowMemory() {
    log_info "Running preflight low-memory mitigation....."
    local mem_kb mem_gb swap_total_kb need_swap_kb swapfile="/swapfile"
    mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
    swap_total_kb=$(awk '/SwapTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
    mem_gb=$(( mem_kb / 1024 / 1024 ))

    # If memory <= 2 GiB and no swap, create a small swapfile (1-2 GiB)
    if (( mem_gb <= 2 )) && (( swap_total_kb < 262144 )); then
        local target_gb=2
        (( mem_gb <= 1 )) && target_gb=1
        need_swap_kb=$(( target_gb * 1024 * 1024 ))
        if [[ ! -f "$swapfile" ]]; then
            log_warn "Creating ${target_gb}G swapfile to protect dnf from OOM....."
            fallocate -l ${target_gb}G "$swapfile" 2>/dev/null || dd if=/dev/zero of="$swapfile" bs=1M count=$((target_gb*1024)) status=none || true
            chmod 600 "$swapfile" || true
            mkswap "$swapfile" >/dev/null 2>&1 || true
            swapon "$swapfile" >/dev/null 2>&1 || true
            # Persist
            if ! grep -q "^/swapfile" /etc/fstab 2>/dev/null; then
                echo "/swapfile none swap sw 0 0" >> /etc/fstab
            fi
        fi
    fi

    # Extra: reduce dnf metadata usage on tiny memory systems
    if [[ -d /etc/dnf ]]; then
        kv_set /etc/dnf/dnf.conf metadata_expire 1h '='
        kv_set /etc/dnf/dnf.conf keepcache True '='
    fi
}

# === 章节：主入口 ===
main() {

    # 严格的 getopts 风格参数解析
    # Short/long options (no conflicts; dedicated flags for addrpools/mirror tuning)
    local short_opts=":hpDZASFTBUNt:fz:i:e:s:d:a:w:j:m:nL:g:v cJRKY"
    local long_opts="help,private,nodocker,nozabbix,noadaptive,nohardenssh,nofwlimit,nothp,nofail2ban,noauditd,nonuma,tuned:,fips,zabbix:,insecure:,execopts:,storage:,dataroot:,addrpools:,mirror-timeout:,mirror-jobs:,mirrors:,dry-run,log-file:,log-level:,verbose,continue-on-error,plan-json,no-reboot,nokernelml,pyenv"

    # Normalize long options to short, preserve order, and safely detect subcommand
    local -a normalized_args=()
    local found_subcommand="false"
    local awaiting_opt_arg=""
    while [[ $# -gt 0 ]]; do
        local tok="$1"
        # If previous short/long option needs a value, take current token as its value
        if [[ -n "$awaiting_opt_arg" ]]; then
            normalized_args+=("$tok")
            awaiting_opt_arg=""
            shift
            continue
        fi
        case "$tok" in
            --help) normalized_args+=("-h") ;;
            --private) normalized_args+=("-p") ;;
            --nodocker) normalized_args+=("-D") ;;
            --nozabbix) normalized_args+=("-Z") ;;
            --noadaptive) normalized_args+=("-A") ;;
            --nohardenssh) normalized_args+=("-S") ;;
            --nofwlimit) normalized_args+=("-F") ;;
            --nothp) normalized_args+=("-T") ;;
            --nofail2ban) normalized_args+=("-B") ;;
            --noauditd) normalized_args+=("-U") ;;
            --nonuma) normalized_args+=("-N") ;;
            --tuned=*) normalized_args+=("-t" "${tok#*=}") ;;
            --tuned)
                normalized_args+=("-t")
                awaiting_opt_arg="-t"
                ;;
            --fips) normalized_args+=("-f") ;;
            --zabbix=*) normalized_args+=("-z" "${tok#*=}") ;;
            --zabbix)
                normalized_args+=("-z")
                awaiting_opt_arg="-z"
                ;;
            --insecure=*) normalized_args+=("-i" "${tok#*=}") ;;
            --insecure)
                normalized_args+=("-i")
                awaiting_opt_arg="-i"
                ;;
            --execopts=*) normalized_args+=("-e" "${tok#*=}") ;;
            --execopts)
                normalized_args+=("-e")
                awaiting_opt_arg="-e"
                ;;
            --storage=*) normalized_args+=("-s" "${tok#*=}") ;;
            --storage)
                normalized_args+=("-s")
                awaiting_opt_arg="-s"
                ;;
            --dataroot=*) normalized_args+=("-d" "${tok#*=}") ;;
            --dataroot)
                normalized_args+=("-d")
                awaiting_opt_arg="-d"
                ;;
            --addrpools=*) normalized_args+=("-a" "${tok#*=}") ;;
            --addrpools)
                normalized_args+=("-a")
                awaiting_opt_arg="-a"
                ;;
            --mirror-timeout=*) normalized_args+=("-w" "${tok#*=}") ;;
            --mirror-timeout)
                normalized_args+=("-w")
                awaiting_opt_arg="-w"
                ;;
            --mirror-jobs=*) normalized_args+=("-j" "${tok#*=}") ;;
            --mirror-jobs)
                normalized_args+=("-j")
                awaiting_opt_arg="-j"
                ;;
            --mirrors=*) normalized_args+=("-m" "${tok#*=}") ;;
            --mirrors)
                normalized_args+=("-m")
                awaiting_opt_arg="-m"
                ;;
            --dry-run) normalized_args+=("-n") ;;
            --log-file=*) normalized_args+=("-L" "${tok#*=}") ;;
            --log-file)
                normalized_args+=("-L")
                awaiting_opt_arg="-L"
                ;;
            --log-level=*) normalized_args+=("-g" "${tok#*=}") ;;
            --log-level)
                normalized_args+=("-g")
                awaiting_opt_arg="-g"
                ;;
            --verbose) normalized_args+=("-v") ;;
            --continue-on-error) normalized_args+=("-c") ;;
            --plan-json) normalized_args+=("-J") ;;
            --no-reboot) normalized_args+=("-R") ;;
            --nokernelml) normalized_args+=("-K") ;;
            --pyenv) normalized_args+=("-Y") ;;
            --)
                shift
                while [[ $# -gt 0 ]]; do
                    normalized_args+=("$1")
                    shift
                done
                break
                ;;
            --*)
                log_error "Unknown option: $tok"
                printHelp
                exit 2
                ;;
            -L | -g | -t | -z | -i | -e | -s | -d | -a | -w | -j | -m)
                normalized_args+=("$tok")
                awaiting_opt_arg="$tok"
                ;;
            -*)
                normalized_args+=("$tok")
                ;;
            *)
                if [[ "$found_subcommand" == "false" ]]; then
                    SUBCOMMAND="$tok"
                    found_subcommand="true"
                else
                    normalized_args+=("$tok")
                fi
                ;;
        esac
        shift || true
    done

    # Remove subcommand from normalized_args if it was captured at the head
    if [[ "$found_subcommand" == "true" ]]; then
        # Keep only options for getopts
        :
    fi

    # Feed normalized args to getopts
    set -- "${normalized_args[@]}"

    while getopts "$short_opts" opt; do
        case "$opt" in
            h)
                printHelp
                exit 0
                ;;
            p) NETWORK_TYPE="private" ;;
            D) INSTALL_DOCKER="false" ;;
            Z) INSTALL_ZABBIX="false" ;;
            A) DO_ADAPTIVE_SYSCTL="false" ;;
            S) DO_HARDEN_SSH="false" ;;
            F) DO_FIREWALL_LIMIT="false" ;;
            T) DO_THP="false" ;;
            B) DO_FAIL2BAN="false" ;;
            U) DO_AUDITD="false" ;;
            N) DO_NUMA_RPS="false" ;;
            t) TUNED_PROFILE="$OPTARG" ;;
            f) ENABLE_FIPS="true" ;;
            z) ZBX_SERVER_IP="$OPTARG" ;;
            i) INSECURE_REG_CSV="$OPTARG" ;;
            e) EXEC_OPTS_CSV="$OPTARG" ;;
            s) STORAGE_DRIVER="$OPTARG" ;;
            d) DATA_ROOT="$OPTARG" ;;
            a) DEFAULT_ADDR_POOLS_CSV="$OPTARG" ;;
            w) MIRROR_PROBE_TIMEOUT="$OPTARG" ;;
            j) MIRROR_MAX_JOBS="$OPTARG" ;;
            m) MIRROR_CANDIDATES_CSV="$OPTARG" ;;
            n) DRY_RUN="true" ;;
            L) LOG_FILE="$OPTARG" ;;
            g) LOG_LEVEL="${OPTARG}" ;;
            v) LOG_LEVEL="DEBUG" ;;
            c) CONTINUE_ON_ERROR=true ;;
            J) PLAN_JSON=true ;;
            R) NO_REBOOT="true" ;;
            K) DO_KERNEL_ML="false" ;;
            Y) INSTALL_PYENV="true" ;;
            :)
                log_error "Option -$OPTARG requires an argument"
                exit 2
                ;;
            \?)
                log_error "Invalid option: -$OPTARG"
                printHelp
                exit 2
                ;;
        esac
    done

    # Setup logging（解析完成后再初始化日志路径）
    if [[ -z "$LOG_FILE" ]]; then
        LOG_FILE="/var/log/system_init_2.log"
    fi
    mkdir -p "$(dirname -- "$LOG_FILE")" 2> /dev/null || true
    { echo "==== $(date '+%F %T') system_init_2.sh start (subcommand=${SUBCOMMAND}, dry_run=${DRY_RUN}) ===="; } >> "$LOG_FILE" 2> /dev/null || true
    USE_TEE_LOGGING=true
    # 使用 tee -a（不加额外选项）以规避 GNU/BSD 差异；确保没有多余的 -v 传给 tee
    exec > >(tee -a "$LOG_FILE") 2> >(tee -a "$LOG_FILE" >&2)

    if [[ "$NETWORK_TYPE" == "private" ]]; then
        log_note "Running in PRIVATE network mode."
    else
        log_note "Running in PUBLIC network mode."
    fi

    # Dry-run: print plan and exit（无需 root/OS 检测）
    if [[ "$DRY_RUN" == "true" ]]; then
        log_important "[DRY-RUN] 将执行的阶段 (subcommand=${SUBCOMMAND}):"
        define_stages_for_subcommand
        print_plan_auto
        exit 0
    fi

    # 参数解析完成后再进行 root/OS 检测与订阅管理器处理
    checkRoot
    checkOSinfo
    disableRHSMOnNonRHEL

    case "$SUBCOMMAND" in
        all | docker | firewall | pyenv | kernel | speedtest) : ;;
        *)
            log_error "未知子命令: ${SUBCOMMAND}"
            exit 2
            ;;
    esac

    define_stages_for_subcommand
    run_orchestrated

    # Print summary
    print_summary_auto

    # 仅在完整流程且未禁止重启时才执行重启
    if [[ "$SUBCOMMAND" == "all" && "$NO_REBOOT" != "true" ]]; then
        log_important "System initialization completed. The system will reboot in 5 seconds."
        sleep 5
        reboot
    else
        log_important "Completed (${SUBCOMMAND}). Reboot skipped."
    fi
}

main "$@"

# --- 9.2 Zabbix Agent 安装 ---

installZabbixAgent() {
    log_info "Installing Zabbix Agent....."
    if is_ubuntu; then
        # Ubuntu: 先尝试官方仓库安装；若无则添加 Zabbix 源，按主版本与发行代号回退（noble→jammy）
        run_pkg apt-get update -y >/dev/null 2>&1 || true
        NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent >/dev/null 2>&1 || true
        if dpkg -s zabbix-agent >/dev/null 2>&1; then
            mkdir -p /etc/zabbix >/dev/null 2>&1 || true
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            enable_now zabbix-agent
            return 0
        fi
        local codename
        codename=$(lsb_release -sc 2> /dev/null || (
            . /etc/os-release 2> /dev/null
            echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}"
        ))
        mkdir -p /usr/share/keyrings /etc/apt/sources.list.d
        if [[ ! -s /usr/share/keyrings/zabbix-official.gpg ]]; then
            curl -fsSL https://repo.zabbix.com/zabbix-official-repo.key | gpg --dearmor > /usr/share/keyrings/zabbix-official.gpg 2> /dev/null || true
        fi
        local installed=false
        local -a majors=(7.0)
        if is_arch_aarch64; then majors=(7.0 6.0); fi
        local -a code_candidates=("${codename}" jammy)
        local maj ucodename apt_arch zbx_base_path
        apt_arch="$(dpkg --print-architecture 2>/dev/null || echo arm64)"
        for maj in "${majors[@]}"; do
            for ucodename in "${code_candidates[@]}"; do
                if is_arch_aarch64; then
                    zbx_base_path="ubuntu-arm64"
                else
                    zbx_base_path="ubuntu"
                fi
                echo "deb [arch=${apt_arch} signed-by=/usr/share/keyrings/zabbix-official.gpg] https://repo.zabbix.com/zabbix/${maj}/${zbx_base_path} ${ucodename} main" > /etc/apt/sources.list.d/zabbix.list
                run_pkg apt-get update -y >/dev/null 2>&1 || true
            NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent || true
            if dpkg -s zabbix-agent > /dev/null 2>&1; then
                installed=true
                    break
            fi
            NEEDRESTART_MODE=${NEEDRESTART_MODE:-l} DEBIAN_FRONTEND=noninteractive run_pkg apt-get install -y zabbix-agent2 || true
            if dpkg -s zabbix-agent2 > /dev/null 2>&1; then
                installed=true
                    break
            fi
            done
            $installed && break || true
        done
        if ! $installed; then
            log_warn "未能在 Ubuntu (${codename}) 上安装 zabbix-agent/agent2（官方源与 Zabbix 源均不可用）。跳过配置。"
            return 0
        fi
        if dpkg -s zabbix-agent2 > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agent2.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf Hostname "$(hostname)"
            enable_now zabbix-agent2
        elif dpkg -s zabbix-agent > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            enable_now zabbix-agent
        fi
    else
        # RHEL 家族：使用官方 Zabbix 源（架构感知 base），避免使用 CentOS release 探测器
        # 安装成功后如为 7.0，屏蔽 EPEL 源中的 zabbix 包，避免被错误升级/覆盖

        maskEpelZabbix() {
            log_info "Masking zabbix* from EPEL repositories....."
            # 1) 用 config-manager 对所有以 epel 开头的 repo id 设置 exclude/excludepkgs
            local -a epel_ids=()
            if command -v dnf >/dev/null 2>&1; then
                # 取第一列 repo-id，筛选 epel*（包含 epel-next、epel-modular 等）
                while read -r rid _; do
                    [[ -n "$rid" && "$rid" == epel* ]] && epel_ids+=("$rid")
                done < <(dnf repolist --all 2>/dev/null | awk 'NF>0 {print $1,$0}')
                local rid
                for rid in "${epel_ids[@]}"; do
                    dnf config-manager --save --setopt="${rid}.excludepkgs=zabbix*" >/dev/null 2>&1 || true
                    dnf config-manager --save --setopt="${rid}.exclude=zabbix*" >/dev/null 2>&1 || true
                done
            fi
            if command -v yum >/dev/null 2>&1 && command -v yum-config-manager >/dev/null 2>&1; then
                epel_ids=()
                while read -r rid _; do
                    [[ -n "$rid" && "$rid" == epel* ]] && epel_ids+=("$rid")
                done < <(yum repolist all 2>/dev/null | awk 'NF>0 {print $1,$0}')
                local rid2
                for rid2 in "${epel_ids[@]}"; do
                    yum-config-manager --save --setopt="${rid2}.exclude=zabbix*" >/devnull 2>&1 || true
                    yum-config-manager --save --setopt="${rid2}.excludepkgs=zabbix*" >/dev/null 2>&1 || true
                done
            fi
            # 2) 兜底：重写 /etc/yum.repos.d/epel*.repo，在 [epel*] 节内确保 excludepkgs 含 zabbix*
            shopt -s nullglob
            local f
            for f in /etc/yum.repos.d/epel*.repo; do
                [ -f "$f" ] || continue
                awk '
                    BEGIN{ins=0}
                    {
                        line=$0
                        if ($0 ~ /^\[/) {
                            if (sect ~ /^epel/) {
                                if (!seen) { print "excludepkgs=zabbix*" }
                            }
                            sect=$0; sub(/^\[/, "", sect); sub(/\].*$/, "", sect); seen=0
                        }
                        if (sect ~ /^epel/ && $0 ~ /^\s*excludepkgs\s*=/) {
                            if ($0 !~ /zabbix\*/) { sub(/$/, " zabbix*", $0) }
                            seen=1
                        }
                        print $0
                    }
                    END{
                        if (sect ~ /^epel/ && !seen) { print "excludepkgs=zabbix*" }
                    }
                ' "$f" >"$f.tmp" 2>/dev/null && mv -f "$f.tmp" "$f" || true
            done
            shopt -u nullglob
        }
        local basearch
        basearch="$(arch_id)"
        case "$basearch" in
            aarch64 | x86_64) : ;;
            *) basearch="x86_64" ;;
        esac

        # Default 7.0 LTS; on aarch64 若不支持则回退 6.0
        local -a zbx_majors=()
        if is_arch_aarch64; then
            zbx_majors=(${ZBX_MAJOR_VERSION:-7.0} 6.0)
        else
            zbx_majors=(${ZBX_MAJOR_VERSION:-7.0})
        fi

        local zrepo_file="/etc/yum.repos.d/zabbix.repo"
        local installed=false
        local installed_from_zabbix_repo=false
        local installed_major=""
        run_pkg dnf -y install dnf-plugins-core || true

        local maj
        for maj in "${zbx_majors[@]}"; do
            # Choose vendor path per OS (rocky/almalinux/rhel)
            local vendor_candidates=()
            if is_rocky; then
                vendor_candidates=(rocky rhel)
            elif is_alma; then
                # Zabbix 官方 AlmaLinux 路径使用 alma 而非 almalinux
                vendor_candidates=(alma rhel)
            else
                vendor_candidates=(rhel)
            fi
            local vendor
            for vendor in "${vendor_candidates[@]}"; do
                # 兼容性处理：历史上可能使用过 "almalinux"，Zabbix 官方实际为 "alma"
                vendor_path="$vendor"
                if [[ "$vendor_path" == "almalinux" ]]; then vendor_path="alma"; fi
                cat > "$zrepo_file" << EOF
[zabbix]
name=Zabbix ${maj} for ${vendor} ${RELEASE} - ${basearch}
baseurl=https://repo.zabbix.com/zabbix/${maj}/${vendor_path}/${RELEASE}/${basearch}/
enabled=1
gpgcheck=0
EOF
                run_pkg dnf -y clean all || true
                run_pkg dnf -y makecache || true
                log_info "Trying Zabbix ${maj} (${vendor}/${RELEASE}/${basearch}) zabbix-agent (zabbix repo only, epel disabled)..."
                if run_pkg dnf -y --disablerepo='epel*' --enablerepo='zabbix' --nobest --skip-broken install zabbix-agent zabbix-sender; then
                    installed=true
                    installed_from_zabbix_repo=true
                    installed_major="$maj"
                    log_ok "Installed Zabbix Agent (${maj}) for ${vendor}/${RELEASE}/${basearch}"
                    break
                fi
                log_info "Trying Zabbix ${maj} (${vendor}/${RELEASE}/${basearch}) zabbix-agent2 (zabbix repo only, epel disabled)..."
                if run_pkg dnf -y --disablerepo='epel*' --enablerepo='zabbix' --nobest --skip-broken install zabbix-agent2; then
                    installed=true
                    installed_from_zabbix_repo=true
                    installed_major="$maj"
                    log_ok "Installed Zabbix Agent2 (${maj}) for ${vendor}/${RELEASE}/${basearch}"
                    break
                fi
                log_warn "Zabbix ${maj} not available for ${vendor}/${RELEASE}/${basearch}, trying next vendor..."
            done
            # If 7.0 全部供应商不可用，且当前考虑 6.0，则尝试 EPEL 6.0 兜底（禁用 zabbix 源）
            if ! $installed && [[ "$maj" == "6.0" ]]; then
                log_info "Zabbix ${maj} fallback via EPEL (zabbix repo disabled)..."
                # agent 与 sender 同源安装避免文件冲突，允许 allowerasing 解决已有包替换
                if run_pkg dnf -y --disablerepo='zabbix' --enablerepo='epel*' --nobest --skip-broken --allowerasing install zabbix-agent zabbix-sender; then
                    installed=true
                    log_ok "Installed Zabbix Agent (${maj}) from EPEL"
                elif run_pkg dnf -y --disablerepo='zabbix' --enablerepo='epel*' --nobest --skip-broken --allowerasing install zabbix-agent2; then
                    installed=true
                    log_ok "Installed Zabbix Agent2 (${maj}) from EPEL"
                else
                    log_warn "EPEL fallback for Zabbix ${maj} failed; will try next major if any."
                fi
            fi
            # If installed for any vendor of this major version, stop trying other majors
            if $installed; then
                break
            fi
        done

        if ! $installed; then
            log_warn "zabbix-agent not installed; no supported version found for ${RELEASE}/${basearch}. Skipping configuration."
            return 0
        fi
        # 若来自官方 Zabbix 源且为 7.0，则屏蔽 EPEL 源中的 zabbix 包
        if $installed_from_zabbix_repo && [[ "$installed_major" == "7.0" ]]; then
            maskEpelZabbix || true
        fi
        # Configure whichever installed
        if rpm -q zabbix-agent2 > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agent2.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agent2.conf Hostname "$(hostname)"
            cat >> /etc/zabbix/zabbix_agentd.conf << EOF
UserParameter=authorized_keys,sudo /usr/bin/cksum /root/.ssh/authorized_keys|awk '{print \$1}'
UserParameter=iptables_lins,/usr/bin/sudo iptables -S |md5sum|awk '{print \$1}'
UserParameter=iptables_file,/usr/bin/sudo /usr/bin/cksum /etc/sysconfig/iptables|awk '{print \$1}'
EOF
            mkdir -p /etc/zabbix/zabbix_agent2.d || true
            gh_download "${GIT_REPO_URL}/Zabbix/disktcp.conf" /etc/zabbix/zabbix_agent2.d/disktcp.conf || curl -fsSL "${GIT_REPO_URL}/Zabbix/disktcp.conf" -o /etc/zabbix/zabbix_agent2.d/disktcp.conf || true
            # 校验配置以避免解析错误（例如误下成 HTML）。应包含以 UserParameter= 开头的行
            if ! grep -q '^UserParameter=' /etc/zabbix/zabbix_agent2.d/disktcp.conf 2> /dev/null; then
                log_warn "Downloaded disktcp.conf seems invalid; removing to avoid agent parse error."
                rm -f /etc/zabbix/zabbix_agent2.d/disktcp.conf || true
            fi
            enable_now zabbix-agent2
        elif rpm -q zabbix-agent > /dev/null 2>&1; then
            ini_set /etc/zabbix/zabbix_agentd.conf Server "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf ServerActive "${ZBX_SERVER_IP}"
            ini_set /etc/zabbix/zabbix_agentd.conf Hostname "$(hostname)"
            cat >> /etc/zabbix/zabbix_agentd.conf << EOF
UserParameter=authorized_keys,sudo /usr/bin/cksum /root/.ssh/authorized_keys|awk '{print \$1}'
UserParameter=iptables_lins,/usr/bin/sudo iptables -S |md5sum|awk '{print \$1}'
UserParameter=iptables_file,/usr/bin/sudo /usr/bin/cksum /etc/sysconfig/iptables|awk '{print \$1}'
EOF
            mkdir -p /etc/zabbix/zabbix_agentd.d || true
            gh_download "${GIT_REPO_URL}/Zabbix/disktcp.conf" /etc/zabbix/zabbix_agentd.d/disktcp.conf || curl -fsSL "${GIT_REPO_URL}/Zabbix/disktcp.conf" -o /etc/zabbix/zabbix_agentd.d/disktcp.conf || true
            if ! grep -q '^UserParameter=' /etc/zabbix/zabbix_agentd.d/disktcp.conf 2> /dev/null; then
                log_warn "Downloaded disktcp.conf seems invalid; removing to avoid agent parse error."
                rm -f /etc/zabbix/zabbix_agentd.d/disktcp.conf || true
            fi
            enable_now zabbix-agent
        fi
    fi
}

# --- 9.3 Speedtest 安装 ---

addSpeedtest() {
    log_info "Installing LibreSpeed CLI....."
    
    # Detect architecture
    local arch=""
    case "$(uname -m)" in
        x86_64|amd64) arch="amd64" ;;
        aarch64|arm64) arch="arm64" ;;
        armv7l|armhf) arch="armv7" ;;
        i386|i686) arch="386" ;;
        *) 
            log_warn "Unsupported architecture: $(uname -m), skipping LibreSpeed CLI installation"
            return 0
            ;;
    esac
    
    # Detect OS
    local os_type=""
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        case "$ID" in
            ubuntu|debian|centos|rhel|rocky|alma|fedora|opensuse*|arch)
                os_type="linux"
                ;;
            *)
                os_type="linux"
                ;;
        esac
    else
        os_type="linux"
    fi
    
    # Download latest release from GitHub
    local base_url=""
    local binary_name="librespeed-cli_${os_type}_${arch}"
    local download_url=""
    local target="/usr/local/bin/librespeed-cli"
    
    # Use xget.xi-xu.me mirror for CN users, GitHub for international users
    if is_cn || [[ "${GH_USE_MIRRORS}" == "force" ]]; then
        base_url="https://xget.xi-xu.me/gh/librespeed/speedtest-cli/releases/latest/download"
        log_info "Downloading LibreSpeed CLI for ${os_type}/${arch} from xget.xi-xu.me (China mirror)..."
    else
        base_url="https://github.com/librespeed/speedtest-cli/releases/latest/download"
        log_info "Downloading LibreSpeed CLI for ${os_type}/${arch} from GitHub..."
    fi
    
    download_url="${base_url}/${binary_name}"
    
    # Try different download methods
    if command -v curl > /dev/null 2>&1; then
        curl -fsSL -o "${target}" "${download_url}" 2>/dev/null || {
            log_error "Failed to download LibreSpeed CLI from ${base_url}"
            return 1
        }
    elif command -v wget > /dev/null 2>&1; then
        wget -q -O "${target}" "${download_url}" 2>/dev/null || {
            log_error "Failed to download LibreSpeed CLI from ${base_url}"
            return 1
        }
    else
        log_error "Neither curl nor wget is available"
        return 1
    fi
    
    # Make executable
    chmod +x "${target}" || {
        log_error "Failed to make LibreSpeed CLI executable"
        return 1
    }
    
    # Create symlink for backward compatibility
    ln -sf "${target}" /usr/local/bin/speedtest 2>/dev/null || true
    
    # Verify installation
    if "${target}" --version > /dev/null 2>&1; then
        local version=$("${target}" --version 2>&1 | head -1)
        log_ok "LibreSpeed CLI installed successfully: ${version}"
        log_info "Usage: librespeed-cli --help"
        log_info "Symlink: speedtest -> librespeed-cli"
    else
        log_error "LibreSpeed CLI installation verification failed"
        return 1
    fi
}

# === 第10层：编排控制层 ===

# --- 10.1 辅助函数 ---

detectComposeArch() {
    local machine
    machine="$(uname -m)"
    case "$machine" in
        x86_64 | amd64) echo "x86_64" ;;
        aarch64 | arm64) echo "aarch64" ;;
        armv7l) echo "armv7" ;;
        *) echo "x86_64" ;;
    esac
}

getLatestComposeVersion() {
    # 首选 GitHub API 查询最新版本
    local latest
    local api_path="/repos/docker/compose/releases/latest"
    latest="$(gh_api "$api_path" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]\+\)".*/\1/p' | head -n1)" || true
    if [[ -n "$latest" ]]; then
        echo "$latest"
        return 0
    fi
    # 回退：通过最新版本跳转 URL 抓取 tag
    local url
    local latest_url_official="https://github.com/docker/compose/releases/latest"
    local candidates
    candidates=$(gh_candidates_for "$latest_url_official")
    local u
    for u in $candidates; do
        url="$(curl -m 8 -fsIL -o /dev/null -w '%{url_effective}' "$u" 2> /dev/null)" || true
        [[ -n "$url" ]] && break
    done
    if [[ "$url" =~ /tag/([^/]+)$ ]]; then
        echo "${BASH_REMATCH[1]}"
        return 0
    fi
    # 最终回退：使用仓库内置保底版本
    echo "v2.37.2"
}

currentComposeVersion() {
    if command -v docker-compose > /dev/null 2>&1; then
        # docker-compose version --short prints like 2.24.6 (v may be missing)
        local v
        v="$(docker-compose version --short 2> /dev/null || true)"
        if [[ -n "$v" ]]; then
            [[ "$v" == v* ]] && echo "$v" || echo "v${v}"
            return 0
        fi
    fi
    if command -v docker > /dev/null 2>&1; then
        # Docker Compose v2 as plugin
        local line
        line="$(docker compose version 2> /dev/null || true)"
        # e.g. Docker Compose version v2.27.0
        if [[ "$line" =~ version[[:space:]](v[0-9.]+) ]]; then
            echo "${BASH_REMATCH[1]}"
            return 0
        fi
    fi
    echo ""
}

versionLT() {
    # 若 $1 < $2 则返回 0（语义化版本比较）
    local a b
    a="${1#v}"
    b="${2#v}"
    [[ "$(printf '%s\n%s\n' "$a" "$b" | sort -V | head -n1)" != "$b" ]]
}

ensureComposeShimIfNeeded() {
    if ! command -v docker-compose > /dev/null 2>&1 && command -v docker > /dev/null 2>&1; then
        if docker compose version > /dev/null 2>&1; then
            cat > /usr/local/bin/docker-compose << 'EOSH'
#!/bin/sh
exec docker compose "$@"
EOSH
            chmod +x /usr/local/bin/docker-compose || true
        fi
    fi
}

changZHCN() {
    log_info "Changing system language to Chinese....."
    ensure_zhcn_langpacks
    localectl set-locale LANG=zh_CN.utf8 || true
}

applyFirewalldPortsFromIptablesRulesFile() {
    # Map simple iptables rules in rules file to firewalld allow ports/services (EL10 nft)
    local rules_file="$1"
    [[ -f "$rules_file" ]] || return 0
    # Extract tcp multiport or single --dport entries and add to firewalld
    local line ports proto p
    while IFS= read -r line; do
        # Skip comments and empty lines
        [[ -z "$line" || "$line" =~ ^# ]] && continue
        # Only INPUT chain accept rules with tcp
        if [[ "$line" =~ -A[[:space:]]+INPUT[[:space:]].*-p[[:space:]]+tcp ]]; then
            proto=tcp
            # multiport: --dports 22,22992
            if [[ "$line" =~ --dports[[:space:]]+([^[:space:]]+) ]]; then
                ports="${BASH_REMATCH[1]}"
                IFS=',' read -r -a arr <<< "$ports"
                for p in "${arr[@]}"; do
                    [[ -n "$p" ]] && firewall-cmd --permanent --add-port="${p}/${proto}" > /dev/null 2>&1 || true
                done
            # single: --dport 22
            elif [[ "$line" =~ --dport[[:space:]]+([0-9]{1,5}) ]]; then
                p="${BASH_REMATCH[1]}"
                firewall-cmd --permanent --add-port="${p}/${proto}" > /dev/null 2>&1 || true
            fi
        fi
        # ICMP accept -> allow ping (firewalld generally permits echo-request by default)
        if [[ "$line" =~ -p[[:space:]]+icmp[[:space:]]+-j[[:space:]]+ACCEPT ]]; then
            :
        fi
    done < "$rules_file"
}

ensureIptablesAllowPort() {
    local port="$1"
    local iptfile="/etc/sysconfig/iptables"
    [[ -f "$iptfile" ]] || return 0
    # If any ACCEPT rule already covers the port (single or multiport), skip
    if grep -Eq "^-A INPUT .*--dport ${port} .* -j ACCEPT" "$iptfile" \
        || grep -Eq "^-A INPUT .*--dports [^#]*\\b${port}\\b.* -j ACCEPT" "$iptfile"; then
        return 0
    fi
    local rule="-A INPUT -p tcp --dport ${port} -m state --state NEW -j ACCEPT"
    if grep -q '^-A INPUT -j REJECT --reject-with icmp-host-prohibited' "$iptfile"; then
        sed -i "/^-A INPUT -j REJECT --reject-with icmp-host-prohibited/i ${rule}" "$iptfile" || true
    elif grep -q '^COMMIT' "$iptfile"; then
        sed -i "/^COMMIT/i ${rule}" "$iptfile" || true
    else
        echo "$rule" >> "$iptfile"
    fi
}

detectMemoryGiB() {
    local kb
    kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2> /dev/null || echo 0)
    if [[ -z "$kb" || "$kb" -eq 0 ]]; then
        echo 0
        return 0
    fi
    # Round down to integer GiB
    echo $((kb / 1024 / 1024))
}

adaptiveSysctl() {
    log_info "Applying adaptive sysctl based on machine memory....."
    local mem_gb
    mem_gb=$(detectMemoryGiB)

    local swappiness dirty_bg dirty_ratio max_map conntrack
    # Swappiness: lower on big memory
    if ((mem_gb >= 64)); then
        swappiness=1
        dirty_bg=3
        dirty_ratio=10
        max_map=1048576
        conntrack=$((mem_gb * 65536))
    elif ((mem_gb >= 32)); then
        swappiness=5
        dirty_bg=5
        dirty_ratio=15
        max_map=524288
        conntrack=$((mem_gb * 49152))
    elif ((mem_gb >= 8)); then
        swappiness=10
        dirty_bg=5
        dirty_ratio=20
        max_map=262144
        conntrack=$((mem_gb * 32768))
    else
        swappiness=20
        dirty_bg=5
        dirty_ratio=25
        max_map=262144
        conntrack=262144
    fi

    # Clamp conntrack between 262144 and 4194304
    ((conntrack < 262144)) && conntrack=262144
    ((conntrack > 4194304)) && conntrack=4194304

    mkdir -p /etc/sysctl.d
    cat > /etc/sysctl.d/90-adaptive.conf << EOF
# 由 system_init_2.sh 的 adaptiveSysctl 生成
vm.swappiness = ${swappiness}
vm.dirty_background_ratio = ${dirty_bg}
vm.dirty_ratio = ${dirty_ratio}
vm.max_map_count = ${max_map}
net.netfilter.nf_conntrack_max = ${conntrack}
EOF

    sysctl --system > /dev/null 2>&1 || sysctl -p > /dev/null 2>&1 || true
}

generateCPUMask() {
    # Generate cpumask string for given number of CPUs (or detected nproc)
    local n cpus hex digits groups result chunk
    n="${1:-}"
    if [[ -z "$n" ]]; then
        n=$(nproc 2> /dev/null || echo 1)
    fi
    ((n < 1)) && n=1
    # Number of hex digits needed
    digits=$(((n + 3) / 4))
    # Build hex string of 'f' repeated digits times
    hex=""
    while ((${#hex} < digits)); do hex+="ffffffff"; done
    hex=${hex:0:digits}
    # Split into groups of 8 from right to left, join with commas (omit leading zeros group)
    result=""
    while ((${#hex} > 8)); do
        chunk=${hex: -8}
        result=",${chunk}${result}"
        hex=${hex:0:${#hex}-8}
    done
    result="${hex}${result}"
    # Remove leading zeros before comma if present
    result=${result#,}
    echo "$result"
}

# --- 10.2 预检查 ---

preflightLowMemory() {
    log_info "Running preflight low-memory mitigation....."
    local mem_kb mem_gb swap_total_kb need_swap_kb swapfile="/swapfile"
    mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
    swap_total_kb=$(awk '/SwapTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
    mem_gb=$(( mem_kb / 1024 / 1024 ))

    # If memory <= 2 GiB and no swap, create a small swapfile (1-2 GiB)
    if (( mem_gb <= 2 )) && (( swap_total_kb < 262144 )); then
        local target_gb=2
        (( mem_gb <= 1 )) && target_gb=1
        need_swap_kb=$(( target_gb * 1024 * 1024 ))
        if [[ ! -f "$swapfile" ]]; then
            log_warn "Creating ${target_gb}G swapfile to protect dnf from OOM....."
            fallocate -l ${target_gb}G "$swapfile" 2>/dev/null || dd if=/dev/zero of="$swapfile" bs=1M count=$((target_gb*1024)) status=none || true
            chmod 600 "$swapfile" || true
            mkswap "$swapfile" >/dev/null 2>&1 || true
            swapon "$swapfile" >/dev/null 2>&1 || true
            # Persist
            if ! grep -q "^/swapfile" /etc/fstab 2>/dev/null; then
                echo "/swapfile none swap sw 0 0" >> /etc/fstab
            fi
        fi
    fi

    # Extra: reduce dnf metadata usage on tiny memory systems
    if [[ -d /etc/dnf ]]; then
        kv_set /etc/dnf/dnf.conf metadata_expire 1h '='
        kv_set /etc/dnf/dnf.conf keepcache True '='
    fi
}

# --- 10.3 编排执行 ---

run_orchestrated() {
    local s rc=0
    for s in "${STAGES[@]}"; do
        # Initialize status to avoid 'unbound variable' with set -u
        STAGE_STATUS["$s"]=RUN
        stage_execute "$s"
        if [[ "${STAGE_STATUS[$s]-}" == "SKIP" ]]; then
            continue
        fi
        rc=$?
        # If stage ran without non-zero rc and status remains RUN, mark it OK
        if [[ $rc -eq 0 && "${STAGE_STATUS[$s]-RUN}" == RUN ]]; then
            STAGE_STATUS["$s"]=OK
        fi
        if [[ $rc -ne 0 ]]; then
            STAGE_STATUS["$s"]=FAIL
            STAGE_REASON_MAP["$s"]="exit code ${rc}"
            log_error "[FAIL] $(stage_desc "$s") (rc=${rc})"
            if [[ "${CONTINUE_ON_ERROR}" != "true" ]]; then
                log_error "停止于阶段: $s"
                exit $rc
            fi
        fi
    done
}

# --- 10.4 结果输出 ---

print_summary_table() {
    echo "执行总结:"
    local i=1 s name st dur reason total=0
    printf "  %-3s %-18s %-18s %-8s %-8s %s\n" "#" "stage" "name" "status" "secs" "reason"
    for s in "${STAGES[@]}"; do
        name=$(stage_desc "$s")
        st="${STAGE_STATUS[$s]-OK}"
        dur="${STAGE_DURATIONS[$s]:-0}"
        reason="${STAGE_REASON_MAP[$s]:-}"
        printf "  %-3d %-18s %-18s %-8s %-8s %s\n" "$i" "$s" "$name" "$st" "$dur" "$reason"
        total=$((total + ${dur:-0}))
        i=$((i + 1))
    done
    # Compose info line
    if [[ -n "${COMPOSE_MODE_EFFECTIVE}" || -n "${COMPOSE_VERSION_EFFECTIVE}" ]]; then
        echo "docker-compose: mode=${COMPOSE_MODE_EFFECTIVE:-n/a}, version=${COMPOSE_VERSION_EFFECTIVE:-n/a}"
    fi
    echo "总耗时: ${total}s"
}

print_summary_json() {
    local first=1 s name st dur reason total=0 tmp
    tmp=$(mktemp 2>/dev/null || echo /tmp/summary.$$)
    {
        printf '{"stages":['
        for s in "${STAGES[@]}"; do
            name=$(stage_desc "$s")
            st="${STAGE_STATUS[$s]-OK}"
            dur="${STAGE_DURATIONS[$s]:-0}"
            reason="${STAGE_REASON_MAP[$s]:-}"
            if [[ $first -eq 0 ]]; then printf ","; fi
            printf '{"id":"%s","name":"%s","status":"%s","duration":%s,"reason":"%s"}' \
                "$s" "$(echo "$name" | sed 's/\"/\\\"/g')" "$st" "$dur" "$(echo "$reason" | sed 's/\"/\\\"/g')"
            total=$((total + ${dur:-0}))
            first=0
        done
        printf '],"total_seconds": %s,"docker_compose": { "mode": "%s", "version": "%s" }}\n' "$total" "${COMPOSE_MODE_EFFECTIVE:-}" "${COMPOSE_VERSION_EFFECTIVE:-}"
    } >"$tmp"
    if command -v jq >/dev/null 2>&1; then
        jq -c -C . <"$tmp" | sed -e 's/},{/},\n  {/g' -e 's/"stages":\[/"stages": [\n  /' -e 's/\],"total_seconds"/\n ], "total_seconds"/'
    else
        cat "$tmp"
    fi
    rm -f "$tmp" 2>/dev/null || true
}

print_summary_auto() {
    if [[ "${PLAN_JSON:-false}" == "true" ]]; then
        print_summary_json
    else
        print_summary_table
    fi
}

# === 第11层：主入口层 ===

main() {

    # 严格的 getopts 风格参数解析
    # Short/long options (no conflicts; dedicated flags for addrpools/mirror tuning)
    local short_opts=":hpDZASFTBUNt:fz:i:e:s:d:a:w:j:m:nL:g:v cJRKY"
    local long_opts="help,private,nodocker,nozabbix,noadaptive,nohardenssh,nofwlimit,nothp,nofail2ban,noauditd,nonuma,tuned:,fips,zabbix:,insecure:,execopts:,storage:,dataroot:,addrpools:,mirror-timeout:,mirror-jobs:,mirrors:,dry-run,log-file:,log-level:,verbose,continue-on-error,plan-json,no-reboot,nokernelml,pyenv"

    # Normalize long options to short, preserve order, and safely detect subcommand
    local -a normalized_args=()
    local found_subcommand="false"
    local awaiting_opt_arg=""
    while [[ $# -gt 0 ]]; do
        local tok="$1"
        # If previous short/long option needs a value, take current token as its value
        if [[ -n "$awaiting_opt_arg" ]]; then
            normalized_args+=("$tok")
            awaiting_opt_arg=""
            shift
            continue
        fi
        case "$tok" in
            --help) normalized_args+=("-h") ;;
            --private) normalized_args+=("-p") ;;
            --nodocker) normalized_args+=("-D") ;;
            --nozabbix) normalized_args+=("-Z") ;;
            --noadaptive) normalized_args+=("-A") ;;
            --nohardenssh) normalized_args+=("-S") ;;
            --nofwlimit) normalized_args+=("-F") ;;
            --nothp) normalized_args+=("-T") ;;
            --nofail2ban) normalized_args+=("-B") ;;
            --noauditd) normalized_args+=("-U") ;;
            --nonuma) normalized_args+=("-N") ;;
            --tuned=*) normalized_args+=("-t" "${tok#*=}") ;;
            --tuned)
                normalized_args+=("-t")
                awaiting_opt_arg="-t"
                ;;
            --fips) normalized_args+=("-f") ;;
            --zabbix=*) normalized_args+=("-z" "${tok#*=}") ;;
            --zabbix)
                normalized_args+=("-z")
                awaiting_opt_arg="-z"
                ;;
            --insecure=*) normalized_args+=("-i" "${tok#*=}") ;;
            --insecure)
                normalized_args+=("-i")
                awaiting_opt_arg="-i"
                ;;
            --execopts=*) normalized_args+=("-e" "${tok#*=}") ;;
            --execopts)
                normalized_args+=("-e")
                awaiting_opt_arg="-e"
                ;;
            --storage=*) normalized_args+=("-s" "${tok#*=}") ;;
            --storage)
                normalized_args+=("-s")
                awaiting_opt_arg="-s"
                ;;
            --dataroot=*) normalized_args+=("-d" "${tok#*=}") ;;
            --dataroot)
                normalized_args+=("-d")
                awaiting_opt_arg="-d"
                ;;
            --addrpools=*) normalized_args+=("-a" "${tok#*=}") ;;
            --addrpools)
                normalized_args+=("-a")
                awaiting_opt_arg="-a"
                ;;
            --mirror-timeout=*) normalized_args+=("-w" "${tok#*=}") ;;
            --mirror-timeout)
                normalized_args+=("-w")
                awaiting_opt_arg="-w"
                ;;
            --mirror-jobs=*) normalized_args+=("-j" "${tok#*=}") ;;
            --mirror-jobs)
                normalized_args+=("-j")
                awaiting_opt_arg="-j"
                ;;
            --mirrors=*) normalized_args+=("-m" "${tok#*=}") ;;
            --mirrors)
                normalized_args+=("-m")
                awaiting_opt_arg="-m"
                ;;
            --dry-run) normalized_args+=("-n") ;;
            --log-file=*) normalized_args+=("-L" "${tok#*=}") ;;
            --log-file)
                normalized_args+=("-L")
                awaiting_opt_arg="-L"
                ;;
            --log-level=*) normalized_args+=("-g" "${tok#*=}") ;;
            --log-level)
                normalized_args+=("-g")
                awaiting_opt_arg="-g"
                ;;
            --verbose) normalized_args+=("-v") ;;
            --continue-on-error) normalized_args+=("-c") ;;
            --plan-json) normalized_args+=("-J") ;;
            --no-reboot) normalized_args+=("-R") ;;
            --nokernelml) normalized_args+=("-K") ;;
            --pyenv) normalized_args+=("-Y") ;;
            --)
                shift
                while [[ $# -gt 0 ]]; do
                    normalized_args+=("$1")
                    shift
                done
                break
                ;;
            --*)
                log_error "Unknown option: $tok"
                printHelp
                exit 2
                ;;
            -L | -g | -t | -z | -i | -e | -s | -d | -a | -w | -j | -m)
                normalized_args+=("$tok")
                awaiting_opt_arg="$tok"
                ;;
            -*)
                normalized_args+=("$tok")
                ;;
            *)
                if [[ "$found_subcommand" == "false" ]]; then
                    SUBCOMMAND="$tok"
                    found_subcommand="true"
                else
                    normalized_args+=("$tok")
                fi
                ;;
        esac
        shift || true
    done

    # Remove subcommand from normalized_args if it was captured at the head
    if [[ "$found_subcommand" == "true" ]]; then
        # Keep only options for getopts
        :
    fi

    # Feed normalized args to getopts
    set -- "${normalized_args[@]}"

    while getopts "$short_opts" opt; do
        case "$opt" in
            h)
                printHelp
                exit 0
                ;;
            p) NETWORK_TYPE="private" ;;
            D) INSTALL_DOCKER="false" ;;
            Z) INSTALL_ZABBIX="false" ;;
            A) DO_ADAPTIVE_SYSCTL="false" ;;
            S) DO_HARDEN_SSH="false" ;;
            F) DO_FIREWALL_LIMIT="false" ;;
            T) DO_THP="false" ;;
            B) DO_FAIL2BAN="false" ;;
            U) DO_AUDITD="false" ;;
            N) DO_NUMA_RPS="false" ;;
            t) TUNED_PROFILE="$OPTARG" ;;
            f) ENABLE_FIPS="true" ;;
            z) ZBX_SERVER_IP="$OPTARG" ;;
            i) INSECURE_REG_CSV="$OPTARG" ;;
            e) EXEC_OPTS_CSV="$OPTARG" ;;
            s) STORAGE_DRIVER="$OPTARG" ;;
            d) DATA_ROOT="$OPTARG" ;;
            a) DEFAULT_ADDR_POOLS_CSV="$OPTARG" ;;
            w) MIRROR_PROBE_TIMEOUT="$OPTARG" ;;
            j) MIRROR_MAX_JOBS="$OPTARG" ;;
            m) MIRROR_CANDIDATES_CSV="$OPTARG" ;;
            n) DRY_RUN="true" ;;
            L) LOG_FILE="$OPTARG" ;;
            g) LOG_LEVEL="${OPTARG}" ;;
            v) LOG_LEVEL="DEBUG" ;;
            c) CONTINUE_ON_ERROR=true ;;
            J) PLAN_JSON=true ;;
            R) NO_REBOOT="true" ;;
            K) DO_KERNEL_ML="false" ;;
            Y) INSTALL_PYENV="true" ;;
            :)
                log_error "Option -$OPTARG requires an argument"
                exit 2
                ;;
            \?)
                log_error "Invalid option: -$OPTARG"
                printHelp
                exit 2
                ;;
        esac
    done

    # Setup logging（解析完成后再初始化日志路径）
    if [[ -z "$LOG_FILE" ]]; then
        LOG_FILE="/var/log/system_init_2.log"
    fi
    mkdir -p "$(dirname -- "$LOG_FILE")" 2> /dev/null || true
    { echo "==== $(date '+%F %T') system_init_2.sh start (subcommand=${SUBCOMMAND}, dry_run=${DRY_RUN}) ===="; } >> "$LOG_FILE" 2> /dev/null || true
    USE_TEE_LOGGING=true
    # 使用 tee -a（不加额外选项）以规避 GNU/BSD 差异；确保没有多余的 -v 传给 tee
    exec > >(tee -a "$LOG_FILE") 2> >(tee -a "$LOG_FILE" >&2)

    if [[ "$NETWORK_TYPE" == "private" ]]; then
        log_note "Running in PRIVATE network mode."
    else
        log_note "Running in PUBLIC network mode."
    fi

    # Dry-run: print plan and exit（无需 root/OS 检测）
    if [[ "$DRY_RUN" == "true" ]]; then
        log_important "[DRY-RUN] 将执行的阶段 (subcommand=${SUBCOMMAND}):"
        define_stages_for_subcommand
        print_plan_auto
        exit 0
    fi

    # 参数解析完成后再进行 root/OS 检测与订阅管理器处理
    checkRoot
    checkOSinfo
    disableRHSMOnNonRHEL

    case "$SUBCOMMAND" in
        all | docker | firewall | pyenv | kernel | speedtest) : ;;
        *)
            log_error "未知子命令: ${SUBCOMMAND}"
            exit 2
            ;;
    esac

    define_stages_for_subcommand
    run_orchestrated

    # Print summary
    print_summary_auto

    # 仅在完整流程且未禁止重启时才执行重启
    if [[ "$SUBCOMMAND" == "all" && "$NO_REBOOT" != "true" ]]; then
        log_important "System initialization completed. The system will reboot in 5 seconds."
        sleep 5
        reboot
    else
        log_important "Completed (${SUBCOMMAND}). Reboot skipped."
    fi
}


# --- 8.2 时间和区域设置 ---

syncTime() {
    log_info "Synchronizing time....."
    
    # Check connectivity to NTP servers and add to /etc/hosts
    # Priority: 203.60.13.52 > 172.30.200.254 > 172.25.200.254 > 172.20.200.254
    if ! grep -q 'ntp.dtops.cc' /etc/hosts; then
        local ntp_ip=""
        local ntp_candidates=("203.60.13.52" "172.30.200.254" "172.25.200.254" "172.20.200.254")
        
        for ip in "${ntp_candidates[@]}"; do
            if timeout 2 ping -c 1 -W 1 "$ip" > /dev/null 2>&1 || timeout 2 nc -z -w 1 "$ip" 123 > /dev/null 2>&1; then
                ntp_ip="$ip"
                log_info "NTP server $ip is reachable, adding to /etc/hosts"
                break
            fi
        done
        
        if [[ -n "$ntp_ip" ]]; then
            echo "${ntp_ip}   ntp.dtops.cc ntp.dtops.co" >> /etc/hosts
        else
            log_warn "No NTP server reachable, using default: 172.25.200.254"
            echo "172.25.200.254   ntp.dtops.cc ntp.dtops.co" >> /etc/hosts
        fi
    fi

    local pm
    pm=$(pkgmgr)

    if is_ubuntu; then
        apt-get install -y chrony || true
        if [[ -f /etc/chrony/chrony.conf ]]; then
            cp -a /etc/chrony/chrony.conf{,_default} || true
            grep -Ev '^(#|$)' /etc/chrony/chrony.conf_default > /etc/chrony/chrony.conf || true
            sed -i '1i pool ntp.dtops.co iburst\npool ntp.dtops.cc iburst' /etc/chrony/chrony.conf || true
        fi
        enable_now chrony
    else
        $pm install -y chrony || true
        if [[ -f /etc/chrony.conf ]]; then
            cp -a /etc/chrony.conf{,_default} || true
            grep -Ev '^(#|$)' /etc/chrony.conf_default > /etc/chrony.conf || true
            sed -i '1i pool ntp.dtops.co iburst\npool ntp.dtops.cc iburst' /etc/chrony.conf || true
        fi
        enable_now chronyd
    fi

    log_warn "Changing Timezone to Asia/Shanghai....."
    timedatectl set-timezone Asia/Shanghai || true
    date
}

fixLocaleWarnings() {
    log_info "Ensuring locale packs installed....."
    local pm desired_lang prev_lc_all
    pm=$(pkgmgr)
    # 判定目标本地化语言（优先系统设置；回退 zh_CN.UTF-8）
    desired_lang="$(awk -F= '/^LANG=/{print $2}' /etc/locale.conf 2> /dev/null || true)"
    # sanitize quotes and whitespace
    desired_lang="$(printf '%s' "$desired_lang" | sed 's/\"//g; s/^\s\+//; s/\s\+$//')"
    [[ -z "$desired_lang" ]] && desired_lang="zh_CN.UTF-8"

    case "$RELEASE" in
        7)
            # Provide zh_CN packs for EL7 to silence warnings
            $pm -y install langpacks-zh_CN > /dev/null 2>&1 || true
            ;;
        8)
            # EL8: align glibc family to repo versions first to avoid 164/203 EVR conflicts
            prev_lc_all="${LC_ALL-}"
            export LC_ALL=C
            dnf -y --disablerepo='*' --enablerepo='base','appstream','extras' \
                distro-sync 'glibc*' --allowerasing --nobest > /dev/null 2>&1 || true

            # Install language packs (gconv-extra may not exist on 8.5; try and ignore if missing)
            dnf -y --disablerepo='*' --enablerepo='base','appstream' \
                install glibc-langpack-en glibc-langpack-zh langpacks-zh_CN --allowerasing > /dev/null 2>&1 || true
            dnf -y --disablerepo='*' --enablerepo='base','appstream' \
                install glibc-gconv-extra --allowerasing > /dev/null 2>&1 || true

            if rpm -q glibc-minimal-langpack > /dev/null 2>&1; then
                dnf -y swap glibc-minimal-langpack glibc-langpack-zh > /dev/null 2>&1 \
                    || dnf -y swap glibc-minimal-langpack glibc-all-langpacks > /dev/null 2>&1 || true
            fi
            # 恢复 LC_ALL
            if [[ -n "${prev_lc_all}" ]]; then export LC_ALL="${prev_lc_all}"; else unset LC_ALL; fi
            ;;
        9)
            # EL9 require glibc-langpack-* packages
            $pm -y install glibc-langpack-en glibc-langpack-zh langpacks-zh_CN glibc-gconv-extra > /dev/null 2>&1 || true
            ;;
        10)
            # EL10: similar langpacks naming as EL9
            $pm -y install glibc-langpack-en glibc-langpack-zh langpacks-zh_CN glibc-gconv-extra > /dev/null 2>&1 || true
            ;;
    esac

    # Re-apply locale and export in current shell
    localectl set-locale LANG="${desired_lang}" > /dev/null 2>&1 || true
    printf 'LANG=%s\n' "${desired_lang}" > /etc/locale.conf || true
    export LANG="${desired_lang}"
    # For current process, also align key LC_* to avoid runtime tool warnings (not persisted globally)
    export LC_CTYPE="${LC_CTYPE:-$LANG}"
    export LC_MESSAGES="${LC_MESSAGES:-$LANG}"

    # Ensure zh_CN.UTF-8 exists; fallback to generate if missing
    if ! locale -a 2> /dev/null | grep -qi '^zh_CN\.utf-8$'; then
        localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 > /dev/null 2>&1 || true
    fi
    # Ensure en_US.UTF-8 exists for per-category settings
    if ! locale -a 2> /dev/null | grep -qi '^en_US\.utf-8$'; then
        localedef -c -f UTF-8 -i en_US en_US.UTF-8 > /dev/null 2>&1 || true
    fi

    # Avoid setting any LC_* globally; clean global overrides to prevent warnings
    sed -ri '/^LC_[A-Z_]+=/{d}' /etc/locale.conf 2> /dev/null || true
    for f in /etc/profile /etc/bashrc /etc/environment /etc/profile.d/*.sh; do
        [[ -f "$f" ]] || continue
        sed -ri 's/^\s*export\s+LC_[A-Z_]+=.*$//;s/^\s*LC_[A-Z_]+=.*$//' "$f" 2> /dev/null || true
    done
    # Provide a profile drop-in: 中文界面，英文时间/数字等分类
    mkdir -p /etc/profile.d
    cat > /etc/profile.d/00-lang.sh << 'EOF'
export LANG=zh_CN.UTF-8
# 英文格式：时间、数字、货币、度量、纸张尺寸
export LC_TIME=en_US.UTF-8
export LC_NUMERIC=en_US.UTF-8
export LC_MONETARY=en_US.UTF-8
export LC_MEASUREMENT=en_US.UTF-8
export LC_PAPER=en_US.UTF-8
# 不设置 LC_ALL，避免覆盖分类变量
EOF
}

# --- 8.3 用户和权限 ---

setPS1() {
    log_info "Setting custom PS1....."
    mkdir -p /etc/profile.d
    if ! grep -q "^# BEGIN custom hostnamectl_ps1$" /etc/profile.d/zz-custom.sh 2> /dev/null; then
        {
            echo "# BEGIN custom hostnamectl_ps1"
            cat <<'__ZZC_HOSTNAME_PS1__'
# 兼容修复：Ubuntu 登录提示 “static hostname is already set...”，并提供统一 PS1
# 此片段在交互式 shell 被 source；包含：
# 1) 主机名设置（幂等、仅 root、Ubuntu 跳过 --transient）
# 2) setPS1：bash/zsh 提示符统一，展示 user@host IP cwd

# 获取 IP 地址（优先公网 IP，否则内网 IP）

# 主机名设置：仅 root 执行；Ubuntu 不设 transient，避免 Hint；幂等
if [ "$(id -u 2>/dev/null)" -eq 0 ] && command -v hostnamectl >/dev/null 2>&1; then
    NODE_IP="$(get_first_ipv4)"
    if [ -n "$NODE_IP" ]; then
        TARGET_HOSTNAME="DS-VM-Node${NODE_IP}.cluster.com"
        CURRENT_STATIC="$(hostnamectl --static 2>/dev/null || true)"
        if [ "$CURRENT_STATIC" != "$TARGET_HOSTNAME" ]; then
            OS_ID="unknown"
            if [ -r /etc/os-release ]; then
                . /etc/os-release
                OS_ID="${ID:-unknown}"
            fi
            if [ "$OS_ID" = "ubuntu" ]; then
                hostnamectl set-hostname "$TARGET_HOSTNAME" --static 2>/dev/null || true
                hostnamectl set-hostname "$TARGET_HOSTNAME" --pretty 2>/dev/null || true
            else
                hostnamectl set-hostname "$TARGET_HOSTNAME" --static 2>/dev/null || true
                hostnamectl set-hostname "$TARGET_HOSTNAME" --pretty 2>/dev/null || true
                # 如确需临时主机名，可放开下一行；默认注释以避免噪声
                # hostnamectl set-hostname "$TARGET_HOSTNAME" --transient 2>/dev/null || true
            fi
        fi
    fi
fi

# 提示符设置：统一 bash/zsh，显示 user@host IP cwd；可通过 SKIP_CUSTOM_PS1=1 跳过
setPS1() {
    case $- in
        *i*) ;;
        *) return 0;;
    esac

    if [ -z "$PROMPT_IP" ]; then
        PROMPT_IP="$(get_first_ipv4)"
        export PROMPT_IP
    fi

    if [ -n "$ZSH_VERSION" ]; then
        PROMPT='%F{cyan}%n%f@%F{green}DSTW_VM_Node${PROMPT_IP}%f %F{blue}%~%f %# '
        export PROMPT
        return 0
    fi

    if [ -n "$BASH_VERSION" ]; then
        PS1="[\[\033[0;34m\]\u\[\033[0;37m\]@\[\033[0;35m\]DSTW_VM_Node-\${PROMPT_IP}\[\033[0;33m\] \w\[\033[0;37m\]]\[\033[0;31m\]\\$\[\033[00m\] "
        export PS1
        return 0
    fi
}

if [ "${SKIP_CUSTOM_PS1:-0}" -ne 1 ]; then
    setPS1
fi
__ZZC_HOSTNAME_PS1__
            echo "# END custom hostnamectl_ps1"
        } >> /etc/profile.d/zz-custom.sh
    fi
    chmod 644 /etc/profile.d/zz-custom.sh || true
    # 确保 bash 非登录交互也生效（Ubuntu 常见）
    if [[ -f /etc/bash.bashrc ]] && ! grep -q '/etc/profile.d/zz-custom.sh' /etc/bash.bashrc 2> /dev/null; then
        echo 'test -r /etc/profile.d/zz-custom.sh && . /etc/profile.d/zz-custom.sh' >> /etc/bash.bashrc
        echo 'test -r /etc/profile.d/zz-custom.sh && . /etc/profile.d/zz-custom.sh' >> ~/.bashrc
    fi
    # RHEL/CentOS 交互 Bash
    if [[ -f /etc/bashrc ]] && ! grep -q '/etc/profile.d/zz-custom.sh' /etc/bashrc 2> /dev/null; then
        echo 'test -r /etc/profile.d/zz-custom.sh && . /etc/profile.d/zz-custom.sh' >> /etc/bashrc
    fi
}

addBINscript() {
    log_info "Adding custom scripts....."
    if command -v systeminfo > /dev/null 2>&1; then
        log_warn "Custom scripts already exist. Skipping."
        return 0
    fi
    curl -Lk "${GIT_REPO_URL}/OS/securityremove" > /usr/local/bin/securityremove || true
    curl -Lk "${GIT_REPO_URL}/OS/lsmod" > /usr/local/bin/lsmod || true
    curl -Lk "${GIT_REPO_URL}/OS/systeminfo" > /usr/local/bin/systeminfo || true
    chmod +x /usr/local/bin/{securityremove,lsmod,systeminfo} || true

    # Setup vim
    curl -Lk "${GIT_REPO_URL}/OS/vim.tar.gz" | tar -xz -C ~ || true
    mkdir -p ~/.vim/syntax
    curl -Lk "${GIT_REPO_URL}/OS/nginx.vim" > ~/.vim/syntax/nginx.vim || true
    grep -q 'setfiletype nginx' ~/.vimrc 2> /dev/null || echo "au BufRead,BufNewFile /etc/nginx/*,/usr/local/nginx/conf/*.conf if &ft == '' | setfiletype nginx | endif" >> ~/.vimrc

    # 追加：取消自动续行注释、提供粘贴模式切换
    if ! grep -q 'NoAutoComment' ~/.vimrc 2> /dev/null && ! grep -q 'formatoptions-=' ~/.vimrc 2> /dev/null; then
        cat >> ~/.vimrc << 'VIMRC'
" 不自动续行注释与回车续注释
augroup NoAutoComment
    autocmd!
    autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o
augroup END

" F2 一键切换粘贴模式，避免自动缩进干扰粘贴
set pastetoggle=<F2>
VIMRC
    fi

    # Setup aliases via /etc/profile.d/zz-custom.sh for cross-distro compatibility (后加载更靠后)
    mkdir -p /etc/profile.d
    {
        echo "# BEGIN custom aliases"
        echo 'alias rm="/usr/local/bin/securityremove"'
        echo "alias ls='ls --color=auto'"
        echo "alias vi='vim'"
        echo "alias grep='grep --color=auto'"
        echo "# END custom aliases"
    } >> /etc/profile.d/zz-custom.sh
    chmod 644 /etc/profile.d/zz-custom.sh || true
    # Ensure non-login interactive shells source it (Ubuntu: /etc/bash.bashrc; RHEL/CentOS: /etc/bashrc)
    if [[ -f /etc/bash.bashrc ]] && ! grep -q '/etc/profile.d/zz-custom.sh' /etc/bash.bashrc 2> /dev/null; then
        echo 'test -r /etc/profile.d/zz-custom.sh && . /etc/profile.d/zz-custom.sh' >> /etc/bash.bashrc
    fi
    if [[ -f /etc/bashrc ]] && ! grep -q '/etc/profile.d/zz-custom.sh' /etc/bashrc 2> /dev/null; then
        echo 'test -r /etc/profile.d/zz-custom.sh && . /etc/profile.d/zz-custom.sh' >> /etc/bashrc
    fi
}

# --- 8.4 SELinux管理 ---

setSELinux() {
    log_info "Disabling SELinux....."
    if [[ -f /etc/selinux/config ]]; then
        sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config || true
        setenforce 0 || true
    fi
}

# --- 8.5 系统参数优化 ---

setSYSCTL() {
    log_info "Configuring sysctl....."
    local sysctl_target
    mkdir -p /etc/sysctl.d
    sysctl_target="/etc/sysctl.d/99-custom.conf"
    : > "$sysctl_target"

    # Defaults with per-release overrides
    local file_max=1048576 rmem_max=16777216 wmem_max=16777216 \
        tcp_rmem_third=4194304 tcp_wmem_third=4194304 \
        tcp_timestamps=1 tcp_tw_reuse=0 tcp_max_orphans=262144 \
        include_arptables=0

    case "$RELEASE" in
        7)
            file_max=1048576
            rmem_max=16777216
            wmem_max=16777216
            tcp_rmem_third=4194304
            tcp_timestamps=0
            tcp_tw_reuse=1
            tcp_max_orphans=3276800
            include_arptables=1
            ;;
        8)
            file_max=1048576
            rmem_max=33554432
            wmem_max=33554432
            tcp_rmem_third=6291456
            tcp_timestamps=1
            tcp_tw_reuse=1
            tcp_max_orphans=262144
            ;;
        9)
            file_max=2097152
            rmem_max=33554432
            wmem_max=33554432
            tcp_rmem_third=6291456
            tcp_timestamps=1
            tcp_tw_reuse=0
            tcp_max_orphans=262144
            ;;
        *)
            :
            ;;
    esac

    cat > "$sysctl_target" << EOF
fs.file-max = ${file_max}

# 队列/回压（Backlog）
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 32768
net.ipv4.tcp_max_syn_backlog = 65536

# 缓冲区（Buffers）
net.core.rmem_default = 8388608
net.core.wmem_default = 8388608
net.core.rmem_max = ${rmem_max}
net.core.wmem_max = ${wmem_max}
net.ipv4.tcp_rmem = 4096 87380 ${tcp_rmem_third}
net.ipv4.tcp_wmem = 4096 16384 ${tcp_wmem_third}

# TCP 行为
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = ${tcp_timestamps}
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_max_orphans = ${tcp_max_orphans}
net.ipv4.ip_local_port_range = 1024 65000
EOF

    if [[ ${tcp_tw_reuse} -eq 1 ]]; then
        echo "net.ipv4.tcp_tw_reuse = 1" >> "$sysctl_target"
    fi

    cat >> "$sysctl_target" << 'EOF'

# 连接跟踪（Conntrack）
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_established = 3600

# 桥接/NAT
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

    if [[ ${include_arptables} -eq 1 ]]; then
        echo "net.bridge.bridge-nf-call-arptables = 1" >> "$sysctl_target"
    fi

    # Common security & routing
    cat >> "$sysctl_target" << 'EOF'

# 安全与路由
net.ipv4.conf.all.rp_filter = 2
net.ipv4.ip_forward = 1
EOF

    modprobe br_netfilter || true

    # Append additional common tunings
    cat >> "$sysctl_target" << 'EOF'

# --- 通用系统调优 ---
# Inotify
fs.inotify.max_user_watches = 1048576
fs.inotify.max_user_instances = 1024

# 内存与虚拟内存（VM）
vm.swappiness = 10
vm.vfs_cache_pressure = 50
vm.dirty_background_ratio = 5
vm.dirty_ratio = 20
vm.max_map_count = 262144

# TCP keepalive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5

# TCP Fast Open（若内核支持）
net.ipv4.tcp_fastopen = 3

# 若内核支持，优先使用 BBR
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 安全加固
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
EOF

    sysctl --system || sysctl -p || true
    # EL10 提示：部分内核参数可能被系统策略覆盖，不强制覆盖，仅提示
    if [[ "$RELEASE" -eq 10 ]]; then
        log_warn "EL10 detected: Some kernel parameters may be managed by system policies (e.g. /usr/lib/sysctl.d/*). Current tunings applied on a best-effort basis."
    fi
}

tuneLimits() {
    log_info "Applying system limits (nofile/nproc)....."
    mkdir -p /etc/security/limits.d
    cat > /etc/security/limits.d/99-custom-limits.conf << 'EOF'
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535
root soft nofile 1048576
root hard nofile 1048576
root soft nproc 65535
root hard nproc 65535
EOF

    # OS-specific handling for systemd limits
    case "$RELEASE" in
        7)
            # EL7 通常存在 system.conf 与 user.conf；若不存在则采用 drop-in
            if [[ -f /etc/systemd/system.conf ]]; then
                kv_set /etc/systemd/system.conf DefaultLimitNOFILE 1048576 '='
                kv_set /etc/systemd/system.conf DefaultLimitNPROC 65535 '='
                kv_set /etc/systemd/system.conf DefaultTasksMax infinity '='
            else
                mkdir -p /etc/systemd/system.conf.d
                cat > /etc/systemd/system.conf.d/99-limits.conf << 'EOF'
[Manager]
DefaultLimitNOFILE=1048576
DefaultLimitNPROC=65535
DefaultTasksMax=infinity
EOF
            fi
            if [[ -f /etc/systemd/user.conf ]]; then
                kv_set /etc/systemd/user.conf DefaultLimitNOFILE 1048576 '='
                kv_set /etc/systemd/user.conf DefaultLimitNPROC 65535 '='
                kv_set /etc/systemd/user.conf DefaultTasksMax infinity '='
            else
                mkdir -p /etc/systemd/user.conf.d
                cat > /etc/systemd/user.conf.d/99-limits.conf << 'EOF'
[Manager]
DefaultLimitNOFILE=1048576
DefaultLimitNPROC=65535
DefaultTasksMax=infinity
EOF
            fi
            ;;
        8 | 9 | 10 | *)
            # 使用 drop-in，兼容 EL8/9/10 与多数 systemd 系统（含 Ubuntu）
            mkdir -p /etc/systemd/system.conf.d /etc/systemd/user.conf.d
            cat > /etc/systemd/system.conf.d/99-limits.conf << 'EOF'
[Manager]
DefaultLimitNOFILE=1048576
DefaultLimitNPROC=65535
DefaultTasksMax=infinity
EOF
            cat > /etc/systemd/user.conf.d/99-limits.conf << 'EOF'
[Manager]
DefaultLimitNOFILE=1048576
DefaultLimitNPROC=65535
DefaultTasksMax=infinity
EOF
            ;;
    esac

    systemctl daemon-reload || true
}

tuneJournald() {
    log_info "Optimizing journald (persistent & size limits)....."
    mkdir -p /etc/systemd/journald.conf.d
    cat > /etc/systemd/journald.conf.d/99-custom.conf << 'EOF'
[Journal]
Storage=persistent
SystemMaxUse=1G
RuntimeMaxUse=512M
RateLimitIntervalSec=30s
RateLimitBurst=1000
EOF
    systemctl restart systemd-journald || true
}

# --- 8.6 系统服务优化 ---

optimizeServices() {
    log_info "Disabling non-essential services....."
    local services=(postfix kdump abrtd abrt-ccpp cups bluetooth)
    for s in "${services[@]}"; do
        systemctl disable --now "$s" > /dev/null 2>&1 || true
    done
}

# --- 8.7 性能调优 ---

enableTunedPerformance() {
    local profile="${1:-throughput-performance}"
    log_info "Enabling tuned profile: ${profile} ....."
    local pm
    pm=$(pkgmgr)
    $pm -y install tuned > /dev/null 2>&1 || true
    enable_now tuned
    tuned-adm profile "$profile" > /dev/null 2>&1 || true
}

configureTHP() {
    log_info "Setting Transparent HugePages mode to madvise....."
    local service_file=/etc/systemd/system/thp.service
    cat > "$service_file" << 'EOF'
[Unit]
Description=Configure Transparent HugePages to madvise
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'for p in /sys/kernel/mm/transparent_hugepage/enabled /sys/kernel/mm/transparent_hugepage/defrag; do [ -w "$p" ] && echo madvise > "$p" || true; done'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload || true
    enable_now thp.service
}

configureNUMANetRPS() {
    log_info "Configuring RPS/RFS for network queues....."
    local cpumask ncpu queues per_queue rps_entries
    ncpu=$(nproc 2> /dev/null || echo 1)
    cpumask=$(generateCPUMask "$ncpu")

    # Global RPS sock flow entries
    rps_entries=$((ncpu * 4096))
    ((rps_entries > 65536)) && rps_entries=65536
    ((rps_entries < 32768)) && rps_entries=32768
    sysctl -w net.core.rps_sock_flow_entries=${rps_entries} > /dev/null 2>&1 || true

    for iface in /sys/class/net/*; do
        iface=${iface##*/}
        [[ "$iface" == "lo" ]] && continue
        # 为每个 RX 队列设置 rps_cpus
        for q in /sys/class/net/"$iface"/queues/rx-*; do
            [[ -e "$q/rps_cpus" ]] || continue
            echo "$cpumask" > "$q/rps_cpus" 2> /dev/null || true
        done
        # 根据总条目数为每个队列设置 rps_flow_cnt
        queues=$(ls -d /sys/class/net/"$iface"/queues/rx-* 2> /dev/null | wc -l | awk '{print $1}')
        ((queues < 1)) && queues=1
        per_queue=$((rps_entries / queues))
        ((per_queue < 2048)) && per_queue=2048
        for q in /sys/class/net/"$iface"/queues/rx-*; do
            [[ -e "$q/rps_flow_cnt" ]] || continue
            echo "$per_queue" > "$q/rps_flow_cnt" 2> /dev/null || true
        done
    done
}

# --- 8.8 安全加固 ---

enableAuditd() {
    log_info "Enabling auditd....."
    local pm
    pm=$(pkgmgr)
    $pm -y install audit > /dev/null 2>&1 || true
    enable_now auditd
}

hardenAndVerifySSH() {
    log_info "═══════════════════════════════════════════════════════════"
    log_info "SSH 完整配置、加固与验证"
    log_info "═══════════════════════════════════════════════════════════"
    
    local cfg=/etc/ssh/sshd_config
    [[ -f "$cfg" ]] || { log_warn "SSH config not found, skipping"; return 0; }
    
    # Initialize package manager
    local pm
    pm=$(pkgmgr)
    
    # Step 1: Update OpenSSH packages to latest version
    log_info "Step 1/5: 更新 OpenSSH 软件包..."
    if pm_is_apt; then
        apt-get update -qq > /dev/null 2>&1 || true
        DEBIAN_FRONTEND=noninteractive apt-get install -y --only-upgrade openssh-server openssh-client > /dev/null 2>&1 || {
            log_warn "OpenSSH upgrade failed, continuing with current version"
        }
    else
        $pm install -y openssh-server openssh-clients openssh > /dev/null 2>&1 || {
            log_warn "OpenSSH upgrade failed, continuing with current version"
        }
    fi
    
    local ssh_version=$(sshd -V 2>&1 | head -1 || echo "unknown")
    log_ok "OpenSSH version: ${ssh_version}"
    
    # Step 2: Backup current configuration
    log_info "Step 2/5: 备份当前 SSH 配置..."
    local backup_cfg="${cfg}.final-harden.$(date +%Y%m%d_%H%M%S)"
    cp -f "$cfg" "$backup_cfg" || true
    log_note "配置备份: $backup_cfg"
    
    # Step 3: Apply comprehensive SSH configuration and hardening
    log_info "Step 3/5: 应用 SSH 配置和安全加固..."
    
    # ===== Basic Configuration =====
    # Allow password and root login (as per original requirements)
    kv_set "$cfg" PasswordAuthentication yes ' '
    kv_set "$cfg" PermitRootLogin yes ' '
    
    # Configure SSH ports
    grep -q '^Port 22$' "$cfg" || echo 'Port 22' >> "$cfg"
    grep -q '^Port 22992$' "$cfg" || echo 'Port 22992' >> "$cfg"
    
    # Disable DNS lookups for faster connections
    grep -q '^UseDNS no$' "$cfg" || echo 'UseDNS no' >> "$cfg"

    # ===== Security Hardening =====
    # Disable GSSAPI to reduce attack surface
    kv_set "$cfg" GSSAPIAuthentication no ' '
    kv_set "$cfg" ChallengeResponseAuthentication no ' '
    
    # Connection timeout and retry limits
    grep -q '^LoginGraceTime' "$cfg" || echo 'LoginGraceTime 30' >> "$cfg"
    grep -q '^MaxAuthTries' "$cfg" || echo 'MaxAuthTries 3' >> "$cfg"
    grep -q '^ClientAliveInterval' "$cfg" || echo 'ClientAliveInterval 300' >> "$cfg"
    grep -q '^ClientAliveCountMax' "$cfg" || echo 'ClientAliveCountMax 2' >> "$cfg"

    # Modern cryptographic algorithms (OpenSSH 7.4+)
    if ! grep -q '^Ciphers ' "$cfg"; then
        echo 'Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr' >> "$cfg"
    fi
    if ! grep -q '^MACs ' "$cfg"; then
        echo 'MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com' >> "$cfg"
    fi
    if ! grep -q '^KexAlgorithms ' "$cfg"; then
        echo 'KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256' >> "$cfg"
    fi

    log_ok "SSH 配置已更新"

    # Step 4: Additional SSH setup
    log_info "Step 4/5: SSH 附加配置..."
    
    # Ensure SSH host keys exist (minimal images may miss them)
    command -v ssh-keygen > /dev/null 2>&1 && ssh-keygen -A > /dev/null 2>&1 || true

    # Allow custom SSH port 22992 in SELinux
    if command -v getenforce > /dev/null 2>&1; then
        local sel
        sel=$(getenforce 2> /dev/null || echo Disabled)
        if [[ "$sel" == Enforcing || "$sel" == Permissive ]]; then
            if command -v semanage > /dev/null 2>&1; then
                if ! semanage port -l 2> /dev/null | grep -qE '^ssh_port_t\b.*\b22992\b'; then
                    semanage port -a -t ssh_port_t -p tcp 22992 2> /dev/null \
                        || semanage port -m -t ssh_port_t -p tcp 22992 2> /dev/null || true
                fi
            fi
        fi
    fi

    # Setup authorized_keys
    install -m 700 -d /root/.ssh
    touch /root/.ssh/authorized_keys
    chmod 600 /root/.ssh/authorized_keys
    # Download and add public keys
    curl -Lks "${GIT_REPO_URL}/ssh_pub_key" > /root/.ssh/authorized_keys || true
    
    log_ok "SSH 附加配置完成"
    
    # Step 5: Reload SSH service (validation is handled by reload_sshd)
    log_info "Step 5/5: 重新加载 SSH 服务..."
    reload_sshd
    
    # Verify SSH is running
    local svc="sshd"
    pm_is_apt && svc="ssh"
    
    if systemctl is-active "$svc" > /dev/null 2>&1 || service "$svc" status > /dev/null 2>&1; then
        log_ok "✅ SSH 服务运行正常"
    else
        log_warn "⚠️  SSH 服务状态异常，请检查"
    fi
    
    # Show listening ports
    local ssh_ports=$(ss -tlnp 2>/dev/null | grep ':22' | awk '{print $4}' | sed 's/.*://g' | sort -u | tr '\n' ',' | sed 's/,$//')
    if [[ -n "$ssh_ports" ]]; then
        log_ok "✅ SSH 监听端口: $ssh_ports"
    fi
    
    log_info "═══════════════════════════════════════════════════════════"
    log_ok "SSH 加固完成，服务器可以安全重启"
    log_info "═══════════════════════════════════════════════════════════"
}

# --- 8.9 防火墙配置 ---

setFirewallUbuntu() {
    log_warn "Configuring UFW firewall (Ubuntu)....."
    DEBIAN_FRONTEND=noninteractive apt-get install -y ufw > /dev/null 2>&1 || true
    ufw --force reset > /dev/null 2>&1 || true
    ufw default deny incoming > /dev/null 2>&1 || true
    ufw default allow outgoing > /dev/null 2>&1 || true
    ufw allow 22/tcp > /dev/null 2>&1 || true
    ufw allow 22992/tcp > /dev/null 2>&1 || true
    # Zabbix agent: 仅在需要安装时放行
    if [[ "${INSTALL_ZABBIX}" == "true" ]]; then
        ufw allow from ${ZBX_SERVER_IP} to any port 10050 proto tcp > /dev/null 2>&1 || true
    fi
    ufw --force enable > /dev/null 2>&1 || true
}

setFirewall() {
    log_info "Setting up firewall....."
    local network_type="${1:-public}"
    if is_ubuntu; then
        setFirewallUbuntu
        return 0
    fi
    case "$RELEASE" in
        7 | 8 | 9)
            log_warn "Changing Firewall to iptables....."
            if [[ "$RELEASE" -eq 7 ]]; then
                curl -Lk4 "${GIT_REPO_URL}/CentOS7/OS/friewall2iptables.sh" | bash || true
            else
                dnf install -y iptables-services || yum install -y iptables-services || true
                systemctl disable --now firewalld || true
            fi
            local rules_file="CentOS${RELEASE}/OS/iptables_init_rules"
            : > /etc/sysconfig/iptables
            if curl -Lk4 "${GIT_REPO_URL}/${rules_file}" -o /etc/sysconfig/iptables 2> /dev/null && [[ -s /etc/sysconfig/iptables ]]; then
                :
            else
                # Fallback: try reuse CentOS9 rules
                if curl -Lk4 "${GIT_REPO_URL}/CentOS9/OS/iptables_init_rules" -o /etc/sysconfig/iptables 2> /dev/null && [[ -s /etc/sysconfig/iptables ]]; then
                    :
                else
                    # Final fallback: minimal sane iptables policy
                    cat > /etc/sysconfig/iptables << 'EOF'
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p tcp -m multiport --dports 22,22992 -m comment --comment "SSH" -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
EOF
                fi
            fi
            if [[ "$network_type" == "public" ]]; then
                ensureIptablesAllowPort 22
                ensureIptablesAllowPort 22992
                sed -ri '/(172\.(30|25|20)|47\.90|119\.28\.51\.253|119\.9\.95\.122|ZABBIX|MOA)/d' /etc/sysconfig/iptables || true
            fi
            enable_now iptables
            # Save current rules immediately to persist the first-time state (with backup)
            if command -v iptables-save > /dev/null 2>&1; then
                backup_dir="/etc/sysconfig/iptables.backup"
                mkdir -p "$backup_dir" || true
                ts=$(date +%Y%m%d_%H%M%S)
                [ -f /etc/sysconfig/iptables ] && cp -a /etc/sysconfig/iptables "$backup_dir/iptables_$ts" || true
                #iptables-save > /etc/sysconfig/iptables || true
            fi
            ;;
        10)
            # EL10: Keep firewalld (nftables backend) to avoid iptables/nft conflicts with Docker
            log_warn "EL10 detected: keeping firewalld (nft). Opening SSH ports..."
            enable_now firewalld
            firewall-cmd --permanent --add-service=ssh > /dev/null 2>&1 || true
            firewall-cmd --permanent --add-port=22992/tcp > /dev/null 2>&1 || true
            if [[ "${INSTALL_ZABBIX}" == "true" ]]; then
                firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${ZBX_SERVER_IP}/32 port port=10050 protocol=tcp accept" > /dev/null 2>&1 || true
            fi
            # 解析项目内 iptables 规则文件并映射到 firewalld 端口放行
            local rules_file="CentOS${RELEASE}/OS/iptables_init_rules"
            local tmp_rules="/tmp/iptables_init_rules.${RELEASE}.$$.txt"
            if curl -Lk4 "${GIT_REPO_URL}/${rules_file}" -o "$tmp_rules" 2> /dev/null && [[ -s "$tmp_rules" ]]; then
                log_info "Translating iptables rules to firewalld allow ports (EL10)..."
                applyFirewalldPortsFromIptablesRulesFile "$tmp_rules"
            fi
            firewall-cmd --reload > /dev/null 2>&1 || true
            ;;
    esac
}

applyFirewallRateLimit() {
    log_info "Applying SSH rate-limit rules to firewall....."
    case "$RELEASE" in
        7 | 8 | 9 | 10)
            if systemctl is-enabled iptables > /dev/null 2>&1; then
                local f=/etc/sysconfig/iptables
                if [[ -f "$f" ]]; then
                    # Add iptables recent limit rules for ports 22 and 22992 if missing
                    if ! grep -q 'recent.*--name SSH22' "$f"; then
                        sed -i '/-A INPUT -p tcp -m multiport --dports 22,22992/ i -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH22' "$f" || true
                        sed -i '/-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH22/ a -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 30 --name SSH22 -j DROP' "$f" || true
                    fi
                    if ! grep -q 'recent.*--name SSH22992' "$f"; then
                        sed -i '/-A INPUT -p tcp -m multiport --dports 22,22992/ i -A INPUT -p tcp --dport 22992 -m state --state NEW -m recent --set --name SSH22992' "$f" || true
                        sed -i '/-A INPUT -p tcp --dport 22992 -m state --state NEW -m recent --set --name SSH22992/ a -A INPUT -p tcp --dport 22992 -m state --state NEW -m recent --update --seconds 60 --hitcount 30 --name SSH22992 -j DROP' "$f" || true
                    fi
                    systemctl restart iptables > /dev/null 2>&1 || true
                fi
            elif systemctl is-active firewalld > /dev/null 2>&1; then
                firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="ssh" log prefix="FWD-SSH " level="warning" limit value="60/m" accept' > /dev/null 2>&1 || true
                firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="22992" protocol="tcp" log prefix="FWD-SSH22992 " level="warning" limit value="60/m" accept' > /dev/null 2>&1 || true
                firewall-cmd --reload > /dev/null 2>&1 || true
            fi
            ;;
    esac
}

setupIptablesPersist() {
    log_info "Enabling iptables auto-persist on shutdown....."
    # 若 firewalld（nftables）正在运行则跳过，避免冲突（EL10 常见）
    if systemctl is-active firewalld > /dev/null 2>&1; then
        log_warn "firewalld active; skipping iptables persist setup."
        return 0
    fi
    local saver=/usr/local/sbin/iptables-save-rules.sh
    cat > "$saver" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
backup_dir="/etc/sysconfig/iptables.backup"
mkdir -p "$backup_dir"
ts=$(date +%Y%m%d_%H%M%S)
if command -v iptables-save >/dev/null 2>&1; then
  [ -f /etc/sysconfig/iptables ] && cp -a /etc/sysconfig/iptables "$backup_dir/iptables_$ts" || true
  iptables-save > /etc/sysconfig/iptables || true
fi
if command -v ip6tables-save >/dev/null 2>&1; then
  [ -f /etc/sysconfig/ip6tables ] && cp -a /etc/sysconfig/ip6tables "$backup_dir/ip6tables_$ts" || true
  ip6tables-save > /etc/sysconfig/ip6tables || true
fi
exit 0
EOF
    chmod +x "$saver" || true

    local unit=/etc/systemd/system/iptables-persist.service
    cat > "$unit" << EOF
[Unit]
Description=Persist iptables rules on shutdown
DefaultDependencies=no
Before=shutdown.target reboot.target halt.target

[Service]
Type=oneshot
ExecStart=/bin/true
ExecStop=$saver
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload || true
    enable_now iptables-persist.service
}

addrPoolsCsvToJson() {
    # CSV format: base/16:24,base2/16:24  => [{"base":"base/16","size":24}, ...]
    local csv="$1"
    [[ -z "$csv" ]] && {
        echo "[]"
        return 0
    }
    echo "$csv" | awk -F',' '{
        printf("[");
        for (i=1;i<=NF;i++) {
            gsub(/^\s+|\s+$/, "", $i);
            split($i, kv, ":");
            base = kv[1]; size = kv[2];
            if (i>1) printf(",");
            printf("{\"base\":\"%s\",\"size\":%s}", base, size);
        }
        printf("]");
    }'
}






LOG_LEVEL="${LOG_LEVEL:-INFO}"



# 单一日志函数：包含级别、时间戳、ANSI 颜色；输出一次（由 tee 负责写入文件）

# log() 的轻量封装包装函数

# 执行命令并忽略错误（尽力而为），适用于幂等操作

# 辅助函数：判断当前日志级别是否为 DEBUG

# 遵循 DEBUG 冗长度的包管理器执行器

# 辅助函数：返回当前系统应使用的包管理器命令（yum/dnf/apt）



# 在非 RHEL 系统上禁用 RHSM 的 subscription-manager 插件，避免无意义提示


# 规范化架构标识

# 架构辅助函数

# 根据发行版与大版本启用 CodeReady Builder/CRB/PowerTools

# AlmaLinux（中国大陆/香港）：优先使用阿里云镜像加速

# 为 RHEL 家族选择 Docker CE 仓库 URL；若为 AlmaLinux 优先使用其专用路径

# 检测现有 EFI 供应商目录以用于重新生成 grub.cfg（centos/rocky/redhat/almalinux）




### 包管理器能力检测 + 实现分发（隐藏 dnf/yum 差异）

# 通用：解析可选参数，返回剩余的包名列表
# 支持：--no-excludes  --enable "repo1,repo2"  --disable "repo*"  --nobest  --allowerasing





# --- 可复用的通用辅助函数 ---

# 统一重载/重启 SSHD（跨发行版）

# 统一启用并启动服务（忽略错误）

# 统一键值写入（INI/KEY=VAL 风格，去重幂等）


# JSON 合并：优先 jq；无 jq 时尝试 python3；再次降级为简单覆盖


# --- 数组小工具（基于 nameref） ---



# 构建常用工具包列表（按 RELEASE），通过 nameref 输出到调用方数组
build_common_packages() {
    local -n __out_arr="$1"
    # 基础包（RHEL 家族通用）
    local -a base_common=(
        lshw vim tree bash-completion git xorg-x11-xauth xterm gettext tmux man screen
        cronie crontabs wget curl iproute tar gdisk fio iotop iftop htop bind-utils 
        telnet mtr irqbalance jq
    )
    __out_arr=("${base_common[@]}")

    case "$RELEASE" in
        7)
            arr_add_unique __out_arr axel yum-utils cronie-anacron
            ;;
        8)
            arr_add_unique __out_arr dnf-plugins-core
            arr_remove __out_arr yum-utils cronie-anacron
            ;;
        9)
            arr_add_unique __out_arr yum-utils dnf-plugins-core
            arr_remove __out_arr cronie-anacron
            ;;
        10)
            arr_add_unique __out_arr dnf-plugins-core
            arr_remove __out_arr yum-utils cronie-anacron
            ;;
        *)
            :
            ;;
    esac
    # 仅在 RHEL 正版系统追加 subscription-manager
    if [[ "${OS,,}" == "rhel" ]]; then
        __out_arr+=(subscription-manager)
    fi
}

# 构建 Ubuntu 常用工具包列表
build_ubuntu_packages() {
    local -n __out_arr="$1"
    local -a base=(
        lshw vim tree bash-completion git xauth xterm gettext tmux vnstat man-db screen
        cron wget curl iproute2 tar gdisk fio iotop iftop htop dnsutils telnet mtr
        net-tools build-essential ca-certificates gnupg lsb-release locales irqbalance jq
    )
    __out_arr=("${base[@]}")
}

# 统一安装中文语言包（各版本）
ensure_zhcn_langpacks() {
    local pm
    pm=$(pkgmgr)
    if is_ubuntu; then
        DEBIAN_FRONTEND=noninteractive apt-get install -y locales > /dev/null 2>&1 || true
        locale-gen zh_CN.UTF-8 > /dev/null 2>&1 || true
        update-locale LANG=zh_CN.UTF-8 > /dev/null 2>&1 || true
        return 0
    fi
    case "$RELEASE" in
        6)
            yum -y groupinstall "Chinese Support" > /dev/null 2>&1 || true
            cat > /etc/sysconfig/i18n <<- 'EOF'
LANG="zh_CN.UTF-8"
SYSFONT="latarcyrheb-sun16"
SUPPORTED="zh_CN.UTF-8:zh_CN:zh"
EOF
            ;;
        7)
            $pm -y install langpacks-zh_CN kde-l10n-Chinese > /dev/null 2>&1 || true
            ;;
        8)
            dnf -y --disablerepo='*' --enablerepo='base','appstream' \
                install glibc-langpack-en glibc-langpack-zh langpacks-zh_CN glibc-gconv-extra \
                --allowerasing > /dev/null 2>&1 || true
            ;;
        9 | 10)
            $pm -y install glibc-langpack-en glibc-langpack-zh langpacks-zh_CN glibc-gconv-extra > /dev/null 2>&1 || true
            ;;
    esac
}

# 分步执行器：为步骤化日志提供统一入口
run_step() {
    local title="$1"
    shift
    log_note "[STEP] ${title}"
    "$@"
}

# 检测 yum/dnf 配置中是否对内核相关包设置了 exclude

# 安装软件包时忽略 yum/dnf 配置中的 exclude（如 exclude=kernel*）

# --- GitHub 加速与镜像（适配中国大陆/香港） ---



# === 章节：编排与计划（阶段化执行） ===
declare -ag STAGES=()
declare -Ag STAGE_DURATIONS=()
declare -Ag STAGE_STATUS=()     # RUN/SKIP/FAIL/OK
declare -Ag STAGE_REASON_MAP=() # reason for SKIP/FAIL
CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-false}"

# 以映射表代替大量包装函数与分支，减少重复代码
declare -Ag STAGE_FUNCS=()
declare -Ag STAGE_DESCS=()
declare -Ag STAGE_COND=()

# 统一的系统信息阶段（可选）

# 填充阶段→命令 映射
STAGE_FUNCS=(
    [preflight]=preflightLowMemory [repos]=configureYumDnf [kernel]=changeKernel [tools]=updateSystemAndInstallTools [time]=syncTime [locale_set]=changZHCN [locale_fix]=fixLocaleWarnings [bin]=addBINscript [ps1]=setPS1 [selinux]=setSELinux [ipv6]=disableIPV6 [sysctl]=setSYSCTL [limits]=tuneLimits [journald]=tuneJournald [services]=optimizeServices [adaptive]=adaptiveSysctl [tuned]="enableTunedPerformance \"$TUNED_PROFILE\"" [thp]=configureTHP [fail2ban]=enableFail2ban [auditd]=enableAuditd [numa_rps]=configureNUMANetRPS [speedtest]=addSpeedtest [docker]=dockerINSTALL [zabbix]=installZabbixAgent [pyenv]=installPyenv [firewall]="setFirewall \"$NETWORK_TYPE\"" [fw_ratelimit]=applyFirewallRateLimit [iptables_persist]=setupIptablesPersist [motd]=addMotd [fips]=enableFIPSMode [devtoolchain]=ensureDevToolchain [systeminfo]=__stage_systeminfo [ssh_harden]=hardenAndVerifySSH
)

# 填充阶段→描述 映射
STAGE_DESCS=(
    [preflight]="低内存预处理" [repos]="配置 YUM/DNF/APT 源" [tools]="安装常用工具" [time]="时间同步与时区" [locale_set]="设置中文语言" [locale_fix]="修复 locale 警告" [bin]="添加自定义脚本" [ps1]="设置 PS1" [selinux]="禁用 SELinux" [ipv6]="禁用 IPv6" [sysctl]="内核参数" [limits]="系统 limits" [journald]="journald 优化" [services]="关闭非必要服务" [adaptive]="自适应 sysctl" [tuned]="tuned 性能配置" [thp]="透明大页" [fail2ban]="fail2ban" [auditd]="auditd" [numa_rps]="NUMA/RPS 网络优化" [speedtest]="安装 speedtest-cli" [docker]="安装/配置 Docker" [zabbix]="安装 Zabbix Agent" [pyenv]="安装 pyenv/Python" [firewall]="配置防火墙(iptables/UFW)" [fw_ratelimit]="SSH 防火墙限速" [iptables_persist]="iptables 持久化" [motd]="设置 MOTD" [systeminfo]="输出系统信息" [fips]="启用 FIPS" [kernel]="切换内核" [devtoolchain]="开发工具链补装" [ssh_harden]="SSH 完整配置、加固与验证"]
)

# 可跳过阶段对应的布尔变量映射（变量值不为 true 时跳过）
STAGE_COND=(
    [adaptive]=DO_ADAPTIVE_SYSCTL [ssh_harden]=DO_HARDEN_SSH [thp]=DO_THP [fail2ban]=DO_FAIL2BAN [auditd]=DO_AUDITD [numa_rps]=DO_NUMA_RPS [docker]=INSTALL_DOCKER [zabbix]=INSTALL_ZABBIX [pyenv]=INSTALL_PYENV [fw_ratelimit]=DO_FIREWALL_LIMIT [fips]=ENABLE_FIPS
)

# 描述查询
stage_desc() { echo "${STAGE_DESCS[$1]:-$1}"; }

stage_execute() {
    local s="$1" start end dur cond_var cmd skipped_reason=""
    start=$(date +%s)
    STAGE_STATUS["$s"]=RUN
    cond_var="${STAGE_COND[$s]:-}"
    if [[ -n "$cond_var" ]]; then
        if [[ "${!cond_var:-false}" != true ]]; then
            skipped_reason="flag ${cond_var}=false"
        fi
    fi
    if [[ -z "$skipped_reason" ]]; then
        cmd="${STAGE_FUNCS[$s]:-}"
        if [[ -z "$cmd" ]]; then
            skipped_reason="Unknown stage"
        else
            eval "$cmd"
        fi
    fi
    end=$(date +%s)
    dur=$((end - start))
    STAGE_DURATIONS["$s"]="$dur"
    if [[ -n "$skipped_reason" ]]; then
        STAGE_STATUS["$s"]=SKIP
        STAGE_REASON_MAP["$s"]="$skipped_reason"
        log_warn "[SKIP] $(stage_desc "$s") (${skipped_reason})"
    else
        STAGE_STATUS["$s"]=OK
        log_note "[DONE] $(stage_desc "$s") 用时 ${dur}s"
    fi
}

declare -Ag SUBCMD_STAGES=()
SUBCMD_STAGES[all]="preflight repos kernel tools time locale_set locale_fix bin ps1 selinux ipv6 sysctl limits journald services adaptive tuned thp fail2ban auditd numa_rps docker zabbix pyenv firewall fw_ratelimit iptables_persist motd fips devtoolchain systeminfo ssh_harden"
SUBCMD_STAGES[docker]="preflight docker"
SUBCMD_STAGES[firewall]="firewall fw_ratelimit iptables_persist"
SUBCMD_STAGES[pyenv]="pyenv"
SUBCMD_STAGES[kernel]="kernel"
SUBCMD_STAGES[speedtest]="speedtest"

define_stages_for_subcommand() {
    local seq="${SUBCMD_STAGES[$SUBCOMMAND]-}"
    if [[ -z "$seq" ]]; then
        log_error "未知子命令: ${SUBCOMMAND}"
        exit 2
    fi
    read -r -a STAGES <<< "$seq"
}

print_plan_table() {
    echo "阶段计划:"
    local i=1 s
    for s in "${STAGES[@]}"; do
        printf "  %2d. %s (%s)\n" "$i" "$s" "$(stage_desc "$s")"
        i=$((i + 1))
    done
}

print_plan_json() {
    local first=1 s tmp
    tmp=$(mktemp 2>/dev/null || echo /tmp/plan.$$)
    {
        printf '['
        for s in "${STAGES[@]}"; do
            if [[ $first -eq 0 ]]; then printf ","; fi
            printf '{"id":"%s","name":"%s"}' "$s" "$(stage_desc "$s" | sed 's/\"/\\\"/g')"
            first=0
        done
        printf ']\n'
    } >"$tmp"
    if command -v jq >/dev/null 2>&1; then
        jq -c -C . <"$tmp" | sed -e 's/},{/},\n  {/g' -e 's/^\[/[\n  /' -e 's/\]$/\n]/'
    else
        cat "$tmp"
    fi
    rm -f "$tmp" 2>/dev/null || true
}

print_plan_auto() {
    if [[ "${PLAN_JSON:-false}" == "true" ]]; then
        print_plan_json
    else
        print_plan_table
    fi
}






# 安全加载 profile：在开启 nounset 的上下文中加载外部 profile 文件

# === 章节：Docker Compose 辅助函数 ===







# --- Docker 仓库镜像辅助函数 ---


# 合并/确保 registry-mirrors：优先使用 jq 对已有 daemon.json 执行安全合并


# === 章节：外观（MOTD/PS1） ===

# === 章节：SSH（基础配置） ===

# === 章节：仓库与包管理 ===




# 助手：在 rackspace 与 aliyun 之间选择最快的 CentOS 8 vault 镜像

# 助手：为所选来源（rackspace|aliyun）写入 CentOS 8 vault 仓库文件

# === 章节：系统软件与工具 ===


# 确保安装所需语言包以避免 locale 警告




# === 章节：内核、时间与 IPv6 ===
installKernelFallbackEl7() {
    # Fallback: install kernel-ml from bundled RPM for EL7
    if ! is_arch_x86_64; then
        log_warn "Fallback kernel-ml RPM is only provided for x86_64; skipping on $(arch_id)"
        return 1
    fi
    local base="${GIT_REPO_URL}/CentOS7/Kernel"
    local rpm_core="$base/kernel-ml-6.9.7-1.el7.elrepo.x86_64.rpm"
    local rpm_headers="$base/kernel-ml-headers-6.9.7-1.el7.elrepo.x86_64.rpm"
    local rpm_devel="$base/kernel-ml-devel-6.9.7-1.el7.elrepo.x86_64.rpm"
    log_warn "Fallback: installing kernel-ml and headers/devel from repo-bundled RPMs (EL7)"
    curl -Lks "$rpm_core" -o /tmp/kernel-ml-el7.rpm || return 1
    yum -y localinstall --nogpgcheck /tmp/kernel-ml-el7.rpm || return 1
    # Try to install headers/devel if available
    if curl -Lks "$rpm_headers" -o /tmp/kernel-ml-headers-el7.rpm; then
        yum -y localinstall --nogpgcheck /tmp/kernel-ml-headers-el7.rpm || true
    fi
    if curl -Lks "$rpm_devel" -o /tmp/kernel-ml-devel-el7.rpm; then
        yum -y localinstall --nogpgcheck /tmp/kernel-ml-devel-el7.rpm || true
    fi
    grub2-mkconfig -o /boot/grub2/grub.cfg || true
    updateEFIGrubCfg
    grub2-set-default 0 || true
    return 0
}

installKernelFallbackEl8() {
    # Fallback: install kernel-ml from bundled RPM for EL8
    if ! is_arch_x86_64; then
        log_warn "Fallback kernel-ml RPM is only provided for x86_64; skipping on $(arch_id)"
        return 1
    fi
    local base="${GIT_REPO_URL}/CentOS8/Kernel"
    local rpm_core="$base/kernel-ml-6.15.9-1.el8.elrepo.x86_64.rpm"
    local rpm_headers="$base/kernel-ml-headers-6.15.9-1.el8.elrepo.x86_64.rpm"
    local rpm_devel="$base/kernel-ml-devel-6.15.9-1.el8.elrepo.x86_64.rpm"
    log_warn "Fallback: installing kernel-ml and headers/devel from repo-bundled RPMs (EL8)"
    curl -Lks "$rpm_core" -o /tmp/kernel-ml-el8.rpm || return 1
    dnf -y install /tmp/kernel-ml-el8.rpm --nogpgcheck || return 1
    # Try to install headers/devel if available
    if curl -Lks "$rpm_headers" -o /tmp/kernel-ml-headers-el8.rpm; then
        dnf -y install /tmp/kernel-ml-headers-el8.rpm --nogpgcheck || true
    fi
    if curl -Lks "$rpm_devel" -o /tmp/kernel-ml-devel-el8.rpm; then
        dnf -y install /tmp/kernel-ml-devel-el8.rpm --nogpgcheck || true
    fi
    grub2-mkconfig -o /boot/grub2/grub.cfg || true
    updateEFIGrubCfg
    grubby --set-default-index=0 || true
    return 0
}



disableIPV6() {
    log_info "Disabling IPv6....."
    if is_ubuntu; then
        # Ubuntu 常使用 GRUB_CMDLINE_LINUX_DEFAULT
        if [[ -f /etc/default/grub ]] && ! grep -q 'ipv6.disable=1' /etc/default/grub; then
            sed -ri 's/^(GRUB_CMDLINE_LINUX_DEFAULT=.*)("$)/\1 ipv6.disable=1\2/' /etc/default/grub || true
            if command -v update-grub > /dev/null 2>&1; then
                update-grub > /dev/null 2>&1 || true
            else
                grub-mkconfig -o /boot/grub/grub.cfg > /dev/null 2>&1 || true
            fi
        fi
    else
        # RHEL/CentOS: 优先用 grubby 更新所有内核条目；再编辑 /etc/default/grub；最后重建 grub2 配置
        if command -v grubby > /dev/null 2>&1; then
            grubby --update-kernel=ALL --args=ipv6.disable=1 > /dev/null 2>&1 || true
        fi
        if [[ -f /etc/default/grub ]] && ! grep -q 'ipv6.disable=1' /etc/default/grub; then
            sed -ri 's/^(GRUB_CMDLINE_LINUX=.*)("$)/\1 ipv6.disable=1\2/' /etc/default/grub || true
        fi
        grub2-mkconfig -o /boot/grub2/grub.cfg > /dev/null 2>&1 || true
        updateEFIGrubCfg
    fi

    # 运行时与持久化 sysctl 兜底（尽力应用）
    mkdir -p /etc/sysctl.d
    cat > /etc/sysctl.d/99-disable-ipv6.conf << 'EOF'
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
EOF
    sysctl --system > /dev/null 2>&1 || true
}

# === 章节：防火墙 ===





# 确保后续添加的 iptables 规则在重启后能够持久化

# === 章节：Sysctl 与性能调优 ===


# === 章节：Docker ===

# === 章节：安全与加固 ===



# === 章节：Sysctl 与性能调优（自适应） ===


# === 章节：安全与加固（SSH 强化、防火墙限速） ===
# Comprehensive SSH configuration, hardening, and validation
# This is the FINAL stage to ensure SSH is properly configured before potential reboot




