#! /bin/bash

#===============================================================================
# 启动指令流云手机分为两种启动方式：
# （1）推包形式
# 步骤一：./cfct run ${镜像名称:tag} ${kbox_id} mesa
# 步骤二：./cfct push InstructionPhoneAndroidPServer.tar.gz kbox_${kbox_id}
# （2）docker制作镜像形式
# ./cfct run ${镜像名称:tag} ${kbox_id} docker
#===============================================================================

MYNAME=$(basename $0)
THISDIR=$(readlink -ef $(dirname $0))

DEBUG=0
dockerStorage=$(docker info|grep 'Docker Root Dir:'|awk '{print $NF}');

DOCKERCMD=docker
DOCKER_WORKSPACE=${ARMNATIVE_DOCKER_WORESPACE:-"$dockerStorage/workspace"}
DOCKER_NAME_PREFIX="kbox_"  # must end with "_"
DOCKER_SUBNET=172.18.0.0/16
DOCKER_IP_PREFIX=${DOCKER_SUBNET%.*}.  # must end with "."

USER_DATA_DIR=/home/mount
BASE_DATA=instruction_data_base
BASE_DATA_DIR=${USER_DATA_DIR}/data/$BASE_DATA/data

#===============================================================================
# Functions
#===============================================================================
LOG_DEBUG() {
    [ "$DEBUG" -eq 1 ] && echo "[DEBUG] $@"
}

LOG_TRACE() {
    echo -e "$@"
}

LOG_INFO() {
    echo -e "\033[1;36m[INFO] $@\033[0m"
}

LOG_WARN() {
    echo -e "\033[1;32m[WARNING] $@\033[0m"
}

LOG_ERROR() {
    echo -e "\033[1;31m[ERROR] $@\033[0m"
}

EXIT_ERROR() {
    LOG_ERROR "$@" ; exit 1
}

param_check_container_id() {
    local containerid=$1
    local name=${DOCKER_NAME_PREFIX}${containerid}

    [ -z "$containerid" ] && EXIT_ERROR "Please specify the container ID"
    echo "$containerid" | grep -q '^[0-9]\+$' || EXIT_ERROR "Container ID should be integer"
    [ "$containerid" -lt 1 -o "$containerid" -gt 250 ] && \
        EXIT_ERROR "Please select the container ID between [1, 250]"
    $DOCKERCMD ps -a | grep -q "$name" && \
        EXIT_ERROR "This container id $containerid is in use."
    return 0
}

param_check_container_indexs() {
    local start=$1
    local end=$2

    [ -z "$start" ] && EXIT_ERROR "Empty container name startID"
    echo "$start" | grep -q '^[0-9]\+$' || EXIT_ERROR "startID should be integer"
    [ "$start" -lt 1 -o "$start" -gt 250 ] && \
        EXIT_ERROR "Please select the startID between [1, 250]"

    [ -z "$end" ] && EXIT_ERROR "Empty container name endID"
    echo "$end" | grep -q '^[0-9]\+$' || EXIT_ERROR "endID should be integer"
    [ "$end" -lt 1 -o "$end" -gt 250 ] && \
        EXIT_ERROR "Please select the endID between [1, 250]"

    [ "$start" -gt "$end" ] && EXIT_ERROR "startID should not be greater than endID"

    return 0
}

param_check_container_name() {
    local container_name=$1
    local status=$2
    local opts="-a"

    [ "$status" == "RUNNING" ] && opts=""

    if [ -z "$container_name" ] ; then
        LOG_ERROR "Container name is not specified!"
    elif ! echo "$container_name" | grep -q "$DOCKER_NAME_PREFIX" ; then
        LOG_ERROR "Container name does not start with '$DOCKER_NAME_PREFIX'"
    elif ! $DOCKERCMD ps $opts | grep -w -q "$container_name" ; then
        if [ "$status" == "RUNNING" ] ; then
            LOG_ERROR "Container named $container_name is not running!"
        else
            LOG_ERROR "Container named $container_name doesn't exist!"
        fi
    else
        return 0
    fi

    LOG_ERROR "Please specify container name below"
    $DOCKERCMD ps $opts
    exit 1
}

param_check_image_id_or_name() {
    local image=$1
    local name=${image%:*}
    local ver=${image#*:}

    if [ -z "$image" ] ; then
        LOG_ERROR "Container image ID is not specified!"
    elif ! $DOCKERCMD images | grep -q "$name" ; then
        LOG_ERROR "Container image $image doesn't exist"
    elif ! $DOCKERCMD images | grep -q "$ver" ; then
        LOG_ERROR "Container image $image doesn't exist"
    else
        return 0
    fi

    LOG_ERROR "Please select the image below"
    $DOCKERCMD images
    exit 1
}

docker_port_map() {
    local name=$1
    local start=$1
    local end=$2
    local id ip port name

    if [ -n "$end" ] ; then
        param_check_container_indexs "$start" "$end"
    else
        param_check_container_name $name
        start="${name#$DOCKER_NAME_PREFIX}"
        end=start
    fi

    for((id=$start; id<=$end; id++)) ; do
        ip=$(($id+1))
        port=$(($id*2+8886))
        name=${DOCKER_NAME_PREFIX}${id}
        if ps -ef | grep -v grep| grep -q "TCP4-LISTEN:$port" ; then
            LOG_DEBUG "port map for $name:$port has already existed"
        else
            LOG_INFO "Add port map $port -> ${DOCKER_IP_PREFIX}$ip:8888"
            legacy_port1=$(ps -ef|grep -w socat|grep -w $port|awk '{print $2}')
            legacy_port2=$(ps -ef|grep -w socat|grep -w $(($port+10000))|awk '{print $2}')
            if [ "x$legacy_port1" != "x" ] ; then
                kill -9 $legacy_port1
            fi
            if [ "x$legacy_port2" != "x" ] ; then
                kill -9 $legacy_port2
            fi
            socat TCP4-LISTEN:$port,reuseaddr,fork TCP4:${DOCKER_IP_PREFIX}$ip:8888 &
            socat TCP4-LISTEN:$(($port+10000)),reuseaddr,fork TCP4:${DOCKER_IP_PREFIX}$ip:5555 &
        fi
    done
}

docker_getNetwork() {
    local netName=$DOCKER_NET_NAME
    local subNet=$DOCKER_SUBNET
    local net

    net=$($DOCKERCMD network inspect $netName 2>/dev/null|grep Subnet|awk '{print $NF}'|tr -d '"')
    if [ ! "$net" ]
    then
        $DOCKERCMD network create --subnet=$subNet $netName >/dev/null
        net=$subNet
    fi
    echo "$net"
}

docker_check_netint_device() {
    local opt=$1
    local cpus=$2
    local cpuNumaNode=$(get_closest_numas $cpus)
    local numaList=(
        0
        0
        1
        1
    )

    if [ -n "$(which nvme)" ]; then
        local select_devices_two=$(nvme list | grep -E "T408-AIC|T432-AIC|T432-8" | awk '{print $1}')
        local subnet_file_one=" --device="
        local subnet_file_two=":"
        local subnet_file_third=":rwm"

        available_name1=()
        available_name2=()
        for i in ${select_devices_two[*]}
        do
            local nvmeRightPart=${i##*/}
            local nvmeNodeTmp=${nvmeRightPart%n*}
            local busID=$(find /sys/devices/ -name $nvmeNodeTmp)
            if [ "$busID" == "" ]; then
                continue
            fi
            local numaNode=$(lspci -vvvs ${busID:37:12} | grep NUMA)
            if [ ${numaList[${numaNode##* }]} -eq ${numaList[$cpuNumaNode]} ]; then
                available_name1[${#available_name1[*]}]=${i%n*}
                available_name2[${#available_name2[*]}]=${i}
            fi
        done
        if [ ${#available_name1[*]} -gt 0 ]; then
            container_num=`expr $3 - 1`
            num=`expr $container_num % ${#available_name1[*]}`
            opt+=${subnet_file_one}${available_name1[$num]}${subnet_file_two}${available_name1[$num]}${subnet_file_third}
            opt+=${subnet_file_one}${available_name2[$num]}${subnet_file_two}${available_name2[$num]}${subnet_file_third}
        fi
    fi
    echo "$opt"
}

function wait_cmd() {
    local count_time=0
    while true; do
        local result=$(wait_async_cmd "$1")
        if [ "$result" != "-1" ]; then
            echo ${result}
            break
        fi

        if [ ${count_time} -gt 3 ]; then
            echo -1
            break
        fi
        count_time=$((count_time + 1))
    done
}

function wait_async_cmd() {
    eval $1 # 执行输入的异步命令，如果命令执行成功且自带返回值（如ls命令），将会在此函数结束后自动返回返回值
    local pid=$(jobs -rp)
    local count_time=0
    while true; do
        local count=$(jobs -rp | wc -l)
        if [ ${count} -eq 0 ]; then
            wait ${pid}
            # $?表示wait的返回状态，用于获取eval $1执行的命令是否执行成功，执行成功返回0
            if [ $? -ne 0 ]; then
                echo -2 # 命令执行失败
            fi
            break
        fi

        if [ ${count_time} -gt 8 ]; then
            kill -9 ${pid}
            echo -1
            break
        fi

        sleep 0.5
        count_time=$((count_time + 1))
    done
}

check_wait_cmd_result() {
    local cmd=$1
    local result=$2
    if [ "$result" == "-1" ]; then
        LOG_WARN "cmd \"${cmd}\" wait_cmd timeout"
    fi
}

docker_run() {
    local image=$1
    local containerid=${2:-0}
    local mode=$3

    local port;
    local adb_port;
    local hostip;
    shift 2
    local opt=""
    while [ $# -gt 0 ]; do
        case $1 in
            -hostip) hostip=$2;shift ;;
            -port)   port=$2;shift ;;
            -adb_port) adb_port=$2;shift ;;
        esac
        shift
    done

    local containerip=$[$containerid+1]
    local name="${DOCKER_NAME_PREFIX}${containerid}"
    local workspace="${DOCKER_WORKSPACE}/$name"
    local volumedata=${USER_DATA_DIR}/data/$name/data
    local gpu
    local nGpu
    local index
    local subnet
    local uinput
    local cmd
    local result

    port=${port:-$(($containerid+8000))}
    adb_port=${adb_port:-$(($containerid +8500))}

    param_check_image_id_or_name $image

    LOG_INFO "Create workspace $workspace"
    mkdir -p $workspace
    workspace=$(readlink -ef $workspace)

    echo 0 > $workspace/present

    # In some secure OS, the default attribute is 600
    chmod 644 $workspace/present

    #allow docker use divies without privileged
    if [ -n "$(ls /dev | grep binder-control)" ]; then
        chmod 600 /dev/binder-control
    elif [ -n "$(ls /dev | grep aosp_binder)" ] || [ -n "$(ls /dev | grep aosp9_binder)" ]; then
        chmod 600 /dev/*binder$containerid
    else
        EXIT_ERROR "Binder device does not exist!"
    fi
    chmod 600 /dev/ashmem
    chmod 600 /dev/fuse

    $DOCKERCMD volume create --name ${name}_data
    $DOCKERCMD volume create --name ${name}_cache
    if [ ! "$(docker volume ls -q 2>/dev/null|grep -w $BASE_DATA)" ]; then
        LOG_INFO "create $BASE_DATA"
        $DOCKERCMD volume create --name $BASE_DATA
    fi

    if [ "$hostip" ]
    then
        opt+=" -e hostip=$hostip"
    fi

    opt+=" -p ${port}:8888 -p ${port}:8888/udp"
    opt+=" -e MONBOX=1"
    opt+=" -e id=$containerid"

    local CPUS_NUM_PER_CNTR_TMP=1.5
    local cpus=($(get_cpus_by_tag_number "$containerid" $CPUS_NUM_PER_CNTR_TMP))

    opt=$(docker_check_netint_device "${opt}" "${cpus}" $containerid)

    LOG_INFO "Create container: $name"

    $DOCKERCMD ps -f name=$name

    start_box_instruction_stream "$image" "$containerid" "$opt" "${cpus[*]}" "$CPUS_NUM_PER_CNTR_TMP"

    LOG_INFO "Initialize container ..."
    if [ "$(ls $BASE_DATA_DIR 2>/dev/null)" ]; then
        LOG_INFO "copy ${name} from $BASE_DATA"
        cp -r -p $BASE_DATA_DIR/* $volumedata/ 2>/dev/null
    fi

    if [ -f $THISDIR/config_armnative.sh ]
    then
        opt=""
        if [ $hostip ]; then opt+=" -hostip $hostip"; fi
        if [ $port ]; then  opt+=" -port $port";fi
        if [ $containerid ]; then  opt+=" -id $containerid";fi
        chmod +x $THISDIR/config_armnative.sh
        source $THISDIR/config_armnative.sh $opt
    fi

    CID=$($DOCKERCMD ps |grep -w $name|awk '{print $1}')
    echo "c 13:* rwm" > $(ls -d /sys/fs/cgroup/devices/docker/$CID*/devices.allow)

    echo "Create base container success"
    
    if [ "${mode}" == "docker" ]; then
        LOG_INFO "container: $name"
        docker_restart "Start" "${name}"
    fi
}

docker_stop() {
    local container_name=$1

    LOG_INFO "Stop container: $container_name"
    param_check_container_name $container_name

    $DOCKERCMD ps | grep -q "$container_name" && $DOCKERCMD stop -t 0 $container_name
}

docker_restart() {
    local action=$1
    local container_name=$2
    local mode=$3
    local cmd
    local result

    if [ -n "$(docker ps --format {{.Names}} | grep "$container_name$")" ]; then
        if [ ! "$mode" ]; then
            cmd="docker exec -ti $container_name env $mode | grep -w mode | awk -F= '{print \$NF}' &"
            result=$(wait_cmd "${cmd}")
            check_wait_cmd_result "${cmd}" "${result}"
            if [ "${result}" != "-1" ] && [ "${result}" != "-2" ]; then
                    mode=${result}
            fi
        fi
        cmd="docker exec -t ${container_name} getprop vmi.sys.prop.soludensity &"
        local solu_density=$(wait_cmd "${cmd}")
        check_wait_cmd_result "${cmd}" "${solu_density}"
        if [ "${solu_density}" != "-1" ] && [ "${solu_density}" != "-2" ] && [ "x${solu_density}" != "x" ]; then
            self_adapt_density_resolution ${container_name} ${solu_density} ${mode}
        fi
    fi

    LOG_INFO "$action container: $container_name"
    param_check_container_name "$container_name"

    bash base_box.sh restart "$container_name" "$USER_DATA_DIR"

    LOG_INFO "Wait container to be ready ..."
    docker_wait_container_ready $container_name
    [ $? -ne 0 ] && return 1

    if [ -n "$(which nvme)" ]; then
        local netint_flag=$(nvme list | grep -E "T408-AIC|T432-AIC|T432-8" | awk '{print $1}')
        if [ -n "${netint_flag}" ]; then
            local netint_nums=$(docker inspect --format='{{range .HostConfig.Devices}}{{.PathOnHost}} {{end}}' $container_name | grep -o 'nvme[0-9]n' | wc | awk '{print $1}')
            let netint_nums--
            local shm_dir=
            local ni_lck_d_name=
            local ni_lck_e_name=
            cmd="$DOCKERCMD exec -i $container_name ls /vendor/bin/ni_rsrc_mon_logan"
            result=$(wait_cmd "${cmd}")
            check_wait_cmd_result "${cmd}" "${result}"
            if [ "${result}" == "/vendor/bin/ni_rsrc_mon_logan" ]; then
                $DOCKERCMD exec -itd $container_name /vendor/bin/ni_rsrc_mon_logan
                shm_dir=/dev/shm_netint
                ni_lck_d_name=NI_LOGAN_lck_d
                ni_lck_e_name=NI_LOGAN_lck_e
            else
                cmd="$DOCKERCMD exec -i $container_name ls /system/bin/ni_rsrc_mon"
                result=$(wait_cmd "${cmd}")
                check_wait_cmd_result "${cmd}" "${result}"
                if [ "${result}" == "/system/bin/ni_rsrc_mon" ]; then
                    $DOCKERCMD exec -itd $container_name /system/bin/ni_rsrc_mon
                    shm_dir=/dev/shm
                    ni_lck_d_name=NI_lck_d
                    ni_lck_e_name=NI_lck_e
                fi
            fi
            while :
            do
                LOG_INFO "wait netint devices to be ready ..."
                sleep 3
                cmd="$DOCKERCMD exec -i $container_name sh -c \"test -f ${shm_dir}/${ni_lck_d_name}${netint_nums}\" &"
                result=$(wait_cmd "${cmd}")
                check_wait_cmd_result "${cmd}" "${result}"
                if [ "$result" == "-1" ] || [ "$result" == "-2" ] || [ "$result" == "1" ]; then
                    continue
                fi
                cmd="$DOCKERCMD exec -i $container_name sh -c \"test -f ${shm_dir}/${ni_lck_e_name}${netint_nums}\" &"
                result=$(wait_cmd "${cmd}")
                check_wait_cmd_result "${cmd}" "${result}"
                if [ "$result" == "-1" ] || [ "$result" == "-2" ] || [ "$result" == "1" ]; then
                    continue
                fi
                break
            done
            $DOCKERCMD exec -itd $container_name chmod 666 /dev/nvm*n*
            $DOCKERCMD exec -itd $container_name chmod 777 -R ${shm_dir}
            LOG_INFO "netint devices ready"
        fi
    fi
}

docker_delete() {
    local container_name=$1
    local delete_image=$2
    local workspace="${DOCKER_WORKSPACE}/$container_name"
    local imageid

    container_num=`echo ${container_name:5}`

    LOG_INFO "Remove container: $container_name"
    param_check_container_name "$container_name"

    imageid="$($DOCKERCMD ps -a | grep $container_name | awk '{print $2}')"
    $DOCKERCMD ps | grep -q "$container_name" && $DOCKERCMD stop -t 0 $container_name
    $DOCKERCMD volume rm ${container_name}_data
    $DOCKERCMD volume rm ${container_name}_cache

    [ -e "$workspace/container_id" ] && rm -rf $workspace

    if [ "$delete_image" == "--image" ] ; then
        LOG_INFO "Remove container image: $imageid"
        $DOCKERCMD rmi $imageid
    fi
    echo "container_num: "$container_num
    bash base_box.sh delete "kbox_$container_num" "$USER_DATA_DIR"
}

docker_check_status() {
    local name=$1
    local start=$1
    local end=$2
    local port=0

    if [ -n "$end" ] ; then
        param_check_container_indexs "$start" "$end"
    else
        param_check_container_name $name
        start="$(echo "$name" | awk -F'_' '{print $2}')"
        end=start
    fi

    for((i=$start; i<=$end; i++)); do
        port=$(($i*2+18886))
        local cmd="$DOCKERCMD exec -i ${DOCKER_NAME_PREFIX}$i ifconfig |grep $DOCKER_IP_PREFIX |awk '{print $2}'|awk -F':' '{print $2}' &"
        local result=$(wait_cmd "${cmd}")
        check_wait_cmd_result "${cmd}" "${result}"
        if [ "$result" != "-1" ] && [ "$result" != "-2" ]; then
            ip=$result
        fi
        name="[${DOCKER_NAME_PREFIX}$i/$port/$ip]"

        cmd="$DOCKERCMD exec -i ${DOCKER_NAME_PREFIX}$i /system/bin/getprop sys.boot_completed | grep 1 >/dev/null 2>&1 &"
        result=$(wait_cmd "${cmd}")
        check_wait_cmd_result "${cmd}" "${result}"
        if [ "$result" == "-1" ] || [ "$result" == "-2" ]; then
            LOG_INFO "$name: checking is boot complete" ; continue
        fi
        LOG_INFO "$name ready"
        return 0
    done

    return -1
}

docker_wait_container_ready() {
    local name=$1
    local timeout=${2:-100}
    local starttime=$(date +%s)
    local currenttime=$starttime
    local endtime=$(($starttime+$timeout))
    local has_restart=0

    while [ "$currenttime" -lt "$endtime" ] ; do
        printf "\r%03d/%03d" $(($currenttime-$starttime)) $timeout
        sleep 1
        docker_check_status $name
        if [ ${?} == 0 ] ; then
            bash base_box.sh chk_key_process ${name}
            [ ${?} -ne 0 ] && has_restart=1 && bash base_box.sh restart "${name}" "$USER_DATA_DIR" 2
            [ ${?} -eq 1 ] && [ $has_restart -eq 1 ] && echo "${name} started failed!" && break
            printf "\r\033[1;32m%03d/%03d\033[0m\n" $(($currenttime-$starttime)) $timeout
            return 0
        fi
        currenttime=$(date +%s)
    done
    printf "\r\033[1;31m%03d/%03d\033[0m\n" $(($currenttime-$starttime)) $timeout
    return 1
}

# All other command bypass to local docker
docker_command() {
    LOG_INFO "[RUNCMD] docker "$@""
    docker "$@"
}

show_help_menu() {
    echo -e "
Usage: $MYNAME <action> [arguments ...]

Actions:
    run <imageID>|<imageName> <containerID> <mesa/swiftshader>
        Create container named ${DOCKER_NAME_PREFIX}<containerID> based on
        image with <imageID> or named <imageName>, and run it after it's created.
    start <containerName> <mesa/swiftshader/adapt_resolution>
        Start the container named <containerName>
    stop <containerName>
        Stop the container named <containerName>
    restart <containerName> <mesa/swiftshader/adapt_resolution>
        Restart the container named <containerName>
    delete <containerName> [--image]
        Remove the container named <containerName>,
        if --image is specified, also remove the related container image.
    check <containerName>|<startID> <endID>
        Check the status of container named <containerName> or containers
        from ${DOCKER_NAME_PREFIX}<stardID> to ${DOCKER_NAME_PREFIX}<endID>
    portmap <containerName>|<startID> <endID>
        Add container port map for container named <containerName> or containers
        from ${DOCKER_NAME_PREFIX}<stardID> to ${DOCKER_NAME_PREFIX}<endID>.
        See also 'ps -ef | grep socat' and 'netsta -antp'.
    *
        All other actions and arguments will be passed to docker command.
    "
}

push()
{
    local target=$1
    local container=$2
    local name=$container
    local mode=$3
    local volumedata=${USER_DATA_DIR}/data/$name/data
    local rc_files
    local version_files
    local cmd
    local result

    version_files=(
        vendor/etc/instructionengine_version.txt
        vendor/etc/baseengine_version.txt
    )

    rc_files=(
        vendor/etc/init/init.instructionengine.rc
        vendor/etc/init/vendor.huawei.vmi@1.0-service.rc
        vendor/etc/init/VmiAgentAndroidP.rc
        system/etc/init/init.vmitouch.rc
    )

    if [ ! "$mode" ]
    then
        cmd="docker exec -ti $container env $mode | grep -w mode | awk -F= '{print \$NF}' &"
        result=$(wait_cmd "${cmd}")
        check_wait_cmd_result "$cmd" "$result"
        if [ "${result}" != "-1" ] && [ "${result}" != "-2" ]; then
            mode=${result}
        fi
    fi

    if [ ! "$(docker ps -a  2>/dev/null | grep -w $container)" ]
    then
        LOG_ERROR "$container doesn't exist!"
        return
    fi

    rootName=$(basename ${target%%.*})
    cmd="docker exec -i $container mount -o rw,remount / &"
    result=$(wait_cmd "${cmd}")
    LOG_INFO $cmd
    check_wait_cmd_result "$cmd" "$result"
    #remove tar patckage if exist
    cmd="docker exec -i $container rm -f /$rootName.tar 2>/dev/null &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"
    # copy push file to container
    cmd="docker cp $target $container:/$rootName.tar.gz"
    LOG_INFO $cmd
    $cmd
    cmd="docker exec -i $container gzip -d /$rootName.tar.gz &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"
    # Delete the last pushed file

    delete_files=$(docker exec -i $container tar -tvf /backups.tar 2>/dev/null | grep ^[^d_] | awk '{print $6}' | tr '\r\n' ' ')
    cmd="docker exec -i $container rm -rf ${delete_files}"
    LOG_INFO $cmd
    $cmd
    # Restore last overwritten file
    cmd="docker exec -i $container tar -xf /covery.tar 2>/dev/null &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"
    # Back up the files of this push
    cmd="docker exec -i $container cp $rootName.tar backups.tar &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"
    # Package the files that will be overwritten this time
    file_path=$(docker exec -i $container tar -tvf /$rootName.tar 2>/dev/null | grep ^[^d_] | awk '{print $6}' | tr '\r\n' ' ')
    cmd="docker exec -i $container tar -cf /covery.tar $file_path"
    LOG_INFO $cmd
    $cmd 2>/dev/null

    cmd="docker exec -i $container mkdir -p /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container cp $rootName.tar /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container tar -xf /InstructionDemo/$rootName.tar -C /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container chmod -R 755 /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container chown -R root:root /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    for rc_file_name in ${rc_files[@]}
    do
        cmd="docker exec -i $container ls /InstructionDemo/$rc_file_name 2>/dev/null &"
        result=$(wait_cmd "${cmd}")
        check_wait_cmd_result "${cmd}" "${result}"
        if [ "$result" == "/InstructionDemo/$rc_file_name" ]; then
            cmd="docker exec -i $container chmod -R 444 /InstructionDemo/$rc_file_name &"
            LOG_INFO $cmd
            result=$(wait_cmd "${cmd}")
            check_wait_cmd_result "$cmd" "$result"
        else
            LOG_WARN "cmd \"${cmd}\" failed or got invalid value, result:${result}"
        fi
    done

    for version_file in ${version_files[@]}
    do
        cmd="docker exec -i $container ls /InstructionDemo/$version_file 2>/dev/null &"
        result=$(wait_cmd "${cmd}")
        check_wait_cmd_result "${cmd}" "${result}"
        if [ "$result" == "/InstructionDemo/$version_file" ]; then
            cmd="docker exec -i $container chmod -R 400 /InstructionDemo/$version_file &"
            LOG_INFO $cmd
            result=$(wait_cmd "${cmd}")
            check_wait_cmd_result "$cmd" "$result"
        else
            LOG_WARN "cmd \"${cmd}\" failed or got invalid value, result:${result}"
        fi
    done

    cmd="docker exec -i $container rm -rf /system/vendor/lib/hw/hwcomposer.kbox.so &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container rm -rf /system/vendor/lib64/hw/hwcomposer.kbox.so &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container rm -rf /InstructionDemo/$rootName.tar &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container cp -pR /InstructionDemo/vendor/ /system &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container cp -pR /InstructionDemo/system/ / &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container cp -pR /InstructionDemo/init.goldfish.rc / &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container chmod -R 444 init.goldfish.rc &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"
    
    cmd="docker exec -i $container rm -rf /InstructionDemo &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    cmd="docker exec -i $container mount -o ro,remount / &"
    LOG_INFO $cmd
    result=$(wait_cmd "${cmd}")
    check_wait_cmd_result "$cmd" "$result"

    LOG_INFO "restarting container: $container...."
    LOG_INFO "$mode"
    cmd="docker_restart "Start" $container $mode"
    LOG_INFO $cmd
    $cmd

    if [ ! "$(ls $BASE_DATA_DIR 2>/dev/null)" ] && [ "$(ls $volumedata 2>/dev/null)" ]; then
        LOG_INFO "copy $container to $BASE_DATA"
        cp -r -p $volumedata/* $BASE_DATA_DIR/ 2>/dev/null
    fi
}

# 指令流云手机容器参数配置策略, 超过满载路数, 分配策略跟低于满载路数策略一样
#                                          1.5核/手机				                            1核/手机
#核数      NUMA	   CPU	      满载路数    空出核数      空出的具体CPU号     kbox编号    满载路数    空出核数    空出的具体CPU号    kbox编号
#160核     0       0-39	      100路       7            0-6                1~22        150路       10         0-9                1~30
#          1       40-79                  1            40                 23~48                   0          /                  31~70
#          2	   80-119                 1            80                 49~74                   0          /                  71~110
#          3	   120-159                1            120                75~100                  0          /                  111~150
#128核     0       0-31	      80路        2            0-1                1~20        120路       8          0-7                1~24
#          1       32-63                  2            32-33              21~40                   0          /                  25~56
#          2	   64-95                  2            64-65              41~60                   0          /                  57~88
#          3	   96-127                 2            96-97              61~80                   0          /                  89~120
#96核      0       0-23	      60路        6            0-5                1~12        90路        6          0-5                1~18
#          1	   24-47                  0            /                  13~28                   0          /                  19~42
#          2	   48-71                  0            /                  29~44                   0          /                  43~66
#          3	   72-95                  0            /                  45~60                   0          /                  67~90
#64核      0       0-31       40路        2            0-1                1~20        60路        4          0-3                1~28
#          1       32-63                  2            32-33              21~40                   0          /                  29~60
get_closest_numas() {
    local NUM_OF_CPUS=$(lscpu | grep -w "CPU(s)" | head -n 1 | awk '{print $2}')
    local NUM_OF_NUMA=$(lscpu | grep -w "NUMA node(s)" | awk '{print $3}')
    local CPUS_EACH_NUMA=$((${NUM_OF_CPUS} / ${NUM_OF_NUMA}))

    local CPU
    for CPU in $@; do
        local NUMA=$((${CPU} / ${CPUS_EACH_NUMA}))
    done

    echo $NUMA
}

get_numa_empty()
{
    local NUM_OF_CPUS=$1 NUM_OF_NUMA=$2 CPUS_EACH_NUMA=$3 CPUS_NUM_PER_CNTR=$4
    if [ $( echo "$CPUS_NUM_PER_CNTR == 1.5" | bc ) -eq 1 ];then
        if((NUM_OF_CPUS==160)); then
            NUMA_EMPTY=(10 10 10 10)
        elif ((NUM_OF_CPUS==128)); then
            NUMA_EMPTY=(2 2 2 2)
        elif ((NUM_OF_CPUS==96)); then
            NUMA_EMPTY=(6 0 0 0)
        elif ((NUM_OF_CPUS==64)); then
            NUMA_EMPTY=(2 2)
        fi

        for ((i=0;i<NUM_OF_NUMA;i++))
        do
            if [ $(((${CPUS_EACH_NUMA} - ${NUMA_EMPTY[${i}]})%3)) -ne 0 ]; then
	            echo "error: NUMA_EMPTY value unsuitable,please check the value."
                exit 1
            fi
        done
    else
        if((NUM_OF_CPUS==160)); then
            NUMA_EMPTY=(10 0 0 0)
        elif ((NUM_OF_CPUS==128)); then
            NUMA_EMPTY=(2 2 2 2)
        elif ((NUM_OF_CPUS==96)); then
            NUMA_EMPTY=(6 0 0 0)
        elif ((NUM_OF_CPUS==64)); then
            NUMA_EMPTY=(4 0)
        fi
    fi
	echo "${NUMA_EMPTY[*]}"
}

get_num_numa_empty()
{
    local NUM_EMPTY=0
    for NUM_CPU_EMPTY in $@; do
        NUM_EMPTY=$((${NUM_EMPTY} + ${NUM_CPU_EMPTY}))
    done
	echo $NUM_EMPTY
}

get_num_full_loads() {
    local NUM_OF_CPUS=$1 NUM_OF_EMPTY=$2 CPUS_NUM_PER_CNTR=$3
    if [ $( echo "$CPUS_NUM_PER_CNTR == 1.5" | bc ) -eq 1 ];then
        FULL_LOADS=$(((${NUM_OF_CPUS}-${NUM_OF_EMPTY})/3*2))
    elif [ $( echo "$CPUS_NUM_PER_CNTR == 1.2" | bc ) -eq 1 ];then
        FULL_LOADS=$(((${NUM_OF_CPUS}-${NUM_OF_EMPTY})/6*5))
    elif [ $( echo "$CPUS_NUM_PER_CNTR == 1" | bc ) -eq 1 ];then
        FULL_LOADS=$((${NUM_OF_CPUS}-${NUM_OF_EMPTY}))
    fi
    echo $FULL_LOADS
}

get_cpu_start() {
    local TAG_NUMBER=$1 FULL_LOADS=$2 CPUS_NUM_PER_CNTR=$3
    local ARRAY_IDX=$((${TAG_NUMBER}-(((${TAG_NUMBER}-1)/${FULL_LOADS})*${FULL_LOADS})))
    if [ $( echo "$CPUS_NUM_PER_CNTR == 1.5" | bc ) -eq 1 ];then
        CPU_START=$(((${ARRAY_IDX}-1)/2*3+(${ARRAY_IDX}-1)%2))
    elif [ $( echo "$CPUS_NUM_PER_CNTR == 1.2" | bc ) -eq 1 ];then
        CPU_START=$(((${ARRAY_IDX}-1)/5*6+(${ARRAY_IDX}-1)%5))
    elif [ $( echo "$CPUS_NUM_PER_CNTR == 1" | bc ) -eq 1 ];then
        CPU_START=$(((${ARRAY_IDX}-1)/2*2))
    fi
    echo $CPU_START
}

get_cpus_by_tag_number() {
    # TAG_NUMBER：容器标号, CPUS_NUM_PER_CNTR：每个容器分配的CPU核数
    local NUM_OF_CPUS=$(lscpu | grep -w "CPU(s)" | head -n 1 | awk '{print $2}')
    local NUM_OF_NUMA=$(lscpu | grep -w "NUMA node(s)" | awk '{print $3}')
    local CPUS_EACH_NUMA=$((${NUM_OF_CPUS} / ${NUM_OF_NUMA}))
    local TAG_NUMBER=$1 CPUS_NUM_PER_CNTR=$2
    local NUMA_EMPTY=($(get_numa_empty $NUM_OF_CPUS $NUM_OF_NUMA $CPUS_EACH_NUMA $CPUS_NUM_PER_CNTR))
    local NUM_NUMA_EMPTY=($(get_num_numa_empty ${NUMA_EMPTY[@]}))
    local FULL_LOADS=($(get_num_full_loads $NUM_OF_CPUS $NUM_NUMA_EMPTY $CPUS_NUM_PER_CNTR))

    CPU_START=($(get_cpu_start $TAG_NUMBER $FULL_LOADS $CPUS_NUM_PER_CNTR))
    local END_DIFF=1
    if [ $( echo "$CPUS_NUM_PER_CNTR == 1.2" | bc ) -eq 1 ];then
        local ARRAY_IDX=$((${TAG_NUMBER}-(((${TAG_NUMBER}-1)/${FULL_LOADS})*${FULL_LOADS})))
        END_DIFF=$((5-(${ARRAY_IDX}-1)%5))
    fi
    local cur_cpu=$CPU_START

    local i=0
    for ((i=0;i<NUM_OF_NUMA;i++))
    do
        cur_cpu=$((${NUMA_EMPTY[${i}]} + ${cur_cpu}))
        cur_numa=$((${cur_cpu} / ${CPUS_EACH_NUMA}))
        if [ ${cur_numa} -eq ${i} ]; then
            break
        fi
    done

    CPU_START=$cur_cpu
    CPU_END=$((${CPU_START} + ${END_DIFF}))
    if [ $( echo "$CPUS_NUM_PER_CNTR == 1.2" | bc ) -eq 1 ];then
        echo ${CPU_START} ${CPU_END}
    else
        local CPU
        for CPU in $(seq $CPU_START $CPU_END); do
            echo $CPU
        done
    fi

    return
}

# 参数1：镜像名，参数2：容器标号，参数3：额外参数(cfct中的opt项)
start_box_instruction_stream() {
    # 镜像名
    local IMAGE_NAME=$1

    # 容器编号
    local TAG_NUMBER=$2

    # 容器名
    local CONTAINER_NAME="kbox_$TAG_NUMBER"

    # 每个容器分配的CPU核数，此参数可选值为1.5（表示1.5核/手机），1（表示：1核/手机）, 1.2(表示6核5手机)
    local CPUS_NUM_PER_CNTR=$5

    # CPUS
    local CPUS=$4

    # 存储大小
    local STORAGE_SIZE_GB=16

    # 运行内存，默认是6G
    local RAM_SIZE_MB=6144

    # NUMA
    local NUMAS=($(get_closest_numas ${CPUS[@]}))

    # 调试端口
    local PORTS=("$((8500 + $TAG_NUMBER)):5555")

    # docker额外启动参数
    local EXTRA_RUN_OPTION=$3

    # 调用基础脚本
    ./base_box.sh start \
    --name "$CONTAINER_NAME" \
    --cpus "${CPUS[*]}" \
    --numas "${NUMAS[*]}" \
    --storage_size_gb "$STORAGE_SIZE_GB" \
    --ram_size_mb "$RAM_SIZE_MB" \
    --ports "${PORTS[*]}" \
    --extra_run_option "$EXTRA_RUN_OPTION" \
    --image "$IMAGE_NAME" \
    --user_data_path "$USER_DATA_DIR"
}

# 参数1：容器名 参数2： 待设置的屏幕分辨率参数 参数3：是否开启自适应屏幕分辨率(使能参数: adapt_resolution 其它参数不开启)
self_adapt_density_resolution()
{
    local container=$1
    local solution_string=$2 # 字符串格式  1080x2340_480 分别代表分辨率宽 高 像素密度
    local mode=$3 # 当值为 "adapt_resolution" 表示开启自适应功能，其它值不开启
    local prop_default_file=/system/vendor/default.prop
    local prop_build_file=/system/vendor/build.prop

    tmp_density=${solution_string#*_}
    density=${tmp_density:0:3}
    width_height=${solution_string%_*}
    width=${width_height%x*}
    height=${width_height#*x}
    is_density_num=$(echo $density| grep '^[[:digit:]]*$')
    is_width_num=$(echo $width| grep '^[[:digit:]]*$')
    is_height_num=$(echo $height| grep '^[[:digit:]]*$')

    if [[ "x${mode}" != "xadapt_resolution" ]]; then
        LOG_INFO "Self adapt resolution is not on"
        return
    fi
    #校验参数是否都为数字
    if [[ -z "${is_width_num}" ]]||[[ -z "${is_height_num}" ]]||[[ -z "${is_density_num}" ]]; then
        LOG_WARN "Resolution or density param is invalid!"
        return
    fi
    LOG_INFO "Set server displayer as [width:${width} height:${height} density:${density}]"
    local result=$(wait_cmd "docker exec  -i $container mount -o rw,remount / > /dev/null 2>&1 &")
    check_wait_cmd_result "$container mount -o rw,remount /" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(qemu.sf.lcd_density=\\).*/\\1${density}/g\" ${prop_default_file} &")
    check_wait_cmd_result "$container sed -i \"s/^\\(qemu.sf.lcd_density=\\).*/\\1${density}/g\" ${prop_default_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(qemu.sf.lcd_density=\\).*/\\1${density}/g\" ${prop_build_file} &")
    check_wait_cmd_result "$container sed -i \"s/^\\(qemu.sf.lcd_density=\\).*/\\1${density}/g\" ${prop_build_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(ro.hardware.width=\\).*/\\1${width}/g\" ${prop_default_file} &")
    check_wait_cmd_result "$container sed -i \"s/^\\(ro.hardware.width=\\).*/\\1${width}/g\" ${prop_default_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(ro.hardware.width=\\).*/\\1${width}/g\" ${prop_build_file} &")
    check_wait_cmd_result "$$container sed -i \"s/^\\(ro.hardware.width=\\).*/\\1${width}/g\" ${prop_build_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(ro.hardware.height=\\).*/\\1${height}/g\" ${prop_default_file} &")
    check_wait_cmd_result "$container sed -i \"s/^\\(ro.hardware.height=\\).*/\\1${height}/g\" ${prop_default_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container sed -i \"s/^\\(ro.hardware.height=\\).*/\\1${height}/g\" ${prop_build_file} &")
    check_wait_cmd_result "$container sed -i \"s/^\\(ro.hardware.height=\\).*/\\1${height}/g\" ${prop_build_file}" "$result"
    result=$(wait_cmd "docker exec  -i $container mount -o ro,remount / >/dev/null 2>&1 &")
    check_wait_cmd_result "$container mount -o ro,remount /" "$result"
}

#===============================================================================
# Main Start
#===============================================================================
if [ "$(whoami)" != "root" ] ; then
    EXIT_ERROR "Please run this tool with root account!"
fi

ACTION=$1; shift
case $ACTION in
    run)        docker_run "$@" ;;
    stop)       docker_stop "$@";;
    start)      docker_restart "Start" "$@" ;;
    restart)    docker_restart "Restart" "$@";;
    delete)     docker_delete "$@";;
    check)      docker_check_status "$@";;
    portmap)    docker_port_map "$@" ;;
    push)       push "$@" ;;
    -h|help)    show_help_menu; exit 0 ;;
    *)          docker_command $ACTION "$@" ;;
esac