2013/11/22

Windows(XAMPP)上の Apache が Perlで制作した CGI で吐くエラーの解決方法

Windows 7 環境に XAMPP をインストールし、別途 Strawberry Perl をインストールしている。稼働中のシステムを、サイトから丸ごとダウンロードし、上記ローカルの開発環境で動かしたいが、Perl で書かれた CGI スクリプトが 500 エラーを吐いてしまう。

#!/usr/bin/perl

use CGI.pm
use DBI.pm

...

エラーは、次のようなものだった(ちなみに、エラーコードは 720003 や 720005 などになることがある模様。720002 はファイルが見つからない、720003 はディレクトリが見つからない、720005 はアクセス権限がない、という意味。オレの環境でも 720005 は見たことがある)。

Server error!

    サーバ内部で障害が発生し、 リクエストに応えることができませんでした。 

    Error message: 
    couldn't create child process: 720002: test.cgi 

    サーバーの障害と思われる場合は、ウェブ管理者までご連絡ください。 

Error 500

    localhost
    Apache/2.4.2 (Win32) OpenSSL/1.0.1c PHP/5.4.4

CGI スクリプトの先頭行に #! で始まる Shebang が記述され、これが邪魔になっている。Shebang の記述する場所に perl.exe が存在しないためだ。CGI ファイルは多数あり、その全ての Shebang を変えるのは手間がかかるし、あとでリモート環境にアップロードしたとき、トラブルを起こす恐れがある。

これを解決する簡単な方法がないか、探ってみた。

  1. Shebang の記述通りの場所に Strawberry Perl をインストールする
  2. Windows のジャンクション機能を使う
  3. Apache の設定で Shebang を無視させる

Shebang の記述通りの場所に Strawberry Perl をインストールする

ネットで検索してみたところ、この方法で回避しているひとが多いようだった。つまり、Strawberry Perl をいったんアンインストールして、C:\usr\ にインストールし直す、というわけである。単純に移動するだけでいいかもしれないが、レジストリなどに影響があるかもしれないので、いったんアンインストールした方がいいだろう。

この方法は、すこし影響が大きすぎるように感じる。

Windows のジャンクション機能を使う

Windows のファイルシステムには、ジャンクションという機能があり、これを使えば、Shebang の /usr/bin/perl を C:\strawberry\per\bin\perl.exe とシステムに読み替えさせることができる。perl.exe に対してではなく、これを含むディレクトリに対して設定した方がいいだろう。コマンドプロンプトを開き、次のコマンドを実行すればよい。

C:\>mkdir C:\usr
C:\>mklink /J "/usr/bin/" "C:\strawberry\perl\bin\"

ただ、この方法では、簡単なスクリプトなら動かせるものの、オレの環境ではあまり大きなシステムを動かすことができなかった。それは、CPAN を通じてインストールした DBI.pm などの各種モジュールのパスが通らないためで、環境変数 PATH にモジュールのパスを追記すれば動きそうだが、オレはあまり環境変数を汚したくなかったので、この方法を試すのを途中でやめた。

C:\strawberry\perl\bin\ だけならまだしも、複数の場所に分散している全ての lib ディレクトリを環境変数に追記したり、それぞれにジャンクションを張ったりするのは難しく、うまくいったとしても、かなりの大仕事になりそうだ。

Apache の設定で Shebang を無視させる

この方法がいちばん簡単だし、他への汚染や影響がいちばん少ないと思う。

Apache の設定ファイル httpd.conf に ScriptInterpreterSource ディレクティブを追記し、Apache を再起動する。記述する場所はどこでもよいが、<VirtualHost> や <Directory> ディレクティブを使用しているなら、その中に記述するのがいいだろう。オレの環境では、次のように設定した。

<VirtualHost *:55555>
    ScriptInterpreterSource Registry-Strict
    DocumentRoot C:\xampp\vhosts\example.com\public_html
    ServerName example.com
</VirtualHost>

このディレクティブでは、スクリプトを Windows システムで関連付けられた実行ファイルで開く、という設定をしている。従って、以下のように、レジストリに関連付けを行う必要がある。

[HKEY_CLASSES_ROOT\.cgi\Shell\ExecCGI\Command]
@="C:\\strawberry\\perl\\bin\\perl.exe"

上記を、execcgi.reg などというファイル名で保存し、実行する。これで拡張子が .cgi のもの、たとえば http://localhost:55555/test.cgi などが呼ばれたとき、Apache はこのレジストリを参照して、Strawberry Perl で CGI スクリプトを実行してくれる。必要があれば、上記 .cgi を .pl に変えてふたたび実行すれば、test.pl なども動かすことができる。

それでもエラーが出るとき

上記の設定をしたあとでも、以下のエラーが発生した。

Server error!

    サーバ内部で障害が発生し、 リクエストに応えることができませんでした。 

    Error message: 
    End of script output before headers: test.cgi 

    サーバーの障害と思われる場合は、ウェブ管理者までご連絡ください。 

Error 500

    localhost
    Apache/2.4.2 (Win32) OpenSSL/1.0.1c PHP/5.4.4

ほとんどの場合、たんに必要とするモジュールがインストールされていないだけなので(エラーログを見ると、やはり予想通りだった)、CPAN コマンドでインストールする。

C:\>\strawberry\perl\bin\cpan
cpan>install CGI.pm
cpan>install DBI.pm

オレの環境では、これで万事うまくいった。モジュールがインストールされてるのに、同じエラーが出る場合は、@INC にそのモジュールまでのパスが通ってないので、なんとかする。

ふだん Perl は使わないので、なにか誤解しているところがあるかもしれない。間違いがあったら、遠慮なくご指摘ください。

2 件のコメント:

  1. 一行目に書くアレをShebangっていうの、初めて知った。

    返信削除
    返信
    1. いろいろ呼び方はあるみたいですけどね、とりあえずいちばん使われてるのは Shebang じゃないかと思います。あとは Hashbang とか。
      2~3 年前に、Google が Shebang を使え、と言いだすまで、この名前は知らなかったです。

      削除