#!/bin/bash

# 公共日志函数，由调用方设置 WRAPPER_TAG 变量区分来源
hostname=${hostname:-$(hostname)}

log() {
    timestamp=$(date -Iseconds)
    logger -t "${WRAPPER_TAG}" "${timestamp} ${hostname} ${WRAPPER_TAG}: $1"
}

ostree_path="/ostree/deploy/kylin/deploy/"
dpkg_status_file="/var/lib/dpkg/status"

get_ostree_main_node(){
    local main_node
    main_node=$(ostree admin status | grep ^\* | awk '{print $3}')
    echo $main_node
}

get_dpkg_status_pkg_list(){
    local pkg_list
    local status_file=$1
    pkg_list=$(awk '/^Package:/ {print $2}' $status_file)
    echo $pkg_list
}

get_ostree_main_node_pkg_list(){
    local main_node
    local pkg_list
    main_node=$(get_ostree_main_node)
    if [ -n "$main_node" ]; then
        pkg_list=$(get_dpkg_status_pkg_list "${ostree_path}/${main_node}/${dpkg_status_file}")
        echo $pkg_list
    else
        return 1
    fi
}

pkg_is_in_ostree(){
    core_pkg_list=$(get_ostree_main_node_pkg_list)
    for pkg in ${core_pkg_list[@]}; do
        if [[ "$pkg" == "$package_name" ]]; then
            log "包 ${package_name} 预集成"
            return 0
        fi
    done
    return 1
}

# 判断是否在 Live Build 环境
is_live_build() {
    if [ -n "$LIVE_BUILD" ] || [ -n "$LB_BUILD_IMAGE" ]; then
        log "检测到Live Build环境: 存在Live Build环境变量"
        return 0
    fi

    if env | grep -Eq '^(LB_|LIVE_BUILD=)'; then
        log "检测到Live Build环境: 存在live-build配置环境变量"
        return 0
    fi

    local pid
    local comm
    local cmdline
    pid=$PPID
    while [ -n "$pid" ] && [ "$pid" -gt 1 ]; do
        comm=$(cat "/proc/$pid/comm" 2>/dev/null)
        cmdline=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)

        case "$comm $cmdline" in
            *live-build*|*"/lb "*|*" lb "*|*"/lb-"*|*" lb-"*)
                log "检测到Live Build环境: 父进程链包含live-build/lb"
                return 0
                ;;
        esac

        pid=$(awk '/^PPid:/ {print $2}' "/proc/$pid/status" 2>/dev/null)
    done

    return 1
}

# 判断是否为 Live CD 环境
is_live_cd() {
    if grep -Eq '(^| )(boot=casper|boot=live)( |$)' /proc/cmdline 2>/dev/null; then
        log "检测到Live CD环境: 内核参数包含boot=casper或boot=live"
        return 0
    fi

    return 1
}

is_normal_mode() {
    boot_mode=$(gdbus call --system \
        -d com.kylin.MaintainMode \
        -o /com/kylin/MaintainMode/Object \
        -m com.kylin.MaintainMode.interface.GetBootMode 2>/dev/null | awk -F\' '{print $2}' 2>/dev/null)

    if [ "$boot_mode" = "normal" ]; then
        log "检测到normal模式"
        return 0
    fi
    
    log "当前不是normal模式: $boot_mode"
    return 1
}

# 判断是否在 unshare -m 创建的挂载命名空间中
is_unshare_mount_ns() {
    if [ ! -e "/proc/self/ns/mnt" ] || [ ! -e "/proc/1/ns/mnt" ]; then
        return 1
    fi
    
    current_mnt_ns=$(readlink /proc/self/ns/mnt 2>/dev/null)
    init_mnt_ns=$(readlink /proc/1/ns/mnt 2>/dev/null)
    
    if [ -z "$current_mnt_ns" ] || [ -z "$init_mnt_ns" ]; then
        return 1
    fi
    
    if [ "$current_mnt_ns" != "$init_mnt_ns" ]; then
        log "检测到unshare -m挂载命名空间: 当前命名空间($current_mnt_ns) != init命名空间($init_mnt_ns)"
        return 0
    fi
    
    return 1
}

run_by_root()
{
    if [ $EUID -ne 0 ]; then
        echo "请使用 root 用户执行命令"
        exit 1
    fi
}

# 检查是否应该执行操作
should_execute_operation() {
    # 如果在unshare空间，执行原生命令
    if is_unshare_mount_ns; then
        log "检测到unshare -m命名空间，执行原生命令"
        return 1
    fi

    # 如果不是Live Build环境 且 不是chroot环境 且 不是Live CD环境 且 是normal模式
    if ! is_live_build && ! ischroot && ! is_live_cd && is_normal_mode; then
        log "满足所有条件: 非Live Build、非chroot、非Live CD、normal模式"
        return 0
    fi
    
    # 记录不满足的具体条件
    if is_live_build; then
        log "不满足条件: 当前是Live Build环境"
    fi
    
    if ischroot; then
        log "不满足条件: 当前是chroot环境"
    fi
    
    if is_live_cd; then
        log "不满足条件: 当前是Live CD环境"
    fi
    
    if ! is_normal_mode; then
        log "不满足条件: 当前不是normal模式"
    fi
    
    return 1
}

tmpdir="/tmp/sdk/ar-deb"

init_tmpdir()
{
    del_tmpdir
    mkdir -p $tmpdir
}

del_tmpdir()
{
    rm -rf $tmpdir
}

# whitelist_runtime_check开关是否开启
get_whitelist_runtime_check()
{
    local res
    local return_code
    res=$(kconf2 get libkysdk-package.installation whitelist_runtime_check)
    return_code=$?
    echo $res
    return $return_code
}

# privilege_runtime_check开关是否开启
get_privilege_runtime_check()
{
    local res
    local return_code
    res=$(kconf2 get libkysdk-package.installation privilege_runtime_check)
    return_code=$?
    echo $res
    return $return_code
}

# 获取_compat字段的值
# 传入解压包的路径,字段名
get_compat_field_value()
{
    local unzip_path=$1
    local field=$2
    local file_name=${unzip_path}"/_compat"
    local runtime_version=''
    mapfile -t lines < $file_name

    for line in "${lines[@]}";do
        if [[ ${line%:*} == $field ]];then
            # 获取冒号后的值
            runtime_version=${line#*:}
        fi
    done
    echo $runtime_version
}

# 解压deb至tmp/sdk/ar-deb
uncompress_deb_to_tmp()
{
    local deb=$1
    local return_code
    ar -x $deb --output $tmpdir
    return_code=$?
    return $return_code
}

# 获取data.tar.**文件在deb包里所占的长度与偏移量
get_data_size_in_deb()
{
    local deb=$1
    local size
    local return_code
    size=$(ar -tvO $deb | grep data.tar | awk '{print $3+$9}')
    return_code=$?
    echo $size
    return $return_code
}

# 导入gpg秘钥
import_gpg_key()
{
    GPGTMP=$(mktemp -d)
    export GNUPGHOME="$GPGTMP"
    gpg --import /usr/share/keyrings/deb-sign/* &> /dev/null
}

# 恢复gpghome
unset_gpg()
{
    unset GNUPGHOME
}

# 验签
gpg_verify()
{
    sign=$1
    plain=$2
    local return_code
    gpg --verify $sign $plain &> /dev/null
    return_code=$?
    return $return_code
}

# 获取deb包sha512的值
get_deb_sha512sum()
{
    size=$1
    deb=$2
    local file_sha512sum=$(head -c $size $deb | sha512sum - | awk '{print $1}')
    echo $file_sha512sum
}

# 查询软件包是否在白名单中
is_in_white_list() {
    array=($(kconf2 list-keys libkysdk-package.direct_install_whitelist))
    for group in ${array[@]}; do
        output=$(kconf2 get libkysdk-package.direct_install_whitelist ${group})
        set -f
        pkgname=($(echo "$output" | sed 's/\[//;s/\]//;s/"//g' | tr ',' ' '))
        for pkg in ${pkgname[@]}; do
            if [[ $1 == $pkg ]]; then
                log "$1 是特权包!!"
                return 0
            fi
        done
        set +f
    done
    log "$1 不是特权包!!"
    return 1
}

is_in_privilege_list() {
    output=$(kconf2 get libkysdk-package.bare_metal pkg-list)
    set -f
    pkgname=($(echo "$output" | sed 's/\[//;s/\]//;s/"//g' | tr ',' ' '))
    for pkg in ${pkgname[@]}; do
        if [[ $1 == $pkg ]]; then
            log "$1 是特权包!!\n"
            return 0
        fi
    done
    set +f
    log "$1 不是特权包!!\n"
    return 1
}

# 添加包到特权列表
add_to_privilege_list() {
    local new_pkg="$1"
    if [ -z "$new_pkg" ]; then
        log "错误: 包名不能为空"
        return 1
    fi

    # 获取当前特权包列表
    local current_list
    current_list=$(kconf2 get libkysdk-package.bare_metal pkg-list 2>/dev/null)
    if [ $? -ne 0 ]; then
        log "错误: 无法获取当前特权包列表"
        return 1
    fi

    # 如果当前列表为空，初始化为包含新包的列表
    if [ -z "$current_list" ] || [ "$current_list" = "[]" ]; then
        local updated_list="['$new_pkg']"
    else
        # 从字符串格式转换为数组，处理现有的列表格式
        # 假设当前列表格式为 ["pkg1","pkg2"]
        set -f
        local cleaned_list=$(echo "$current_list" | sed "s/\[//;s/\]//;s/\"//g;s/'//g")
        local existing_pkgs=($(echo "$cleaned_list" | tr ',' ' '))

        # 检查包是否已经存在
        for pkg in "${existing_pkgs[@]}"; do
            if [ "$pkg" = "$new_pkg" ]; then
                log "包 $new_pkg 已经在特权列表中"
                return 0
            fi
        done

        # 构造新的列表，添加新包
        local updated_list="["
        local first=true
        for pkg in "${existing_pkgs[@]}"; do
            if [ "$first" = true ]; then
                updated_list="${updated_list}'$pkg'"
                first=false
            else
                updated_list="${updated_list},'$pkg'"
            fi
        done
        set +f
        # 添加新包
        if [ "$first" = false ]; then
            updated_list="${updated_list},'$new_pkg'"
        else
            updated_list="${updated_list}'$new_pkg'"
        fi
        updated_list="${updated_list}]"
    fi

    # 设置更新后的列表
    if kconf2 set libkysdk-package.bare_metal pkg-list "$updated_list"; then
        log "成功将 $new_pkg 添加到特权列表"
        return 0
    else
        log "错误: 无法设置更新后的特权列表"
        return 1
    fi
}

is_package_installed_in_sys()
{
    local deb_file="$1"
    
    if [ ! -f "$deb_file" ]; then
        log "错误: deb 文件不存在 $deb_file"
        return 1
    fi
    
    local package_name=$(dpkg-deb -f "$deb_file" Package)
    local version=$(dpkg-deb -f "$deb_file" Version)
    
    if [ -z "$package_name" ] || [ -z "$version" ]; then
        log "错误: 无法获取包名或版本"
        return 1
    fi
    
    local search_result=$(dpkg.orig -l | grep "^ii[[:space:]]\+${package_name}[[:space:]]" 2>/dev/null)
    
    if [ -z "$search_result" ]; then
        log "包 ${package_name} 未安装"
        return 1
    fi
    
    local installed_version=$(echo "$search_result" | head -n1 | awk '{print $3}')
    
    if [ "$version" = "$installed_version" ]; then
        log "包 ${package_name} 已安装且版本一致 (${version})"
        return 0
    else
        log "包 ${package_name} 版本不一致：期望 ${version}, 已安装 ${installed_version}"
        return 1
    fi
}

whitelist_deb_check()
{
    local deb_abs_path=$1
    local switch_status
    local switch_return
    switch_status=$(get_whitelist_runtime_check)
    switch_return=$?
    if [ $switch_return -eq 0 ];then
        if [[ $switch_status == 'true' ]];then
            init_tmpdir
            uncompress_deb_to_tmp ${deb_abs_path}
            local uncompress_status
            uncompress_status=$?
            if [ $uncompress_status -eq 0 ];then
                local runtime_version=$(get_compat_field_value $tmpdir runtime_version)
                if [[ $runtime_version != *v11* ]] && [[ $runtime_version != *ok2* ]];then
                    echo "软件包标签匹配失败"
                    del_tmpdir
                    return 1
                fi
            else
                echo "解压软件包失败"
                del_tmpdir
                return 1
            fi
            del_tmpdir
        fi
    else
        echo "获取白名单开关失败"
        return 1
    fi
    return 0
}

privilege_deb_check()
{
    local deb_abs_path=$1
    local switch_status
    local switch_return
    switch_status=$(get_privilege_runtime_check)
    switch_return=$?
    if [ $switch_return -eq 0 ];then
        if [[ $switch_status == 'true' ]];then
            init_tmpdir
            uncompress_deb_to_tmp ${deb_abs_path}
            local uncompress_status
            uncompress_status=$?
            if [ $uncompress_status -eq 0 ];then
                local runtime_version=$(get_compat_field_value $tmpdir runtime_version)
                if [[ $runtime_version == *v11* ]] || [[ $runtime_version == *ok2* ]];then
                    # 验签
                    local bare_metal=$(get_compat_field_value $tmpdir bare_metal)
                    if [[ $bare_metal == "yes" ]];then
                        local plaintxt="${tmpdir}/plain.txt"
                        local signtxt="${tmpdir}/sign.txt"
                        local signtxtsig="${signtxt}.sig"
                        local data_size=$(get_data_size_in_deb ${deb_abs_path})
                        local deb_sha512=$(get_deb_sha512sum $data_size $deb_abs_path)
                        echo -n "${deb_sha512}${bare_metal}" > $plaintxt
                        local bare_metal_sig=$(get_compat_field_value $tmpdir bare_metal_sig)
                        echo -n ${bare_metal_sig} > $signtxt
                        base64 -d $signtxt > $signtxtsig
                        import_gpg_key
                        gpg_verify $signtxtsig $plaintxt
                        verify_status=$?
                        if [ $verify_status -ne 0 ];then
                            echo gpg验签失败
                            unset_gpg
                            return 1
                        fi
                        unset_gpg
                    else
                        echo "软件包内无标签"
                        return 1
                    fi
                else
                    echo "软件包标签匹配失败"
                    del_tmpdir
                    return 1
                fi
            else
                echo "解压软件包失败"
                del_tmpdir
                return 1
            fi
            del_tmpdir
        fi
    else
        echo "获取特权标签开关失败"
        return 1
    fi
    return 0
}

deb_install_check()
{
    local pkg_name=$1
    local pkg_abs_path=$2
    if ! kylinsigntool -v $pkg_abs_path > /dev/null;then
        echo "麒麟签名验证不通过，安装失败"
        return 1
    fi

    # 区分白名单包和特权标签包
    if is_in_white_list ${pkg_name};then
        local check_res
        local check_status
        check_res=$(whitelist_deb_check $pkg_abs_path)
        check_status=$?
        if [ $check_status -ne 0 ];then
            echo $check_res
            return 1
        fi
    else
        # 特权标签包
        local check_res
        local check_status
        check_res=$(privilege_deb_check $pkg_abs_path)
        check_status=$?
        if [ $check_status -ne 0 ];then
            echo $check_res
            return 1
        fi
    fi
    return 0
}

is_package_installed_in_kare()
{
    local deb_file="$1"
    
    if [ ! -f "$deb_file" ]; then
        log "错误: deb 文件不存在 $deb_file"
        return 1
    fi
    
    local package_name=$(dpkg-deb -f "$deb_file" Package)
    local version=$(dpkg-deb -f "$deb_file" Version)
    
    if [ -z "$package_name" ] || [ -z "$version" ]; then
        log "错误: 无法获取包名或版本"
        return 1
    fi
    
    local search_result=$(kare -l | awk -v pkg="$package_name" '$1 == pkg {print; exit}')

    if [ -z "$search_result" ]; then
        log "包 ${package_name} 未安装"
        return 1
    fi

    local installed_version=$(echo "$search_result" | awk '{print $2}')
    
    if [ "$version" = "$installed_version" ]; then
        log "包 ${package_name} 已安装且版本一致 (${version})"
        return 0
    else
        log "包 ${package_name} 版本不一致：期望 ${version}, 已安装 ${installed_version}"
        return 1
    fi
}

# 成功搜索返回0 否则返回1
# 用户模式下dpkg -l结果由kypkg_list提供
# 返回软件包在系统内信息
pkg_in_sys()
{
    package_name=$1
    field=$2
    res=`dpkg -l | grep "^ii[[:space:]]\+${package_name}[[:space:]]"`
    return_code=$?
    # file 为version返回$3,为env返回$4
    # 如果为kare软件包 $4的值为 kare_v10sp1或 kare_shadow
    # 这两个值由sdk接口提供
    if [[ ${field} == 'version' ]];then
        echo $res | awk '{print $3}'
    elif [[ ${field} == 'env' ]];then
        echo $res | awk '{print $4}'
    fi
    return $return_code
}
