#!/usr/local/bin/perl # Web Counter Ver. 0.29 # USAGE : # 1) webcnt.cgi?test --- テスト # 2) webcnt.cgi? --- カウント # 3) webcnt.cgi?hide --- 隠しカウント # 4) webcnt.cgi?cmt+xxx --- ログに'xxx'とコメントを付加する 指定無の場合はカウンター名 # 5) http://〜/cgi-bin/webcnt.cgi?syuukei --- アクセス状況の集計 # *) SSIのコマンドモードやコマンドラインでの実行の場合は'?'や'+'は空白にしてください ####################### # 設定 ####################### $print_http_header = 0; $column = 6; $lock_timeout = 240; $file_lock_enable = 1; $address_check_enable = 1; $cnt_timeout = 60; @reject_domain = (""); @reject_address = (""); $create_history = 1; $log_max = 1000; $script_dir = ''; $my_url = ''; ####################### # 設定項目の説明 ####################### # $print_http_header : "Content-type: text/html"の出力 (0:Off / 1:On) # : 集計、テスト等のCGIモードでは強制表示 # $column : 表示桁数 ('0'を補間して指定桁数で表示。0の場合は有効な数字のみ) # $lock_timeout : ロックファイルタイムアウト ファイルロックのタイムアウト (秒) # $file_lock_enable : ファイルロック (0: Off / 1: On) # $address_check_enable : アドレスチェック (0: Off / 1: On) # 以前にアクセスされたかをチェックしカウントを制限する # $cnt_timeout : カウントタイムアウト(アドレスチェックが有効のときに有効) # 最後のアクセスから次のカウントを有効にするまでの時間(分) # カウントを行う ( 0は日付が変わるまで) # @reject_domain : カウントしないドメイン # ex. @reject_domain = ("foo.var.com", "abc.org"); # *.foo.var.com, *.abc.org はカウントしない # @reject_address : カウントしないIPアドレス # ex. @reject_address = ("172","192.168.19.101-128","200.1.1.1"); # 172.*, 192.168.19.101〜192.168.19.128, 200.1.1.1 # のアドレスからはカウントしない # "202.19"と指定すると 202.19.*.* や 202.19?.*.* が対象になります #   202.19.* を対象にしたい場合は "202.19." と指定してください # reject_domain,reject_address に書かれた場合はログも出力しません # $create_history : アクセスログのヒストリー作成 ( 0: Off / 1: On ) # $log_max : ログファイルの最大行数 # $script_dir : スクリプトのある絶対ディレクトリ cmd=で実行する場合のみ # $my_url : 自分のウェブサイトのURL (リンク元がこの中のページの場合は # ログ中のリンク元を「OwnPage」とします ################# # 使用ファイル ################# $my_name = 'webcnt'; $counter_file = "$script_dir" . "$my_name" . ".cnt"; $access_log = "$script_dir" . "$my_name" . ".acs"; $bkp_log = "$script_dir" . "$my_name" ."_bkp.acs"; $lock_file = "$script_dir" . "lock/$my_name" . "_lck"; $sum_dom = "$script_dir" . "$my_name" . ".s1"; $sum_idx = "$script_dir" . "$my_name" . ".s2"; $cmnt = "$script_dir" . "$my_name"; # $my_name : カウンター名 # $counter_file : カウンターファイル (カウント数を保存) # $access_log : アクセスログ(過去にアクセスのあったアドレスを保存) # $bkp_log : アクセスログヒストリー (1世代前のログ) # $lock_file : ロックファイル # $sum_dom : ドメイン毎集計ファイル # $sum_idx : ページインデックス毎の集計ファイル # $cmnt : ログに書き出すコメント(ページインデックス) ####################### # 処理 ####################### # オプションの判断 $mode = ""; for ($i = 0 ; $i <= $#ARGV ; $i++) { if ($ARGV[$i] eq "test") { $mode="test"; } elsif ($ARGV[$i] eq "hide") { $mode = "hide"; } elsif ($ARGV[$i] eq "name") { $my_name = $ARGV[++$i]; $counter_file = "$script_dir" . "$my_name" . ".cnt"; $access_log = "$script_dir" . "$my_name" . ".acs"; $bkp_log = "$script_dir" . "lock/$my_name" .".acs.bkp"; $lock_file = "$script_dir" . "lock/$my_name" . "_lck"; } elsif ($ARGV[$i] eq "cmt") { $cmnt = $ARGV[++$i]; } elsif ($ARGV[$i] eq "syuukei") { $mode = "syuukei"; } } if ($mode eq "test" ) { # テスト &cgi_test; } elsif ($mode eq "syuukei") { # 集計 &syuukei("text"); } else { # カウント &go_count; } exit(0); ############################################################################ # サブルーチン ############################################################################ # ロックファイルの残留防止 sub sigexit { rmdir($file_lock); exit(0); } $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "&sigexit"; # CGIのテスト sub cgi_test { print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "テスト\n"; print "\n"; print "\n"; print "\n"; print "スクリプトは動作可能です。\n"; print "\n"; print "


\n"; print "\n"; if ( -d $lock_file ) { print "ロックファイル : $lock_file が残っています

\n"; } if ( ! -r $counter_file ) { print "カウンターファイル : $counter_file が存在しません

\n"; } elsif ( -w $counter_file ) { print "カウンターファイル : $counter_file が書き込み可能でありません

\n"; } if ( ! -r $access_log ) { print "アクセスログ : $access_log が存在しません

\n"; } elsif ( -w $access_log ) { print "アクセスログ : $access_log が書き込み可能でありません

\n"; } print "\n"; print "\n"; } # カウント sub go_count { $count_step = 1; # タイムゾーンの設定 $ENV{'TZ'} = "JST-9"; # 日付の取得 ($sec, $min, $hour, $mday, $mon, $year) = localtime(time()); $year += 1900; $ndate = sprintf("%04d/%02d/%02d", $year, $mon + 1, $mday); $ntime = sprintf("%02d:%02d:%02d", $hour, $min, $sec); # クライアント情報 $raddr = $ENV{'REMOTE_ADDR'}; $rhost = $ENV{'REMOTE_HOST'}; $ragnt = $ENV{'HTTP_USER_AGENT'}; # リンク元 自分のページの場合は OwnPage と出力 $refer = $ENV{'HTTP_REFERER'}; $refer =~ s/%[0-9a-fA-F][0-9a-fA-F]/pack("C", hex($1))/eg; if ( $refer =~ /$my_url/ ) { $refer = "OwnPage"; } if ( $file_lock_enable ) { # ファイルのロック &file_lock; } # カウンターの取得 if ( open(CNT, "< $counter_file") ) { $wrk = ; ($count, $acnt) = split(/:/, $wrk); close(CNT); } else { $count = -1; $acnt = -1; } # カウントしないドメインやアドレスのチェック $reject = &rej_chk; $count_step = $reject; if ($address_check_enable && $count_step == 1 ) { # アドレスチェック $count_step = &add_chk; } # カウントアップ $acnt : 全通しカウント、$count 通常カウント $acnt++; if ($count >= 0 && $count_step > 0) { $count++; } if ( $column == 0 ) { $scount = sprintf("%ld",$count); $acount = sprintf("%ld",$acnt); } else { $scount = sprintf(sprintf("%%0%dld", $column), $count); $acount = sprintf(sprintf("%%0%dld", $column), $acnt); } if ( $count > 0 && $reject == 1 ) { # カウンターの出力 if ( open(CNT, "> $counter_file") ) { print(CNT "$count:$acnt"); close(CNT); } # ログファイルへ記録 &logwrite; } # 出力 if ($print_http_header == 1) { print "Content-type: text/html\n\n"; } if ( $mode ne "hide" ) { printf("$scount"); } printf(" \n"); # ロック解除 if ( $file_lock_enable ) { rmdir($lock_file); } } # ファイルロック sub file_lock { foreach $i (1, 1, 2, 4, 8, 16, 20) { if (mkdir("$lock_file", 755)) { last; } elsif ( $i == 1 ) { # ロックファイルのタイムアウト判定 ($lock_time) = (stat($lock_file))[9]; if ($lock_time < time() - $lock_timeout) { rmdir($lock_file); } } elsif ( $i < 20 ) { # ロック失敗 wait sleep($i); } else { # ロックあきらめ ABORT exit(1); } } } # カウントしないドメインやアドレスのチェック sub rej_chk { # ドメインのチェック if ($rhost ne "") { foreach $r (@reject_domain) { if ($r ne "" && $rhost =~ /$r/ ) { return 0; } } } # アドレスのチェック foreach $r (@reject_address) { if ($r ne "") { if ( index($r,'-') > 0 ) { @rmip = split(/\./, $raddr); @rjip = split(/\./, $r); $i = 0; foreach $ip (@rjip) { if ( index($ip,'-') > 0 ) { ($imin, $imax) = split(/-/, $ip); if ( $rmip[$i] <= $imax && $rmip[$i] >= $imin ) { return 0; } else { last; } } else { if ( $rmip[$i] != $ip ) { last; } $i++; } } } else { if ( $raddr =~ /^$r/ ) { return 0; } } } } return 1; } # アドレスチェック sub add_chk { # カウントしない時間中を検出すれば「0:カウントしない」を返す # ログを最後まで読んで非カウント時間中のログがない場合は「1:カウントする」を返す if ($cnt_timeout > 0) { # タイムアウトの有効期限の計算 ($tss, $tmm, $thh, $tdd, $tmo, $tyy) = localtime(time() - ($cnt_timeout * 60) ); $limdate = sprintf("%04d%02d%02d", $tyy + 1900, $tmo + 1, $tdd); $limtime = sprintf("%02d%02d", $thh, $tmm); } open(FADDR, "$access_log" ); while() { ($tcount, $tacnt, $tdate, $ttime, $taddr, $remainder) = split(/\t/, $_, 6); if ($taddr eq $raddr ) { ($lyy, $lmo, $ldd) = split(/\//, $tdate); $logdate = sprintf("%04d%02d%02d", $lyy, $lmo, $ldd); # カウントのタイムアウトチェック if ( $cnt_timeout > 0 ) { # 時間のチェック if ($logdate > $limdate) { close(FADDR); return 0; } else { ($thh, $tmm, $tss) = split(/:/, $ttime); $logtime = sprintf("%02d%02d", $thh, $tmm); if ($logdate == $limdate && $logtime >= $limtime) { close(FADDR); return 0; } } } else { # タイムアウト時間が0のときは日付が変わるまでカウントしない $nowdate = sprintf("%04d%02d%02d", $year, $mon + 1, $mday); if ($logdate >= $nowdate ) { close(FADDR); return 0; } } } } close(FADDR); return 1; } # ログファイルへ記録 sub logwrite { if ($acnt % $log_max == 0) { # ログファイルのバックアップ (ログの最大行数を超えた場合) if ( $create_history == 1 ) { &syuukei('file'); open(BLOG, "> $bkp_log"); open(FLOG, "$access_log"); while() { print(BLOG "$_"); } close(FLOG); close(BLOG); } open(FADDR, "> $access_log" ); } else { open(FADDR, ">> $access_log" ); } # ログの書き出し # 出力項目:カウント、カウント(全通し)日付、時間、クライアントIP、 # クライアント名、ブラウザ、ページ識別用コメント、リンク元 print(FADDR "$scount\t$acount\t$ndate\t$ntime\t$raddr\t$rhost\t$ragnt\t$cmnt\t$refer\n"); close(FADDR); } # 集計 (集計ファイルの作成/結果表示) sub syuukei { $smode = shift(@_); open(FASUM, "$sum_dom" ); while() { ($tsdom, $tscnt) = split(/\t/, $_, 2); $wkacnt{$tsdom} = $tscnt; } close(FASUM); open(FISUM, "$sum_idx" ); while() { ($tsidx, $tscnt) = split(/\t/, $_, 2); $wkicnt{$tsidx} = $tscnt; } close(FISUM); $ocnt = 0; open(FLOG, "$access_log" ); while() { chop($_); ($scnt, $sacnt, $sdate, $stime, $sadd, $sdom, $sagnt, $sidx, $ref) = split(/\t/, $_, 9); # ドメイン毎のカウント if ( $ocnt != $scnt ) { $sdom = &cut_hostname($sdom); if ( defined $wkacnt{$sdom} ) { $wkacnt{$sdom}++; } else { $wkacnt{$sdom} = 1; } } $ocnt = $scnt; # ページインデックス毎のカウント if ( defined $wkicnt{$sidx} ) { $wkicnt{$sidx}++; } else { $wkicnt{$sidx} = 1; } } close(FLOG); if ($smode eq "file") { open(FASUM, "> $sum_dom" ); foreach $w ( sort keys %wkacnt) { print(FASUM "$w\t$wkacnt{$w}\n"); } close(FASUM); open(FISUM, "> $sum_idx" ); foreach $w ( sort keys %wkicnt) { print(FISUM "$w\t$wkicnt{$w}\n"); } close(FISUM); } else { # カウンターの取得 if ( open(CNT, "< $counter_file") ) { $wrk = ; ($count, $acnt) = split(/:/, $wrk); close(CNT); } else { $count = -1; $acnt = -1; } print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "アクセス状況集計結果\n"; print "\n"; print "\n"; print "\n"; print "

アクセス状況集計結果

\n"; print"

アクセス数: $count ($acnt)


\n"; print"\n"; print"\n"; print"\n"; @swkacnt = sort { $wkacnt{$b} <=> $wkacnt{$a} } keys %wkacnt; foreach $w ( @swkacnt ) { $ratio = $wkacnt{$w} * 100 / $count ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
クライアント毎アクセス数集計
クライアントホスト/アドレス アクセス     比  率     
$w$wkacnt{$w}$hosi ($sratio\%)
\n"; print"


\n"; print"\n"; print"\n"; print"\n"; @swkicnt = sort { $wkicnt{$b} <=> $wkicnt{$a} } keys %wkicnt; foreach $w ( @swkicnt ) { $ratio = $wkicnt{$w} * 100 / $count ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
ページ毎アクセス数集計(累計)
ページインデックス アクセス     比  率     
$w$wkicnt{$w}$hosi ($sratio\%)
\n"; } } # IPアドレスはそのまま、ホスト名は後ろから指定された数の名前?だけ残す # sub cut_hostname { $wkdomain = shift(@_); if ( $wkdomain =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ ) { # IPアドレスはそのまま返す return $wkdomain; } else { ($host, $domain) = split(/\./, $wkdomain ,2) ; return $domain; } } # ------------- 使用に際してはこれ以降を切り取ってしまっても構いません ------------- #       但し、配布等に関してはオリジナルのままのファイルを配布してください ############### # 更新履歴 ############### # #1999/06/04 Ver. 0.10 完成 # #1999/06/07 Ver. 0.20 ログ出力方法の変更、通しカウントの追加 # Ver. 0.21 カウントタイムアウトの不具合を修正 # #1999/06/09 Ver. 0.22 タイムアウト時間の算出方法の修正 # #1999/06/11 Ver. 0.23 cmd モードで、ファイルの絶対パスを使用できるように修正 # #1999/06/17 Ver. 0.24 集計機能を追加 # #1999/07/23 Ver. 0.25 リンク元をログに追加 # #1999/08/19 Ver. 0.26 httpヘッダの記述ミスを訂正(^^; # #1999/08/20 Ver. 0.27 httpヘッダの出力方法を変更 # #1999/10/22 Ver. 0.28 集計方法の変更(ホスト毎集計をドメイン毎に変更) # #1999/10/27 Ver. 0.29 集計に比率を追加、集計のバグ取り ############## # その他 ############## # # このカウンターはフリーソフトです。 # 使用、転載、再配布、改変、について、作者への問い合わせは必要ありません。 # 但し、改変した場合は、その履歴と改変者をドキュメントに付加してください。 # # このカウンターの使用にあたって発生した障害等について、作者は一切の責任を負いかねます。 # # 作者 しましま   e-mail : shima@propel.ne.jp # Web Page : http://www.propel.ne.jp/~shima/ # # このカウンターは とほほ氏の wwwcount.cgi Ver2.92 を参考に作りました # webcount.cgi 最新版入手先: http://www2e.biglobe.ne.jp/~s-hasei/