答案就在plugin.h文件中的下面一系列函数声明:
handler_t plugins_call_handle_uri_raw(server * srv, connection * con); handler_t plugins_call_handle_uri_clean(server * srv,connection * con); handler_t plugins_call_handle_subrequest_start(server * srv,connection * con); handler_t plugins_call_handle_subrequest(server * srv,connection * con); handler_t plugins_call_handle_request_done(server * srv,connection * con); handler_t plugins_call_handle_docroot(server * srv,connection * con); handler_t plugins_call_handle_physical(server * srv,connection * con); handler_t plugins_call_handle_connection_close(server * srv,connection * con); handler_t plugins_call_handle_joblist(server * srv,connection * con); handler_t plugins_call_connection_reset(server * srv,connection * con); handler_t plugins_call_handle_trigger(server * srv); handler_t plugins_call_handle_sighup(server * srv); handler_t plugins_call_init(server * srv); handler_t plugins_call_set_defaults(server * srv); handler_t plugins_call_cleanup(server * srv);这些函数就是插件系统对外的接口。在运行过程中,lighttpd靠调用上面的这些函数调用插件。比如:在server.c的main函数中,就调用了plugins_call_set_defaults函数:
if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; }如果你使用ctags+vim看代码,当在这些函数的调用处想跳转到定义时发现ctags没有找到这些函数的定义。难道这些函数没有实现?这显然是不会的。其实,这正是由于本文的重点───PLUGIN_TO_SLOT宏造成的。
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE,handle_request_done) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,handle_connection_close) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START,handle_subrequest_start) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical) PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup) PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup) PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)再看看PLUGIN_SLOT宏的前两行:
#define PLUGIN_TO_SLOT(x, y) \
handler_t plugins_call_##y(server *srv, connection *con) {\
这下明白了吧。上面那些函数是由这些宏调用模板化生成的。由于这些函数的代码具有很高的相似度(仅仅是调用的插件函数不同),通过宏模板进行生成,可以节约大量的代码,同时又不容易出错。这类似于C++中的模板。注:C语言预处理器运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则改参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。(摘自:C语言程序设计 K&R)在这里,plugins_call_和实参y拼接成函数名。
#define PLUGIN_TO_SLOT(x, y) \ handler_t plugins_call_##y(server *srv, connection *con) {\ plugin **slot;\ size_t j;\ if (!srv->plugin_slots) return HANDLER_GO_ON;\ slot = ((plugin ***)(srv->plugin_slots))[x];\ if (!slot) return HANDLER_GO_ON;\ for (j = 0; j < srv->plugins.used && slot[j]; j++) { \ plugin *p = slot[j];\ handler_t r;\ switch(r = p->y(srv, con, p->data)) {\ case HANDLER_GO_ON:\ break;\ case HANDLER_FINISHED:\ case HANDLER_COMEBACK:\ case HANDLER_WAIT_FOR_EVENT:\ case HANDLER_WAIT_FOR_FD:\ case HANDLER_ERROR:\ return r;\ default:\ log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\ return HANDLER_ERROR;\ }\ }\ return HANDLER_GO_ON;\ }根据后面的宏调用可以看出,参数x是plugin_t枚举类型,y是plugin结构体中函数指针成名的名字。在宏调用中,x和y是相对应的。