lsof コマンドでポートが開いているかどうか監視するシェルスクリプト

経緯

自前プログラムで特定のポートを開いて待ち受けている仕掛けを稼働させているのですが、今度はこのポートがきちんと開いているかどうかを確認したくなりました。

そこで以下の内容を考えました。

  • 10分おきに cron でシェルスクリプトを実行。
    • lsof コマンドで特定ポート (今回は 3102) が開いているかをチェック。
      • ポートが開いていない場合は警告メールを送信する。
  • 上述とは別に一定時間おき( 3:10 と 10:02 )に同じシェルスクリプトを実行。
    • 正常の場合に定期レポートを送信する。
  • とりあえず動けば良いので日本語(文字コードやマルチバイト文字)の問題は考えないものとする。

成果物

早速ですが成果物を。

#!/bin/sh

# constants: port number
THREE=3
A=10
TWO=2
## 3102
PORT="$THREE$A$TWO"

# variables: time
HOUR=`TZ='Asia/Tokyo' date '+%-H'`
MINUTES=`TZ='Asia/Tokyo' date '+%-M'`

# constants: mail
FROM=johndoe@example.jp
TO=janedoe@example.jp


# main process
if [[ "$(uname -n):$PORT" == "$(lsof -i:$PORT|awk '{print $NF}'|grep "$PORT")" ]];then
    if [ "$THREE" = "$HOUR" -a "$A" = "$MINUTES" ] || [ "$A" = "$HOUR" -a "$TWO" = "$MINUTES" ] ;then
        sendmail -f $FROM -t <<EOL
From: <$FROM>
To: <$TO>
Subject: [SATORI] Regular Report

This email is a regular report.


EOL

    fi
else
    sendmail -f $FROM -t <<EOL
From: <$FROM>
To: <$TO>
Subject: [KOISHI] REQUIRE ACTION TO OPEN PORT

PLEASE TAKE ACTION TO OPEN PORT:$PORT ASAP!


EOL

fi

本当に最小限を最短で実装するため愚直な書き方をしています。ベッタベタにハードコーディングしていますし。

if [[ "$(uname -n):$PORT" == "$(lsof -i:$PORT|awk '{print $NF}'|grep "$PORT")" ]];then

肝はこの if 文ですね。

  • 右辺を lsof -i:{ポート番号}awkgrep で絞り込みをして、 {ホスト名}:{ポート番号} の文字列のみになるように調整
  • これと左辺の uname -n で出力されるホスト名にコロンとポート番号を繋いだ文字列が
  • 同値になるかどうか

で判定しています。

メール送信は sendmail -f $FROM -t ... と、 sendmail コマンドで Envelope From を -f オプションで書き換えて送信しています。

他は細々と条件や時・分のみを現在時刻から抽出して比較したりして定期レポートを送信するかどうか判定する、という感じです。

やや古めの環境想定なのであえて -a を使っています。

参考

lsof

sendmail

awk

uname, ホスト名出力

date

シェルスクリプト

変数に改行を含める

ヒアドキュメント

if文

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。