#!/bin/bash
set -euo pipefail  # 严格模式：报错即退出、禁止未定义变量、管道错误传递

##############################################################################
# 常量定义（默认值，可通过参数覆盖）
##############################################################################
DEFAULT_SHARE_DIRS="/opt/data"  # 默认共享目录
EXPORTS_FILE="/etc/exports"    # NFS配置文件路径
BACKUP_SUFFIX=$(date +%Y%m%d%H%M%S)  # 备份文件后缀（时间戳，避免覆盖）
# NFS共享权限模板（{DIR}会自动替换为实际共享目录）
# 权限说明：rw(读写)、sync(同步写入)、no_root_squash(保留root权限)、no_all_squash(保留用户权限)、insecure(允许非特权端口访问)
NFS_CONFIG_TEMPLATE="{DIR} *(rw,sync,no_root_squash,no_all_squash,insecure)"
TEST_MOUNT_DIR_PREFIX="/mnt/nfs_test_$$"  # 临时测试挂载点前缀（$$为进程ID，确保唯一性）

##############################################################################
# 全局变量（通过命令行参数初始化）
##############################################################################
ACTION=""  # 操作类型：install(安装) / uninstall(卸载)
SHARE_DIRS=()  # 共享目录数组（支持多个目录）
OS_TYPE=""  # 系统类型（提前检测，避免重复调用）

##############################################################################
# 工具函数（中文注释）
##############################################################################
# 日志输出（带时间戳，便于排查问题）
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

# 错误退出（打印错误信息后终止脚本）
error_exit() {
    log "ERROR: $1"
    exit 1
}

# 检查是否为root用户（NFS安装/卸载需要root权限）
check_root() {
    if [ $EUID -ne 0 ]; then
        error_exit "请使用root用户运行脚本（执行 sudo -i 或 su - 切换）"
    fi
}

# 检测系统发行版（返回：centos/ubuntu/debian，适配不同系统的包管理）
detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        case $ID in
            centos|rhel) echo "centos" ;;
            ubuntu) echo "ubuntu" ;;
            debian) echo "debian" ;;
            *) error_exit "不支持的系统发行版：$ID（仅支持CentOS/RHEL 7+、Ubuntu 18.04+、Debian 9+）" ;;
        esac
    else
        error_exit "无法检测系统发行版（缺少/etc/os-release文件）"
    fi
}

# 目录路径转义（将 / 替换为 \/，避免sed命令语法冲突）
escape_dir() {
    local dir="$1"
    echo "${dir//\//\\/}"  # 替换所有 / 为 \/
}

# 检查服务是否正在运行
is_service_running() {
    local service_name=$1
    systemctl is-active --quiet "$service_name" 2>/dev/null
}

# 修复Ubuntu/Debian下NFS服务启动异常（核心修复函数）
fix_ubuntu_nfs_start() {
    log "开始修复NFS服务启动环境（Ubuntu/Debian）"
    
    # 1. 停止残留服务进程，避免状态冲突
    log "清理残留NFS相关进程"
    pkill -f nfsd || true
    pkill -f rpc.mountd || true
    
    # 2. 确保依赖服务（rpcbind）启动（Ubuntu 18.04+ 部分版本rpcbind默认未启动）
    log "启动依赖服务：rpcbind"
    systemctl enable --now rpcbind || error_exit "rpcbind服务启动失败（NFS核心依赖）"
    
    # 3. 校验/etc/exports配置语法（避免配置错误导致服务启动失败）
    log "校验NFS配置文件语法（$EXPORTS_FILE）"
    if ! exportfs -r; then  # 重载配置并检查语法
        error_exit "NFS配置文件语法错误！请检查 $EXPORTS_FILE 内容（比如目录路径、权限参数是否正确）"
    fi
    log "NFS配置语法校验通过"
    
    # 4. 重启nfs-kernel-server服务（分两步：停止→启动，避免状态残留）
    log "重启nfs-kernel-server服务"
    systemctl stop nfs-kernel-server || true  # 确保服务已停止
    sleep 2  # 等待进程清理
    if systemctl start nfs-kernel-server; then
        log "nfs-kernel-server服务启动成功"
        systemctl enable nfs-kernel-server || log "警告：设置nfs-kernel-server开机自启失败（不影响当前使用）"
    else
        # 启动失败时输出详细日志，便于排查
        log "nfs-kernel-server服务启动失败，查看详细错误日志："
        journalctl -xeu nfs-kernel-server | tail -20
        error_exit "nfs-kernel-server服务启动失败（见上方错误日志）"
    fi
}

# 解析命令行参数（处理--install/--uninstall/--share-dirs参数）
parse_args() {
    if [ $# -eq 0 ]; then
        show_help  # 无参数时显示帮助信息
        exit 0
    fi

    # 遍历所有传入参数
    for arg in "$@"; do
        case $arg in
            --install)
                ACTION="install"
                ;;
            --uninstall)
                ACTION="uninstall"
                ;;
            --share-dirs=*)
                # 提取--share-dirs后的目录字符串（逗号分隔）
                local dir_str=${arg#--share-dirs=}
                # 按逗号分割为数组
                IFS=',' read -r -a dir_array <<< "$dir_str"
                # 处理目录数组：去重、过滤空值、校验绝对路径
                for dir in "${dir_array[@]}"; do
                    dir=$(echo "$dir" | xargs)  # 去除目录名前后的空格
                    if [ -z "$dir" ]; then
                        log "警告：跳过空的共享目录（可能是逗号前后多写了空格）"
                        continue
                    fi
                    # 校验是否为绝对路径（必须以/开头）
                    if [[ ! "$dir" =~ ^/ ]]; then
                        error_exit "共享目录必须是绝对路径（当前输入：$dir），例如 /opt/data"
                    fi
                    # 去重：如果目录已在数组中，不再重复添加
                    if ! [[ " ${SHARE_DIRS[@]} " =~ " $dir " ]]; then
                        SHARE_DIRS+=("$dir")
                    fi
                done
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            *)
                error_exit "无效参数：$arg，请使用 --help 查看正确用法"
                ;;
        esac
    done

    # 校验必填参数：必须指定--install或--uninstall
    if [ -z "$ACTION" ]; then
        error_exit "必须指定操作类型：--install（安装） 或 --uninstall（卸载）"
    fi

    # 若未指定--share-dirs，使用默认共享目录
    if [ ${#SHARE_DIRS[@]} -eq 0 ]; then
        SHARE_DIRS=($DEFAULT_SHARE_DIRS)
        log "未指定共享目录，使用默认值：${SHARE_DIRS[*]}"
    else
        log "已指定共享目录列表：${SHARE_DIRS[*]}"
    fi
}

# 显示帮助信息（中文说明，方便用户使用）
show_help() {
    echo "======================================= NFS一键安装卸载脚本 ======================================="
    echo "用法：$0 [选项]"
    echo "核心选项："
    echo "  --install                安装NFS服务端+客户端，并配置共享目录（全程使用root权限）"
    echo "  --uninstall              卸载NFS服务端+客户端，清理相关配置（保留数据）"
    echo "  --share-dirs=DIR1,DIR2   指定共享目录（多个目录用逗号分隔，必须为绝对路径）"
    echo "  --help/-h                显示当前帮助信息"
    echo ""
    echo "使用示例："
    echo "  1. 安装NFS（使用默认共享目录 /opt/data）："
    echo "     $0 --install"
    echo "  2. 安装NFS（指定多个共享目录）："
    echo "     $0 --install --share-dirs=/opt/data1,/opt/data2,/mnt/nfs_share"
    echo "  3. 卸载NFS（清理默认共享目录的配置）："
    echo "     $0 --uninstall"
    echo "  4. 卸载NFS（清理指定共享目录的配置）："
    echo "     $0 --uninstall --share-dirs=/opt/data1,/mnt/nfs_share"
    echo "=================================================================================================="
}

# 打印NFS常用维护命令（用户要求新增）
print_maintain_commands() {
    log "======================================= NFS常用维护命令 ======================================="
    log "📊 查看NFS服务状态："
    if [ "$OS_TYPE" = "centos" ]; then
        log "  systemctl status nfs-server"
    else
        log "  systemctl status nfs-kernel-server"
    fi
    log ""
    log "🔄 重启NFS服务："
    if [ "$OS_TYPE" = "centos" ]; then
        log "  systemctl restart nfs-server"
    else
        log "  systemctl restart nfs-kernel-server"
    fi
    log ""
    log "📁 查看已配置的NFS共享目录："
    log "  exportfs -v"
    log ""
    log "⚙️  重载NFS配置（修改/etc/exports后执行）："
    log "  exportfs -r"
    log ""
    log "🖥️  客户端挂载NFS共享（示例）："
    log "  mount -t nfs 服务器IP:/共享目录 本地目标目录（如：mount -t nfs 192.168.1.100:/opt/data /mnt/client_data）"
    log ""
    log "🗑️  客户端卸载NFS共享："
    log "  umount 本地目标目录（如：umount /mnt/client_data）"
    log ""
    log "🔥 查看NFS服务日志（排查问题）："
    if [ "$OS_TYPE" = "centos" ]; then
        log "  journalctl -u nfs-server -f"
    else
        log "  journalctl -u nfs-kernel-server -f"
    fi
    log "=================================================================================================="
}

##############################################################################
# 安装NFS服务（支持多共享目录，中文步骤说明）
##############################################################################
install_nfs() {
    log "开始安装NFS服务端+客户端（系统：$OS_TYPE，共享目录：${SHARE_DIRS[*]}，全程使用root权限）"

    ##########################################################################
    # 步骤1：安装NFS依赖包（根据系统类型选择yum或apt）
    ##########################################################################
    log "步骤1/6：安装NFS相关依赖包"
    case $OS_TYPE in
        centos)
            # CentOS/RHEL：nfs-utils（服务端+客户端）、rpcbind（端口映射）
            yum install -y nfs-utils rpcbind || error_exit "YUM安装依赖失败，请检查yum源是否正常"
            ;;
        ubuntu|debian)
            # Ubuntu/Debian：nfs-kernel-server（服务端）、nfs-common（客户端）、rpcbind（依赖）
            apt update -y >/dev/null 2>&1 || error_exit "APT更新源失败，请检查网络或apt源配置"
            apt install -y nfs-kernel-server nfs-common rpcbind || error_exit "APT安装依赖失败"
            ;;
    esac
    log "依赖包安装完成"

    ##########################################################################
    # 步骤2：创建共享目录并设置全权限（777+root属主，满足用户要求）
    ##########################################################################
    log "步骤2/6：创建共享目录并配置全权限（共${#SHARE_DIRS[@]}个目录，root属主）"
    for dir in "${SHARE_DIRS[@]}"; do
        if [ -d "$dir" ]; then
            log "共享目录 $dir 已存在，跳过创建"
        else
            # -p：递归创建父目录（如/opt/data不存在时，自动创建/opt和/opt/data）
            mkdir -p "$dir" || error_exit "创建共享目录 $dir 失败（权限不足或路径非法）"
            log "成功创建共享目录：$dir"
        fi
        # 设置全权限（777），保留root属主（创建目录时默认就是root，无需额外chown）
        chmod 777 "$dir" || error_exit "设置 $dir 权限为777失败"
        log "目录 $dir：权限配置完成（全权限777，属主root:root）"
    done

    ##########################################################################
    # 步骤3：配置/etc/exports（NFS核心配置文件，先备份再修改）
    ##########################################################################
    log "步骤3/6：配置NFS共享（自动备份原有配置）"
    # 备份原有配置文件（添加时间戳后缀，便于回滚）
    if [ -f "$EXPORTS_FILE" ]; then
        cp "$EXPORTS_FILE" "${EXPORTS_FILE}.bak.${BACKUP_SUFFIX}" || error_exit "备份 $EXPORTS_FILE 失败"
        log "已备份原有配置文件：${EXPORTS_FILE}.bak.${BACKUP_SUFFIX}"
    fi

    # 批量添加/更新共享目录配置（核心修复：使用转义后的目录路径）
    for dir in "${SHARE_DIRS[@]}"; do
        local escaped_dir=$(escape_dir "$dir")  # 转义目录中的 /
        # 替换配置模板中的{DIR}为实际目录
        local nfs_config=${NFS_CONFIG_TEMPLATE//\{DIR\}/$dir}
        # 若该目录已存在配置，先删除旧配置（避免重复）
        if grep -q "^${escaped_dir}" "$EXPORTS_FILE" 2>/dev/null; then
            log "目录 $dir 的NFS配置已存在，更新为最新配置"
            # sed命令使用转义后的目录，避免语法冲突
            sed -i "/^${escaped_dir}/d" "$EXPORTS_FILE" || error_exit "删除 $dir 的旧配置失败"
        fi
        # 写入新配置到/etc/exports
        echo "$nfs_config" >> "$EXPORTS_FILE" || error_exit "写入 $dir 的NFS配置失败"
        log "目录 $dir：配置已添加到 $EXPORTS_FILE"
    done

    ##########################################################################
    # 步骤4：启动NFS服务并设置开机自启（优化启动逻辑，解决启动失败问题）
    ##########################################################################
    log "步骤4/6：启动NFS服务并配置开机自启"
    case $OS_TYPE in
        centos)
            # CentOS：先启动rpcbind，再启动nfs-server
            systemctl enable --now rpcbind || error_exit "启动rpcbind服务失败"
            systemctl stop nfs-server || true
            sleep 2
            if systemctl start nfs-server; then
                systemctl enable nfs-server || log "警告：设置nfs-server开机自启失败"
                log "nfs-server服务启动成功"
            else
                log "nfs-server启动失败，详细日志："
                journalctl -xeu nfs-server | tail -20
                error_exit "nfs-server服务启动失败"
            fi
            ;;
        ubuntu|debian)
            # Ubuntu/Debian：使用修复函数启动服务（处理依赖、配置校验、残留清理）
            fix_ubuntu_nfs_start
            ;;
    esac

    # 验证服务是否启动成功
    local service_name=$([ "$OS_TYPE" = "centos" ] && echo "nfs-server" || echo "nfs-kernel-server")
    if ! is_service_running "$service_name"; then
        error_exit "$service_name服务启动失败，请检查系统日志（journalctl -u $service_name）"
    fi
    log "$service_name服务启动成功（已配置开机自启）"

    ##########################################################################
    # 步骤5：开放防火墙端口（企业环境必备，确保客户端能访问NFS服务）
    ##########################################################################
    log "步骤5/6：开放防火墙NFS相关端口"
    case $OS_TYPE in
        centos)
            # CentOS使用firewalld
            if is_service_running "firewalld"; then
                firewall-cmd --permanent --add-service=nfs || error_exit "开放NFS端口（2049）失败"
                firewall-cmd --permanent --add-service=rpc-bind || error_exit "开放rpc-bind端口（111）失败"
                firewall-cmd --permanent --add-service=mountd || error_exit "开放mountd端口失败"
                firewall-cmd --reload || error_exit "防火墙重载配置失败"
                log "Firewalld已开放NFS相关端口（2049、111等）"
            else
                log "Firewalld未运行，跳过端口开放（若后续客户端无法连接，请手动开放端口）"
            fi
            ;;
        ubuntu|debian)
            # Ubuntu/Debian使用ufw
            if is_service_running "ufw"; then
                ufw allow nfs || error_exit "开放NFS端口失败"
                ufw allow 111/tcp || error_exit "开放rpcbind端口（111）失败"
                ufw reload || error_exit "UFW重载配置失败"
                log "UFW已开放NFS端口（2049）和rpcbind端口（111）"
            else
                log "UFW未运行，跳过端口开放（若后续客户端无法连接，请手动开放端口）"
            fi
            ;;
    esac

    ##########################################################################
    # 步骤6：测试验证（每个共享目录都执行挂载+读写测试，确保可用）
    ##########################################################################
    log "步骤6/6：测试所有共享目录的可用性"
    for dir in "${SHARE_DIRS[@]}"; do
        # 为每个目录创建唯一的临时挂载点（替换/为_，避免路径冲突）
        local test_mount_dir="${TEST_MOUNT_DIR_PREFIX}_$(echo "$dir" | tr '/' '_')"
        # 创建临时挂载点
        mkdir -p "$test_mount_dir" || error_exit "创建测试挂载点 $test_mount_dir 失败"
        # 本地挂载测试（使用localhost模拟客户端）
        log "正在测试挂载：localhost:$dir -> $test_mount_dir"
        mount -t nfs localhost:"$dir" "$test_mount_dir" || error_exit "目录 $dir 挂载测试失败（NFS配置可能有误）"
        # 读写测试：创建测试文件并验证内容
        local test_file="${test_mount_dir}/nfs_test_${BACKUP_SUFFIX}.txt"
        echo "NFS测试内容：$(date) - 共享目录：$dir（root权限）" > "$test_file" || error_exit "目录 $dir 写入测试失败"
        if [ -f "$test_file" ] && grep -q "NFS测试内容" "$test_file"; then
            log "目录 $dir：读写测试成功（测试文件路径：$test_file）"
        else
            error_exit "目录 $dir：读写测试失败（文件创建或读取异常）"
        fi
        # 卸载临时挂载点并清理
        umount "$test_mount_dir" || error_exit "卸载测试挂载点 $test_mount_dir 失败"
        rm -rf "$test_mount_dir" || log "清理测试挂载点 $test_mount_dir 失败（可手动删除）"
    done

    ##########################################################################
    # 安装完成提示+常用维护命令
    ##########################################################################
    log "========================================"
    log "🎉 NFS服务安装完成！"
    log "📁 共享目录列表：${SHARE_DIRS[*]}"
    log "🔑 共享权限：全权限（读写+同步写入+保留root权限）"
    log "👤 目录属主：root:root（全程使用root权限）"
    log "⚙️  配置文件：$EXPORTS_FILE（备份文件：${EXPORTS_FILE}.bak.${BACKUP_SUFFIX}）"
    log "🖥️  客户端挂载命令：mount -t nfs 服务器IP:共享目录 本地目标目录"
    log "========================================"
    
    # 打印常用维护命令（用户要求）
    print_maintain_commands
}

##############################################################################
# 卸载NFS服务（支持多共享目录清理，中文步骤说明）
##############################################################################
uninstall_nfs() {
    log "开始卸载NFS服务端+客户端（系统：$OS_TYPE，清理目录：${SHARE_DIRS[*]}）"

    ##########################################################################
    # 步骤1：卸载所有NFS挂载点（避免服务删除时因挂载占用失败）
    ##########################################################################
    log "步骤1/5：卸载所有关联的NFS挂载点"
    for dir in "${SHARE_DIRS[@]}"; do
        local escaped_dir=$(escape_dir "$dir")  # 转义目录中的 /
        # 匹配所有挂载了该共享目录的挂载点（转义/避免正则匹配错误）
        local mount_points=$(mount | grep -E "nfs.*${escaped_dir}" | awk '{print $3}')
        if [ -n "$mount_points" ]; then
            for mp in $mount_points; do
                log "正在卸载挂载点（关联目录：$dir）：$mp"
                # -l：强制卸载（即使占用），-f：强制杀死占用进程
                umount -lf "$mp" || log "卸载 $mp 失败（可能已被卸载）"
            done
        fi
    done
    # 清理剩余的NFS挂载点（避免遗漏）
    local remaining_mounts=$(mount | grep -E "nfs|nfs4" | awk '{print $3}')
    if [ -n "$remaining_mounts" ]; then
        log "清理剩余的NFS挂载点：$remaining_mounts"
        for mp in $remaining_mounts; do
            umount -lf "$mp" || true
        done
    fi
    log "挂载点清理完成"

    ##########################################################################
    # 步骤2：停止NFS相关服务并禁用开机自启
    ##########################################################################
    log "步骤2/5：停止NFS相关服务"
    case $OS_TYPE in
        centos)
            systemctl stop nfs-server rpcbind || true
            systemctl disable nfs-server rpcbind || true
            ;;
        ubuntu|debian)
            systemctl stop nfs-kernel-server rpcbind || true
            systemctl disable nfs-kernel-server rpcbind || true
            ;;
    esac
    log "NFS相关服务已停止并禁用开机自启"

    ##########################################################################
    # 步骤3：清理/etc/exports配置（核心修复：使用转义后的目录路径）
    ##########################################################################
    log "步骤3/5：清理指定共享目录的NFS配置"
    if [ -f "$EXPORTS_FILE" ]; then
        for dir in "${SHARE_DIRS[@]}"; do
            local escaped_dir=$(escape_dir "$dir")  # 转义目录中的 /
            if grep -q "^${escaped_dir}" "$EXPORTS_FILE" 2>/dev/null; then
                # sed命令使用转义后的目录，避免语法冲突
                sed -i "/^${escaped_dir}/d" "$EXPORTS_FILE" || error_exit "清理 $dir 的NFS配置失败"
                log "已清理 $dir 的NFS配置"
            else
                log "$dir 的NFS配置不存在，跳过清理"
            fi
        done
        # 若配置文件为空，直接删除（避免残留空文件）
        if [ ! -s "$EXPORTS_FILE" ]; then
            rm -f "$EXPORTS_FILE" || log "删除空配置文件 $EXPORTS_FILE 失败"
            log "已删除空的NFS配置文件"
        fi
    else
        log "NFS配置文件 $EXPORTS_FILE 不存在，跳过清理"
    fi

    ##########################################################################
    # 步骤4：卸载NFS相关依赖包（彻底清理安装文件）
    ##########################################################################
    log "步骤4/5：卸载NFS相关依赖包"
    case $OS_TYPE in
        centos)
            yum remove -y nfs-utils rpcbind || error_exit "YUM卸载依赖包失败"
            ;;
        ubuntu|debian)
            apt purge -y nfs-kernel-server nfs-common rpcbind || error_exit "APT卸载依赖包失败"
            # 自动清理无用依赖
            apt autoremove -y >/dev/null 2>&1 || true
            ;;
    esac
    log "NFS相关依赖包已卸载完成"

    ##########################################################################
    # 步骤5：提示共享目录数据处理（默认保留，避免误删业务数据）
    ##########################################################################
    log "步骤5/5：共享目录数据处理提示"
    for dir in "${SHARE_DIRS[@]}"; do
        if [ -d "$dir" ]; then
            log "共享目录 $dir 仍存在（已保留其中数据，属主root:root），如需删除请执行：rm -rf '$dir'"
        else
            log "共享目录 $dir 不存在，跳过提示"
        fi
    done

    ##########################################################################
    # 卸载完成提示+常用维护命令（用户要求）
    ##########################################################################
    log "========================================"
    log "🗑️  NFS服务卸载完成！"
    log "✅ 已清理内容：NFS服务、指定共享目录配置、相关依赖包"
    log "⚠️  未清理内容：共享目录数据（需手动删除，避免误删重要文件）"
    log "📋 已清理的共享目录列表：${SHARE_DIRS[*]}"
    log "========================================"
    
    # 打印常用维护命令（用户要求）
    print_maintain_commands
}

##############################################################################
# 主逻辑（脚本入口）
##############################################################################
main() {
    check_root  # 先检查是否为root用户
    parse_args "$@"  # 解析命令行参数
    OS_TYPE=$(detect_os)  # 检测系统类型并保存（避免重复调用）

    # 根据操作类型执行对应函数
    case $ACTION in
        install)
            install_nfs
            ;;
        uninstall)
            uninstall_nfs
            ;;
        *)
            error_exit "无效操作类型：$ACTION（仅支持 install 或 uninstall）"
            ;;
    esac
}

# 启动主逻辑
main "$@"