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

  • 相关阅读:
    C#磁吸屏幕窗体类库
    准备
    我写的诗
    How to turn off a laptop keyboard
    How to tell which commit a tag points to in Git?
    Why should I care about lightweight vs. annotated tags?
    How to get rid of “would clobber existing tag”
    Facebook, Google and Twitter threaten to leave Hong Kong over privacy law changes
    The need for legislative reform on secrecy orders
    Can a foreign key be NULL and/or duplicate?
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/14271691.html
Copyright © 2011-2022 走看看