浙林龙哥

   :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Apache Module 开发后记



上一篇文章

开发出 apache 2.0 的模块以后,又面对着要将其移植到 apache 其他版本的需求,经过这段时间一点点的修补,现在我的模块已经可以同时在 1.3/2.0/2.2 下编译。甚至在 2.0/Win32 环境下也编译出了 dll,供在个人PC上做开发的同事使用。

我感觉如果项目不复杂的话,可以学习我这样把所有的内容放在一个文件里面的做法。

最重要的就是利用 MODULE_MAGIC_COOKIE 的定义,把 1.3 和 2.0/2.2 的不同之处融合在一起。例子:

#if MODULE_MAGIC_COOKIE == 0x41503230UL || MODULE_MAGIC_COOKIE == 0x41503232UL
 
#include "util_filter.h"
#include "apr_strings.h"
module AP_MODULE_DECLARE_DATA foobar_module;
#define APLOG_FOOBAR APLOG_ERR,0
 
#else
 
module MODULE_VAR_EXPORT foobar_module;
#define apr_pool_t          pool
#define apr_table_t         table
#define apr_pcalloc         ap_pcalloc
#define apr_table_unset     ap_table_unset
#define apr_table_set       ap_table_set
#define apr_table_get       ap_table_get
#define apr_table_make      ap_make_table
#define apr_pstrdup         ap_pstrdup
#define apr_snprintf        ap_snprintf
#define apr_pstrndup        ap_pstrndup
#define APLOG_FOOBAR APLOG_ERR
 
#endif /* MODULE_MAGIC_COOKIE == 0x41503230UL */

同样,模块初始化的部分也这样针对不同版本定义一下。

这样主要的功能函数就可以使用同样的代码模块,并使用 apr_* 系列函数族了。

之所以要对 APLOG_ERR 做定义,是因为在 1.3 和 2.x 版本中,ap_log_error 和 ap_log_rerror 所使用的参数数目不一致,2.x 的参数要多一个,因此针对 2.x自动增加一个参数——",0"

2.0 和 2.2 有些地方也有小差别,我的代码里面就碰到了 apr_socket_create 的参数不一样。同样简单 #if 就可以处理了。

在 1.3 里面 module initializer 如果 2.x 里面的 post_config 会运行两次,而且2.x 上的这个小技巧无法直接使用了。本来是不影响程序运行的,但还是想出了一个变态的办法来解决它。module initializer 两次执行之间的一个重要事件就是 apache 的 daemonize。那样怎么来判断当前进程是否在 daemon 状态下呢?我的方案是:

/* 小技巧,用于帮助 init_module 只执行一次检查 */
#define MAX_FDS 1024
int daemon_flag(int fds[MAX_FDS])
{
    int fd;
    int newfd;
    int i;
    int opt;
    socklen_t optlen = sizeof(int);
    int ret = 0;
 
    memset(fds, 0, sizeof(int) * MAX_FDS);
    newfd = fd = socket(AF_INET,SOCK_STREAM,0);
    while (newfd < MAX_FDS && newfd > 0) {
        fds[newfd] = 1;
        newfd = dup(fd);
    }
    for (i = 0; i < MAX_FDS; i++) {
        if (fds[i] == 1) {
            close(i);
        } else {
            if (0 == getsockopt(i, SOL_SOCKET, SO_REUSEADDR, &opt, &optlen)) {
                fds[i] = -1; //这样返回以后也知道哪些 fd 被监听
                ret = 1;
            }
        }
    }
    return ret;
}

因为我知道 apache 必然会对监听套接字设置 SO_REUSEADDR,所以可如此判断。

由于 apache2 有了 apr 的支持,几乎不用修改任何代码模块就可以在 win32 下编译。而且只需要 MS 的免费工具就可以了,包括 MSVC C++ Toolkit 2003 和 Platform SDK。设置好 INCLUDE/LIB 路径后,只需要执行
cl /MD /D "WIN32" /c mod_foobar.c
link /DLL mod_foobar.obj libhttpd.lib libapr.lib

这样就得到了可被 LoadModule 的 mod_foobar.dll

win32 下我碰到的问题稍微麻烦一些。启动 apache 无法成功,报告什么 OPENSSL_Applink 错误。看了 openssl 的 FAQ,说什么要 include 一个 applink.c,但仍然无济于事。不过查看 applink.c 后发现,它似乎和 IO 库相关;最后我把以前使用的 stdio 替换成了 openssl 自己的 bio 函数族,该问题就消失了。

openssl 的 win32 库从这里下载并安装

posted on 2007-03-24 19:32  浙林龙哥  阅读(2586)  评论(0编辑  收藏  举报