`
zhangziyueup
  • 浏览: 1171938 次
文章分类
社区版块
存档分类
最新评论

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将会被执行。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics