shell脚本自动检测程序是否运行(bash根据配置文件以及设备的角色、模式检测程序是否死掉,如果死掉则拉起来)

前言

在嵌入式设备中,我们有时候有这样一个需求:需要一个程序(python,shell,c程序)来检测我们设备侧运行的程序是否异常退出(需要根据设备的角色,模式以及运行的环境来检测不同的程序)。因为linux嵌入式设备有些是没有python的(python写是最简单的)。所以我们考虑用c语言或者shell脚本来实现。

程序设计思路:

  • 创建一个配置文件,配置文件中包含需要检测的程序,以及该程序运行的环境等,我的配置文件如下:
# space and tab are not allowed in names.
#
# - app_name: executable name
# - interval: check interval in second
# - role: in which role this check is enabled, aligned
#         with MIB_MERCKU_MODE (weird name):
#   - any: undefined - always enabled
#   - gw: gw
#   - node: node
# - mode: op_mode follows MIB definition
#   - any: any
#   - router: router
#   - bridge: bridge
# - operation:program call method
#
# 
# app_name        interval(s)      role      mode              operation
# This configuration file is used to detect program running
thttpd                  30         gw       router        thttpd -C /www/thttpd.conf 
elinksdk                30         gw       any           elinksdk
elinkclient             5          gw       any           elinkclient
routerd                 30         any      any           routerd
smbd			30         gw       router        samba.sh restart

该配置文件包含了检测的引用程序名称、检测的间隔时间、设备的角色、设备当前的模式、以及检测死掉之后需要运行的脚本。

TODO: 强烈建议采用配置文件的方式。这样做可读性和可扩展性更高

  • 配置文件创建好后,我们需要编写程序来检测程序是否运行,如果没有运行,则拉起来。我推荐使用shell脚本的方式。因为使用c语言编写的话没有shell方便(解析文件没有shell方便)。同时shell方便调试。检测的程序我们没有运行效率的要求,那么原则上什么方式简单我们就采用什么方式

检测程序如下

#!/bin/bash

. /lib/functions.sh
. /lib/services.sh

#读取到的格式为 app  interval role mode operation
#thttpd 30 1 1 "thttpd -C /www/thttpd.conf"

TIMELYCHECK_CONFIG_FILE="/lib/etc/timelycheck.conf"
BASE_TIME_S=5

__read_timelycheck_conf()
{
    while read line
    do
        if [[ "$line" =~ ^[0-9a-zA-Z]+ ]]; then
            timelycheck_array[i]="$line"
            let i=i+1
        fi
    done < $TIMELYCHECK_CONFIG_FILE
}

__check_app()
{
    local app_name=$1
    local interval=$2
    local role=$3
    local mode=$4
    local operation=$5

    if [[ "$app_name" == "" || "$interval" == "" || "$role" == "" || "$mode" == "" || "$operation" == "" ]]; then
        echo "check_app paramter error"
        return -1
    fi

    is_running=`ps | grep $app_name | grep -v grep`
    if [[ "$is_running" == "" ]]; then
        ##########如果程序没有运行,则检查是否应该运行  NODE不会工作在主路由模式
        ###如果在节点和主路由器都要运行
        if [[ "$role" == "any" ]]; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "router" ]] && ! is_bridge; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi

        elif [[ "$role" == "gw" ]] && is_gw; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "router" ]] && ! is_bridge; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi

        elif [[ "$role" == "node" ]] && is_gw; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi
        fi
    fi
}

__read_timelycheck_conf
#声明一个关联数组,对应着 app和当前运行的时间,用于超时判断
declare -A assArray_app
for i in ${!timelycheck_array[@]}
do
    idx_app_name=`echo ${timelycheck_array[i]} | awk '{print $1}'`
    interval=`echo ${timelycheck_array[i]} | awk '{print $2}'`
    assArray_app[$idx_app_name]=$interval
done

while true
do
    for i in ${!timelycheck_array[@]}
    do
        app_name=`echo ${timelycheck_array[i]} | awk '{print $1}'`
        interval=`echo ${timelycheck_array[i]} | awk '{print $2}'`
        role=`echo ${timelycheck_array[i]} | awk '{print $3}'`
        mode=`echo ${timelycheck_array[i]} | awk '{print $4}'`
        operation=`echo ${timelycheck_array[i]} | awk '{for (i=5;i<=NF;i++){printf("%s ", $i)}}'`
        cure_time=${assArray_app[$app_name]}
        #判断是否满足检测时间,如果满足才检测
        if [[ $interval -le $cure_time ]]; then
            __check_app $app_name $interval $role $mode "$operation"
            assArray_app[$app_name]=0
        else
            let assArray_app[$app_name]=$cure_time+$BASE_TIME_S
        fi
    done
    sleep $BASE_TIME_S
done

程序解析

  • 在程序入口处,我们循环读取了配置文件。这里使用了if的正则表达式,排除了以#开头的注释行。只获取有效数据
  • 将读取到的有效数据解析,然后存储到一个关联数组中(关于关联数组,其实就类似于python中的字典)。关联数组中的key为检测程序的名字,val为当前app记录的时间(程序中会使用该时间来判断是否大于等于interval时间,如果是才会去检测对应的app)
  • 进入while循环,分离出app_name,interval,role,mode和operation的值。判断app上次检测间隔的时间是否满足大于interval,如果是才会去检测
  • 检测程序很简单,就是根据设备的信息来判断是否需要检测,如果需要,同时app又没有运行,则这行operation程序

TODO: sh是不支持关联数组以及数组的,这里必须采用bash。如果你的设备没有bash,那么就需要移植,或者改写该程序

sh脚本如下

  • 如果我们的设备为了跑一个脚本而去移植bash,我觉得是得不偿失的。bash很大,会占用flash空间,导致固件变大,所以我改写了代码,下面是sh的实现方式
#!/bin/sh

. /lib/functions.sh
. /lib/services.sh

#读取到的格式为 app  interval role mode operation
#thttpd 30 1 1 "thttpd -C /www/thttpd.conf"

TIMELYCHECK_CONFIG_FILE="/lib/etc/timelycheck.conf"
BASE_TIME_S=5

__read_timelycheck_conf()
{
    arry_cnt=0
    while read line
    do
        if ! echo $line | grep -E "^#"; then
            eval timelycheck_arry$arry_cnt=\"$line\"   #使用该方式来代替数组(变量名加下标的方式)
            let arry_cnt=arry_cnt+1
        fi
    done < $TIMELYCHECK_CONFIG_FILE
}

__check_app()
{
    local app_name=$1
    local interval=$2
    local role=$3
    local mode=$4
    local operation=$5

    if [[ "$app_name" == "" || "$interval" == "" || "$role" == "" || "$mode" == "" || "$operation" == "" ]]; then
        echo "check_app paramter error"
        return -1
    fi

    is_running=`ps | grep $app_name | grep -v grep`
    if [[ "$is_running" == "" ]]; then
        #TODO: 如果发现程序有没有运行,先杀掉启动程序命令再去拉起,防止运行多个拉起的命令
        cmd=`echo $operation | awk '{print $1}'`
        cmd_pid=`ps | grep $cmd | grep -v grep | awk '{print $1}'`
        [[ "$cmd_pid" != "" ]] && kill -9 $cmd_pid
        ##########如果程序没有运行,则检查是否应该运行  NODE不会工作在主路由模式
        ###如果在节点和主路由器都要运行
        if [[ "$role" == "any" ]]; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "router" ]] && ! is_bridge; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi

        elif [[ "$role" == "gw" ]] && is_gw; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "router" ]] && ! is_bridge; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi

        elif [[ "$role" == "node" ]] && is_gw; then
            if [[ "$mode" == "any" ]]; then
                $operation & >/dev/null
            elif [[ "$mode" == "bridge" ]] && is_bridge; then
                $operation & >/dev/null
            fi
        fi
    fi
}

__read_timelycheck_conf
let arry_cnt=arry_cnt-1
for i in $(seq 0 $arry_cnt)
do
    #eval echo '$'{timelycheck_arry$i}
    #TODO: 这里的格式为assArray_appName1=interval的格式 下标{1,2,3}和timelycheck_arry1 2 3对应
    eval assArray_appcure_time${i}=`eval echo '$'{timelycheck_arry$i} | awk '{print $2}'`
done

while true
do
    for i in $(seq 0 $arry_cnt)
    do
        app_name=`eval echo '$'{timelycheck_arry$i} | awk '{print $1}'`
        interval=`eval echo '$'{timelycheck_arry$i} | awk '{print $2}'`
        role=`eval echo '$'{timelycheck_arry$i} | awk '{print $3}'`
        mode=`eval echo '$'{timelycheck_arry$i} | awk '{print $4}'`
        operation=`eval echo '$'{timelycheck_arry$i} | awk '{for (i=5;i<=NF;i++){printf("%s ", $i)}}'`
        cure_time=`eval echo '$'{assArray_appcure_time$i}`
        #判断是否满足检测时间,如果满足才检测
        if [[ $interval -le $cure_time ]]; then
            __check_app $app_name $interval $role $mode "$operation"
            eval assArray_appcure_time$i=0
        else
            let cure_time=cure_time+$BASE_TIME_S
            eval assArray_appcure_time$i=$cure_time
        fi
    done
    sleep $BASE_TIME_S
done

有问题欢迎留言交流

猜你喜欢

转载自blog.csdn.net/u011551613/article/details/106924199