当前位置: 首页 > 工具软件 > tr069-lib > 使用案例 >

TR069协议与商业应用6——TR069动态库开发

裴令秋
2023-12-01

声明:原创作品,严禁用于商业目的。 如有任何技术问题,欢迎和我交流:19826269205(微信同号)。

环境:华为CPE设备、公网服务器acs(注册acs云账号:acs服务端
阅读:开发者
学习链接
免费学习

1. 加载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);

  ......

2. 设备相关初始化

//用于需要平台一开始初始化
void dev_init(trf_param_t* param, callback_reg_func_t func, pthread_mutex_t *pmutex_param, LogFunc log_func)
{
    pthread_t       thd;
    monitor_info_t  *info = NULL;

    //init local  pointer
    cwmplog_func = log_func;
    g_reg_func = func;
    g_root_param = param;
    g_pmutex_param = pmutex_param;

    info = (monitor_info_t *)malloc_check(sizeof(monitor_info_t));
    info->func = func;
    info->param = param;
    // info->log_func = log_func;

    closeinout();

    //初始化CPE 与ACS 连接状态
   CpeSetValue(NULL, "0", "cpeagent.tr069.acs_status");

    /*
        1. define user-defined event code in the  device.xml.
    */
    inform_bind(func);

    // monitor_socket_event 线程函数用于与其他程序或进程指定的socket进行通信
    // 比如源码目录下的sendSocket/client.c  程序,可用于测试或其他
    // CWMP_SOCK "/opt/cwmp.sock"
    // pthread_create(&thd, NULL, monitor_socket_event, (void*)info);
    return;
}

主要完成初始化操作:将内存中的根节点参数位置赋给动态库中的全局变量g_root_param指针,以及初始化信号量,日志记录函数,以及FUNC回调函数。 inform_bind(func)函数实现用户自定义的事件,比如电信运营商自定义了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>.......

3. 解析ACS URL

同时支持域名和ip地址解析。

/*
     url 是对应InternetGatewayDevice.ManagementServer.URL
     src_ip 是用于RPC方法的download或者upload的本设备ip地址
     new_url 当解析域名对应的ip地址发生变化的时候,保存新的URL地址
*/
int dev_url_dns_resolve(const char *url, char *src_ip, char **new_url)
{
    int  ret = FALSE;
    BOOL isIP = FALSE;
    const char *ps = NULL, *pe = NULL;
    char domain[128] = {0};
    char domain_ip[IPV4_ADDRESS_LEN] = {0};
    char *newurl = NULL;
    char *dns_param_name = NULL;
    struct hostent * phostent = NULL;
    unsigned int mark = 108;

    if(url == NULL)
    {
        device_error("url_dns_resolve: param is NULL\n");
        return FALSE;
    }
    
    //get domain name
    ps = strstr(url, "//");
    if(ps != NULL)
        ps = ps + strlen("//");
    else
        ps = url;

    pe = strchr(ps, ':');
    if(pe == NULL)
        pe = strchr(ps, '/');
    if(pe == NULL)
        pe = url + strlen(url);

    strncpy(domain, ps, pe - ps);
    device_debug("ACS domain name=%s\n", domain);
    //need resolving or not
    isIP = isIpStr(domain);

    // 调用开源eboxservice程序解析DNS
    if(isIP == FALSE)
    {
        // 基于mark的策略路由
        phostent = gethostbynameext(domain, mark);

        if(phostent != NULL)
        {
            snprintf(domain_ip, IPV4_ADDRESS_LEN, "%s", inet_ntoa(*((struct in_addr *)phostent->h_addr)));
            device_debug("domain_ip(%s)\n", domain_ip);
            newurl = (char *)malloc_check(strlen(url) + IPV4_ADDRESS_LEN);
            if(newurl == NULL)
            {
                device_error("url_dns_resolve: malloc_check failed.\n");
                ret = FALSE;
                goto resove_fail;
            }

            strcpy(newurl, url);
            strncpy(newurl + (ps - url), domain_ip, strlen(domain_ip));
            strcpy(newurl + (ps - url) + strlen(domain_ip), pe);
            *new_url = newurl;
            ret = TRUE;
        }
        ......

4. 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);
本文开头已经介绍了调用设备相关函数的方法,其中包括除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"/>

5.总结

本篇围绕协议规范和程序模块化思想分析了业务数据模型层的动态库源码的实现,涉及的代码比较多,接下来会通过编译代码来运行和测试我们的程序设计。

 类似资料: