2008年04月25日

dicedでDDNSのIP更新に失敗したら?

ZoneEditを使っている、ウチの自宅サーバですが、IP更新時のDDNS自動更新にDicedを使ってます。

が、たまに更新に失敗します。。。
しかもリトライしてくれないぽ(´・ω・`)

しかも、何故か家を離れているときに多い(涙

そんなわけで、dicedのログを監視して、IPが変わったのにDDNSのIP更新に失敗したときに、メールで通知するスクリプト(daemon動作可)を書いてみました。
#!/usr/bin/perl -w

use strict;
use POSIX qw(strftime);
use POSIX qw(setsid);

######################################################################
# 設定項目
######################################################################

# デーモンにするか?: 1(オン) / 0 (オフ)
our $DAEMON = 1;

# スリープの間隔(秒)
our $SLEEP_INTERVAL = 600;

# このスクリプトのログファイル名
our $LOG_FILE = "/var/log/chk-diced-log.log";

# diced のログファイル名(パスを含む)
our $DICED_LOG_FILE = "/usr/local/DiCE/log/events.log";

# pidファイル
our $PID_FILE = "/var/run/chk-diced-log.pid";

######################################################################
# メイン処理
######################################################################

&init;
&run;

######################################################################
# サブルーチン
######################################################################

#---------------------------------------------------------------------
# メイン処理
#---------------------------------------------------------------------
sub action {
    # 現在日時を取得
    my $now_time = strftime "%-m/%-d %-H:%M", localtime;

    print "wake at $now_time\n";

    # 自ログから前回の実行時間を取得
    my ($prev_time,$prev_ip) = &get_prev_info;

    # dicedのログをチェック
    my $ng_flg = 0;
    my $current_ip;

    open IN, $DICED_LOG_FILE or die "can't open diced log file!";

    foreach(<IN>){
        if( />(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ){
            # 最新のIPを保持
            $current_ip = $1;
        }
        if( /▲/ ){
            # 実行に失敗しました
            $ng_flg = 1;
        }
        if( /★/ ){
            # 実行されました
            $ng_flg = 0;
        }
    }

    close IN;

    # 失敗したまま&以前のIPと変化があったなら通知を出す
    if( $ng_flg ){
        if( $current_ip ne $prev_ip ){
            # 通知
            &send_notify_mail( $now_time, $current_ip );
            # ログファイル書き出し
            &write_log( $now_time, $current_ip );
        }
    }
}

#---------------------------------------------------------------------
# 自ログから前回の実行時間とその時点のIPを取得
#---------------------------------------------------------------------
sub get_prev_info {
    my @prev_info;
    my $line;
    # ログファイルがなければ、空データを返す
    if( ! -e $LOG_FILE ){
        @prev_info = ( "", "" );
        return @prev_info;
    }

    # ログファイルを開く
    open IN, $LOG_FILE or die "can't open log file!";

    # ログファイルから前回実行時間とその時点のIPを取り出す
    $line = <IN>;
    close IN;
    chomp($line);
    @prev_info = split( /<>/, $line );
    return @prev_info;
}

#---------------------------------------------------------------------
# ログ書き出し
#---------------------------------------------------------------------
sub write_log {
    open OUT, ">$LOG_FILE" or die "can't open log file for writing!";

    print OUT "$_[0]<>$_[1]";

    close OUT;
}

#---------------------------------------------------------------------
# 更新失敗を通知
#---------------------------------------------------------------------
sub send_notify_mail {
    #open OUT, ">>/tmp/hoge.txt";
    #print OUT "send mail at $_[0]\n";
    #print OUT "new ip : $_[1]\n";
    #close OUT;
}

#---------------------------------------------------------------------
# pid書き出し
#---------------------------------------------------------------------
sub write_pid {
    open OUT, ">$PID_FILE";

    print OUT $$;

    close OUT;
}

#---------------------------------------------------------------------
# 割り込みハンドラ
#---------------------------------------------------------------------
sub interrupt {
    my $sig = shift;
    setpgrp;                 # I *am* the leader
    $SIG{$sig} = 'IGNORE';
    kill $sig, 0;            # death to all-comers
    die "killed by $sig";

    exit(0);
}

#---------------------------------------------------------------------
# 初期設定
#---------------------------------------------------------------------
sub init {
    $SIG{INT}  = 'interrupt';         # Ctrl-C が押された場合
    $SIG{HUP}  = 'interrupt';         # HUP  シグナルが送られた場合
    $SIG{QUIT} = 'interrupt';         # QUIT シグナルが送られた場合
    $SIG{KILL} = 'interrupt';         # KILL シグナルが送られた場合
    $SIG{TERM} = 'interrupt';         # TERM シグナルが送られた場合

    # daemon動作?
    if( $DAEMON ){
        # カレントディレクトリを移動しとく。
        chdir '/';
        # 標準入出力を閉じる
        open STDOUT, ">/dev/null";
        open STDIN,  ">/dev/null";
        # 一時的に無視
        $SIG{CHLD} = 'IGNORE';

        my $pid = fork;
        # forkできた?
        if( $pid < 0 ){
            # 失敗
            exit -1;
        }elsif( $pid ){
            # 成功したので、親側はさようなら。
            exit 0;
        }

        # 子プロセスで動く処理
        # 無視してたのを戻す
        $SIG{CHLD} = 'DEFAULT';
        # プロセスIDを書き出しておく
        &write_pid or exit 1;

        # セッションを新しく始める
        setsid or die "Can't start a new session: $!";
        # 標準エラーも閉じる
        open STDERR, ">/dev/null";
    }
}

#---------------------------------------------------------------------
# デーモン動作
#---------------------------------------------------------------------
sub run {

    while(1) {

        # SLEEP_INTERVAL ごとに action を呼び出す
        &action;

        sleep($SLEEP_INTERVAL);
    }
}

1;

サービス登録用のスクリプトは近日中には。。。
posted by nariki at 16:40| Comment(0) | TrackBack(0) | 自宅サーバ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック