アプリケーション開発ポータルサイト
ServerNote.NET
カテゴリー【C/C++ApacheDebian
DebianでApache2.4モジュールをC++言語で作成する
POSTED BY
2023-07-23

モジュールをCでなくC++言語で書いた場合、単純にフラグに-lstdc++をつけるだけではだめで、Apache再起動時にundefined symbol: __gxx_personality_v0またはCan't locate API module structureなどと言われ無視されてしまう。

C++を使う場合apxs2コマンドでなくMakefileを修正してmakeでコンパイル、インストールを行う。

1、ソースファイル名の変更

.cファイルは.cppファイルに変更する。

mv mod_mytest.c mod_mytest.cpp

2、サンプルC++ソースコード

ap_関数,AP_変数は元々Cでの利用を想定しているため、C++で書くにはいくつかルールがある。
ためしにstd::stringを使って日本語文字を出力しつつ、処理時間をerror_logに出力するサンプルとした。

C/C++mod_mytest.cppGitHub Source
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "http_log.h"
#include "util_script.h"
#include <string>

extern "C" {

#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(mytest);
#endif

};

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

  if (r->header_only){
    return OK;
  }

  struct timeval tv_fr,tv_to;
  double tm_fr,tm_to;
  gettimeofday(&tv_fr, NULL);

    ap_rputs("The sample page from mod_mytest.c\n", r);

  std::string s = std::string("てすとばる");

  ap_rprintf(r, "てすときー = [%s]\n", s.c_str());

  gettimeofday(&tv_to, NULL);

  tm_fr = (((double)tv_fr.tv_sec)*((double)1000000)+((double)tv_fr.tv_usec));
  tm_to = (((double)tv_to.tv_sec)*((double)1000000)+((double)tv_to.tv_usec));
 
  ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, "time: %lf MICROSEC (%lf MSEC)",tm_to - tm_fr,(tm_to - tm_fr) / 1000);

    return OK;
}

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

extern "C" {

/* 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                      */
};

};

2つの箇所をextern Cで囲う。さらに、Cでは必須ではなかった、include直後のモジュール宣言(APLOG_USE_MODULE(mytest)の箇所)が、ap_log_rerrorなどでログ出力する場合は必須となる。宣言しなければ、コンパイル時に

/usr/include/apache2/http_log.h:166:30: error: ‘aplog_module_index’ was not declared in this scope
 #define APLOG_MODULE_INDEX (*aplog_module_index)

などとコンパイルエラーになる。このことは、/usr/include/apache2/http_log.hに、

#ifdef __cplusplus
/**
 * C++ modules must invoke ::APLOG_USE_MODULE or ::AP_DECLARE_MODULE in
 * every file which uses ap_log_* before the first use of ::APLOG_MARK
 * or ::APLOG_MODULE_INDEX.
 * (C modules *should* do that as well, to enable module-specific log
 * levels. C modules need not obey the ordering, though).
 */

と記載があり、C++でap_logを使いたくばAPLOG_USE_MODULEを宣言せよとのアドバイスが書かれている。

3、Makefileの修正

初回作成時のapxs2コマンドによりMakefileは自動生成されているはずである。これのLIBS記述のあるあたりに以下を追記する。

vi Makefile

#   the used tools
#APACHECTL=apachectl
APACHECTL=/etc/init.d/apache2

#   additional defines, includes and libraries
#DEFS=-Dmy_define=my_value
#INCLUDES=-Imy/include/dir
#LIBS=-Lmy/lib/dir -lmylib
CC=g++
CXX=g++
LIBS=-L/usr/local/lib -lstdc++

APACHECTL=/etc/init.d/apache2としているのは、組み込みコマンドapachectlでは子プロセスの再起動しかしてくれずモジュール変更が反映されなかったのでinit.dを使うこととした。

4、ビルド、インストール

make clean
make
sudo make install

途中の出力で、libtool: compile: g++などと、ちゃんとg++コマンドでコンパイルされている旨が出力されているはず。

5、/etc/apache2/mods-available/mytest.loadでライブラリを直接ロードする

-lstdc++としたのに何故か無視されるならばLoadModule直前に外部ライブラリファイルを直接ロードする記述をしてしまえば良い。

https://theunixtips.com/howto-develop-apache-module-in-c/

に、ヒントが載っていた。

cd /etc/apache2/mods-available
vi mytest.load

LoadFile /usr/lib/x86_64-linux-gnu/libstdc++.so.6
LoadModule mytest_module      /usr/lib/apache2/modules/mod_mytest.so

LoadFileディレクティブで直接ライブラリファイルをロード。この方法なら、前回のredisライブラリも/usr/local/lib/libhiredis.soと直接記述してしまえばMakefileで同じようにビルドできそうである。

6、Apache再起動、テスト

さきほどMakefileを修正したので、

sudo make restart

でいける。インストール・再起動を同時に行う場合

sudo make reload

でいける。
ここでエラーが出なければ、無事C++のLIBが読み込まれてモジュールが有効になった。
あとは前回と変更なく下記URI設定になっているとして、

<virtualhost>
(中略)
    <location>
        SetHandler mytest
    </location>
</virtualhost>

http://192.168.1.55/mytest

などと打って

The sample page from mod_mytest.c
てすときー = [てすとばる]

と表示されれば、C++の証であるstd::stringクラスの利用ができていることとなる。
さらにerror_logには、

[Mon Jun 22 20:56:18.457636 2020] [mytest:crit] [pid 10166:tid 140352675399424] [client 172.18.255.116:55740] time: 12.000000 MICROSEC (0.012000 MSEC)

と、実行時間計測のap_logが無事出力されているのが確認できた。

※本記事は当サイト管理人の個人的な備忘録です。本記事の参照又は付随ソースコード利用後にいかなる損害が発生しても当サイト及び管理人は一切責任を負いません。
※本記事内容の無断転載を禁じます。
【WEBMASTER/管理人】
自営業プログラマーです。お仕事ください!
ご連絡は以下アドレスまでお願いします★

【キーワード検索】