应用程序通过将对Sysrepo的调用通过Sysrepo提供的相应的API接口访问方法,称为Syrepo的间接访问方法。该方法是应用程序通过创建Deamon进程,通过IPC Shm机制与Sysrepo通信。可以做到对Sysrepo的即插即用,最后由Sysrepo纳管,这就是Plugind,命名为sysrepo-plugind。要快速的使用Sysrepo,并快速开发出适配于Sysrepo的插件,就要先了解sysrepo-plugind的实现原理与机制,就需要先从实现sysrepo-plugind的源码处着手。Sysrepo源码路径:git clone https://github.com/sysrepo/sysrepo.git Sysrepo-plugind实现的路径为sysrepo/src/executables/sysrepo-plugind.c。下面也就从该文件开始说。

2.1 数据结构

struct srpd_plugin_s {void *handle;srp_init_cb_t init_cb;srp_cleanup_cb_t cleanup_cb;void *private_data;
};
/*结构参数说明*/
handle:动态库句柄,在load_plugin中细说srp_init_cb_t:
/*Sysrepo plugin initialization callback.*/
typedef int (*srp_init_cb_t)(sr_session_ctx_t *session, void **private_data);srp_cleanup_cb_t :
/*brief Sysrepo plugin cleanup callback.*/
typedef void (*srp_cleanup_cb_t)(sr_session_ctx_t *session, void *private_data);private_data:  Private context opaque to sysrepo

2.2 main函数实现

int
main(int argc, char** argv)
{struct srpd_plugin_s *plugins = NULL;       /*plugin结构*/sr_conn_ctx_t *conn = NULL;                 /*sysrepo连接的上下文,该结构定义于common.h.in*/sr_session_ctx_t *sess = NULL;              /*sysrepo会话的上下文,该结构定义于common.h.in中*/sr_log_level_t log_level = SR_LL_ERR;       /*输出log等级,默认是ERR*/int plugin_count = 0, i, r, rc = EXIT_FAILURE, opt, debug = 0;struct option options[] = {{"help",      no_argument,       NULL, 'h'},{"version",   no_argument,       NULL, 'V'},{"verbosity", required_argument, NULL, 'v'},{"debug",     no_argument,       NULL, 'd'},{NULL,        0,                 NULL, 0},};                                          /*命令行支持的参数*//* process options */opterr = 0;/*整个while循环是解析命令的参数,例如,在调试时,输入“sysrepo-plugind -d -v 4” 是debug模式        *下log级别DBG级,将会打印全部的调试信息*/while ((opt = getopt_long(argc, argv, "hVv:d", options, NULL)) != -1) {switch (opt) {case 'h':version_print();help_print();rc = EXIT_SUCCESS;goto cleanup;case 'V':version_print();rc = EXIT_SUCCESS;goto cleanup;case 'v':if (!strcmp(optarg, "none")) {log_level = SR_LL_NONE;} else if (!strcmp(optarg, "error")) {log_level = SR_LL_ERR;} else if (!strcmp(optarg, "warning")) {log_level = SR_LL_WRN;} else if (!strcmp(optarg, "info")) {log_level = SR_LL_INF;} else if (!strcmp(optarg, "debug")) {log_level = SR_LL_DBG;} else if ((strlen(optarg) == 1) && (optarg[0] >= '0') && (optarg[0] <= '4')) {log_level = atoi(optarg);} else {error_print(0, "Invalid verbosity \"%s\"", optarg);goto cleanup;}break;case 'd':debug = 1;break;default:error_print(0, "Invalid option or missing argument: -%c", optopt);goto cleanup;}}/* check for additional argument */if (optind < argc) {error_print(0, "Redundant parameters");goto cleanup;}/* load plugins:将所有的pluginl加载,这是整个main第一处核心点,这关系用户开发的plugin能否正确加载*/if (load_plugins(&plugins, &plugin_count)) {goto cleanup;}/* daemonize, sysrepo-plugind no longer directly logs to stderr */daemon_init(debug, log_level);/* create connection (after we have forked so that our PID is correct) *//*调用sysrepo API(sr_connect)创建与sysrepo的连接,并将返回创建连接的上下发*/if ((r = sr_connect(0, &conn)) != SR_ERR_OK) {error_print(r, "Failed to connect");goto cleanup;}/* create session *//*调用sysrepo API(sr_session_start)创建与sysrepo running库的会话,并启动该会话*/if ((r = sr_session_start(conn, SR_DS_RUNNING, &sess)) != SR_ERR_OK) {error_print(r, "Failed to start new session");goto cleanup;}/*sr_connect(), sr_session_start(),是连接sysrepo基础,这两点基础实现,在后面sysrepo源码 *分析做详细说明,不在sysrepo-plugin中说明*//* init plugins *//*对所有已加载的plugin通过调用init_cb注册的回调初始化,这是整个main第二处核心点,与用户是强 *相关用户开发的插件,注册,订阅,初始化都通过init_cb,否则不能将sysrepol通信连接*/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 *//* sysrepo-plugindf正常结束后,回收plugin初始化时分配的资源*/for (i = 0; i < plugin_count; ++i) {plugins[i].cleanup_cb(sess, plugins[i].private_data);}/* success */rc = EXIT_SUCCESS;/*结束后,回收已分配的全部资源*/
cleanup:for (i = 0; i < plugin_count; ++i) {dlclose(plugins[i].handle);}free(plugins);sr_disconnect(conn);return rc;
}

2.3 load_plugins

static int
load_plugins(struct srpd_plugin_s **plugins, int *plugin_count)
{void *mem, *handle;DIR *dir;struct dirent *ent;const char *plugins_dir;char *path;int rc = 0;*plugins = NULL;*plugin_count = 0;/* get plugins dir from environment variable, or use default one *//* bin_common.h.in #define SRPD_PLUGINS_PATH "@PLUGINS_PATH@"* @PLUGINS_PATH@在CMakeList.txt中定义,在编译时也可以自定义* CMakeList.txt对其定义如下* if(NOT PLUGINS_PATH)* set(PLUGINS_PATH             *    "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/sysrepo/plugins/" CACHE PATH*   "Sysrepo plugin daemon plugins path.")*   endif()*  用户不指定plugin的路径时,debian系统默认将plugin的动态库文件*.so安装 *    于/usr/lib/x86_64-linux-gnu/sysrepo/plugins/目录下,*  而Centos系统的默认安装路径为/usr/lib/sysrepo/plugins,在开发plugind时,安装路径也需要 *  指定到该路径下,否则,*.so找不到,则load不成功。*/plugins_dir = getenv("SRPD_PLUGINS_PATH");if (!plugins_dir) {plugins_dir = SRPD_PLUGINS_PATH;}/* create the directory if it does not exist *//* access函数,:检查调用进程是否可以对指定的文件执行某种操作, F_OK文件是否存在* 本段代码是检测SRPD_PLUGINS_PATH目录是否存在,如果不存在,调用sr_mkpath创建目录,并设置*            * 目录的访问权限000777。本段代码是安全性代码,确保指定的路径存在。对于实际开发中,是通过编 * 译是指定,不存在路径的动态库无法安装。*/if (access(plugins_dir, F_OK) == -1) {if (errno != ENOENT) {error_print(0, "Checking plugins dir existence failed (%s).", strerror(errno));return -1;}if (sr_mkpath(plugins_dir, 00777) == -1) {error_print(0, "Creating plugins dir \"%s\" failed (%s).", plugins_dir, strerror(errno));return -1;}}/* opendir函数,找开指定的目录文件,并返回DIR*形态的目录流,* 目录的读取与搜查也都需要此目录流 */dir = opendir(plugins_dir);if (!dir) {error_print(0, "Opening \"%s\" directory failed (%s).", plugins_dir, strerror(errno));return -1;}/*readdir函数,读取目录,返回参数dir目录流的下个目录进入点* 返回的结果是struct dirent的内容*/while ((ent = readdir(dir))) {/*Linux系统中存在"." ".."两类目录,这两类目录名结构,在实际是不需要使用,需要跳过。*/if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {continue;}/* open the plugin *//*将SRPD_PLUGINS_PATH与也读取的目录文件名,组成一个完成的动态库路径,供后面操作。*/if (asprintf(&path, "%s/%s", plugins_dir, ent->d_name) == -1) {error_print(0, "asprintf() failed (%s).", strerror(errno));rc = -1;break;}/*RTLD_LAZY:暂缓决定,等有需要时再解出符号 *以RTLD_LAZY模式打开动态库,返回一个句柄给调用进程,如果失败,则返回。*/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 *//* 分配一个新的plugin空间,并将新分配的men挂载plugins结构下*/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 *//* plugins结构中有两个必须的回调函数,一个是init_cb,另一个是cleanup_cb* 通过 void *dlsym(void *handle, const char* symbol);,* handle是使用dlopen函数之后返回的句柄,* symbol是要求获取的函数的名称。* SRP_INIT_CB定义如下:#define SRP_INIT_CB     "sr_plugin_init_cb"* SRP_CLEANUP_CB定义下:#define SRP_CLEANUP_CB  "sr_plugin_cleanup_cb"* 此两个CB函数,也就是在开发插件中必须实现的两个入口函数,如果不存在,则加载失败。*/*(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 *//*最后,本次so解析成功,保存本次so的解析结果,执行一下次目录文件的读取*/(*plugins)[*plugin_count].handle = handle;(*plugins)[*plugin_count].private_data = NULL;++(*plugin_count);}/*目录文件读取结束,关闭目录读取流,返回的参考中有插件结构plugins。*/closedir(dir);return rc;
}

2.4 init_cb

srpd_plugin_s结构中定义了init的回调函数
如下:
typedef int (*srp_init_cb_t)(sr_session_ctx_t *session, void **private_data);
在load plugin时,
#define SRP_INIT_CB     "sr_plugin_init_cb"
init_cb = dlsym(handle, SRP_INIT_CB);
在sysrepo-plugind的main实现时,需要对plugin的初始化,实际就是需要用户对sr_plugin_init_cb()实现,是完成该plugin的资源分配,用户感兴趣的事情做订阅,Module change RPC/Action,Notify, get——items等操作,均在此处完成。有如下例子,请参考。int
sr_plugin_init_cb(sr_session_ctx_t *session, void **private_ctx)
{int rc;struct plugind_ctx *ctx;ctx = calloc(1, sizeof *ctx);if (!ctx) {rc = SR_ERR_NOMEM;goto error;}/*在下面初始与之有关的操作,例如,本地数据结构的初始化,sysrepo的订阅初始化*/...SRP_LOG_DBGMSG("plugin initialized successfully.");ctx->session = session;*private_ctx = ctx;return SR_ERR_OK;error:SRP_LOG_ERR("plugin initialization failed (%s).", sr_strerror(rc));sr_unsubscribe(ctx->subscription);free(ctx);return rc;
}

2.5 cleanup_cb

srpd_plugin_s结构中定义了cleanup的回调函数
如下:
typedef void (*srp_cleanup_cb_t)(sr_session_ctx_t *session, void *private_data);
在load plugin时,
#define SRP_CLEANUP_CB  "sr_plugin_cleanup_cb"
cleanup_cb = dlsym(handle, SRP_CLEANUP_CB);
所以,对于用户来就,是需要对sr_plugin_cleanup_cb()实现,回收plugin在初始化时分配的资源。例如下面的cleanup实现,可以参考void
sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_ctx)
{(void)session;struct plugind_ctx *ctx = (struct plugind_ctx *)private_ctx;sr_unsubscribe(ctx->subscription);free(ctx);nb_terminate();yang_terminate();SRP_LOG_DBGMSG("plugin cleanup finished.");
}

整个sysrepo-plugind.c代码结构简单,注释丰富,没有使用复杂的语法,还是非常容易理解的。

第三章 sysrepo-plugind源码分析相关推荐

  1. 深入java并发包源码(三)AQS独占方法源码分析

    深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 AQS 的实现原理 学完用 AQS 自定义一个锁以后,我们可以来看 ...

  2. 创建线程的三种方法_Netty源码分析系列之NioEventLoop的创建与启动

    前言 前三篇文章分别分析了 Netty 服务端 channel 的初始化.注册以及绑定过程的源码,理论上这篇文章应该开始分析新连接接入过程的源码了,但是在看源码的过程中,发现有一个非常重要的组件:Ni ...

  3. 基于比原链开发Dapp(三)-Dapp-demo前端源码分析

    # 简介 ​    本章内容会针对比原官方提供的dapp-demo,分析里面的前端源码,分析清楚整个demo的流程,然后针对里面开发过程遇到的坑,添加一下个人的见解还有解决的方案. ### 储蓄分红合 ...

  4. .NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入

    写在前面 上篇文章我给大家讲解了ASP.NET Core的概念及为什么使用它,接着带着你一步一步的配置了.NET Core的开发环境并创建了一个ASP.NET Core的mvc项目,同时又通过一个实战 ...

  5. tensorflow入门教程(三十五)facenet源码分析之MTCNN--人脸检测及关键点检测

    # #作者:韦访 #博客:https://blog.csdn.net/rookie_wei #微信:1007895847 #添加微信的备注一下是CSDN的 #欢迎大家一起学习 # ------韦访 2 ...

  6. 在参考了众多博客之后,我写出了多达三万字的HashMap源码分析,比我本科毕业论文都要精彩

    HashMap源码分析 以下代码都是基于java8的版本 HashMap简介 源码: public class HashMap<K,V> extends AbstractMap<K, ...

  7. LDD3源码分析之字符设备驱动程序

    http://blog.csdn.net/liuhaoyutz/article/details/7383313 作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 编译 ...

  8. NEO从源码分析看NEOVM

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 前言 这篇文章是为下一篇<NEO从源码分析看UTXO转账交易>打前站,为交易的构造及执行的一些技术基础做 ...

  9. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 )

    文章目录 一.Activity 启动源码分析 ( AMS | ActivityManagerService ) 1.Instrumentation 调用 AMS 方法 2.ActivityStarte ...

  10. 【Android 安全】DEX 加密 ( 多 DEX 加载 | 65535 方法数限制和 MultiDex 配置 | PathClassLoader 类加载源码分析 | DexPathList )

    文章目录 一.65535 方法数限制和 MultiDex 配置 二.多 DEX 加载引入 三.PathClassLoader 类加载源码分析 四.BaseDexClassLoader 类加载源码分析 ...

最新文章

  1. java 枚举使用简书_java枚举类型使用和详解
  2. 重建包含快照的vmdk描述文件。
  3. 职场中的那点事--小领导大智慧
  4. php发布post到wordpress,PHP-如何将数据发布到wordpress插件?
  5. DB2的ErrorCode
  6. .net mvc 导出excel表格
  7. 鞋城模板+html,西安锦绣鞋城整合营销策划方案
  8. 耗资52亿美元,历时15年,人类有史以来建造的最复杂机器
  9. 鼠标按下并移动事件的解决方案
  10. php 图片合成,PHP实现将几张照片拼接到一起的合成图片功能【便于整体打印输出】...
  11. linux网络编程常用头文件总结
  12. python 回溯法 01背包问题_Python基于回溯法解决01背包问题实例
  13. su组件在什么窗口,【答疑】草图大师Sketchup组件窗口快捷键是什么呢? - 羽兔网问答...
  14. 【深入理解Kotlin协程】Google的工程师们是这样理解Flow的?
  15. 网络游戏需要辅助性外挂?
  16. 【课程作业】西瓜书 机器学习课后习题 : 第七章
  17. 鸿蒙系统操作界面跟苹果很像,鸿蒙系统逻辑近似苹果iOS13?这华为在搞什么?...
  18. 威漫哨兵机器人_漫威电影中,差点让变种人团灭的哨兵机器人,如何才能打败他?...
  19. 下载有道词典遇到的问题
  20. python实时脚本_web 监控 python 脚本

热门文章

  1. java jsp 获得网页源代码三种方式
  2. 基恩士XG-XvisionEditor程序的上传与下载
  3. uniapp swiper 添加视频
  4. [差分 上下界最大流] SRM 694 div1 SRMDiv0Easy
  5. 办公技巧巧用Excel群发电子邮件(转)
  6. 非分区表转换为分区表的三种方式
  7. python print 字体大小_Python print 玩转“点阵字”
  8. 面向自动驾驶的高精地图及数据采集生产体系
  9. ubuntu卸载福昕阅读器
  10. 检查excel指定列中是否有错别字存在