动态库实现插件-sysrepo笔记(4)

504 阅读2分钟

插件顾名思义就是通过宿主程序提供的机制,影响宿主程序而实现特定功能的一种程序。

sysrepo数据库的一个重要的功能是在数据变更的时候,通过共享内存文件保存变更的内容,然后通过变更内容的模块找到相关路径订阅的管道id,通过往管道文件中写入随机数据通知订阅处理线程取数据,然后进行对应的操作处理。 

每个对数据变化感兴趣的处理流程都可以通过sysrpo提供的接口注册处理函数,每个处理流程对于sysrepo数据更新进程而言,都是客户端,可以通过一个独立的进程sysrepo-plugind来管理,每个处理流程可以做成一个个插件。根据订阅的参数,sysrepo会发送不同的事件,处理流程根据事件的类型,进行不同的处理。

根据处理的结果,sysrepo可以按序确认是否需要新增额外的修改 , 确认修改是否合法,不合法则结束更新;合法则写入数据库,最后通告最终的配置变更,客户端接收到最终变更后做具体的动作。

插件的实现很简单,只要实现两个接口sr_plugin_init_cb() 和sr_plugin_cleanup_cb() 。在sr_plugin_init_cb实现对某个模块的路径、关心的数据变化事件进行注册回调函数, 在sr_plugin_cleanup_cb对回调函数解除注册。每个代码都编译成动态库so,放到指定的路径下。sysrepo-plugind程序启动的时候会从该路径下读取所有的so文件,并依次执行sr_plugin_init_cb函数进行注册。

// src/executables/sysrepo-plugind.c
static int
load_plugins(struct srpd_plugin_s **plugins, int *plugin_count)
{
    ...

    while ((ent = readdir(dir))) {
        if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
            continue;
        }

        /* open the plugin */
        if (asprintf(&path, "%s/%s", plugins_dir, ent->d_name) == -1) {
            error_print(0, "asprintf() failed (%s).", strerror(errno));
            rc = -1;
            break;
        }
        handle = dlopen(path, RTLD_LAZY);
        if (!handle) {
            error_print(0, "Opening plugin \"%s\" failed (%s).", path, dlerror());
            free(path);
            rc = -1;
            break;
        }
        free(path);

        /* allocate new plugin */
        mem = realloc(*plugins, (*plugin_count + 1) * sizeof **plugins);
        if (!mem) {
            error_print(0, "realloc() failed (%s).", strerror(errno));
            dlclose(handle);
            rc = -1;
            break;
        }
        *plugins = mem;

        /* find required functions */
        *(void **)&(*plugins)[*plugin_count].init_cb = dlsym(handle, SRP_INIT_CB);
        if (!(*plugins)[*plugin_count].init_cb) {
            error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", SRP_INIT_CB, ent->d_name);
            dlclose(handle);
            rc = -1;
            break;
        }

        *(void **)&(*plugins)[*plugin_count].cleanup_cb = dlsym(handle, SRP_CLEANUP_CB);
        if (!(*plugins)[*plugin_count].cleanup_cb) {
            error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", SRP_CLEANUP_CB, ent->d_name);
            dlclose(handle);
            rc = -1;
            break;
        }

        /* finally store the plugin */
        (*plugins)[*plugin_count].handle = handle;
        (*plugins)[*plugin_count].private_data = NULL;

        name_len = length_without_extension(ent->d_name);
        if (name_len == 0) {
            error_print(0, "Wrong filename \"%s\".", ent->d_name);
            dlclose(handle);
            rc = -1;
            break;
        }

        (*plugins)[*plugin_count].plugin_name = strndup(ent->d_name, name_len);
        if (!((*plugins)[*plugin_count].plugin_name)) {
            error_print(0, "strndup() failed.");
            dlclose(handle);
            rc = -1;
            break;
        }

        ++(*plugin_count);
    }

    closedir(dir);
    return rc;
}


int
main(int argc, char **argv)
{

    ... 
    /* init plugins */
    for (i = 0; i < plugin_count; ++i) {
        r = plugins[i].init_cb(sess, &plugins[i].private_data);
        if (r != SR_ERR_OK) {
            SRP_LOG_ERR("Plugin initialization failed (%s).", sr_strerror(r));
            goto cleanup;
        }
    }

    /* wait for a terminating signal */
    pthread_mutex_lock(&lock);
    while (!loop_finish) {
        pthread_cond_wait(&cond, &lock);
    }
    pthread_mutex_unlock(&lock);

    /* cleanup plugins */
    for (i = 0; i < plugin_count; ++i) {
        plugins[i].cleanup_cb(sess, plugins[i].private_data);
    }
    ...
}

我们从代码里可以看到具体的流程:

  • 读取指定目录下的so, 获取init和clean函数入口地址, 插件名等信息
  • 依次执行init函数
  • 等待结束信号, 调用clean函数清理注册函数

sysrepo的官方文档里面也有一个插件的示例,感兴趣可以去看看。

netopeer.liberouter.org/doc/sysrepo…


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。