#!/bin/bash
#============================================================
# CentOS 7 Swap 一键管理脚本
# 功能：创建 / 清空回内存 / 调 swappiness / 禁用 / 状态查看
# 用法：
#   ./swap-manager.sh              # 交互式菜单
#   ./swap-manager.sh create       # 创建 4G swap
#   ./swap-manager.sh create 8G    # 创建指定大小 swap
#   ./swap-manager.sh flush        # 把 swap 内容全部换回物理内存
#   ./swap-manager.sh swappiness 0 # 设置 swappiness（0-100）
#   ./swap-manager.sh disable      # 永久禁用 swap
#   ./swap-manager.sh status       # 查看内存与 swap 状态
#============================================================
set -e

SWAP_FILE="/swapfile"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

info()  { echo -e "${GREEN}[INFO]${NC} $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }

# ---------- 状态查看 ----------
do_status() {
    echo "==================== Swap / 内存状态 ===================="
    swapon --show 2>/dev/null || echo "未启用任何 swap"
    echo ""
    free -h
    echo ""
    echo "当前 swappiness: $(cat /proc/sys/vm/swappiness)"
    echo "=========================================================="
}

# ---------- 检测文件系统 ----------
get_fs_type() {
    local TARGET_DIR
    TARGET_DIR=$(dirname "$SWAP_FILE")
    df -T "$TARGET_DIR" 2>/dev/null | awk 'NR==2{print $2}' | tr '[:upper:]' '[:lower:]'
}

# ---------- 用 dd 创建 swap 文件（XFS 兼容） ----------
create_with_dd() {
    local SIZE="$1"
    local COUNT
    COUNT=$(echo "$SIZE" | sed 's/[Gg]//')
    warn "使用 dd 创建（XFS 兼容模式，较慢请稍候）..."
    dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$((COUNT * 1024)) status=progress
}

# ---------- 创建 swap ----------
do_create() {
    local SIZE="${1:-4G}"
    local FS_TYPE
    info "开始创建 ${SIZE} swap 文件: ${SWAP_FILE}"

    if swapon --show | grep -q "$SWAP_FILE"; then
        warn "swap 已存在并启用，跳过创建。"
        do_status
        return 0
    fi

    if [ -f "$SWAP_FILE" ]; then
        warn "检测到旧的 ${SWAP_FILE}，将删除重建。"
        rm -f "$SWAP_FILE"
    fi

    # 检测文件系统：XFS 必须用 dd，fallocate 创建的文件 swapon 会报 Cannot allocate memory
    FS_TYPE=$(get_fs_type)
    info "根分区文件系统: ${FS_TYPE:-unknown}"

    if [ "$FS_TYPE" = "xfs" ]; then
        create_with_dd "$SIZE"
    else
        # ext4/ext3 等优先 fallocate（秒级），失败自动回退 dd
        if fallocate -l "$SIZE" "$SWAP_FILE" 2>/dev/null; then
            info "fallocate 创建完成。"
        else
            create_with_dd "$SIZE"
        fi
    fi

    chmod 600 "$SWAP_FILE"
    mkswap "$SWAP_FILE"

    # swapon 失败时自动清理并用 dd 重建（兜底）
    if ! swapon "$SWAP_FILE" 2>/dev/null; then
        warn "swapon 失败，自动切换 dd 模式重建..."
        swapoff "$SWAP_FILE" 2>/dev/null || true
        rm -f "$SWAP_FILE"
        create_with_dd "$SIZE"
        chmod 600 "$SWAP_FILE"
        mkswap "$SWAP_FILE"
        swapon "$SWAP_FILE"
    fi

    # 开机自启（去重写入）
    grep -q "$SWAP_FILE" /etc/fstab || echo "$SWAP_FILE swap swap defaults 0 0" >> /etc/fstab

    info "swap 创建并启用成功，已写入 /etc/fstab 开机自启。"
    do_status
}

# ---------- 把 swap 内容换回物理内存 ----------
do_flush() {
    local SWAP_USED MEM_AVAIL
    SWAP_USED=$(free -b | awk '/Swap:/{print $3}')
    MEM_AVAIL=$(free -b | awk '/Mem:/{print $7}')

    if [ "$SWAP_USED" -eq 0 ]; then
        info "swap 当前未使用，无需清空。"
        return 0
    fi

    if [ "$MEM_AVAIL" -lt "$SWAP_USED" ]; then
        error "物理内存不足！可用: $((MEM_AVAIL/1024/1024))MB，swap已用: $((SWAP_USED/1024/1024))MB，无法安全换回。"
    fi

    warn "正在将 swap 内容全部换回物理内存（请勿中断）..."
    swapoff -a
    swapon -a
    info "swap 已清空并重新启用。"
    do_status
}

# ---------- 设置 swappiness ----------
do_swappiness() {
    local VAL="${1:-10}"
    if ! [[ "$VAL" =~ ^[0-9]+$ ]] || [ "$VAL" -gt 100 ]; then
        error "swappiness 必须是 0-100 的整数。"
    fi

    sysctl vm.swappiness="$VAL"
    if grep -q "vm.swappiness" /etc/sysctl.conf; then
        sed -i "s/^vm.swappiness.*/vm.swappiness=$VAL/" /etc/sysctl.conf
    else
        echo "vm.swappiness=$VAL" >> /etc/sysctl.conf
    fi
    info "swappiness 已设置为 $VAL 并持久化。"
}

# ---------- 禁用 swap ----------
do_disable() {
    warn "即将永久禁用 swap 并删除 ${SWAP_FILE}！"
    read -rp "确认继续？(yes/no): " CONFIRM
    [ "$CONFIRM" = "yes" ] || { info "已取消。"; exit 0; }

    swapoff -a
    sed -i "/${SWAP_FILE//\//\\/}/d" /etc/fstab
    sed -i '/swap/d' /etc/fstab
    rm -f "$SWAP_FILE"
    systemctl mask swap.target 2>/dev/null || true

    info "swap 已永久禁用并删除。"
    do_status
}

# ---------- 交互式菜单 ----------
show_menu() {
    echo ""
    echo "============== CentOS 7 Swap 管理工具 =============="
    echo "  1) 创建 4G swap（默认）"
    echo "  2) 创建指定大小 swap"
    echo "  3) 把 swap 内容换回物理内存"
    echo "  4) 设置 swappiness（0=尽量不用swap，10=推荐）"
    echo "  5) 永久禁用 swap"
    echo "  6) 查看状态"
    echo "  0) 退出"
    echo "======================================================"
    read -rp "请选择 [0-6]: " CHOICE

    case "$CHOICE" in
        1) do_create 4G ;;
        2) read -rp "输入大小（如 4G / 8G / 16G）: " SZ; do_create "$SZ" ;;
        3) do_flush ;;
        4) read -rp "输入 swappiness 值 (0-100，推荐10): " SV; do_swappiness "$SV" ;;
        5) do_disable ;;
        6) do_status ;;
        0) exit 0 ;;
        *) error "无效选择。" ;;
    esac
}

# ---------- 主入口 ----------
[ "$(id -u)" -eq 0 ] || error "请用 root 运行此脚本。"

case "${1:-}" in
    create)     do_create "${2:-4G}" ;;
    flush)      do_flush ;;
    swappiness) do_swappiness "${2:-10}" ;;
    disable)    do_disable ;;
    status)     do_status ;;
    "")         show_menu ;;
    *)          error "未知命令: $1。可用: create|flush|swappiness|disable|status" ;;
esac
