--[[

  ●ping 応答監視スクリプト
  指定したアドレスに宛に ping を実行してその応答を監視し、応答がなかった場合
  に管理者にメールを送信して知らせるスクリプトです。ping を実行する宛先IPアド
  レスは複数指定することができます。

  指定した回数連続して ping に対する応答がなかった場合には、管理者にメールを
  送信して知らせます。その後、指定した回数連続して応答があった場合には、応答
  が回復したと判断します。設定値 down_mail を true に設定している場合には、応
  答が回復した際にもメールを送信します。

  <説明>
  ・このファイルを RTFS か外部メモリに保存してください。
  ・本項目の config の設定では schedule at コマンドでルーター起動時に Lua スク
    リプトが実行されるように設定しています。
  ・スクリプトを停止するときは terminate lua コマンドを実行してください。
  ・再度、Lua スクリプトを実行する場合は lua コマンドで実行してください。
  ・★マークの付いた設定値は変更が可能です。

  <ノート>
 ・メールの送信失敗時に出力する SYSLOG レベルを指定可能です。
  SYSLOG のレベルを指定するには、log_level を設定してください。
  debug レベル、notice レベルの SYSLOG を出力するためには、それぞれ以下の設定
  が必要です。
   debug レベル ・・・ syslog debug on
   notice レベル・・・ syslog notice on
 ・本スクリプトファイルを編集する場合、文字コードは必ず Shift-JIS を使用してく
  ださい。

]]


--------------------------##  設定値  ##--------------------------------

-- 監視間隔(1 - 864000 秒)
idle_time = (監視間隔)			-- ★

-- ping を実行する宛先 IP アドレス
dst_tbl = {				-- ★
	"(宛先 IP アドレス1)",
	"(宛先 IP アドレス2)"
}

-- ping への応答がない、または応答が回復したと判断する連続回数(1, 2 ..)
count = (回数)				-- ★

-- 応答が回復したときにもメールを送るかどうか(送る: true / 送らない: false)
down_mail = (true / false)		-- ★

-- メールの設定
mail_tbl = {				-- ★
	smtp_address = "(SMTP サーバのアドレス)",
	from = "(送信元メールアドレス)",
	to = "(宛先メールアドレス)"
}

-- メールの送信に失敗したときに出力する SYSLOG のレベル(info, debug, notice)
log_level = "(SYSLOG レベル)"		-- ★

----------------------##  設定値ここまで  ##----------------------------

------------------------------------------------------------
-- ping を実行し、到達したかどうかを返す関数              --
------------------------------------------------------------
function ping_reach(adr)
	local rtn, str, loss
	local reach = false
	local cmd = "ping " .. adr
	local ptn = "(%d+)%.%d+%%"

	rtn, str = rt.command(cmd)
	if (rtn) and (str) then
		loss = str:match(ptn)
		if (loss) then
			loss = tonumber(loss)
			if (loss == 0) then
        			reach = true
      			end
    		end
  	end

	return rtn, reach, str
end

--------------------------------------------------------------
-- 連続何回 ping に応答がないかを示すカウンターの処理関数   --
--------------------------------------------------------------
function count_proc(t, reach, th)
	local rtn = 0

	if (not reach) then
		if (not t.flag) then
			t.ng = t.ng + 1
			if (t.ng == th) then
				rtn = 1
				t.flag = true
			end
		else
			if (t.ok > 0) then
				t.ok = 0
			end
		end
	else
		if (t.flag) then
			t.ok = t.ok + 1
			if (t.ok == th) then
				rtn = -1
				t.flag = false
				t.ng = 0
				t.ok = 0
			end
		else
			if (t.ng > 0) then
				t.ng = 0
			end
		end
	end

	return rtn
end

------------------------------------------------------------
-- メール本文を作成する関数                               --
------------------------------------------------------------
function make_pingmsg(tbl, reach, adr, cnt, sec, down)
	local rtn
	local str = ""

	rtn = count_proc(tbl, reach, cnt)
	if (rtn < 0) then
		if (down) then
			str = "pingの応答が回復しました。\r\n"
			str = str .. string.format("  送信先: %s\r\n  監視間隔: %d(秒)\r\n\r\n",adr, sec)
		end
	elseif (rtn > 0) then
		str = "pingの応答がありません。\r\n"
		str = str .. string.format("  送信先: %s\r\n  応答がなかった回数: %d回\r\n  監視間隔: %d(秒)\r\n\r\n",
		 		adr, cnt, sec)
	end

	return str
end

------------------------------------------------------------
-- 連続不応答回数を記録するテーブルの初期化関数           --
------------------------------------------------------------
function init_count_tbl(n, cnt_t)
	for i = 1, n do
		cnt_t[i] = {ng = 0, ok = 0, flag = false}
	end
end

------------------------------------------------------------
-- 現在の日時を取得する関数                               --
------------------------------------------------------------
function time_stamp()
	local t

	t = os.date("*t")
	return string.format("%d/%02d/%02d %02d:%02d:%02d", 
		t.year, t.month, t.day, t.hour, t.min, t.sec)
end

------------------------------------------------------------
-- メインルーチン                                         --
------------------------------------------------------------
local rtn, reach, str, adr
local cnt_tbl = {}

init_count_tbl(#dst_tbl, cnt_tbl)

while (true) do
	mail_tbl.text = ""

	for i, adr in ipairs(dst_tbl) do
		rtn, reach, str = ping_reach(adr)
		if (rtn) then
			mail_tbl.text = mail_tbl.text .. make_pingmsg(cnt_tbl[i], reach, adr,
									count, idle_time, down_mail)
		else
			mail_tbl.text = string.format("%s (ping送信先: %s\r\n\r\n)", str, dst_tbl[i])
		end
	end

	if (mail_tbl.text:len() > 0) then
		mail_tbl.subject = string.format("watch ping : multi destination (%s)", time_stamp())
		rtn = rt.mail(mail_tbl)
		if (not rtn) then
			rt.syslog(log_level, "failed to send mail. (Lua スクリプトファイル名)")
		end
	end

	rt.sleep(idle_time)
end