#!/usr/bin/perl use strict; use English qw/ $POSTMATCH /; use FileHandle; use File::Basename qw/ basename /; use POSIX qw/ SEEK_END EAGAIN /; use Sys::Syslog qw/ openlog syslog closelog /; # バージョン番号 my $VERSION = sprintf( '0.%d.%02d', q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/ ); my $BASENAME = &basename( $0, ".perl" ); =head1 NAME safeconnect - ondemand 接続を行う PPP リンク上で安全に接続する =head1 SYNOPSIS safeconnect [OPTIONS] HOST PORT =head1 DESCRIPTION ondemand 接続を行う PPP リンク上で安全に通信を開始するための connect(1) のラッパースクリプト.指定されたオプション,接続先ホスト名 およびポート番号は,最終的には全て connect によって処理される. pppd(8) に I オプションを指定して,ondemand 接続を行うように設 定している場合,ppp インタフェースの IP アドレス は起動時に設定済みで ある.そのため,ISP によって動的に IP アドレスが割り当てられる場合, PPP 接続の確立時にインターフェースの IP アドレスが変化し,既存の接続が 破壊されることがある. この問題を避けるため,このスクリプトは以下の手順で接続を行う. =over 4 =item (1) connect を使って指定されたサーバに最初の接続を行う. =item (2) 既に PPP 接続が確立している場合は接続に成功する. 確立していなかった場合は dialup が始まるので,その間に connect を強制 終了させ,F を監視して接続が確立されるのを待つ. =item (3) 実際に connect を実行して,指定されたサーバに接続する. =back =cut # 実際に実行する connect コマンドのパス my $PROG = "/usr/bin/connect"; # pppd の接続記録が保存されているファイル my $LOGFILE = "/var/log/messages"; # 接続記録の末尾 $SIZE byte だけを調べる対象とする my $SIZE = 4096; # 最初に接続状態の確認のために実行される connect コマンドの接続待ち時間 my $TIMEOUT = 20; # 接続記録を syslog に記録する場合の属性 my $FACILITY = "daemon"; # デバッグオプションが指定されているか? my $DEBUG = grep( $_ eq "-d", @ARGV ); # 本体 if( $FACILITY and ! $DEBUG ){ &openlog( $BASENAME, 'pid', $FACILITY ); &syslog( "info", join( ' ', $PROG, @ARGV ) ); &closelog(); } &watch() unless &check(); &debug( "2nd connect" ); &start(); # connect を実行して,ネットワーク接続が利用可能な状態か検査する関数 sub check { &debug( "check()" ); # 子プロセスを fork する for( my $i; $i < 5; $i++ ){ if( my $pid = fork ){ # 親プロセス側の処理 local $SIG{ALRM} = sub { die "SIGALRM is received\n"; }; eval { alarm $TIMEOUT; waitpid( $pid, 0 ); alarm 0; }; if( $@ =~ /SIGALRM is received/ ){ # timeout した場合は,必ず子プロセスを終了させておく必 # 要がある. &debug( "Timeout occured" ); kill 9, $pid; return 0; } else { &debug( "Connection succeeded" ); return 1; } } elsif( defined $pid ){ # 子プロセス側の処理 STDIN->close; &debug( "1st connect" ); if( $DEBUG ){ &start(); } else { my $null = new FileHandle( "/dev/null", "w" ); if( $null ){ STDOUT->fdopen( $null, "w" ); STDERR->fdopen( $null, "w" ); &start(); } } exit 0; # Not reach. } elsif( $! == &EAGAIN ){ sleep 5; next; } else { die "Can't fork $PROG: $!\n"; } } die "Can't fork $PROG: retry conter exceeded\n"; } # $LOGFILE を監視して,pppd が接続を確立するまで待機する関数 sub watch { &debug( "watch()" ); my( $fh, $data, $pid ); while( $fh = new FileHandle( $LOGFILE, "r" ) ){ $fh->seek( -$SIZE, &SEEK_END ); $fh->read( $data, $SIZE ); $fh->close; while( $data =~ /\spppd\[(\d+)\]: Starting link/m ){ $pid = $1; $data = $POSTMATCH; } last if $data =~ /\spppd\[$pid\]: Local IP address changed to/m; sleep 5; } } # 実際に connect を実行する関数 sub start { &debug( "start()" ); exec( $PROG, @ARGV ); } sub debug { if( $DEBUG ){ STDERR->print( "DEBUG: ", sprintf( shift @_, @_ ), "\n" ); STDERR->flush; } } =head1 SEE ALSO connect(1) または http://www.imasy.or.jp/~gotoh/ssh/connect.html =head1 AUTHOR =over 4 =item TSUCHIYA Masatoshi =back =head1 COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, you can either send email to this program's maintainer or write to: The Free Software Foundation, Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA. Last Update: $Date: 2002/11/30 14:10:21 $ =cut