COUNTERSince June 16, 2000
STUDIO KAMADAJapanese to English by Excite
自作CGI | サイトマップ | ホーム
counter.cgi2002-06-11(Tue) 08:50

 M.Kamadaの自作CGIカウンタのPerlのソースリストを公開します。この自作CGIカウンタは、STUDIO KAMADAの各ページの右下のカウンタとして実際に使用しているものです。


#!/usr/local/bin/perl

#許可する名前
@dirs = ("diary", "docsx68k", "x68ksoft", "docsproc", "docsmath", "puzzgame", "fractal", "downcgi");

#名前を確認する
$cntdir = $ARGV[0];
$err = 1;
for (@dirs) {
  if ("$cntdir" eq $_) {
    $err = 0;
    last;
  }
}
if ($err) {
  exit(1);
}

#カウンタの桁数(0のときはゼロサプレスする)
$cntlen = 0;

#カウンタが1度にどの程度大きくなっていたら壊れたと判断するか
$cntbro = 1000;

#数字のGIFファイルのディレクトリ
$figdir = "fig";

#ロックファイルをいつまで残すか(秒数)
$locspn = 60*5;

#IPアドレス履歴ファイルをいつまで残すか(秒数)
#IPアドレス履歴ファイルは毎回コピーするのであまり長くしないこと
$adrspn = 60*10;

#今回の時刻を得る
$curtim = time();
@days = ("日","月","火","水","木","金","土");
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($curtim);
$curdate = sprintf("%04d-%02d-%02d(%s)", 1900+$year, 1+$mon, $mday, @days[$wday]);
$curtime = sprintf("%02d:%02d:%02d", $hour, $min, $sec);

#IPアドレス履歴ファイルをロックできなかったときは無条件にカウントアップする
$inccnt = 1;

#今回のIPアドレスを得る
$curadr = $ENV{'REMOTE_ADDR'};

if (length("$curadr") != 0) {
  #IPアドレスが得られたとき

  #IPアドレスハッシュ値
  ($ip1, $ip2, $ip3, $ip4) = split(/\./, "$curadr");
  $iphash = ($ip1*257 + $ip2*263 + $ip3*269 + $ip4*271) & 0x3f;

  #IPアドレス履歴ファイル名
  $adrlog = "$cntdir/log$iphash.txt";
  $adrnew = "$cntdir/log$iphash.new";
  $adrloc = "$cntdir/log$iphash.loc";

  $adrlcd = 0;

  #アボートしてもロックファイルが残らないようにする
  #他のスレッドが作ったロックファイルを消さないようにするため$adrlcdをチェックする
  #mkdirしてから$adrlcdをセットするまでの間にアボートするとロックファイルを消し損ねてしまう
  #クリティカルな時間をこれより短くすることは可能か?
  #ロックファイルを消す前に自分が作ったロックファイルかどうかを調べる術があればよいのだが…
  sub sigadr {
    if ($adrlcd) {
      unlink("$adrnew");
      rmdir("$adrloc");
    }
    exit(0);
  }
  $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigadr";

  #IPアドレス履歴ファイルをロックする
  unless ($adrlcd = mkdir("$adrloc", 0755)) {
    #ロック失敗

    #古いロックファイルを削除する
    #ロックファイルの日付を調べてからそれを削除するまでの間に
    #他のスレッドがロックファイルの削除と作成を行っていると
    #それを誤って削除してしまうことになる
    if (stat("$adrloc")) {
      if ($_[9] < $curtim-$locspn) {
        rmdir("$adrloc");
      }
    }
    for (1..5) {
      #1秒待つ
      sleep(1);
      if ($adrlcd = mkdir("$adrloc", 0755)) {
        #ロック成功
        last;
      }
    }
  }

  if ($adrlcd) {
    #IPアドレス履歴ファイルをロックできたとき

    #新しいIPアドレス履歴ファイルを作る
    #IPアドレス履歴ファイルの後ろのほうが古い履歴になる
    if (open(OUT, "> $adrnew")) {
      printf(OUT "%010d %s %s %s\n", $curtim, $curdate, $curtime, $curadr);
      if (open(IN, "$adrlog")) {
        while (<IN>) {
          #前回の時刻(秒数),前回の日付,前回の時刻,アドレス
          ($logtim, $logdate, $logtime, $logadr) = split(/ /, $_, 4);
          $logadr = substr($logadr, 0, length($logadr)-1);
          #古いログは無視する
          if ($logtim < $curtim-$adrspn) {
            last;
          }
          if ($logadr eq $curadr) {
            #最近同じアドレスからアクセスされた
            $inccnt = 0;
          } else {
            #アドレスが違うのでそのままコピーする
            printf(OUT "%010d %s %s %s\n", $logtim, $logdate, $logtime, $logadr);
          }
        }
        close(IN);
      }
      close(OUT);

      #IPアドレス履歴ファイルを入れかえる
      rename("$adrnew", "$adrlog");
    }

    #IPアドレス履歴ファイルのロックを解除する
    rmdir("$adrloc");
  }
}

#カウンタ関連ファイル名
$cntdat = "$cntdir/counter.dat";
$cntnew = "$cntdir/counter.new";
$cntloc = "$cntdir/counter.loc";
$cntdxx = sprintf("%s/counter.d%02d", $cntdir, $mday);
$cnthxx = sprintf("%s/counter.h%02d", $cntdir, $hour);
$cntsxx = sprintf("%s/counter.s%02d", $cntdir, $sec);

$cntlcd = 0;

#アボートしてもロックファイルが残らないようにする
#他のスレッドが作ったロックファイルを消さないようにするため$cntlcdをチェックする
#mkdirしてから$cntlcdをセットするまでの間にアボートするとロックファイルを消し損ねてしまう
#クリティカルな時間をこれより短くすることは可能か?
#ロックファイルを消す前に自分が作ったロックファイルかどうかを調べる術があればよいのだが…
sub sigcnt {
  if ($cntlcd) {
    rmdir("$cntloc");
  }
  exit(0);
}
$SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigcnt";

#カウンタファイルをロックする
unless ($cntlcd = mkdir("$cntloc", 0755)) {
  #ロック失敗

  #古いロックファイルを削除する
  #ロックファイルの日付を調べてからそれを削除するまでの間に
  #他のスレッドがロックファイルの削除と作成を行っていると
  #それを誤って削除してしまうことになる
  if (stat("$cntloc")) {
    if ($_[9] < $curtim-$locspn) {
      rmdir("$cntloc");
    }
  }
  for (1..5) {
    #1秒待つ
    sleep(1);
    if ($cntlcd = mkdir("$cntloc", 0755)) {
      #ロック成功
      last;
    }
  }
}

if ($cntlcd) {
  #カウンタファイルをロックできたとき

  #カウンタを読み込む
  if (open(IN, "$cntdat")) {
    $count = <IN>;
    close(IN);
  } else {
    $count = 0;
  }

  #カウンタが壊れていたら修復を試みる
  $broken = 0;
  $k = 0;
  for ($i = 0; $i < 60; $i++) {
    #バックアップの中で一番大きなカウンタを探す
    if (open(IN, sprintf("%s/counter.s%02d", $cntdir, $i))) {
      $j = <IN>;
      close(IN);
      if ($j > $k) {
        $k = $j;
      }
    }
  }
  if ($k > 0) {
    #バックアップが1つでもあるとき
    if (($k > $count) || ($k + $cntbro < $count)) {
      #カウンタが壊れていると思われるので修復する
      $count = $k;
      $broken = 1;
    }
  }

  #最近アクセスしていなければカウントアップする
  if ($inccnt) {
    $count++;
  }

  #カウントアップしたときとカウンタが壊れていたときはカウンタを更新する
  if ($inccnt || $broken) {
    #カウンタを更新する
    if (open(OUT, "> $cntnew")) {
      print OUT "$count";
      close(OUT);
    }

    #カウンタファイルを入れかえる
    rename("$cntnew", "$cntdat");
  }

  #修復しなかったときだけバックアップする
  if ($inccnt) {
    if (open(OUT, "> $cntdxx")) {
      print OUT "$count";
      close(OUT);
    }
    if (open(OUT, "> $cnthxx")) {
      print OUT "$count";
      close(OUT);
    }
    if (open(OUT, "> $cntsxx")) {
      print OUT "$count";
      close(OUT);
    }
  }

  #ロックを解除する
  rmdir("$cntloc");

  #カウンタを文字列に変換する
  if ($cntlen) {
    $cntstr = sprintf(sprintf("%%0%dld", $cntlen), $count);
  } else {
    $cntstr = "$count";
  }

  #カウンタの数字の画像を出力する
  #本当はカウンタを増やしたときだけGIFファイルを作りたい
  require "gifcat.pl";
  for ($i = 0; $i < length($cntstr); $i++) {
    $n = substr($cntstr, $i, 1);
    push(@files, "$figdir/$n.gif");
  }
  printf "Content-type: image/gif\n";
  printf "\n";
  binmode(STDOUT);
  print &gifcat'gifcat(@files);

} else {

#ダミーの画像を吐き出す
#サイズが1x1の透明なGIF
printf "Content-type: image/gif\n";
printf "\n";
binmode(STDOUT);
print pack("C*", (0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x21, 0xf9, 0x04, 0x05, 0x14, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b));
}

#終わり
exit(0);
自作CGI | サイトマップ | ホーム
E-mail: m_kamada at nifty dot comMirrorCopyright (C) 1999-2004 M.Kamada All Rights Reserved.