中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

アパッチモジュールの初歩・その壱

これからはモジュールの話に入ります。

今回はapxsによるモジュールスケルトンの生成。
次回以降は生成したモジュールソースの解説や
モジュール開発のTipsを紹介していきます。

さて早速始めます。
今回の内容は書くと長くなりますが
作業自体はストレートに進めれば3分と掛かりません。

まず前提の環境ですが、以前の日記の通りです。
Linux/Apache事始め-ビルド編- - LinuxとApacheの憂鬱

apxs
アパッチの標準ツール。
モジュールの開発/ビルド/インストール利用する。

manも用意されているので、詳しく知りたい場合はこちら。

$ export MANPATH=/usr/local/apache2/man:$MANPATH
$ man apxs

主に仕様するオプションは以下の3つ。

-g
モジュールのスケルトンを生成。
-c
モジュールをビルド。
-i
モジュールをインストール。

サードパーティのモジュールや
./configure時に含めなかったモジュールの追加などにも使えるので
覚えておくと幸せになれるかも知れません。

モジュール・スケルトン生成

まずはモジュールのスケルトンを作成してみます。

$ /usr/local/apache2/bin/apxs -g mytest
Creating [DIR]  mytest
Creating [FILE] mytest/Makefile
Creating [FILE] mytest/modules.mk
Creating [FILE] mytest/mod_mytest.c
Creating [FILE] mytest/.deps

これでmytestというモジュールが出来ました。
中身は後で確かめるとして取りあえずビルドしてみます。

モジュール・ビルド

$ cd mytest/
$ /usr/local/apache2/bin/apxs -c mod_mytest.c 
/usr/local/apache2/build/libtool --silent --mode=compile gcc -prefer-pic  -DAP_HAVE_DESIGNATED_INITIALIZER -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -g -O2 -pthread -I/usr/local/apache2/include  -I/usr/local/apache2/include   -I/usr/local/apache2/include   -c -o mod_mytest.lo mod_mytest.c && touch mod_mytest.slo
/usr/local/apache2/build/libtool --silent --mode=link gcc -o mod_mytest.la  -rpath /usr/local/apache2/modules -module -avoid-version    mod_mytest.lo

これでmod_mytest.laが出来ました。
.laという拡張子はlibtoolのリンク形式です。
見慣れない人も居るかもしれませんので簡単に説明します。


Linux環境ではプログラムのリンク形式には以下のものがあり通常ビルド(リンク)時に決定する。

  • 動的リンク(実行時にリンク対象のライブラリを探しリンク)
  • 静的リンク(リンク対象のライブラリは実行ファイルの中に含まれている)

リンク形式の選択をインストール時まで先延ばししたい場合にlibtoolを用いる。
多くのオープンソース系のライブラリはlibtoolを利用している。

libtoolを利用する事によるメリットは色々あるのですが本筋では無いので詳しくはlibtoolのmanでお願いします。

では、出来上がったモジュールを早速アパッチに組み込んでみます。

モジュール・インストール

$ sudo /usr/local/apache2/bin/apxs -i -A -n mytest mod_mytest.la 
/usr/local/apache2/build/instdso.sh SH_LIBTOOL='/usr/local/apache2/build/libtool' mod_mytest.la /usr/local/apache2/modules
/usr/local/apache2/build/libtool --mode=install cp mod_mytest.la /usr/local/apache2/modules/
cp .libs/mod_mytest.so /usr/local/apache2/modules/mod_mytest.so
cp .libs/mod_mytest.lai /usr/local/apache2/modules/mod_mytest.la
cp .libs/mod_mytest.a /usr/local/apache2/modules/mod_mytest.a
chmod 644 /usr/local/apache2/modules/mod_mytest.a
ranlib /usr/local/apache2/modules/mod_mytest.a
PATH="$PATH:/sbin" ldconfig -n /usr/local/apache2/modules
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/local/apache2/modules

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,--rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
chmod 755 /usr/local/apache2/modules/mod_mytest.so
[preparing module `mytest' in /usr/local/apache2/conf/httpd.conf]

-iの際には-A / -a というオプションを指定できますが
これにより自動的にhttpd.confにLoadModule行が挿入されます。

-A
コメントアウト状態で挿入
-a
有効な設定として挿入

まあ、全然便利では無いので忘れてもらって構いませんが・・・(^^
参考までに。。
(自動的にhttpd.conf.bakというバックアップが取られます。)

$ diff /usr/local/apache2/conf/httpd.conf{,.bak}
30d29
< #LoadModule mytest_module      modules/mod_mytest.so

因みに実はこの行
LoadModule mytest_module modules/mod_mytest.so
赤字の部分が非常に重要です。

重要なので次回以降にも繰り返して説明しますが
この文字列がアパッチとモジュールを繋ぐ役割を果たします。

次にインストールしたモジュール自身を確認します。

$ nm /usr/local/apache2/modules/mod_mytest.so | grep mytest_module
00001760 D mytest_module

はい。出てきました。
mytest_module
nmは、この名前のデータセクションが存在すると言っています。


アパッチは起動の際、httpd.confのLoadModuleディレクティブを見つけると
指定されたモジュール*1を読み込み
そこに指定されたデータセクション名*2を探しに行くわけです。

さて、後は起動して動きを確かめるだけです。

httpd.confの設定

その前に少しソースを眺めなければなりません。

mod_mytest.c
/* 
**  mod_mytest.c -- Apache sample mytest module
**  [Autogenerated via ``apxs -n mytest -g'']
**
**  To play with this sample module first compile it into a
**  DSO file and install it into Apache's modules directory 
**  by running:
**
**    $ apxs -c -i mod_mytest.c
**
**  Then activate it in Apache's httpd.conf file for instance
**  for the URL /mytest in as follows:
**
**    #   httpd.conf
**    LoadModule mytest_module modules/mod_mytest.so
**    <Location /mytest>
**    SetHandler mytest
**    </Location>
**
**  Then after restarting Apache via
**
**    $ apachectl restart
**
**  you immediately can request the URL /mytest and watch for the
**  output of this module. This can be achieved for instance via:
**
**    $ lynx -mime_header http://localhost/mytest 
**
**  The output should be similar to the following one:
**
**    HTTP/1.1 200 OK
**    Date: Tue, 31 Mar 1998 14:42:22 GMT
**    Server: Apache/1.3.4 (Unix)
**    Connection: close
**    Content-Type: text/html
**  
**    The sample page from mod_mytest.c
*/ 

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

/* The sample content handler */
static int mytest_handler(request_rec *r)
{
    if (strcmp(r->handler, "mytest")) {
        return DECLINED;
    }
    r->content_type = "text/html";      

    if (!r->header_only)
        ap_rputs("The sample page from mod_mytest.c\n", r);
    return OK;
}

static void mytest_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(mytest_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA mytest_module = {
    STANDARD20_MODULE_STUFF, 
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    mytest_register_hooks  /* register hooks                      */
};

httpd.confには以下の行を書きなさいと言っているので書きます。

LoadModule mytest_module modules/mod_mytest.so
<Location /mytest>
    SetHandler mytest
</Location>

viで編集

$ sudo vi /usr/local/apache2/conf/httpd.conf


ついでにどんな動作をするのか確かめておきます。

    r->content_type = "text/html";      

    if (!r->header_only)
        ap_rputs("The sample page from mod_mytest.c\n", r);

どうやら、メッセージを表示するだけのようです。

それではアパッチを起動します。

アパッチ起動

$ sudo vi /usr/local/apache2/bin/apachectl start

これでブラウザでhttp://localhost/mytestにアクセスすればメッセージが表示されます。

私は面倒なので端末で確認。

$ wget http://localhost/mytest -q -O /tmp/1 ; cat /tmp/1
The sample page from mod_mytest.c

OK!!

最後に。
モジュールのビルドはapxsを使わなくても出来ます。
普通にgccを使うだけですね。
今回のモジュールではこんな感じになります。

$ gcc -shared -pthread -fPIC mod_mytest.c -o mod_mytest.so -I /usr/local/apache2/include  -L /usr/local/apache2/lib

まあ何てことは無いです。


実は私はこちらの方法を良く使ってたりします。
(当然Makefileを書きますが・・・)
必要に応じてデバッグオプションを付けたり何かと融通が利くんですよね。


今回はここまでです。
次回からは本格的にモジュールプログラミングの世界に入門します!

*1:ここではmodules/mod_mytest.so

*2:ここではmytest_module