#!/bin/bash

# Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
# Description: auto del and scan sg device in host
#              consider three condition below：modify lunid、cancel mapping、modify mapping
# Author: shengkun

# get all scsi_generic device
function get_all_sg_device()
{
    local dev_info_list=""
    dev_info_list=$(lsscsi -g | grep HUAWEI | while read s; do a="$a,$s"; echo $a; done | tail -n 1)
    dev_info_list=${dev_info_list#*,}
    echo "${dev_info_list}"
}

# get sg device node name from dev_info
function get_sg_device_node_name()
{
    local dev_info=$1
    local node_name=""

    node_name=${dev_info#*/dev/sg}
    node_name="/dev/sg${node_name}"
    node_name=$(eval echo ${node_name})

    echo "${node_name}"
}

# get sg device host id
function get_sg_host_id()
{
    local dev_info=$1
    local host_id=""

    host_id=${dev_info%%:*}
    host_id=${host_id#*[}

    echo "${host_id}"
}

# get origin lunwwn from device file
function get_origin_lun_wwn()
{
    local node_name=$1  
    local origin_lun_wwn=""

    node_name=${node_name##*/}

    if [ -z ${node_name} ]
    then
        echo "node name is empty!"
        return 1
    fi

    if [ ! -f "/sys/class/scsi_generic/${node_name}/device/wwid" ]
    then
        echo "wwid file not exist ${node_name}."
        return 1
    fi

    origin_lun_wwn=$(cat /sys/class/scsi_generic/${node_name}/device/wwid 2>>/dev/null)
    origin_lun_wwn=${origin_lun_wwn#*.}

    echo "${origin_lun_wwn}"
}

# get new lunwwn from storage
function get_new_lun_wwn()
{
    local node_name=$1
    local page_info=""
    local new_lun_wwn=""
    local start_index=""
    local end_index=""

    page_info=$(sg_inq ${node_name} -p 0x83 2>>/dev/null)
    if [ -z "${page_info}" ]
    then
        echo "83 page info is null."
        return 1
    fi

    start_index=$(expr index "${page_info}" \[)
    end_index=$(expr index "${page_info}" \])

    if [ ${end_index} -le ${start_index} ]
    then
        return 1
    fi
    new_lun_wwn=${page_info:${start_index}:${end_index}-${start_index}-1}
    new_lun_wwn=${new_lun_wwn#*x}

    echo "${new_lun_wwn}"
}

# get sg device type
function get_sg_device_type()
{
    local dev_info=$1
    local dev_type=""

    dev_type=${dev_info#*\]}
    dev_type=${dev_type%HUAWEI*}
    dev_type=$(eval echo "${dev_type}")

    echo "${dev_type}"
}

# check block device
function check_block_device()
{
    local node_name=$1
    local is_block_device=""

    node_name=${node_name##*/}    
    if [ ! -d "/sys/class/scsi_generic/${node_name}/device/block/" ]
    then
        is_block_device="0"
    else
        is_block_device="1"
    fi

    echo "${is_block_device}"
}

# check channel lun sg device
function check_channel_lun_device()
{
    local node_name=$1
    local line=""
    local tmp_file="/tmp/stmp_${RANDOM}_$$"

    if [ -z "${node_name}" ]
    then
        echo "check lun device fail node name is null."
        return 1
    else
        sg_raw -r 1k ${node_name} 12 01 83 00 e8 00 2>>"${tmp_file}"
        line=$(grep -A 1 'Received' "${tmp_file}"|awk '{print $2}'|more)
        line=${line:3:1}
    fi
    rm -rf "${tmp_file}"

    if [ "${line}" == 0 ]
    then
        echo "0"
        return 0
    fi

    echo "1"
    return 1
}

# delete scsi_generic device
function delete_sg_device()
{
    local node_name_list=$1
    local node_name=""

    if [ -z "${node_name_list}" ]
    then
        echo "delete list is null."
        return 1
    else
        OLD_IFS="${IFS}"
        IFS=","
        node_name_list=(${node_name_list})
        IFS="${OLD_IFS}"

        for node_name in "${node_name_list[@]}"
        do
            node_name=${node_name##*/}
            echo 1 > /sys/class/scsi_generic/${node_name}/device/delete
            if [ $? -ne 0 ]
            then
                echo "delete sg device ${node_name} failed."
            else
                echo "delete sg device ${node_name} sucess."
            fi
        done
    fi

    return 0
}

# scan scsi_generic device
function scan_sg_device()
{
    for dir in $(ls /sys/class/fc_host/)
    do
        [ -d ${dir} ] && echo ${dir}
        echo scan $dir
        echo "- - -" > /sys/class/scsi_host/$dir/scan;
    done
}

# exist valid sg device need up to udev
function add_exist_valid_sg_device()
{
    local node_name_list=$1
    local node_name=""

    if [ -z "${node_name_list}" ]
    then
        echo "add exist sg list is null."
        return 1
    else
        OLD_IFS="${IFS}"
        IFS=","
        node_name_list=(${node_name_list})
        IFS="${OLD_IFS}"

        for node_name in "${node_name_list[@]}"
        do
            node_name=${node_name##*/}
            echo add > /sys/class/scsi_generic/${node_name}/uevent
            if [ $? -ne 0 ]
            then
                echo "add sg device ${node_name} failed."
            fi
        done
    fi

    return 0
}

# main
function main()
{
    local dev_info_list=""
    local dev_info=""
    local node_name=""
    local host_id=""
    local origin_lun_wwn=""
    local new_lun_wwn=""
    local dev_type=""
    local del_node_name_list=""
    local add_node_name_list=""
    local is_block_device=""
    
    dev_info_list=$(get_all_sg_device)
    if [ -z "${dev_info_list}" ]
    then
        echo "current not exist sg device."
    else
        OLD_IFS="${IFS}"
        IFS=","
        dev_info_list=(${dev_info_list})
        IFS="${OLD_IFS}"

        for dev_info in "${dev_info_list[@]}"
        do
            node_name=$(get_sg_device_node_name "${dev_info}")
            host_id=$(get_sg_host_id "${dev_info}")
            origin_lun_wwn=$(get_origin_lun_wwn ${node_name})
            new_lun_wwn=$(get_new_lun_wwn ${node_name})

            if [ $(check_channel_lun_device ${node_name}) == 0 ]
            then
                if [ "${origin_lun_wwn}"x == "${new_lun_wwn}"x ]
                then
                    if [ -z "${add_node_name_list}" ]
                    then
                        add_node_name_list=${node_name}
                    else
                        add_node_name_list="${add_node_name_list},${node_name}"
                    fi

                    continue
                fi
            else
                dev_type=$(get_sg_device_type "${dev_info}")
                is_block_device=$(check_block_device "${node_name}")
                if [ "${origin_lun_wwn}" == "${new_lun_wwn}" ]||[ "${dev_type}" != "process" ]||[ "${is_block_device}" == "1" ]
                then
                    continue
                fi
            fi

            echo "remove ${origin_lun_wwn} sg device"
            if [ -z "${del_node_name_list}" ]
            then
                del_node_name_list="${node_name}"
            else
                del_node_name_list="${del_node_name_list},${node_name}"
            fi
        done

        if [ -n "${del_node_name_list}" ]
        then
            delete_sg_device "${del_node_name_list}"
        fi
    fi
    add_exist_valid_sg_device "${add_node_name_list}"
    scan_sg_device
}

main
