Apache Hook机制解析(上)——钩子机制的实现

Apache中大量使用了Hook机制,使得第三方开发Module能够扩展Apache服务器的默认处理。

Apache Hook功能能够简述以下:
1.    程序主框架根据名称声明和定义Hook
2.    第三方 Module 经过实现和挂载Hook来扩展主框架的行为。
3.    程序主框架在某些操做发生时显示触发Hook

例如, Apache的事务日志(也即访问日志)就是用Hook机制实现的,对应上面的3个环节以下:
1.    在 Apache核心代码 protocol.c中定义了名为log_transaction的Hook:
APR_HOOK_STRUCT(
    …
    APR_HOOK_LINK(log_transaction)
    …
)



AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
                          (request_rec *r), (r), OK, DECLINED)

2.    可选模块 mod_log_config经过挂载Hook来扩展主程序的行为:
static void register_hooks(apr_pool_t *p)
{
        …   
        ap_hook_log_transaction(multi_log_transaction,NULL,
                                NULL,APR_HOOK_MIDDLE);
        …
}    

3.    在 Apache的核心流程 ap_process_request函数中显式触发了该Hook:
void ap_process_request(request_rec *r)
{
    …

    ap_run_log_transaction(r)
    …
}

上述机制是如何实现的呢,在此先作一个简单的描述:
1.    首先,Hook的基础实现是在apr库中,由 apr_hooks.hapr_hooks.c两个源文件来实现,这两个文件定义了大量的宏,并实现了一系列查找,排序相关的函数。
2.    其次,Hook是根据名字定义出来的,本例能够看做一个名为log_transaction的Hook。
3.    再其次,Hook经过 apr_hooks.h中提供的宏被定义,这些宏扩展开来之后,将产生多个函数和结构——本例中看到的ap_run_log_transaction函数和ap_hook_log_transaction函数就是这些宏定义出来的。

咱们就以log_transaction为例,来看一下 Apache对此Hook的实现。

(一)声明和定义
本小结暂时不对各个函数的流程进行解释,只是先说明 Apache如何经过宏来产生Hook所需函数和结构的声明和定义。

声明位于 http_protocol.h文件中:
AP_DECLARE_HOOK(int,log_transaction,(request_rec *r))

这个宏展开之后以下:
typedef int ap_HOOK_log_transaction_t (request_rec *r);

AP_DECLARE(void) ap_hook_log_transaction(ap_HOOK_log_transaction_t *pf,
                                const char *const *aszPre,
                                const char * const *aszSucc, int nOrder);
AP_DECLARE(int) ap_run_log_transaction (request_rec *r);
AP_DECLARE(apr_array_header_t *) ap_hook_get_log_transaction(void);

typedef struct ap_AP_log_transaction_t {
    ap_HOOK_log_transaction_t *pFunc;
    const char *szName;
    const char * const *aszPredecessors;
    const char * const *aszSuccessors;
    int nOrder;
} ap_AP_log_transaction_t;

也即声明了三个函数,并定义了一个函数指针类型和一个结构。上述三个函数的定义位于 protocol.c文件的代码中,也是由一个宏来定义:
AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
                          (request_rec *r), (r), OK, DECLINED)

此宏展开之后以下:
AP_DECLARE(void) ap_hook_log_transaction(ap_HOOK_log_transaction_t *pf,
                                const char *const *aszPre,
                                const char * const *aszSucc, int nOrder)

    ap_AP_log_transaction_t *pHook;
    if(!_hooks.link_log_transaction)
    {
        _hooks.link_log_transaction=apr_array_make(apr_hook_global_pool,
                                                                                             1,
                                                                                            sizeof(ap_AP_log_transaction_t));
        apr_hook_sort_register(log_transaction,&_hooks.link_log_transaction);
    }

    pHook=apr_array_push(_hooks.link_log_transaction);
    pHook->pFunc=pf;
    pHook->aszPredecessors=aszPre;
    pHook->aszSuccessors=aszSucc;
    pHook->nOrder=nOrder;
    pHook->szName=apr_hook_debug_current;
    if(apr_hook_debug_enabled)
        apr_hook_debug_show(log_transaction,aszPre,aszSucc);
}
   
AP_DECLARE(apr_array_header_t *) ap_hook_get_log_transaction(void);
{
    return _hooks.link_log_transaction;
}

AP_DECALRE(int) ap_run_log_transaction (request_rec *r)
{
    ap_AP_log_transaction_t *pHook;
    int n;
    ret rv;

    if(!_hooks.link_log_transaction)
    return ok;

    pHook=(ap_AP_log_transaction_t *)_hooks.link_log_transaction->elts;
    for(n=0 ; n < _hooks.link_log_transaction->nelts ; ++n)
    {
    rv=pHook[n].pFunc (r);

    if(rv != ok && rv != decline)
        return rv;
    }
    return ok;
}

这里用到了一个静态变量_hooks,这个变量也是由宏定义出来的,在 protocol.c文件的最开始:
APR_HOOK_STRUCT(
    APR_HOOK_LINK(post_read_request)
    APR_HOOK_LINK(log_transaction)
    APR_HOOK_LINK(http_scheme)
    APR_HOOK_LINK(default_port)
)

此宏展开之后以下:
static struct {
    apr_array_header_t* link_post_read_request;
    apr_array_header_t* link_log_transaction;
    apr_array_header_t* link_http_scheme;
    apr_array_header_t* link_default_port;
} _hooks;

也即声明了一个拥有4个成员(每一个成员都是一个数组)的匿名结构,并用此结构定义了一个名为_hooks的静态变量。


(二)触发
触发是由ap_run_log_transaction函数实现的:
AP_DECALRE(int) ap_run_log_transaction (request_rec *r)
{
    ap_AP_log_transaction_t *pHook;
    int n;
    ret rv;

    if(!_hooks.link_log_transaction)
    return ok;

    pHook=(ap_AP_log_transaction_t *)_hooks.link_log_transaction->elts;
    for(n=0 ; n < _hooks.link_log_transaction->nelts ; ++n)
    {
    rv=pHook[n].pFunc (r);

    if(rv != ok && rv != decline)
        return rv;
    }
    return ok;
}

    此函数遍历静态变量_hooks的成员link_log_transaction(类型为apr_array_header_t*,是 Apache内部使用的数组),并根据数组每一个元素中的函数指针进行函数调用——此函数指针指向各个模块挂载到此Hook上的函数。
    值得注意的是for循环中的条件语句——若是钩子函数的返回值既不是ok也不是decline,则终止循环,直接退出。

(三)挂载
挂载由函数ap_hook_log_transaction函数实现:
AP_DECLARE(void) ap_hook_log_transaction(ap_HOOK_log_transaction_t *pf,
                                const char *const *aszPre,
                                const char * const *aszSucc, int nOrder)

    ap_AP_log_transaction_t *pHook;
    if(!_hooks.link_log_transaction)
    {
        _hooks.link_log_transaction=apr_array_make(apr_hook_global_pool,
1,
sizeof(ap_AP_log_transaction_t));
        apr_hook_sort_register(“log_transaction”, &_hooks.link_log_transaction);
    }

    pHook=apr_array_push(_hooks.link_log_transaction);
    pHook->pFunc=pf;
    pHook->aszPredecessors=aszPre;
    pHook->aszSuccessors=aszSucc;
    pHook->nOrder=nOrder;
    pHook->szName=apr_hook_debug_current;
    if(apr_hook_debug_enabled)
        apr_hook_debug_show(log_transaction,aszPre,aszSucc);
}

    此函数先检查静态变量_hooks中的成员link_log_transaction是否为空,若是为空则建立一个初始大小为1的数组。而后往此数组中添加一个元素。

(四)综述
综上所述,对log_transaction这个Hook的具体实现和使用以下:
1.    框架定义静态结构变量_hooks,其成员link_log_transaction用于存放log_transaction相关的Hook。
2.    框架声明和定义Hook所需的函数:
a)    ap_hook_log_transaction,此函数的功能是将一个函数指针(注册)添加到对应的Hook数组中。
b)    ap_run_log_transaction,此函数的功能是执行一个Hook数组中的全部已注册函数。
3.    模块mod_log_config经过ap_hook_log_transaction注册(添加)本身的日志扩展函数multi_log_transaction
4.    框架调用ap_run_log_transaction,此时multi_log_transaction将会被执行。