cygportでApache2?

 前回CygwinでApache2のWEBサーバーを立ち上げて、MOD_Perl.soをインストールしたわけですな。
で、libapreq2というライブラリをコンパイルして、Apache2::Requestというperlモジュールで、ブラウザでINPUTされたフォームデータとかを受け取りたいと思って、で、ここ数日間、MOD_apreq2.soをコンパイルする方法を探っていたのですが、何度やってもMOD_apreq2.soを作れない。
誰か、cygwinでMOD_apreq2.soをコンパイル出来た人はいないのかとググッたところ、cygwinのcygportというToolでlibapreq2をインストール出来るパケージが提供されているという情報を発見。
cygport自体は、cygwinのsetup.exeからインストールが可能である。
で、どうすればcygportでプログラムをインストール出来るのか
付属のドキュメントが"/usr/share/doc/cygport/cygport.1.html"にあった。
しかし、よく分からない。。。
で、まずはcygportでapache2-2.2.6-1をインストールしてみるところから始める。

cygportでダウンロドできるファイルの、ほぼ一覧はここからたぐっていく。
wwwからapache2を選んで、まず、"2.2.9-apr_pool.patch","2.2.9-docs.patch","2.2.9-layout.patch","2.2.9-no-undefined.patch","apache2-2.2.16-1.cygport","postinstall.sh","preremove.sh"を/usr/srcに保存する。
で、"/usr/src"で

cygport apache2-2.2.16-1.cygport download

 でソースファイルをdownloadする。
次に、

cygport apache2-2.2.16-1.cygport prep

 すると、downloadしたパッケージが展開されるが、先に*.patchファイルを/usr/srcにdownloadしていないか、*.patchファイルが無いと怒られる
で、今度は

cygport apache2-2.2.16-1.cygport compile

 とやると、コンパイルが始まる。
今回、configureで

** Error: unable to find layout Cygwin

 というエラーで、configureがストップした。
何がおかしいのか調べるために、その周辺のコードをコピペして、次のようなシェルスクリプトを作ってテストしてみた

#! /bin/sh
  pldconf=./config.pld
  LAYOUT=Cygwin
  sed -e "1s/[     ]*<[lL]ayout[     ]*$LAYOUT[     ]*>[     ]*//;1t" \
      -e "1,/[     ]*<[lL]ayout[     ]*$LAYOUT[     ]>[     ]*/d" \
      -e '/[     ]<\/Layout>[     ]*/,$d' \
      -e "s/^[     ]*//g" \
      -e "s/:[     ]*/=\'/g" \
      -e "s/[     ]*$/'/g" \
      config.layout >; $pldconf
  layout_name=$LAYOUT
  if test ! -s $pldconf; then
    echo "** Error: unable to find layout $layout_name"
    exit 1
  fi

 すると、config.pldというファイルは無事作られた。
腑に落ちないが、既にconfig.pldというファイルが出来ているため、もう一度

cygport apache2-2.2.16-1.cygport compile

 とやると、そのconfig.pldのチェックをくぐり抜け、configureは無事終了し、コンパイルも問題なく終わった。

cygport apache2-2.2.16-1.cygport install

 で無事、インストールされ、apache2の起動および、動作は無事確認された。
で、error_log見て気が付いた。
前から"Apache/2.2.6 (Unix) "だったんじゃん。
libapreq2付属のversion_check.plを実行すると、

$ perl build/version_check.pl
==================================================
Build system (core C API) prerequisites

                       apache2:  2.0.48
                           apr:  0.9.4  (bundled with apache2 2.0.48)
                           apu:  0.9.4  (bundled with apache2 2.0.48)
                          perl:  5.6.1

 って表示されるので、勘違いしていた。この"version_check.pl"は、現在の自分の環境のversionを表示するものじゃなくって、libapreq2-2.12の動作条件を表示していたのね。。。ずっと勘違いしていた。

次に、apr1をビルドしました。libsの下にあります。

cygport apr1-1.3.2-1.cygport download

 を実行すると、"Error:404 not found"に。仕方が無いので、Apacheのアーカイブサイトからブラウザで、"apr-1.3.2.tar.bz2"をdownload。

cygport apr1-1.3.2-1.cygport prep
cygport apr1-1.3.2-1.cygport compile
cygport apr1-1.3.2-1.cygport install

 で、インストールされたみたいな感じ。

$ apr-1-config --version
1.4.2

 あれっ?1.3.2じゃなくっていいのか?不安になってきた。

次にaprutil1をインストールしたいのですが、依存関係で、"libdb4.6-4.6.21.1-1"以上が必要となっているが、現在、cygwinのsetup.exeでインストール出来るのは、"libdb4.5"までなので、cygportからlibdb4.6をインストールすることにしました。
が、ソースの"db4.6-4.6.21.4-10-src.tar.bz2"がどこにあるのか分からないので、download出来ない。
ぐぐったらこんなところにあった。
で、db4.6-4.6.21.4-10-src.tar.bz2をダウンロードして

$ cygport db4.6-4.6.21.4-10.cygport prep

 とやると、

*** ERROR: tcl is required to build this package

 と出たので、Tclとやらを先にcygwinのsetup.exeでインストールすることに。
で、Tcl/ktをインストール後、またcygportで

$ cygport db4.6-4.6.21.4-10.cygport prep

 とやったら、

*** ERROR: Cannot find source package db-4.6.21.tar.gz

 となったので、今度はOracleのダウンロードサイトから"db-4.6.21.tar.gz"をダウンロード。
しかし、今度は

*** ERROR: patch patch.4.6.21.1 not found

 このpatchはOracleがリリースしたものらしいのだが、現在、Oracleのサイトには無いので困った。
ググったら理化学研究所のftpサイトにあった。pacthは4つあって、4個とも必要だった。
本音を言えば、データベース使うような機会はないと思うのだが。

次にaprutil1をインストールしたい。ので、ダウンロードしようとすると、

$ cygport aprutil1-1.3.2-1.cygport download
--2010-10-09 11:53:30--  http://www.apache.org/dist/apr/apr-util-1.3.2.tar.bz2
Connecting to www.apache.org (www.apache.org)|140.211.11.131|:80... connected.
HTTP request sent, awaiting response... 404 Not Found

 またApacheアーカイブサイトからダウンロード。

$ cygport aprutil1-1.3.2-1.cygport prep
$ cygport aprutil1-1.3.2-1.cygport compile
configure: error: Berkeley db4 not found
*** ERROR: configure failed

そんなアホな。

/usr/src/db4.6-4.6.21.4-10/log/db4.6-4.6.21.4-10-install.log

 の中を見ると、

Installing DB library: /usr/src/db4.6-4.6.21.4-10/inst/usr/lib ...

どこにインストールしとんねん!
これは。。。! Apache2も、コンパイル後、インストールされてなかった!
$apr-1-config --version のバージョンがおかしい時点で気が付くべきだった。。。

cpし直して、再度挑戦!

$ cygport aprutil1-1.3.2-1.cygport compile
configure: error: Berkeley db4 not found
*** ERROR: configure failed

がーん。configureのlogを見ると、installされたdbのファイルの名前が、どうもいけないらしい。

/usr/included/b4.6 → /usr/included/b46

 と、変えるとOK。
でも、コンパイル中に別のエラーが出る。

/bin/sh /usr/share/apr/build-1/libtool --silent --mode=link gcc    -O2 -pipe
 -L/usr/lib -release 1 -module -no-undefined -rpath /usr/lib/apr-util-1 libaprutil-1.la -o ldap/apr_ldap.la ldap/apr_ldap_init.lo ldap/apr_ldap_option.lo ldap/apr_ldap_rebind.lo -lldap
libtool: link: cannot find the library `libaprutil-1.la' or unhandled argument `libaprutil-1.la'
make[1]: *** [dbd/apr_dbd_sqlite3.la] Error 1
make[1]: *** Waiting for unfinished jobs....
libtool: link: cannot find the library `libaprutil-1.la' or unhandled argument `libaprutil-1.la'

 探したら、"libaprutil-1.la"というファイルは/aprutil1-1.3.2-1/build/.libsの中にあった。
ので、フルパスで"libaprutil-1.la"を指定するようにMakefileを書き換えた。

<LINK_MODULE = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(APRUTIL_LDFLAGS) -release $(APRUTIL_MAJOR_VERSION) -module -no-undefined -rpath $(APU_DSO_LIBDIR) libaprutil-${APRUTIL_MAJOR_VERSION}.la
---
>LINK_MODULE = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(APRUTIL_LDFLAGS) -release $(APRUTIL_MAJOR_VERSION) -module -no-undefined -rpath $(APU_DSO_LIBDIR) /usr/src/aprutil1-1.3.2-1/build/.libs/libaprutil-${APRUTIL_MAJOR_VERSION}.la

 すると、うまく行ったように見える。ので、install

で、MOD_Perlを再ビルド。前回のソースがそのままあるので、そのままの手順で。。。ん?

[  error] '/usr/sbin/apxs2 -q INCLUDEDIR' failed:
[  error] apxs:Error: Sorry, no shared object support for Apache.
apxs:Error: available under your platform. Make sure.
apxs:Error: the Apache module mod_so is compiled into.
apxs:Error: your server binary `/usr/sbin/httpd2'..
[  error] Unable to determine server version, aborting.
[  error] Invalid MP_APXS specified?

はぁ?なんだそりゃ?

泣きながら、cygwinのsetup.exeでApache2とlibapr1とlibaprutil1をreinstallしました。。

cygportでapreq2?

泣きながら、MOD_Perlを再ビルドしました。
連休だし、夜はこれから。
というわけで、やっと本命、libapreq2だ

$cygport libapreq2-2.08-2.cygport download
$cygport libapreq2-2.08-2.cygport prep

 までは良かったが、compileすると

/bin/sh ../../libtool --tag=CC --mode=link gcc  -g -O2 -pipe  -fno-strict-aliasing   -o mod_apreq2.la -rpath `/usr/sbin/apxs2 -q LIBEXECDIR` -export-dynamic -module -avoid-version `/usr/src/libapreq2-2.08-2/build/apreq2-config --link-libtool --libs`  /usr/lib/libapr-1.la /usr/lib/libaprutil-1.la handle.lo filter.lo -no-undefined -shrext
libtool: link: the `-shrext' option requires an argument
make[2]: *** [mod_apreq2.la] Error 1

 libtoolの引数"-shrext"の後に、パラメーターが無いと言っているらしい。
libtool/libtoolの呼び出しによると
「システム標準の共有ライブラリに対するファイル名の拡張子を"引数名"に置換します」とのこと。
Makefileをいろいろ探したが、"-shrext"という文字列はどこにも無かった。
"-shrext"は意外なところにあった。
libapreq2-2.08-2.cygportの中である。

cygmake \
	mod_apreq2_la_LIBADD="-no-undefined -shrext ${APACHE_MOD_SHREXT} ${LIBHTTPD}" \
	OTHERLDFLAGS="${B}/module/apache2/.libs/mod_apreq2.dll.a ${APXS_LIBEXECDIR}/mod_perl.dll.a"

 *.cygportがMakefileをハイジャックしていたのだ。
libapreq2-2.08-2.cygportの過去ログを見ると、昔はApacheのヴァージョンで"${APACHE_MOD_SHREXT}"の値を".dll"にするか".so"にするかを決めるロジックがあったのだが、「libapreq2はApache2でしか使えないのだから、".so"で統一!」みたいな変更の時に、"${APACHE_MOD_SHREXT}"を決めるロジックが消えてしまい、"${APACHE_MOD_SHREXT}"が空のままになってしまって、"-shrext"の後に何も付かないのだ。
"${APACHE_MOD_SHREXT}"を".so"に書き換え、再コンパイル
しかし、同じlinkコマンドで、libtoolはようやくlinkコマンドを実行してくれるも、エラーの嵐

In function `apreq_post_init':undefined reference to `_ap_add_version_component'
In function `apreq_filter_prefetch':undefined reference to `_ap_log_rerror'
.....

 みたいなのが山のように出る。
それらはどこにあるのか。

$ nm -o /lib/*.a |grep "_ap_add_version_component"
libhttpd2core.dll.a:d000164.o:00000000 T _ap_add_version_component

 libhttpd2core.dll.aへのリンクが漏れているのだ。
これの対処法は、libhttpd2core.laを追加するのだが、これでやっと、libapreq2-2.08-2.cygportの中の"${LIBHTTPD}"が何だったのかがわかった。libhttpd2core.laの事だったのだ。
つまり、

cygmake \
	mod_apreq2_la_LIBADD="-no-undefined -shrext .so /lib/libhttpd2core.la" \
	OTHERLDFLAGS="${B}/module/apache2/.libs/mod_apreq2.dll.a ${APXS_LIBEXECDIR}/mod_perl.dll.a"

 ということだったのだ。
libhttpd2core.laはテキストファイルで、中にライブラリーの依存関係が書かれている。

# Libraries that this one depends upon.
dependency_libs=' -L/usr/lib /usr/lib/libaprutil-1.la /usr/lib/libdb-4.2.la /usr/lib/libexpat.la (後略)'

 と書かれているので、体裁を整えるために、cygwinのseup.exeで、libdb-4.2をインストールしておく。
これで、このlink工程はパスして、無事(?)mod_apreq2.soを手に入れた。
のだが、本来の目的は、あくまでApache2::Requestのビルドです。
で、またエラーが
Typemapがconvert出来ないというwarningは、ちゃんとビルド出来ている人でも出るwarningらしいので無視していいが、このエラーは致命的である。

g++-4  --shared  -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--stack,8388608 -Wl,--enable-auto-image-base -L/usr/local/lib -fstack-protector Apache2.o
 /usr/src/libapreq2-2.08-2/build/module/apache2/.libs/mod_apreq2.dll.a /mod_perl.dll.a -o ../../../../blib/arch/auto/APR/Request/Apache2/Apache2.dll    \
          /usr/lib/perl5/5.10/i686-cygwin/CORE/cygperl5_10.dll -L/usr/src/libapreq2-2.08-2/build/library/.libs -lapreq2 -L/usr/lib -laprutil-1 -L/usr/lib -lapr-1 -lldap -llber -ldb-4.5 -lgdbm -lexpat -liconv -lcrypt         \

g++-4: /mod_perl.dll.a: No such file or directory

 確かに、正しいパスは"/lib/mod_perl.dll.a"なのであるが、どこで定義されているのか。
どのMakefileにも"mod_perl"の文字列はなかった。
あっ!またlibapreq2-2.08-2.cygportの中だ!
そう、"${APXS_LIBEXECDIR}"の値が"/lib"なのだ。
結局、

cygmake \
	mod_apreq2_la_LIBADD="-no-undefined -shrext .so /lib/libhttpd2core.la" \
	OTHERLDFLAGS="${B}/module/apache2/.libs/mod_apreq2.dll.a /lib/mod_perl.dll.a"

 になるのだ。
これで、コンパイルは成功(?)したみたいで、installする。
といっても、また、"/usr/src/libapreq2-2.08-2/inst/"にinstallするのでmvしないといけない。。。
で、/inst/etc/の中に2つのシェルスクリプトがあった。
シェルスクリプトと言っても、
#! /usr/sh はないし、何のコメント行も無い。それぞれ1行だけ書かれている

/inst/etc/preremove/libapreq2.sh 内
  /usr/sbin/apxs2 -e -A -n apreq /usr/lib/apache2/mod_apreq2.so

/inst/etc/postinstall/libapreq2.sh 内
  /usr/sbin/apxs2 -e -a -n apreq /usr/lib/apache2/mod_apreq2.so

 どちらも同じファイル名というのが、センスを疑うし、preremoveのほうのコマンドも、mod_apreq2を追加するコマンドである。
"-a"のほうが、"apreq"という名前で追加され、"-A"のほうが"#apreq"で追加されるらしい。
"-a"のほうで追加。
で、httpd.confを編集しようとしたところ、

LoadModule apreq_module    lib/apache2/mod_apreq2.so

 と、既に追加されていた。
"/usr/sbin/apachectl2 -t"してみて"Syntax OK"だったので、Apache2起動。error_logには

[notice] Apache/2.2.6 (Unix) DAV/2 mod_ssl/2.2.6 OpenSSL/0.9.8o mod_apreq2-20051231/2.6.0 mod_perl/2.0.4 Perl/v5.10.1 configured -- resuming normal operations

をを、読み込まれている。

libapreq2でやれんのか?

で、これまでは、普通にCGIモジュールを使って、POSTメソッドでのファイルのアップロードを処理していました。

use CGI::Fast qw(:standard :no_xhtml);

my $buf;
my $post = new CGI::Fast;
my $postfile =$post->param('editfile');
my $tempfile = "temp.dat";
open(TEMP,">", "./save/$tempfile") or die("Can't open $tempfile.\n");
binmode TEMP;
while (read($postfile, $buf, 1024)) {
  print TEMP $buf;
}
close(TEMP);
print $post->header(-charset=>'Shift_JIS');
・・・・・

 が、mod_Perlを使うと、次のように書けって、mod_perlのサイトに書いてある。

use APR::Brigade ();
use APR::Bucket ();
use Apache2::Filter ();

use Apache2::Const -compile => qw(MODE_READBYTES);
use APR::Const    -compile => qw(SUCCESS BLOCK_READ);

use constant IOBUFSIZE => 8192;

sub read_post {
    my $r = shift;
    my $bb = APR::Brigade->new($r->pool,$r->connection->bucket_alloc);
    my $data = '';
    my $seen_eos = 0;
    do {
        $r->input_filters->get_brigade($bb, Apache2::Const::MODE_READBYTES,
                                          APR::Const::BLOCK_READ, IOBUFSIZE);
          for (my $b = $bb->first; $b; $b = $bb->next($b)) {
            if ($b->is_eos) {
                $seen_eos++;
                last;
            }
            if ($b->read(my $buf)) {
                $data .= $buf;
            }
            $b->remove; # optimization to reuse memory
        }
    } while (!$seen_eos);  
    $bb->destroy; 
    return $data;
}

 正直、これ見て、mod_Perlをやめようと思った。
EOS(end of string:ファイルの終端文字)を自分で検出しなければいけないなんて!
が、しかし、Apache2::Uploadの解説ページを見て、「これならやれる!」と思った。
本当にやれんのか?
動いた!

#!/usr/bin/perl

use Apache2::RequestRec();
use CGI::Carp;

my $r =shift;
savefile::handler($r);

package savefile;

use CGI::Carp;
use Apache2::Upload;

sub handler {
my ($gomi,$r) =@_;
local $req = Apache2::Request->new($r);
local $upload = $req->upload('editfile');
my $tempfile = "temp.dat";
open(TEMP,">", "./save/$tempfile") or die("Can't open $tempfile.\n");
binmode TEMP;
my $buf='';
read($upload->fh, $buf, $upload->size);
print TEMP $buf;
$buf='';
close(TEMP);
return 1;
}
return 1;

 CGI::Fastから普通のCGIモジュールに戻したのは、naoyaのはてなダイアリーの中で、CGIモジュール内にmod_perlモードがあると書いてあったので。
packageの中にサブルーチン書いて、mainから呼び出すように書かないと、"my"や"local"で変数を宣言しても、mod_perl内全体でのグローバル変数になってしまうために、おかしな挙動になるから。
でも、基本的なサブルーチンだから、インラインパッケージじゃなくって、モジュール化しても良いかも。
で、ボヤキなのですが、本当は元のソースみたいに、

while (read($upload->fh, $buf, 1024)) {
  print TEMP $buf;
}

 って、1KBごとに読みたかったのですが、こう書くと、最初の1KBだけが延々と出力された。Apacheを停止する他なく、その間に400MBのファイルが出来てしまった。
もし、この書き方で成功していても、事前にファイルサイズをチェックして、それ以上は読み込まないような処理を入れなきゃいけない気がしたので、この方法を追求するのはやめました。
まぁ、libapreq2が動いただけでも、ひと山越せた気がします。

Apache2::Requestの解説ページで、

my $req = Apache2::Request->new($r, POST_MAX => "1M");

 って、例が書かれていて、uploadのファイルサイズを制限できるって書いてあるので、コピペして使ったら、600KBぐらいのファイルをアップロードしたのにエラーになった。
Apacheのerror_logをみたら、

[error] [client ::1] (20014)Internal error: Content-Length header (588573) exceeds configured max_body limit (1),
[error] Exceeds configured maximum limit

をいをい、1MBじゃなくって、1バイトに設定されているがな。
どっちにしても、500KBあたりを超えると、

[error] Internal apreq error

 というエラーでアップロード出来ない。。。