wifidog认证源码分析Lighttpd1.4.20源码分析之插件系统(2)---插件的加载和初始化(1)

前面讲了lighttpd插件系统的接口,下面我们来看看插件是怎么加载 和初始化的。
lighttpd的插件是以动态链接库的形式存在的。在服务器启动的时候,在初始化阶段将所有插件都加载进来。在server.c中的main函数中,加载插件是调用plugins_load函数:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 if (plugins_load(srv))
{
    log_error_write(srv, __FILE__, __LINE__, "s","loading plugins finally failed");
    plugins_free(srv);
    server_free(srv);
    return -1;
}

请读者注意一下这个函数调用的位置。这个函数是在服务器的初始化阶段进行调用的,并且该函数就在这调用了一次,其他地方没有再被调用过。虽然插件是以动态链接库的形式存在,但这些库是在服务器启动阶段一次性加载完毕,如果想再增加插件,只能配置好配置文件后重新启动服务器。
下面看一看plugins_load函数的实现:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 #ifdef LIGHTTPD_STATIC
 int plugins_load(server * srv)
{
    plugin *p;
 #define PLUGIN_INIT(x)\
    p = plugin_init(); \
    if (x ## _plugin_init(p)) { \
        log_error_write(srv, __FILE__, __LINE__, "ss",#x, "plugin init failed" ); \
        plugin_free(p); \
        return -1;\
    }\
    plugins_register(srv, p);
#include "plugin-static.h"
    return 0;
}
 #else
 //动态链接
 int plugins_load(server * srv)
{
    plugin *p;
    int (*init) (plugin * pl);
    const char *error;
    size_t i;
    for (i = 0; i < srv->srvconf.modules->used; i++)
    {
        //获得动态链接库的名称。
         data_string *d = (data_string *) srv->srvconf.modules->data[i];
        char *modules = d->value->ptr;
        //库所在目录
         buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir);

        buffer_append_string_len(srv->tmp_buf,     CONST_STR_LEN("/"));
        //拼接库的名称。
         buffer_append_string(srv->tmp_buf, modules);
        buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
        p = plugin_init();
                //linux调用函数dlopen加载动态库
         if (NULL == ( p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW | RTLD_GLOBAL)))
        {
            log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", srv->tmp_buf, dlerror());
            plugin_free(p);
            return -1;
        }
        //调用动态库中的XXX_plugin_init函数。
        //XXX是库的名称
         buffer_reset(srv->tmp_buf);
        buffer_copy_string(srv->tmp_buf, modules);
        buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
 #if 1
        //调用dlsym函数获得XXX_plugin_init函数的地址。
         init = (int (*)(plugin *)) (intptr_t) dlsym(p->lib, srv->tmp_buf->ptr);
 #else
        //这句没有用
         *(void **) (&init) = dlsym(p->lib, srv->tmp_buf->ptr);
 #endif
        if ((error = dlerror()) != NULL)
        {
            log_error_write(srv, __FILE__, __LINE__, "s", error);
            plugin_free(p);
            return -1;
        }
        //初始化插件
    //在初始化的过程中,模块将自己所有的对外接口函数的入口地址都存入到p中。
         if ((*init) (p))
        {
            log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed");
            plugin_free(p);
            return -1;
        }
 #if 0
        log_error_write(srv, __FILE__, __LINE__, "ss", modules,
                        "plugin loaded");
 #endif
        plugins_register(srv, p);
    }
    return 0;
}
#endif //end of #ifdef LIGHTTPD_STATIC

上面的函数中删除了处理在windows下加载动态链接库的部分。有兴趣的读者可以自行查看源代码。下面全部讨论在linux下的实现。
这个函数作者编写了两个版本,从宏LIGHTTPD_STATIC可以看出,作者貌似是想提供一个加载静态链接库的版本。但是,该版本的函数并没有什么实质性的实现(在最新的版本中(1.4.26),该函数依然没有实现)。我们主要来看看后面的动态链接库的版本。
加载的过程如下:
(1)从配置文件中读取到动态链接库所在的文件夹和动态库的名称。
(2)创建一个plugin结构体的实例。
(3)调用dlopen函数加载动态库。在plugin结构体中保存返回的句柄。
(4)通过dlsym函数获得XXXXXX_plugin_init函数的地址。其中XXXXXX是配置文件中定义的这个插件的内容。
(5)调用XXXXXX_plugin_init函数。
(6)调用plugins_register函数注册插件。
XXXXXX_plugin_init函数在插件中定义,对这个插件进行初始化。其中,最重要的部分就是对plugin结构体中那一系列的函数指针进行赋值。如,mod_cgi模块中:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 int mod_cgi_plugin_init(plugin * p)
{
    p->version = LIGHTTPD_VERSION_ID;
    p->name = buffer_init_string("cgi");
    p->connection_reset = cgi_connection_close_callback;
    p->handle_subrequest_start = cgi_is_handled;
    p->handle_subrequest = mod_cgi_handle_subrequest;
#if 0
    p->handle_fdevent = cgi_handle_fdevent;
#endif
    p->handle_trigger = cgi_trigger;
    p->init = mod_cgi_init;
    p->cleanup = mod_cgi_free;
    p->set_defaults = mod_fastcgi_set_defaults;
    p->data = NULL;
    return 0;
}

plugins_register函数是将plugin结构体的指针存放在server结构体的plugins数组中。
插件加载完毕之后,main函数有处理了一些初始化的工作,然后,调用plugins_call_init函数对所有插件进行初始化:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 if (HANDLER_GO_ON != plugins_call_init(srv)) 
{
    log_error_write(srv, __FILE__, __LINE__, "s",
            "Initialization of plugins failed. Going down.");
    plugins_free(srv);
    network_close(srv);
    server_free(srv);
    return -1;
}

本文章由 http://www.wifidog.pro/2015/04/17/wifidog%E8%AE%A4%E8%AF%81%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90lighttpd%E5%8A%A0%E8%BD%BD%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96-1.html 整理编辑,转载请注明出处

标签: wifidog认证, wifidog源码, wifidog分析