#!/usr/bin/perl #========================================================================================= # ログ等のバックアップスクリプト batch.pl # (リネーム機能付きFTPクライアント) # by ymch #========================================================================================= #----- Usage & Function ----- # ダウンロードしたファイルを「日付(6桁)ファイル名」という形で、上書きではなく別名保存します。 # ex) 20050607access_log 20050608access_log ...etc... # # Windowsでしたらperlをインストールし、「perl batch.pl」とだけ書いたバッチファイルを用意して # タスクに登録。Unix系でしたら同様にシェルスクリプトを用意して Crontab にでも食わせればOK。 # これを使わないことには話にならない。 use Socket; # バッファ防止 $| = 1; # ログイン情報 $username = 'userID'; $password = 'Password'; $hostname = 'ftp.host.ne.jp'; # ターゲットファイルの指定(サーバ側) # FTPでアクセスする際のパスで指定する。カンマで区切って複数指定可。 @target_file = ( "/var/log/httpd/access_log", "/var/log/httpd/error_log" ); # ローカルパス(保存先)の指定(相対パスで指定) # 上記のファイル順に対応。カンマで区切って複数指定可。 @local_path = ( "./AccessLog/", "./ErrorLog/" ); $verbose = 1; # 処理内容の表示・記録をするかどうか。 # …というか、この辺の処理はきちんと実装してませんので、 # 1のままにしといてください。 # ---------------------------------------------------------------------------------------- # ファイル名の先頭に日付を付けるので… ($sec, $min, $hour, $mday, $mon, $year) = localtime(time); $mon = $mon+1; $year = $year+1900; $date = sprintf("%04d%02d%02d", $year, $mon, $mday); $datelog = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec); # ローカルファイル名の決定(日付+ファイル名)→ @outfile foreach $target_file(@target_file) { # ↓拡張子があるファイルの場合は /([A-Za-z0-9_]+\.[a-z0-9]+)/ に適宜書き換える if ( $target_file =~ /([A-Za-z0-9_]+_[a-z0-9]+)/ ) { $filename = $1; $name_tmp = $date . $filename; push(@outfile,$name_tmp); } } # 通信ログファイルの指定とファイル書き込みモードOPEN open(LOG, "> ./Backup.log") || die "Can not open: $!"; #----------------- 処理の詳細表示 ------------------- if ( $verbose ){ print LOG "\$\$ Backup Start at $datelog \$\$\n\n"; print STDERR "\$\$ Backup Start at $datelog \$\$\n\n"; print LOG "--------------------------------------------------------------------------------\n"; print STDERR "--------------------------------------------------------------------------------\n"; print LOG "hostname = $hostname\n"; print STDERR "hostname = $hostname\n"; print LOG "username = $username\n"; print STDERR "username = $username\n"; print LOG "password = *********\n"; print STDERR "password = *********\n"; foreach $target_file(@target_file) { print LOG "filename = $target_file\n"; print STDERR "filename = $target_file\n"; } $i=0; foreach $outfile(@outfile) { print LOG "outfile = $local_path[$i]$outfile\n"; print STDERR "outfile = $local_path[$i]$outfile\n"; $i++; } print LOG "--------------------------------------------------------------------------------\n\n"; print STDERR "--------------------------------------------------------------------------------\n\n"; } #============= コマンド用のコネクションを作成してログインする ==================== $port = getservbyname('ftp', 'tcp'); &client_work(COMMAND, $hostname, $port); &read_response; &send_command("USER $username\r\n"); &read_response; &send_command("PASS $password\r\n"); $ret = &read_response; if ( $ret =~ m/^5\d\d/ ){ print LOG "ログインできませんでした。\n"; print STDERR "ログインできませんでした。\n"; exit; } $ii = 0; # ローカルファイル指定用の添え字 foreach $target_file(@target_file) { &send_command("TYPE I\r\n"); # BINARYモードにしておかないと改行が 0D 0D 0A となってしまう。 &read_response; #=============== データコネクションをオープンする =========================== # ルータの内側なので当然 Passive モード &send_command("PASV\r\n"); $ret = &read_response; # レスポンスから情報を取得 if ( $ret =~ m/^2\d\d .*\((\d+,\d+,\d+,\d+),(\d+),(\d+)\)/ ){ $data_connection_port = $2*256+$3; $data_connection_host = $1; $data_connection_host =~ s/,/\./g; } else { print LOG $ret; print STDERR $ret; exit; } #================== RETR を送信(って、見れば分かるわな…(^_^ゞ) ===================== &send_command("RETR $target_file\r\n"); #======================== データ送信/受信 ============================= # 親プロセス if ( $pid = fork() ){ $ret = &read_response; # エラー。子プロセスを殺す if ( $ret =~ m/^5/ ){ print LOG $ret; print STDERR $ret; kill 'TERM', $pid; } else { &read_response; } # これは子プロセス…。データコネクションから読み込む処理です。 } else { &client_work(DATA, $data_connection_host, $data_connection_port); open(OUT, ">$local_path[$ii]$outfile[$ii]") || die "$local_path[$ii]$outfile[$ii]: $!"; print OUT ; close(OUT); close(DATA); exit; # これがないと子プロセスが終了しない。 } $ii++; } # QUITを送ってセッションを終了させる。 &send_command("QUIT\r\n"); &read_response; close(COMMAND); close(LOG); exit; #=============================================================================== # サーバにデータを送信ルーチン #=============================================================================== sub send_command { $hide = $_[0]; $hide =~ s/$password/*********/g; # パスワードはログに記録しない。 $hide =~ s/\r\n/\n/g; $verbose && print LOG "$hide"; $verbose && print STDERR "$hide"; print COMMAND $_[0]; } #=============================================================================== # サーバレスポンス読み込みルーチン #=============================================================================== sub read_response { $buf = ; #$buf =~ s/\r\n/\n/g; $verbose && print LOG "--> $buf"; $verbose && print STDERR "--> $buf"; # 複数行のレスポンス。 if ( $buf =~ m/^(\d\d\d)-/ ){ $return_code = $1; while (){ $_ =~ s/\r\n/\n/g; $verbose && print LOG "--> $_"; $verbose && print STDERR "--> $_"; $buf .= $_; if ( /^$return_code / ){ last; } } } return $buf; } #=============================================================================== # ソケット名・ホスト・ポート番号を引数にして接続するルーチン #=============================================================================== sub client_work { ($SOCK, $host, $port) = @_; # ホスト名を、IP アドレスの構造体に変換する。 unless ( $iaddr = inet_aton($host) ){ die "$host は存在しないホストです。$!"; } # ポート番号と IP アドレスをまとめて構造体に変換する。 $sock_addr = pack_sockaddr_in($port, $iaddr); # ソケット生成(って当たり前の処理です。) socket($SOCK, PF_INET, SOCK_STREAM, 0) || die "ソケットを生成できません。\n"; # 指定のホストの指定の port に接続させる。 connect($SOCK, $sock_addr) || die "$hostname のポート $port に接続できません。\n"; # ファイルハンドルをバッファリングさせない。 select($SOCK); $|=1; select(STDOUT); }