zoukankan      html  css  js  c++  java
  • 第三章 sysrepo-plugind源码分析

    摘自:https://blog.csdn.net/m0_47413019/article/details/105867406

    应用程序通过将对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代码结构简单,注释丰富,没有使用复杂的语法,还是非常容易理解的。

  • 相关阅读:
    BETA冲刺(6/7)
    BETA冲刺(5/7)
    BETA冲刺(4/7)
    BETA冲刺(3/7)
    BETA冲刺(2/7)
    福大软工 · 第十次作业
    BETA冲刺(1/7)
    第08组 Beta版本演示
    第08组 Beta冲刺(4/4)
    第08组 Beta冲刺(3/4)
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/14271691.html
Copyright © 2011-2022 走看看