更新

Value-Domain用のDDNS更新スクリプト・デーモン版

デーモンとして起動するように、色々書き換えた。

設定ファイルの書き方などは前回の記事参照のこと。

致命的なミスが見つかったので、/usr/local/sbin/vdddnsを更新。

/usr/local/sbin/vdddns

#!/usr/bin/perl

use LWP::UserAgent;
use File::Slurp;
use Sys::Syslog;
use POSIX qw(setsid);
use Digest::MD5 qw(md5_hex);

# settings
$interval = 600; # seconds
$save_file = '/var/vdddns/ip';
$conf_hash_file = '/var/vdddns/conf_hash';
$ip_check_uri = "http://dyn.value-domain.com/cgi-bin/dyn.fcg?ip";
$update_uri_template = 'http://dyn.value-domain.com/cgi-bin/dyn.fcg?d=@DOMAIN@&p=@PASSWORD@&h=@HOST@&i=@IP_ADDRESS@';
$conf_file = "/etc/vdddns.conf";



# load conf list
@entries = ();
my $conf_list = read_file($conf_file, err_mode => 'quiet');
unless ($conf_list) {
    print("Could not load the configration file: $conf_file\n");
    die;
} else {
    my @lines = split /\n/, $conf_list;
    foreach my $line (@lines) {
        my @values = split /\s+/, $line;
        if (@values > 0) { # remove empty line
            push(@entries, [@values]);
        }
    }
}

# load conf hash
$conf_hash = read_file($conf_hash_file, err_mode => 'quiet');
unless ($conf_hash) {
    # the first running
    $conf_hash = 'UNSET';
}

$new_conf_hash = md5_hex($conf_list);

# load saved ip address
$ip = read_file($save_file, err_mode => 'quiet');
if (! $ip || ($conf_hash ne $new_conf_hash)) {
    $ip = 'UNSET';
}

# save conf hash
unless (write_file($conf_hash_file, $new_conf_hash)) {
    print( "Could not write conf hash.\n");
    die;
}


# HTTP requset
$ua = LWP::UserAgent->new;
$ip_req = HTTP::Request->new(GET => $ip_check_uri);


# daemon process
if (my $pid = fork) {
    # exit the parent process
    exit 0;

} elsif (defined $pid)  {
    # leave the child process as a daemon
    setsid;
    sleep 2;
    main();
    exit 0;

} else {
    # fork error
    print "Could not create a daemon process\n";
    die;
}

sub main {
    if ($ip eq 'UNSET') {
        print_log("Conf file updated. Trying to update the entries.", 'info');
    } else {
        print_log("Current IP address: $ip", 'info');
    }

    # start observation
    while (1) {
        my $ip_res = $ua->request($ip_req);
        if ($ip_res->is_success) {
            my $new_ip =  $ip_res->content;
            if ($new_ip ne $ip) {
                if ($ip eq 'UNSET') {
                    print_log("Current IP address: $new_ip", 'info');
                } else {
                    print_log("IP address changed: $ip -> $new_ip", 'info');
                }

                $ip = $new_ip;

                unless (write_file($save_file, $ip)) { # save to the file
                    print_log( "Could not write the ip address: $save_file", 'err');
                }

                foreach my $entry (@entries) {
                    # make uri for updating
                    my ($account, $password, $domain, $host) = @{$entry};
                    my $update_uri = $update_uri_template;
                    $update_uri =~ s/\@ACCOUNT\@/$account/g;
                    $update_uri =~ s/\@PASSWORD\@/$password/g;
                    $update_uri =~ s/\@DOMAIN\@/$domain/g;
                    $update_uri =~ s/\@HOST\@/$host/g;
                    $update_uri =~ s/\@IP_ADDRESS\@/$ip/g;

                    # request to update
                    my $update_req = HTTP::Request->new(GET => $update_uri);
                    my $update_res = $ua->request($update_req);
                    if ($update_res->is_success) {
                        # parse result
                        my ($status_code_line, $status_message) = split /\n/, $update_res->content;
                        $status_code_line =~ /^status=(.*)$/;
                        my $status_code = $1;

                        # print result
                        if ($status_code eq "0") {
                            print_log("Updating success: $host.$domain", 'info');
                        } else {
                            print_log("Updating error: $host.$domain $status_message", 'err');
                        }
                    } else {
                        print_log("Updating HTTP error: $update_uri " . $update_res->status_line, 'err');
                    }

                    sleep 10;
                }

                print_log("All updating done.", 'info');
            }
        } else {
            my $trymax = 3;
            for (my $trycount = 0; $trycount < $trymax; $trycount++) {
                print_log ("IP address lookup error: " . $ip_res->status_line, 'warning');
                sleep 60;
            }
            if ($trycount eq $trymax) {
                print_log ("IP address lookup error $try times.", 'err');
            }
        }

        sleep $interval;
    }
}




sub print_log {
    my ($message, $priority) = @_;

    openlog('vdddns', 'cons,pid', 'user');
    syslog($priority, $message);
    closelog();
}

/etc/init.d/vdddns

#!/bin/sh
#
# chkconfig: - 92 34
# description: Starts and stops VDDDNS daemon.
#
# config:  /etc/vdddns.conf


# Source function library.
if [ -f /etc/init.d/functions ] ; then
  . /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
  . /etc/rc.d/init.d/functions
else
  exit 0
fi

# Avoid using root's TMPDIR
unset TMPDIR


RETVAL=0


start() {
        KIND="vdddns"
	echo -n $"Starting $KIND services: "
	daemon /usr/local/sbin/vdddns
	RETVAL=$?
	echo

	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/vdddns || \
	   RETVAL=1
	return $RETVAL
}

stop() {
        KIND="vdddns"
	echo -n $"Shutting down $KIND services: "
	killproc /usr/local/sbin/vdddns
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/vdddns
	return $RETVAL
}

restart() {
	stop
	start
}

rhstatus() {
	status vdddns
}


# Allow status as non-root.
if [ "$1" = status ]; then
       rhstatus
       exit $?
fi



case "$1" in
  start)
  	start
	;;
  stop)
  	stop
	;;
  restart)
  	restart
	;;
  status)
  	rhstatus
	;;
  condrestart)
  	[ -f /var/lock/subsys/vdddns ] && restart || :
	;;
  *)
	echo $"Usage: $0 {start|stop|restart|status|condrestart}"
	exit 1
esac

exit $?

init.dスクリプトは一応手元では動いているが、適当に拾ってきたものを編集しただけなので微妙に変なところがあるかもしれない。

[root@example.com ~]# chkconfig --add vdddns
[root@example.com ~]# chkconfig vdddns on
[root@example.com ~]# service vdddns start

このように打てばデーモンとして起動する。設定ファイルを更新したらservice vdddns restartで再起動すること。設定ファイルが更新されていると、再起動時にIPアドレスが変化していなくても更新作業を行ってくれる。