#!/bin/bash
# check_docker_permissions.sh - 容器权限检测工具

# 配置参数
LOG_DIR="./docker_check"  # 定义日志目录的路径
TS=$(date +%Y%m%d_%H%M%S)  # 获取当前时间戳,格式为 YYYYMMDD_HHMMSS
LOG_FILE="${LOG_DIR}/check_${TS}.log"  # 定义日志文件的完整路径,包含时间戳

# 颜色定义
RED='\033[31m'; GREEN='\033[32m'; YELLOW='\033[33m'; CYAN='\033[36m'; RESET='\033[0m'  # 定义不同颜色的 ANSI 转义码,用于在终端输出不同颜色的文本

# 检查命令是否存在
check_command() {
    # 函数功能:检查指定的命令是否存在
    # 参数:$1 为要检查的命令名称
    local cmd=$1
    if ! command -v "$cmd" >/dev/null 2>&1; then
        # 如果命令不存在,输出错误信息并退出脚本,错误信息使用红色字体
        echo -e "${RED}错误: $cmd 命令未找到,请确保已安装。${RESET}" >&2
        exit 1
    fi
}

# 初始化日志
init_log() {
    # 函数功能:初始化日志,包括创建日志目录和日志文件,并记录检测开始时间
    check_command mkdir  # 检查 mkdir 命令是否存在
    check_command touch  # 检查 touch 命令是否存在
    check_command date  # 检查 date 命令是否存在

    if ! mkdir -p "$LOG_DIR"; then
        # 如果无法创建日志目录,输出错误信息并退出脚本,错误信息使用红色字体
        echo -e "${RED}错误: 无法创建日志目录 $LOG_DIR。${RESET}" >&2
        exit 1
    fi

    if ! touch "$LOG_FILE"; then
        # 如果无法创建日志文件,输出错误信息并退出脚本,错误信息使用红色字体
        echo -e "${RED}错误: 无法创建日志文件 $LOG_FILE。${RESET}" >&2
        exit 1
    fi

    # 输出检测开始时间,使用青色字体,并将信息同时输出到终端和日志文件
    echo -e "${CYAN}检测开始时间: $(date +'%Y-%m-%d %H:%M:%S')${RESET}\n" | tee -a "$LOG_FILE"
}

# 权限检测
check_volume_permission() {
    local container=$1
    check_command docker

    echo -e "\n${CYAN}持久化目录检测:${RESET}" | tee -a "$LOG_FILE"
    
    local mounts=()
    if ! docker inspect "$container" --format '{{range .Mounts}}{{.Source}}:{{.Destination}}{{"\n"}}{{end}}'| grep -v '^$' >/dev/null 2>&1; then
        echo -e "${RED}错误: 无法获取容器 $container 的挂载信息。${RESET}" >&2
        return 1
    fi

    # 创建一个临时文件
    temp_file=$(mktemp)

    # 将 docker inspect 的输出保存到临时文件中
    docker inspect "$container" --format '{{range .Mounts}}{{.Source}}:{{.Destination}}{{"\n"}}{{end}}' | grep -v '^$' > "$temp_file"

    # 使用 while 循环和 read 命令逐行读取临时文件内容并添加到数组中
    while IFS= read -r line; do
        mounts+=("$line")
    done < "$temp_file"

    # 删除临时文件
    rm "$temp_file"

    for i in "${!mounts[@]}"; do
        IFS=':' read -r host_path container_path <<< "${mounts[$i]}"
        
        echo -e "  ${YELLOW}[映射$((i+1))]${RESET}" | tee -a "$LOG_FILE"
        echo -e "  ├─ 宿主机路径: $host_path" | tee -a "$LOG_FILE"
        echo -e "  ├─ 容器内路径: $container_path" | tee -a "$LOG_FILE"
        
        # 获取目录或文件权限信息
        local permissions=$(docker exec "$container" ls -lhd "$container_path" 2>/dev/null)
        if [ -z "$permissions" ]; then
            echo -e "  ├─ 权限信息: ${RED}无法获取权限信息${RESET}" | tee -a "$LOG_FILE"
        else
            echo -e "  ├─ 权限信息: $permissions" | tee -a "$LOG_FILE"
        fi
        
        # 判断是文件还是目录
        local is_file=$(docker exec "$container" test -f "$container_path" && echo "true" || echo "false")
        if [ "$is_file" = "true" ]; then
            # 文件权限检测
            # 尝试读取文件以检测读取权限
            if docker exec "$container" cat "$container_path" &>/dev/null; then
                echo -e "  ├─ 读取权限: ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
            else
                echo -e "  ├─ 读取权限: ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
            fi
            # 尝试执行文件以检测执行权限
            if docker exec "$container" chmod +x "$container_path" &>/dev/null && docker exec "$container" "$container_path" &>/dev/null; then
                echo -e "  ├─ 执行权限: ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
            else
                echo -e "  ├─ 执行权限: ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
            fi
            # 尝试写入文件(追加)以检测写入权限
            if docker exec -i "$container" sh -c "echo 'test' >> $container_path" &>/dev/null; then
                echo -e "  ├─ 写入权限: ${GREEN}✓ 写入成功${RESET}" | tee -a "$LOG_FILE"
                # 恢复文件
                docker exec "$container" sed -i '$d' "$container_path" &>/dev/null
            else
                echo -e "  ├─ 写入权限: ${RED}✗ 拒绝写入${RESET}" | tee -a "$LOG_FILE"
            fi
        else
            # 目录权限检测
            local test_file="${container_path}/.perm_test_${RANDOM}"
            
            # 尝试创建文件以检测写入权限
            if docker exec "$container" touch "$test_file" &>/dev/null; then
                echo -e "  ├─ 写入权限: ${GREEN}✓ 写入成功${RESET}" | tee -a "$LOG_FILE"
                # 尝试读取文件以检测读取权限
                if docker exec "$container" cat "$test_file" &>/dev/null; then
                    echo -e "  ├─ 读取权限: ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
                else
                    echo -e "  ├─ 读取权限: ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
                fi
                # 尝试执行文件以检测执行权限
                if docker exec "$container" chmod +x "$test_file" &>/dev/null && docker exec "$container" "$test_file" &>/dev/null; then
                    echo -e "  ├─ 执行权限: ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
                else
                    echo -e "  ├─ 执行权限: ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
                fi
                # 清理测试文件
                if docker exec "$container" rm "$test_file" &>/dev/null; then
                    echo -e "  └─ 清理测试文件: ${GREEN}✓ 成功${RESET}" | tee -a "$LOG_FILE"
                else
                    echo -e "  └─ 清理测试文件: ${RED}✗ 失败${RESET}" | tee -a "$LOG_FILE"
                fi
            else
                echo -e "  ├─ 写入权限: ${RED}✗ 拒绝写入${RESET}" | tee -a "$LOG_FILE"
                # 尝试寻找已存在的文件进行读取和执行权限检测
                local existing_files=$(docker exec "$container" ls "$container_path" 2>/dev/null)
                if [ -z "$existing_files" ]; then
                    echo -e "  ├─ 目录为空,无法完成读取和执行权限检测。${RED}${RESET}" | tee -a "$LOG_FILE"
                else
                    for existing_file in $existing_files; do
                        # 尝试读取文件以检测读取权限
                        if docker exec "$container" cat "${container_path}/${existing_file}" &>/dev/null; then
                            echo -e "  ├─ 读取权限 (${existing_file}): ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
                        else
                            echo -e "  ├─ 读取权限 (${existing_file}): ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
                        fi
                        # 尝试执行文件以检测执行权限
                        if docker exec "$container" chmod +x "${container_path}/${existing_file}" &>/dev/null && docker exec "$container" "${container_path}/${existing_file}" &>/dev/null; then
                            echo -e "  ├─ 执行权限 (${existing_file}): ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
                        else
                            echo -e "  ├─ 执行权限 (${existing_file}): ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
                        fi
                    done
                fi
            fi
        fi
    done
}

# 主流程
init_log  # 初始化日志
check_command docker  # 检查 docker 命令是否存在

if [ -z "$1" ]; then
    # 如果没有提供参数,输出错误信息并退出脚本,错误信息使用红色字体
    echo -e "${RED}错误: 请提供 '容器名称' 或 'all' 作为参数。${RESET}" >&2
    exit 1
fi

if [ "$1" = "all" ]; then
    # 如果参数为 all,获取所有运行容器的 ID和名称,以列表展示
	container_names=$(docker ps|awk '{print $1,$NF}')
	echo -e "\n${CYAN}=== 容器列表 ===${CYAN}" | tee -a "$LOG_FILE"
	echo -e "${CYAN}${GREEN}${container_names}${CYAN}" | tee -a "$LOG_FILE"
	
	# 如果参数为 all,获取所有运行容器的 ID
    container_ids=$(docker ps -q)
    if [ -z "$container_ids" ]; then
        # 如果没有运行的容器,输出错误信息并退出脚本,错误信息使用红色字体
        echo -e "${RED}错误: 没有运行的容器。${RESET}" >&2
        exit 1
    fi
    for container_id in $container_ids; do
        # 获取容器的名称
        container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's|^/||')
        # 输出检测容器信息,使用青色和绿色字体,并将信息同时输出到终端和日志文件
        echo -e "\n${CYAN}=== 检测容器: ${GREEN}${container_name}${CYAN} (${container_id:0:12}) ===${RESET}" | tee -a "$LOG_FILE"

        ## 系统信息
        #echo -e "${CYAN}[系统信息]" | tee -a "$LOG_FILE"
        #if ! docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release" >/dev/null 2>&1; then
        #    # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
        #    echo -e "${RED}错误: 无法获取容器 $container_id 的操作系统版本信息。${RESET}" >&2
        #else
        #    # 输出容器的操作系统信息,并将信息同时输出到终端和日志文件
		#	docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release"|awk -F\" '{print $2}'| tee -a "$LOG_FILE"
        #fi

        ## 用户信息
        #echo -e "${CYAN}[系统信息]" | tee -a "$LOG_FILE"
        #if ! docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release" >/dev/null 2>&1; then
        #    # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
        #    echo -e "${RED}错误: 无法获取容器 $container_id 的操作系统版本信息。${RESET}" >&2
        #else
        #    # 输出容器的操作系统信息,并将信息同时输出到终端和日志文件
		#	docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release"|awk -F\" '{print $2}'| tee -a "$LOG_FILE"
        #fi

        # 进程检测
        echo -e "${CYAN}[进程信息]" | tee -a "$LOG_FILE"
        if ! docker top "$container_id" >/dev/null 2>&1; then
            # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
            echo -e "${RED}错误: 无法获取容器 $container_id 的进程信息。${RESET}" >&2
        else
            # 格式化输出容器的进程信息,并将信息同时输出到终端和日志文件
			docker top "$container_id" |awk -F"            " '{print $1,$2,$3,$NF}'| tee -a "$LOG_FILE"
        fi
        check_volume_permission "$container_id"  # 检测容器的持久化目录权限
    done
else
    # 如果参数不是 all,获取指定容器的 ID
    container_id=$(docker ps -q -f "name=$1")
    if [ -z "$container_id" ]; then
        # 如果未找到指定的容器,输出错误信息并退出脚本,错误信息使用红色字体
        echo -e "${RED}错误: 未找到名称为 $1 的运行容器。${RESET}" >&2
        exit 1
    fi
    # 获取容器的名称
    container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's|^/||')
    # 输出检测容器信息,使用青色和绿色字体,并将信息同时输出到终端和日志文件
    echo -e "\n${CYAN}=== 检测容器: ${GREEN}${container_name}${CYAN} (${container_id:0:12}) ===${RESET}" | tee -a "$LOG_FILE"

    # 进程检测
    echo -e "${CYAN}[进程信息]" | tee -a "$LOG_FILE"
    if ! docker top "$container_id" >/dev/null 2>&1; then
        # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
        echo -e "${RED}错误: 无法获取容器 $container_id 的进程信息。${RESET}" >&2
    else
        # 格式化输出容器的进程信息,并将信息同时输出到终端和日志文件
        docker top "$container_id" |awk -F"            " '{print $1,$2,$3,$NF}' | tee -a "$LOG_FILE"
    fi

    check_volume_permission "$container_id"  # 检测容器的持久化目录权限
fi

# 输出检测日志的保存路径,使用青色和黄色字体,并将信息同时输出到终端和日志文件
echo -e "\n${CYAN}检测日志已保存至: ${YELLOW}${LOG_FILE}${RESET}" | tee -a "$LOG_FILE"s