• CWMP开源代码研究6——libcwmp动态库开发


    原创作品,转载请注明出处,严禁非法转载。如有错误,请留言!

    email:40879506@qq.com

      为了使程序具有通用性,便于扩展和维护。采用了"模块"插入的思想。将设备业务相关的实现以动态库的形式加载进来。

      在上篇文章已经介绍了CWMP的程序处理流程。本篇主要分析一下在CWMP core的程序里如何加载lib库。。比如如何实现调用so库函数, 实现ACS URL解析, CPE get/set函数怎么被调用,怎样添加/删除/更新 obj对象等。

    一. 加载lib库

      1) 打开动态连接库

      还记得上节我们定义的cwmp进程上下文结构体cwmp_context,使用dlopen以指定的模式打开动态库文件,并返回设备library的handle。

        //打开设备handle
        cwmp_ctx->handle_lib = dlopen(cwmp_ctx->dev_info.dev_lib, RTLD_LAZY);

      2) 调用设备相关函数

      上节已经介绍了相关设备函数,并定义在device.xml。根据xml定义的tag头取得函数名称,并赋给CWMP进程上下文。比如

      cwmp_ctx->dev_info.func_bootstrap = dlsym(cwmp_ctx->handle_lib, attr_value);
      cwmp_ctx->dev_info.func_init = dlsym(cwmp_ctx->handle_lib, attr_value);

      cwmp_ctx->dev_info.func_get_listenport = dlsym(cwmp_ctx->handle_lib, attr_value);

      cwmp_ctx->dev_info.func_get_auth = dlsym(cwmp_ctx->handle_lib, attr_value); 

      cwmp_ctx->dev_info.func_url_dns_resolve= dlsym(cwmp_ctx->handle_lib, attr_value);

      ......

    二. 设备相关初始化 

     1 //用于需要平台一开始初始化
     2 void dev_init(trf_param_t* param, callback_reg_func_t func, pthread_mutex_t *pmutex_param, LogFunc log_func)
     3 {
     4     pthread_t       thd;
     5     monitor_info_t  *info = NULL;
     6 
     7     //init local  pointer
     8     cwmplog_func = log_func;
     9     g_reg_func = func;
    10     g_root_param = param;
    11     g_pmutex_param = pmutex_param;
    12         
    13     info = (monitor_info_t *)malloc_check(sizeof(monitor_info_t));
    14     info->func = func;
    15     info->param = param;
    16     // info->log_func = log_func;
    17     
    18     closeinout();
    19 
    20     //初始化CPE 与ACS 连接状态
    21    CpeSetValue(NULL, "0", "cpeagent.tr069.acs_status");
    22 
    23     /*
    24         1. define user-defined event code in the  device.xml.
    25     */
    26     inform_bind(func);
    27 
    28     // monitor_socket_event 线程函数用于与其他程序或进程指定的socket进行通信
    29     // 比如源码目录下的sendSocket/client.c  程序,可用于测试或其他
    30     // CWMP_SOCK "/opt/cwmp.sock"
    31     // pthread_create(&thd, NULL, monitor_socket_event, (void*)info);
    32     return;
    33 }
    View Code

    主要完成初始化操作:将内存中的根节点参数位置赋给动态库中的全局变量g_root_param指针,以及初始化信号量,日志记录函数,以及FUNC回调函数。    inform_bind(func)函数实现用户自定义的<EventCode>事件,比如电信运营商自定义了X CT-COM BIND事件类型,只用上报正确才能进行工单下发业务。

    dev_bootstrap主要用来判断是否是首次连接ACS,如果是把0 BOOTSTRAP和1 BOOT加入Inform事件中,否则把1 BOOT加入Inform事件中。

    Inform中带有如下结构信息:

            <cwmp:Inform>
                <DeviceId xsi:type="cwmp:DeviceIdStruct">
                    <Manufacturer>TEST</Manufacturer>
                    <OUI>A1B2C4</OUI>
                    <ProductClass>TEST_PC</ProductClass>
                    <SerialNumber>821281000054321</SerialNumber>
                </DeviceId>
                <Event SOAP-ENC:arrayType="cwmp:EventStruct[2]">
                    <EventStruct>
                        <EventCode>1 BOOT</EventCode>
                        <CommandKey></CommandKey>
                    </EventStruct>
                    <EventStruct>
                        <EventCode>X CT-COM BIND</EventCode>
                        <CommandKey></CommandKey>
                    </EventStruct>
                </Event>
                <MaxEnvelopes>1</MaxEnvelopes>
                <CurrentTime>2017-01-09T11:53:00</CurrentTime>
                <RetryCount>0</RetryCount>
                <ParameterList SOAP-ENC:arrayType="cwmp:ParameterValueStruct[10]">
                    <ParameterValueStruct>
                        <Name>InternetGatewayDevice.DeviceSummary</Name>
    .......

     三. 解析ACS  URL

      同时支持域名和ip地址解析。(放在后续高级部分专门讲解)

    . 每个节点RPC Method

      节点结构体如下:

    struct trf_param
    {
        char                name[PARAM_NAME_LEN+1];     //参数名
        int                 type;                       //参数类型 trf_datatype_e
        int                 writable;                   //是否可写。0:不可写,1:可写,如果object
                                                        //可以Add,则可写
        int                 max_instance;               //属于Object, 最大instance值,-1表示无限制
        int                 notification;               //属于Parameter,  0:off,1:passive,2:active
        unsigned char       noti_rw;                    //属于Parameter,  是否可以设置上报属性,0 不可以 1 可以
        unsigned long       acl;                        /*属于Parameter, access list */
        TRFGetParamValueFunc    getparamval_func;       //属于Parameter, 取得参数值函数
        TRFSetParamValueFunc    setparamval_func;       //属于Parameter, 设置参数值函数
        TRFAddObjectFunc        addobject_func;         //属于Object, AddObject
        TRFDelObjectFunc        delobject_func;         //属于Object, DeleteObject
        TRFRefreshFunc          refresh_func;           //属于Object, 刷新
        struct trf_param    *parent;                    //父节点
        struct trf_param    *child;                     //子节点
        struct trf_param    *nextSibling;               //兄弟节点
    };

      每个参数节点拥有自己的属性和方法,并且通过递归方式创建初始化参数树,把初始化后的结果保存在cwmp_context进程上下文中

      create_param(&cwmp_ctx->param_root, xmldata->doc->root->firstChild);

    五. 其他RPC方法

      本文开头已经介绍了调用设备相关函数的方法,其中包括除TR069规范中的升级,恢复出厂,Reboot,Download等方法,我们也可以通过”插件"的形式实现自己的或者私有厂商定义的方法。

    <devlib name="/usr/lib/libcwmp.so"></devlib>
    <auth name="dev_get_auth"></auth>
    <listenport name="dev_get_listenport"></listenport>
    <wanparamname name="dev_get_wanparam_name"></wanparamname>
    <bootstrap name="dev_bootstrap"></bootstrap>  
    <init name="dev_init"></init>  
    <reboot name="dev_reboot"></reboot>
    <factoryreset name="dev_factoryreset"></factoryreset>
    <download name="dev_download"></download>
    <acsstatus name="dev_set_acs_status"></acsstatus>
    <urldnsresolve name="dev_url_dns_resolve"></urldnsresolve>
    <upload name="dev_upload"></upload>
    <cwmpenable name="dev_cwmp_enable"/>

    六. 总结

      tr069只是个协议栈,按照规范实现这个协议并不困难,在github上我们也可以去找到各种各用的开源程序,有Python写的,java写的,C写的等等。但是,能够从开源程序中找到一个优秀的程序设计架构就如同大海捞针,很困难,更不要说商业用途了。

      至此,已经基本写完了关于程序设计部分的内容,本篇涉及的代码比较多,尽量不深入代码,而只是围绕协议规范和程序模块化思想分析了部分代码的实现。

    七. 参考

     1)http://www.cnblogs.com/Anker/p/3746802.html

    附:遗留问题

      程序开发中在使用dl库中遇到一个问题,若有心的读者能解答,欢迎留言告诉我:

      若dlopen打开的库中不存在线程,或线程不在运行状态,则调用dlclose时,进程不会死;
      若存在运行时的线程(比如dev_init函数里的 pthread_create(&thd, NULL, monitor_socket_event, (void*)info);),则dlclose时出现segmentation fault。
      若不调用dlclose则会出现内存漏洞

  • 相关阅读:
    C语言头文件stdarg.h(cstdarg)
    C语言头文件ctype.h(cctype)
    内存数库库种类
    C#设计模式单件模式(Singleton Pattern)
    vs2005快捷键,vs2008通用
    移动MYSQL数据库经常遇到乱码的问题
    ie6 ie7 ie8 共存以及Firefox浏览器CSS兼容写法
    用正则表达式替换a标记href值
    IE6 PNG透明终极解决方案(打造W3CfunsIE6PNG最强帖)
    IE的hack
  • 原文地址:https://www.cnblogs.com/myblesh/p/6260159.html
Copyright © 2020-2023  润新知