Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
#!/usr/bin/perl
use strict;
use English qw/ $PID /;
use FileHandle;
use File::Basename qw/ basename /;
use Getopt::Long qw/ :config no_ignore_case /;
use Sys::Syslog;
use Time::Local;
use Time::localtime;
use File::stat;
# バージョン番号
my $VERSION = sprintf( '0.%d.%02d', q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/ );
my $BASENAME = &basename( $0, ".perl" );
=head1 NAME
ppplog - PPP の接続記録を集計するスクリプト
=head1 SYNOPSIS
ppplog [OPTIONS] [FILES...]
=head1 DESCRIPTION
一般的な Linux システムでは,pppd(8) の接続記録は syslog(3) を通じて
F に蓄積されている.F は,この接続記録を集
計し,通話時間・料金を計算して標準出力に出力する.
=head1 OPTIONS
=over 4
=item -a
=item --all
全ての接続記録を集計する(デフォルトは,前回集計時の mark 以前の記録は
集計しない).
=cut
my $ALL;
=item -q
=item --quiet
集計結果を標準出力に出力しない(デフォルトは出力する).
=cut
my $QUIET;
=item -l C
=item --log-facility=C
syslog に実行記録を送るときに用いる facility を指定する.明示的な指定
がなければ,I が用いられる.このオプションには,pppd と同じ
facility と使うように設定しておくべきである.
F は,syslog に記録された自分自身の動作記録を利用して,集計済
みの PPP 接続記録を検出している.言い換えれば,PPP の接続記録と,この
スクリプトの動作記録が同一の記録ファイルに記録されていなければならない.
そのためには,同じ facility を利用するのが最も簡単で確実な方法である.
Debian では I が用いられているが,その他の環境では I
が用いられていることが多い.
=cut
my $FACILITY = "local2";
# syslog に実行記録を送らずに動作確認だけを行う.
my $DEBUG;
&GetOptions( 'all|a+' => \$ALL,
'quiet|q+' => \$QUIET,
'log-facility|l=s' => \$FACILITY,
'debug|d+' => \$DEBUG );
=back
=head1 FILES
=over 4
=item F
=item F
ファイルが明示的に指定されなかった場合は,これらの2つのファイルを対象
として接続記録を集計する.
=back
=cut
my( @LOGFILES ) = @ARGV;
unless( @LOGFILES ){
@LOGFILES = ( '/var/log/messages', '/var/log/messages.0' );
}
my $MARK = sprintf( '^(\w+) +(\d+) (\d+):(\d+):(\d+) \w+ %s(?:\.perl)?\[(\d+)\]: (MARK)\s*$',
quotemeta($BASENAME) );
unless( $ALL or $DEBUG ){
openlog( $BASENAME, 'pid', $FACILITY );
syslog( "info", "MARK" )
}
#======================================================================
# 接続記録を解析
#======================================================================
my $START; # 集計を開始した時間
my $END; # 集計を終了した時間
my $SECOND; # 通信時間の合計
my $CHARGE; # 通信料金の合計
my $COUNT; # 通信回数
my @LOG;
my @ERROR;
my $RETRY;
TOTAL: {
my $mark = ( $ALL or $DEBUG ) ? 1 : 0;
my $end; # 通話が終了した時間
my $ppid; # 通話を終了した pppd の PID
for my $file ( @LOGFILES ){
my $fh = new FileHandle( $file, "r" );
unless( $fh ){
&adderror( "Can't open $file: %s", $! );
next;
}
for( reverse( $fh->getlines ) ){
if( my( $mon, $mday, $hour, $min, $sec, $pid, $event ) = /$MARK/o ){
if( $mark++ == 0 ){
if( $PID == $pid ){
# 自分自身の集計開始 mark が発見された時間を記録する
# => この時間までの接続記録が集計の対象となる
$END = &logtime( $sec, $min, $hour, $mday, $mon );
} else {
# 自分自身の集計開始 mark が発見できなかった場合
$fh->close;
if( $RETRY++ < 5 ){
redo TOTAL;
} else {
&adderror( "No marker was found. Check the syslog facility of this script." );
last TOTAL;
}
}
} else {
# 以前の集計時の mark が発見された時間を記録する
# => この時間以降の接続記録が集計の対象となる
$START = &logtime( $sec, $min, $hour, $mday, $mon );
}
}
elsif( ! $mark ){
# 集計開始の mark が見つかるまで読み飛ばす.
next;
}
elsif( ( $mon, $mday, $hour, $min, $sec, $pid, $event ) =
/^(\w+) +(\d+) (\d+):(\d+):(\d+) \w+ pppd\[(\d+)\]: (Connect:|Connection terminated\.)/ ){
my $time = &logtime( $sec, $min, $hour, $mday, $mon );
if ( $event eq 'Connect:' ) {
if( ! $end and $SECOND == 0 ){
# 次回集計時に対象となる接続記録なので無視する
;
}
elsif( $time <= $end and $ppid == $pid ){
my $lapse = $end - $time;
my $charge = &charge( $time, $end );
$SECOND += $lapse;
$CHARGE += $charge;
$COUNT++;
&addlog( "%6d: %s %s -> %s (%d)",
$lapse,
&date_string($time),
&time_string($time),
&time_string($end),
$charge );
}
elsif( ! $end ){
&adderror( "Missing the closing log of the connection opened at %s.",
ctime($time) );
&addlog( " : %s %s -> unknown",
&date_string($time), &time_string($time) );
}
elsif( $time > $end ){
&adderror( "The connection closed at %s was opened in the future at %s.",
ctime($end), ctime($time) );
}
else { # $ppid != $pid
&adderror( "Missing the closing log of the connection opened at %s.",
ctime($time) );
}
$end = 0;
$ppid = 0;
} else { # Disconnect.
if( $end ){
&adderror( "Missing the opening log of the connection closed at %s.",
ctime($end) );
&addlog( " : unknown -> %s %s",
&date_string($end), &time_string($end) );
}
$end = $time;
$ppid = $pid;
}
# 前回集計時の mark の直前の動作記録が発見された時点で終了する
last TOTAL if ( ! $ALL and $mark >= 2 );
}
}
$fh->close;
}
$mark or &adderror( "No marker was found. Check the syslog facility of this script." );
}
sub addlog ($@) {
my( $format, @arg ) = @_;
push( @LOG, sprintf( $format."\n", @arg ) );
}
sub adderror ($@) {
my( $format, @arg ) = @_;
push( @ERROR, sprintf( $format."\n", @arg ) );
}
#======================================================================
# 解析結果を出力
#======================================================================
unless( $QUIET ){
unless( $ALL ){
$END ||= time;
printf( "PPP Connection Log Summary\n from %s %s to %s %s\n\n",
&date_string( $START ), &time_string( $START ),
&date_string( $END ), &time_string( $END ) );
}
printf( "Telephone charge: %6d yen.\n", $CHARGE );
printf( "Elapsed time: %7d seconds / %d times = %.2d seconds.\n\n",
$SECOND, $COUNT, $SECOND / $COUNT );
@ERROR and print( "Errors:\n", reverse(@ERROR), "\n" );
print "Connections:\n", reverse(@LOG);
}
if( ! $DEBUG and @LOG ){
syslog( "info", "telephone charge = %d yen", $CHARGE );
syslog( "info", "elapsed time = %d seconds", $SECOND );
}
exit 0;
#======================================================================
# 内部関数
#======================================================================
# 指定された日付データを UNIX 形式の時刻表示に変換する関数
sub logtime {
my( $sec, $min, $hour, $mday, $mon ) = @_;
my %mon = qw/ Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5 Jul 6 Aug 7 Sep 8 Oct 9 Nov 10 Dec 11 /;
$mon = $mon{$mon};
timelocal( $sec, $min, $hour, $mday, $mon,
localtime->year() + ( ( $mon > localtime->mon() ) ? -1 : 0 ) );
}
# 指定された時間から YYYY/MM/DD 形式の文字列を生成する関数
sub date_string {
my( $time ) = @_;
sprintf( "%04d/%02d/%02d",
localtime($time)->year + 1900,
localtime($time)->mon + 1,
localtime($time)->mday );
}
# 指定された時間から HH:MM:SS 形式の文字列を生成する関数
sub time_string {
my( $time ) = @_;
sprintf( "%02d:%02d:%02d",
localtime($time)->hour,
localtime($time)->min,
localtime($time)->sec );
}
# 通信料金を計算する関数
sub charge {
my( $start, $end ) = @_;
my $unit = ( localtime($start)->hour >= 22 && localtime($end)->hour < 6 ) ? 240 : 180;
use integer;
( ( $end - $start + $unit + 10 ) / $unit ) * 10;
}
=head1 USAGE
本スクリプトを Debian で使用する場合は,F にこのス
クリプトをインストールすると良い.
Example:
# install -o root -g root -m 755 ppplog.perl /etc/cron.weekly/ppplog
これだけで,PPP の接続記録が週毎に集計され,root 宛にメールされるよう
になる.
他の環境で利用する場合は cron(8) などを参照.
=head1 SEE ALSO
pppd(8), syslog(3), cron(8).
=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/12/13 13:04:22 $
=cut