应用程序通过将对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 数据结构

 1 struct srpd_plugin_s {
 2     void *handle;
 3     srp_init_cb_t init_cb;
 4     srp_cleanup_cb_t cleanup_cb;
 5     void *private_data;
 6 };
 7 /*结构参数说明*/
 8 handle:动态库句柄,在load_plugin中细说
 9  
10 srp_init_cb_t:
11 /*Sysrepo plugin initialization callback.*/
12 typedef int (*srp_init_cb_t)(sr_session_ctx_t *session, void **private_data);
13  
14 srp_cleanup_cb_t :
15 /*brief Sysrepo plugin cleanup callback.*/
16 typedef void (*srp_cleanup_cb_t)(sr_session_ctx_t *session, void *private_data);
17  
18 private_data:  Private context opaque to sysrepo

 

2.2 main函数实现

  1 int
  2 main(int argc, char** argv)
  3 {
  4     struct srpd_plugin_s *plugins = NULL;       /*plugin结构*/
  5     sr_conn_ctx_t *conn = NULL;                 /*sysrepo连接的上下文,该结构定义于common.h.in*/
  6     sr_session_ctx_t *sess = NULL;              /*sysrepo会话的上下文,该结构定义于common.h.in中*/
  7     sr_log_level_t log_level = SR_LL_ERR;       /*输出log等级,默认是ERR*/
  8     int plugin_count = 0, i, r, rc = EXIT_FAILURE, opt, debug = 0;
  9     struct option options[] = {
 10         {"help",      no_argument,       NULL, 'h'},
 11         {"version",   no_argument,       NULL, 'V'},
 12         {"verbosity", required_argument, NULL, 'v'},
 13         {"debug",     no_argument,       NULL, 'd'},
 14         {NULL,        0,                 NULL, 0},
 15     };                                          /*命令行支持的参数*/
 16  
 17     /* process options */
 18     opterr = 0;
 19  
 20     /*整个while循环是解析命令的参数,例如,在调试时,输入“sysrepo-plugind -d -v 4” 是debug模式        
 21      *下log级别DBG级,将会打印全部的调试信息
 22      */
 23     while ((opt = getopt_long(argc, argv, "hVv:d", options, NULL)) != -1) {
 24         switch (opt) {
 25         case 'h':
 26             version_print();
 27             help_print();
 28             rc = EXIT_SUCCESS;
 29             goto cleanup;
 30         case 'V':
 31             version_print();
 32             rc = EXIT_SUCCESS;
 33             goto cleanup;
 34         case 'v':
 35             if (!strcmp(optarg, "none")) {
 36                 log_level = SR_LL_NONE;
 37             } else if (!strcmp(optarg, "error")) {
 38                 log_level = SR_LL_ERR;
 39             } else if (!strcmp(optarg, "warning")) {
 40                 log_level = SR_LL_WRN;
 41             } else if (!strcmp(optarg, "info")) {
 42                 log_level = SR_LL_INF;
 43             } else if (!strcmp(optarg, "debug")) {
 44                 log_level = SR_LL_DBG;
 45             } else if ((strlen(optarg) == 1) && (optarg[0] >= '0') && (optarg[0] <= '4')) {
 46                 log_level = atoi(optarg);
 47             } else {
 48                 error_print(0, "Invalid verbosity \"%s\"", optarg);
 49                 goto cleanup;
 50             }
 51             break;
 52         case 'd':
 53             debug = 1;
 54             break;
 55         default:
 56             error_print(0, "Invalid option or missing argument: -%c", optopt);
 57             goto cleanup;
 58         }
 59     }
 60  
 61     /* check for additional argument */
 62     if (optind < argc) {
 63         error_print(0, "Redundant parameters");
 64         goto cleanup;
 65     }
 66  
 67     /* load plugins:将所有的pluginl加载,这是整个main第一处核心点,这关系用户开发的plugin能否正确加载*/
 68     if (load_plugins(&plugins, &plugin_count)) {
 69         goto cleanup;
 70     }
 71  
 72     /* daemonize, sysrepo-plugind no longer directly logs to stderr */
 73     daemon_init(debug, log_level);
 74  
 75     /* create connection (after we have forked so that our PID is correct) */
 76     /*调用sysrepo API(sr_connect)创建与sysrepo的连接,并将返回创建连接的上下发*/
 77     if ((r = sr_connect(0, &conn)) != SR_ERR_OK) {
 78         error_print(r, "Failed to connect");
 79         goto cleanup;
 80     }
 81  
 82     /* create session */
 83     /*调用sysrepo API(sr_session_start)创建与sysrepo running库的会话,并启动该会话*/
 84     if ((r = sr_session_start(conn, SR_DS_RUNNING, &sess)) != SR_ERR_OK) {
 85         error_print(r, "Failed to start new session");
 86         goto cleanup;
 87     }
 88     /*sr_connect(), sr_session_start(),是连接sysrepo基础,这两点基础实现,在后面sysrepo源码 
 89      *分析做详细说明,不在sysrepo-plugin中说明
 90      */
 91  
 92     /* init plugins */
 93     /*对所有已加载的plugin通过调用init_cb注册的回调初始化,这是整个main第二处核心点,与用户是强 
 94      *相关用户开发的插件,注册,订阅,初始化都通过init_cb,否则不能将sysrepol通信连接*/
 95     for (i = 0; i < plugin_count; ++i) {
 96         r = plugins[i].init_cb(sess, &plugins[i].private_data);
 97         if (r != SR_ERR_OK) {
 98             SRP_LOG_ERR("Plugin initialization failed (%s).", sr_strerror(r));
 99             goto cleanup;
100         }
101     }
102  
103     /* wait for a terminating signal */
104     pthread_mutex_lock(&lock);
105     while (!loop_finish) {
106         pthread_cond_wait(&cond, &lock);
107     }
108     pthread_mutex_unlock(&lock);
109  
110     /* cleanup plugins */
111     /* sysrepo-plugindf正常结束后,回收plugin初始化时分配的资源*/
112     for (i = 0; i < plugin_count; ++i) {
113         plugins[i].cleanup_cb(sess, plugins[i].private_data);
114     }
115  
116     /* success */
117     rc = EXIT_SUCCESS;
118  
119     /*结束后,回收已分配的全部资源*/
120 cleanup:
121     for (i = 0; i < plugin_count; ++i) {
122         dlclose(plugins[i].handle);
123     }
124     free(plugins);
125  
126     sr_disconnect(conn);
127     return rc;
128 }

 

2.3 load_plugins

  1 static int
  2 load_plugins(struct srpd_plugin_s **plugins, int *plugin_count)
  3 {
  4     void *mem, *handle;
  5     DIR *dir;
  6     struct dirent *ent;
  7     const char *plugins_dir;
  8     char *path;
  9     int rc = 0;
 10  
 11     *plugins = NULL;
 12     *plugin_count = 0;
 13  
 14     /* get plugins dir from environment variable, or use default one */
 15     /* bin_common.h.in #define SRPD_PLUGINS_PATH "@PLUGINS_PATH@"
 16      * @PLUGINS_PATH@在CMakeList.txt中定义,在编译时也可以自定义
 17      * CMakeList.txt对其定义如下
 18      * if(NOT PLUGINS_PATH)
 19      * set(PLUGINS_PATH             
 20      *    "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/sysrepo/plugins/" CACHE PATH
 21      *   "Sysrepo plugin daemon plugins path.")
 22      *   endif()
 23      *  用户不指定plugin的路径时,debian系统默认将plugin的动态库文件*.so安装 
 24      *    于/usr/lib/x86_64-linux-gnu/sysrepo/plugins/目录下,
 25      *  而Centos系统的默认安装路径为/usr/lib/sysrepo/plugins,在开发plugind时,安装路径也需要 
 26      *  指定到该路径下,否则,*.so找不到,则load不成功。
 27      */
 28     plugins_dir = getenv("SRPD_PLUGINS_PATH");
 29     if (!plugins_dir) {
 30         plugins_dir = SRPD_PLUGINS_PATH;
 31     }
 32  
 33     /* create the directory if it does not exist */
 34     /* access函数,:检查调用进程是否可以对指定的文件执行某种操作, F_OK文件是否存在
 35      * 本段代码是检测SRPD_PLUGINS_PATH目录是否存在,如果不存在,调用sr_mkpath创建目录,并设置*            
 36      * 目录的访问权限000777。本段代码是安全性代码,确保指定的路径存在。对于实际开发中,是通过编 
 37      * 译是指定,不存在路径的动态库无法安装。
 38      */
 39     if (access(plugins_dir, F_OK) == -1) {
 40         if (errno != ENOENT) {
 41             error_print(0, "Checking plugins dir existence failed (%s).", strerror(errno));
 42             return -1;
 43         }
 44         if (sr_mkpath(plugins_dir, 00777) == -1) {
 45             error_print(0, "Creating plugins dir \"%s\" failed (%s).", plugins_dir, strerror(errno));
 46             return -1;
 47         }
 48     }
 49     
 50     /* opendir函数,找开指定的目录文件,并返回DIR*形态的目录流,
 51      * 目录的读取与搜查也都需要此目录流 
 52      */
 53     dir = opendir(plugins_dir);
 54     if (!dir) {
 55         error_print(0, "Opening \"%s\" directory failed (%s).", plugins_dir, strerror(errno));
 56         return -1;
 57     }
 58     
 59     /*readdir函数,读取目录,返回参数dir目录流的下个目录进入点
 60      * 返回的结果是struct dirent的内容*/
 61     while ((ent = readdir(dir))) {
 62         /*Linux系统中存在"." ".."两类目录,这两类目录名结构,在实际是不需要使用,需要跳过。*/
 63         if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
 64             continue;
 65         }
 66         
 67         /* open the plugin */
 68         /*将SRPD_PLUGINS_PATH与也读取的目录文件名,组成一个完成的动态库路径,供后面操作。*/
 69         if (asprintf(&path, "%s/%s", plugins_dir, ent->d_name) == -1) {
 70             error_print(0, "asprintf() failed (%s).", strerror(errno));
 71             rc = -1;
 72             break;
 73         }
 74         
 75         /*RTLD_LAZY:暂缓决定,等有需要时再解出符号 
 76          *以RTLD_LAZY模式打开动态库,返回一个句柄给调用进程,如果失败,则返回。
 77          */
 78         handle = dlopen(path, RTLD_LAZY);
 79         if (!handle) {
 80             error_print(0, "Opening plugin \"%s\" failed (%s).", path, dlerror());
 81             free(path);
 82             rc = -1;
 83             break;
 84         }
 85         free(path);
 86         
 87         /* allocate new plugin */
 88         /* 分配一个新的plugin空间,并将新分配的men挂载plugins结构下*/
 89         mem = realloc(*plugins, (*plugin_count + 1) * sizeof **plugins);
 90         if (!mem) {
 91             error_print(0, "realloc() failed (%s).", strerror(errno));
 92             dlclose(handle);
 93             rc = -1;
 94             break;
 95         }
 96         *plugins = mem;
 97  
 98         /* find required functions */
 99         /* plugins结构中有两个必须的回调函数,一个是init_cb,另一个是cleanup_cb
100          * 通过 void *dlsym(void *handle, const char* symbol);,
101          * handle是使用dlopen函数之后返回的句柄,
102          * symbol是要求获取的函数的名称。
103          * SRP_INIT_CB定义如下:#define SRP_INIT_CB     "sr_plugin_init_cb"
104          * SRP_CLEANUP_CB定义下:#define SRP_CLEANUP_CB  "sr_plugin_cleanup_cb"
105          * 此两个CB函数,也就是在开发插件中必须实现的两个入口函数,如果不存在,则加载失败。
106          */
107         *(void **)&(*plugins)[*plugin_count].init_cb = dlsym(handle, SRP_INIT_CB);
108         if (!(*plugins)[*plugin_count].init_cb) {
109             error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", SRP_INIT_CB, ent->d_name);
110             dlclose(handle);
111             rc = -1;
112             break;
113         }
114  
115         *(void **)&(*plugins)[*plugin_count].cleanup_cb = dlsym(handle, SRP_CLEANUP_CB);
116         if (!(*plugins)[*plugin_count].cleanup_cb) {
117             error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", SRP_CLEANUP_CB, ent->d_name);
118             dlclose(handle);
119             rc = -1;
120             break;
121         }
122         
123         /* finally store the plugin */
124         /*最后,本次so解析成功,保存本次so的解析结果,执行一下次目录文件的读取*/
125         (*plugins)[*plugin_count].handle = handle;
126         (*plugins)[*plugin_count].private_data = NULL;
127         ++(*plugin_count);
128     }
129     /*目录文件读取结束,关闭目录读取流,返回的参考中有插件结构plugins。*/
130     closedir(dir);
131     return rc;
132 }

 

 

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等操作,均在此处完成。有如下例子,请参考。

 1 int
 2 sr_plugin_init_cb(sr_session_ctx_t *session, void **private_ctx)
 3 {
 4     int rc;
 5     struct plugind_ctx *ctx;
 6     ctx = calloc(1, sizeof *ctx);
 7     if (!ctx) 
 8     {
 9         rc = SR_ERR_NOMEM;
10         goto error;
11     }
12  
13     /*在下面初始与之有关的操作,例如,本地数据结构的初始化,sysrepo的订阅初始化*/
14     ...
15   
16     SRP_LOG_DBGMSG("plugin initialized successfully.");
17     ctx->session = session;
18     *private_ctx = ctx;
19     return SR_ERR_OK;
20  
21 error:
22     SRP_LOG_ERR("plugin initialization failed (%s).", sr_strerror(rc));
23     sr_unsubscribe(ctx->subscription);
24     free(ctx);
25     return rc;
26 }

 

 

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实现,可以参考

 1 void
 2 sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_ctx)
 3 {
 4     (void)session;
 5  
 6     struct plugind_ctx *ctx = (struct plugind_ctx *)private_ctx;
 7     sr_unsubscribe(ctx->subscription);
 8     free(ctx);
 9     
10     nb_terminate();
11     yang_terminate();
12     
13     SRP_LOG_DBGMSG("plugin cleanup finished.");
14 }

 

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