PETROOM大改造
【萌え萌えキャラクターズと遊ぼー。】

同時アクセスからファイルを守ろう(ファイルロック:flock関数)

長い間、悩まされ続けてきたユーザーデータの消失、もしくは多重重複。
これらは、複数の人が同時に同じファイルにアクセスすることで発生している模様なのですが、
遙たん専用Webリング「遙なるリング」のスクリプトを作成された、ゆ〜りさまより、

flockという関数を使ったファイルロックの方法を教えていただきました(^-^)

ゆ〜りさま、ありがとございます〜っ。


ではでは、改造の前に。

大変申し訳ありませんが、「ことねっつ」からダウンロードした、
「琴音ちゃんと遊ぼー。」「あかりと一緒Y」「さっちんのお部屋」
「翡翠、ちょっといいかな?」
のスクリプトをご使用の方は、
この改造はそのままご利用になれません〜(T_T)


おいらが余計なことをしていまして、すんなり追加だけでは、改造できないのです、うぅ。
いずれ対応させますので、もう少しお待ち下さい。

今回はオリジナルのPETROOMをお使いの方、もしくは「ことねっつ」の、
「標準改造スクリプトサンプル」「お嬢様は本気(マジ)」をお使いの方が対象です。



次に、今現在お使いのサーバで、flock関数が使えるかどうか、ご確認下さいませ。
残念ながら、すべてのサーバで動作するわけではないのです。
頂いた情報によりますと、このflock関数を、

使えるサーバと、
エラーが出るサーバと、
エラーは出ないけど、実は使えていないサーバ、


この3種類があるようです。
問題は3つ目ですね、ロックを掛けていると思っているのに、実は掛かってないという…(^^;;;

使えるか、使えないかに関しましては、
現時点では、各自サーバ管理者へ問い合わせて頂くしかありません。
ホームページなどで情報を公開していたり、ヘルプ掲示板みたいなモノがあれば、
そちらでお問い合わせしてもよいと思います。


flock関数が使えることが分かりましたら、次はPETROOMのバージョンをご確認下さい。
ver2.1からver2.3へのバージョンアップで、ファイルロック周辺のスクリプトが、
変更になっていますので、それぞれ別の方法で改造しないといけません。
共通部分を改造後、それぞれのバージョンをご覧下さい。

改造ページの「ver2.1→ver2.3」を施してある方は、「ver2.3」をご覧下さい。

ver2.1、ver2.3 共通部分

# データファイル名
$userdata = './user.dat';               # 来客者情報が入ってるデータ
$petdata  = './pet.dat';                # ペットの情報が入ってるデータ
$lockkey  = 3;                          # ファイルのロック(0=no 1=mkdir 2=symlink 3=flock)
$lockfile = './petlock';                # ロックファイル名 ver2.1のみ
# flockモード定数
sub LOCK_EX{2}                          # 排他ロック
sub LOCK_UN{8}                          # ロック解除
$method   = 'POST';                     # POSTかGETを選択

# ------------------------- #
# Sub Open In Pet Data
# ペットデータの読みこみ
# ------------------------- #
sub openinpetdata {
    if ($lockkey != 0) { &lock }
    open(IN,"$petdata") || &error("Can't open $petdata");
    $line = <IN>;
    close(IN);
    ($petname,$godfather,$birth,$sex,$dead,$lastfeed,$lastplay,$lastvisit,$good,$bad,$out,$outdate,$num)
                                                     = split(/<>/,$line);
    &unlock;
}

# ------------------------- #
# Sub Open Out Pet Data
# ペットデータの更新
# ------------------------- #
sub openoutpetdata {
    $lastvisit = time;
    $line = "$petname<>$godfather<>$birth<>$sex<>$dead<>$lastfeed<>$lastplay<>$lastvisit<>$good<>$bad<>$out<>$outdate<>$num";
    if ($lockkey != 0) { &lock }
    open(OUT,">$petdata") || &error("Can't write $petdata");
    print OUT $line;
    close(OUT);
    &unlock;
}

# ------------------------- #
# Sub Open In User Data
# ユーザーデータの読みこみ
# ------------------------- #
sub openinuserdata {
    if ($lockkey != 0) { &lock }
    open(IN,"$userdata") || &error("Can't open $userdata");
    @lines = <IN>;
    close(IN);
    &unlock;

    foreach $line (@lines) {
        ($name,$pass,$gdate,$love) = split(/<>/,$line);
        if ($_[0] eq 'checkname') {
            if ($FORM{'name'} eq $name) {
                $flag = 1; # データの中にユーザーを確認
                if ($FORM{'pass'} ne $pass) { &error("パスワードが間違っています") }

# ------------------------- #
# Sub Open Out User Data
# ユーザーデータの書きこみ
# ------------------------- #
sub openoutuserdata {
    if ($lockkey != 0) { &lock }
    open(OUT,">$userdata") || &error("Can't write $userdata");
    print OUT @new;
    close(OUT);
    &unlock;
}

…実は、上記で直したところ、ver2.3のバグと思われるのですが、
$lockkey = 2;」のsymlink関数を使っていた方は、実はロックが掛かっていない状態でした。
この修正で、symlink関数もちゃんと動くようになります。






PETROOMver2.1

ロック(sub lock)とアンロック(sub unlock)をそっくり置き換えてください。

# ------------------------- #
# Sub Lock
# ロック
# ------------------------- #
sub lock {
    return if !$lockkey;
    if ($lockkey == 1) {
        local($flag) = 10;
        rmdir($lockfile) if (time - (stat($lockfile))[9] > 60);
        while (!mkdir($lockfile, 606)) {
            --$flag or &error("現在、他の人が使用しています", 1);
            select(undef, undef, undef, 1);
        }
    }
    elsif ($lockkey == 2) {
        unlink($lockfile) if (time - (stat($lockfile))[9] > 60);
        while (!symlink(".", $lockfile)) {
            if (--$retry <= 0) { &error('現在、他の人が使用しています') }
            sleep(1);
        }
    }
    elsif ($lockkey == 3) {
        open(LOCK_FH,"> $lockfile");
        flock(LOCK_FH,LOCK_EX);
    }
}

# ------------------------- #
# Sub Unlock
# アンロック
# Amend for Ver 2.2
# ------------------------- #
sub unlock {
    if    ($lockkey == 1) { rmdir($lockfile)  }
    elsif ($lockkey == 2) { unlink($lockfile) }
    elsif ($lockkey == 3) { close(LOCK_FH); flock(LOCK_FH,LOCK_UN); }
}

ver2.1の方は、これで終了です。
ファイルロックがver2.3と同様になり、+flock関数が選択できます。
最初の方で設定する「$lockkey」に入れる数字で、選ぶことが出来ます。
後半の「flock(LOCK_FH,LOCK_UN);」は、なくてもいいそうです。

アップロードする前に、必ずユーザーデータ/ペットデータのバックアップを取って下さい。






PETROOMver2.3

# ------------------------- #
# Sub Lock
# ロック
# Amend for Ver 2.2
# ------------------------- #
sub lock {
    return if !$lockkey;
    if ($lockkey == 1) {
        local($flag) = 10;
        rmdir($lockfile) if (time - (stat($lockfile))[9] > 60);
        while (!mkdir($lockfile, 606)) {
            --$flag or &error("現在、他の人が使用しています", 1);
            select(undef, undef, undef, 1);
        }
    }
    elsif ($lockkey == 2) {
        unlink($lockfile) if (time - (stat($lockfile))[9] > 60);
        while (!symlink(".", $lockfile)) {
            if (--$retry <= 0) { &error('現在、他の人が使用しています') }
            sleep(1);
        }
    }
    elsif ($lockkey == 3) {
        open(LOCK_FH,"> $lockfile");
        flock(LOCK_FH,LOCK_EX);
    }
}

# ------------------------- #
# Sub Unlock
# アンロック
# Amend for Ver 2.2
# ------------------------- #
sub unlock {
    if    ($lockkey == 1) { rmdir($lockfile)  }
    elsif ($lockkey == 2) { unlink($lockfile) }
    elsif ($lockkey == 3) { close(LOCK_FH); flock(LOCK_FH,LOCK_UN); }
}

ver2.3の方は、これで終了です。
今までのファイルロックの他に、+flock関数が選択できます。
最初の方で設定する「$lockkey」に入れる数字で、選ぶことが出来ます。
後半の「flock(LOCK_FH,LOCK_UN);」は、なくてもいいそうです。

アップロードする前に、必ずユーザーデータ/ペットデータのバックアップを取って下さい。