插件顾名思义就是通过宿主程序提供的机制,影响宿主程序而实现特定功能的一种程序。
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,沟通交流。