2010年11月18日木曜日

[SA-4-1] PINGによる疎通確認(Shell Script)

PINGを用いてネットワークの向こう側にある機器との間の疎通確認を行うシェルスクリプト

/usr/local/etc/ping_hosts
192.168.147.1
192.168.147.2
192.168.147.3
192.168.147.4
192.168.147.5
192.168.147.6
192.168.147.7
192.168.147.8
192.168.147.9
192.168.147.10

util_common.bash
#
# 共通関数群(bash 用)
#

# BSD スタイルの ps コマンド
# 引数: ps コマンドの引数と同じ
ucb_ps () {
    local CMD_PS=/usr/ucb/ps

    if [ ! -x ${CMD_PS} ]; then
 CMD_PS=ps
    fi

    ${CMD_PS} "$@"
}

# ロケール(言語)設定をデフォルト(POSIX)にする
# 引数: なし
unset_linguas () {
    export LC_ALL=C
    export LC_MESSAGES=C
    export LANGUAGE=C
    export LANG=C
}

# 一時ディレクトリの作成
# 引数: 一時ディレクトリの親ディレクトリ
#       (省略時デフォルト: ${TMP_ROOT})
# 出力: 作成したディレクトリ名
# 終了ステータス: 0: 成功, 1: 失敗
# シェル変数:
#   TMP_ROOT(IN): 一時ディレクトリの親ディレクトリ
#                 (省略時デフォルト: ${HOME})
make_tmp_dir () {
    # 一時ディレクトリの親ディレクトリ
    local TMP_ROOT="${1:-${TMP_ROOT:-${HOME}}}"
    local tmpdir=$(basename "$0")$$

    tmpdir="${TMP_ROOT}/${tmpdir}"

    mkdir "${tmpdir}" > /dev/null 2>&1
    if [ $? -eq 0 ] ; then
 echo "${tmpdir}"
 chmod 700 "${tmpdir}"
    else
 echo "Can't create directory: ${tmpdir}" 1>&2
 return 1
    fi
}

# 一時ディレクトリの削除
# 引数: 一時ディレクトリの親ディレクトリ
#       (省略時デフォルト: ${TMP_ROOT})
# 出力: なし
# 終了ステータス: 0: 成功, 1以上: 失敗
#   TMP_ROOT(IN): 一時ディレクトリの親ディレクトリ
#                 (省略時デフォルト: ${HOME})
clean_tmp_dir () {
    # 一時ディレクトリの親ディレクトリ
    local TMP_ROOT="${1:-${TMP_ROOT:-${HOME}}}"
    local tmpdir=$(basename "$0")$$

    tmpdir="${TMP_ROOT}/${tmpdir}"

    # ディレクトリが存在しなければエラーにします
    if [ ! -d "${tmpdir}" ]; then
 echo "No such directory: ${tmpdir}" 1>&2
 return 1
    fi
    # 対象がシンボリックの場合は、想定外のためエラーにします
    if [ -L "${tmpdir}" ]; then
 echo "Can't delete unexpected symlink: ${tmpdir}" 1>&2
 return 1
    fi
    # rm コマンドの終了ステータスをそのまま返します
    rm -rf "${tmpdir}" > /dev/null 2>&1
}


ping.bash
#!/bin/bash
# usage: ping.bash

# 監視対象ホストを記述したファイル
# (書式は 1 行に 1 ホスト)
PING_HOSTS="/usr/local/etc/ping_hosts"

# 汎用pingコマンドインターフェース
# 使い方:
#   ping_common  [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   timeout: pingのタイムアウト(秒)
# 出力:
#   なし(エラー出力はあり)
# 終了ステータス:
#   0: 応答あり
#   1: 応答なし
#   2以上: エラー
ping_alive_common () {
    # システム(unameの結果)ごとに処理を分岐
    case $(uname -s) in
      Linux | NetBSD | OpenBSD)
        ping_alive_bsd $1 $2
        status=$?
        ;;
      SunOS)
 ping_alive_solaris $1 $2
        status=$?
 ;;
      FreeBSD)
        ping_alive_freebsd $1 $2
        status=$?
        ;;
      *)
 # 上記以外のシステムでは、自分で調べて修正してください
 echo "This system is not supported." 1>&2
 return 2
 ;;
    esac

    return ${status}
}

# Linux/NetBSD/OpenBSD用pingコマンドインタフェース
# 使い方:
#   ping_alive_bsd  [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   timeout: pingのタイムアウト(秒)
# 出力:
#   なし(エラー出力はあり)
# 終了ステータス:
#   0: 応答あり
#   1: 応答なし(またはエラー)
#   2以上: エラー
ping_alive_bsd () {
    local timeout=${2:+"-w $2"}
    local status

    ping -c 1 ${timeout} $1 > /dev/null
    status=$?

    # 不正な引数の場合は終了ステータスは2以上(?)
    if [ $status -ge 2 ]; then
 # timeout オプションを削除して再試行
 ping -c 1 $1 > /dev/null
 status=$?
    fi

    return ${status}
}

# FreeBSD用pingコマンドインタフェース
# 使い方:
#   ping_alive_freebsd  [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   timeout: pingのタイムアウト(秒)
# 出力:
#   なし(エラー出力はあり)
# 終了ステータス:
#   0: 応答あり
#   1: 応答なし
#   3以上: エラー
ping_alive_freebsd () {
    local timeout=${2:+"-t $2"}

    ping -c 1 ${timeout} $1 > /dev/null
    status=$?

    # 不正な引数の場合 = 古い実装は終了ステータスが2以上
    if [ $? -ge 2 -a "${timeout}" != "" ]; then
 # timeout オプションを削除して再試行
 ping -c 1 $1 > /dev/null
 status=$?
    fi

    case ${status} in
      0)
        return 0
        ;;
      1 | 2)
        return 1
        ;;
      *)
        return ${status}
        ;;
    esac
}

# Solaris(SunOS)用pingコマンドインタフェース
# 使い方:
#   ping_alive_solaris  [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   timeout: pingのタイムアウト(秒)
# 出力:
#   なし(エラー出力はあり)
# 終了ステータス:
#   0: 応答あり
#   1: 応答なし
#   2以上: エラー
ping_alive_solaris () {
    ping $1 $2 > /dev/null
}

# 非同期 ping 実行
# 使い方:
#   ping_async  [  ] [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   directory: ログ出力先ディレクトリ(デフォルトはカレントディレクトリ)
#   timeout: ping タイムアウト(秒)
# 出力:
#   なし
# 終了ステータス:
#   0: pingの実行に成功
#   1以上: エラー
ping_async () {
    local hostname=$1
    local resultfile="${2:+${2}/}${hostname}"
    local timeout=$3

    rm -f ${resultfile} > /dev/null 2>&1

    (
        ping_alive_common ${hostname} ${timeout} \
     > /dev/null 2> "${resultfile}"
 echo $? >> "${resultfile}"
    ) > /dev/null 2>&1 &
}

# 非同期 ping 実行結果取得
# 使い方:
#   ping_check_result  [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   directory: ログ出力先ディレクトリ(デフォルトはカレントディレクトリ)
# 出力:
#   なし
# 終了ステータス:
#   0: 応答あり
#   1: 応答なし
#   2以上: エラー
#   70: 結果不明(結果ファイルが存在しない/読み込めない)
ping_check_result () {
    local hostname=$1
    local resultfile="${2:+${2}/}${hostname}"
    local num_line
    local status

    [ -r "${resultfile}" ] || return 70
    status=$(tail -1 "${resultfile}")

    # エラーの表示
    if [ ${status} -ge 2 ]; then
 num_line=$(wc -l "${resultfile}" | awk '{print $1}')
 if [ ${num_line} -gt 1 ]; then
     sed ${num_line}d "${resultfile}" 1>&2
 fi
    fi

    return ${status}
}

# 複数ホストへのping同時実行
# 使い方:
#   ping_from_file  [  ] [  ]
# 引数:
#   hostname: ホスト名/IPアドレス
#   directory: ログ出力先ディレクトリ(デフォルトはカレントディレクトリ)
#   timeout: ping タイムアウト(秒)
# 出力:
#   <ホスト名>: <ステータス>
#   ...
#
#   ステータスは 0:応答あり 1:応答なし 2以上:エラー
# 終了ステータス:
#   常に 0
ping_from_file () {
    local inputfile="$1"
    local hostname
    local dummy

    # 入力ファイルの読み込みチェック
    if [ ! -r "${inputfile}" ]; then
 echo "No such file/Can't read file: ${inputfile}" 1>&2
 return 1
    fi

    # 位置パラメータを以下のようにする
    # $1: result_dir(省略可), $2: timeout(省略可)
    shift

    # 第2要素以降は無視する。すなわち
    #   192.168.1.1 # ルーター
    # のような記述も OK である
    while read hostname dummy; do
 # 空行/コメント行は無視
 case "${hostname}" in
   "")   continue ;;
   "#"*) continue ;;
        esac

 ping_async ${hostname} "$@" > /dev/null
    done < "${inputfile}"

    # ping_async で実行した非同期コマンドの終了を待つ
    wait

    # 位置パラメータを以下のようにする
    # $1: result_dir(省略可)
    if [ $# -ge 2 ]; then
 set -- "$1"
    fi

    while read hostname dummy; do
 # 空行/コメント行は無視
 case "${hostname}" in
   "")   continue ;;
   "#"*) continue ;;
        esac

 ping_check_result ${hostname} "$@"
 echo "${hostname}: $?"
    done < "${inputfile}"

    return 0
}

# メイン処理

# util_common.bash を読み込む(make_tmp_dir/clean_tmp_dir)
FILE_UTIL=util_common.bash
. "${FILE_UTIL}" 2> /dev/null \
    || . "${0%/*}/${FILE_UTIL}" 2> /dev/null
if [ $? -ne 0 ]; then
    echo "Can't load ${FILE_UTIL}!" 1>&2
    exit 1
fi

# 作業ディレクトリの作成
tmpdir=$(make_tmp_dir 2>/dev/null)

if [ $? -ne 0 ]; then
    echo "Can't create temporary directory!" 1>&2
    exit 2
fi

# trap の設定
SIGNALS="HUP INT QUIT PIPE TERM"
trap "{ clean_tmp_dir; exit; }" ${SIGNALS}

# ping 監視の実行
ping_from_file "${PING_HOSTS}" "${tmpdir}"

# 作業ディレクトリの削除と trap 解除
clean_tmp_dir
trap ${SIGNALS}

0 件のコメント: