Openwrt下C调用UCI API

Posted KOBE_ZERO-q576926484

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Openwrt下C调用UCI API相关的知识,希望对你有一定的参考价值。

本文参考http://blog.csdn.net/bywayboy/article/details/20866287

“uci”是”Unified Configuration Interface”(统一配置界面)的缩写,意在OpenWrt整个系统的配置集中化。

许多程序在系统某处拥有自己的配置文件,比如/etc/network/interfaces, /etc/exports, /etc/dnsmasq.conf或者 /etc/samba/samba.conf,有时它们还使用稍有不同的语法。

共同原则

OpenWrt的所有配置文件皆位于/etc/config/目录下。每个文件大致与它所配置的那部分系统相关。可用文本编辑器、”uci” 命令行实用程序或各种编程API(比如 Shell, Lua and C)来编辑/修改这些配置文件。

配置文件

| 文件位置 | 描述 |
| 基本配置 |
|/etc/config/dhcp | dnsmasq和DHCP的配置
|/etc/config/dropbear | SSH服务端选项
|/etc/config/firewall | 中央防火墙配置
|/etc/config/network | 交换,接口和路由配置
|/etc/config/system | 杂项与系统配置
|/etc/config/timeserver | rdate的时间服务器列表
|/etc/config/wireless | 无线设置和无线网络的定义

IPv6
|/etc/config/ahcpd | Ad-Hoc配置协议(AHCP) 服务端配置以及转发器配置
|/etc/config/aiccu | AICCU 客户端配置
|/etc/config/dhcp6c | WIDE-DHCPv6 客户端配置
|/etc/config/dhcp6s | WIDE-DHCPv6 服务端配置
|/etc/config/gw6c | GW6c 客户端配置
|/etc/config/radvd | 路由通告 (radvd) 配置

其他
|/etc/config/etherwake | 以太网唤醒: etherwake
|/etc/config/fstab | 挂载点及swap
|/etc/config/hd-idle | 另一个可选的硬盘空闲休眠进程(需要路由器支持usb硬盘)
|/etc/config/httpd | 网页服务器配置选项(Busybox 自带httpd, 已被舍弃)
|/etc/config/luci | 基础 LuCI 配置
|/etc/config/luci_statistics | 包统计配置
|/etc/config/mini_snmpd | mini_snmpd 配置
|/etc/config/mountd | OpenWrt 自动挂载进程(类似autofs)
|/etc/config/multiwan | 简单多WAN出口配置
|/etc/config/ntpclient | ntp客户端配置,用以获取正确时间
|/etc/config/pure-ftpd | Pure-FTPd 服务端配置
|/etc/config/qos | QoS配置(流量限制与整形)
|/etc/config/samba | samba配置(Microsoft文件共享)
|/etc/config/snmpd | SNMPd(snmp服务进程) 配置
|/etc/config/sshtunnel | sshtunnel配置
|/etc/config/stund | STUN 服务端配置
|/etc/config/transmission | BitTorrent配置
|/etc/config/uhttpd | Web服务器配置(uHTTPd)
|/etc/config/upnpd | miniupnpd UPnP服务器配置
|/etc/config/ushare | uShare UPnP 服务器配置
|/etc/config/vblade | vblade 用户空间AOE(ATA over Ethernet)配置
|/etc/config/vnstat | vnstat 下载器配置
|/etc/config/wifitoogle | 使用按钮来开关WiFi的脚本
|/etc/config/wol | Wake-on-Lan: wol
|/etc/config/znc | ZNC 配置

文件语法

在UCI的配置文件通常包含一个或多个配置语句,包含一个或多个用来定义实际值的选项语句的所谓的节。

下面是一个简单的配置示例文件:

package 'example'

config 'example' 'test'
        option   'string'      'some value'
        option   'boolean'     '1'
        list     'collection'  'first item'
        list     'collection'  'second item'
  • config ‘example’ ‘test’ 语句标志着一个节的开始。这里的配置类型是example,配置名是test。配置中也允许出现匿名节,即自定义了配置类型,而没有配置名的节。配置类型对应配置处理程序来说是十分重要的,因为配置程序需要根据这些信息来处理这些配置项。

  • option ‘string’ ‘some value’ 和 option ‘boolean’ ‘1’ 定义了一些简单值。文本选项和布尔选项在语法上并没有差异。布尔选项中可以用’0’ ,’no’, ‘off’, 或者’false’来表示false值,或者也可以用’1’, ‘yes’,’on’或者’true’来表示真值。

  • 以list关键字开头的多个行,可用于定义包含多个值的选项。所有共享一个名称的list语句,会组装形成一个值列表,列表中每个值出现的顺序,和它在配置文件中的顺序相同。如上例种中,列表的名称是’collection’,它包含了两个值,即’first item’和’second item’。

  • ‘option’和’list’语句的缩进可以增加配置文件的可读性,但是在语法不是必须的。

通常不需要为标识符和值加引号,只有当值包括空格或者制表符的时候,才必须加引号。同时,在使用引号的时候,可以用双引号代替单引号。

UCI的几个结构体

1.struct uci_package: 包结构体。它对应一个配置文件内容

struct uci_package
{
    struct uci_element e;
    struct uci_list sections;
    struct uci_context *ctx;
    bool has_delta;
    char *path;

    /* private: */
    struct uci_backend *backend;
    void *priv;
    int n_section;
    struct uci_list delta;
    struct uci_list saved_delta;
};

2.struct uci_section:节结构体,它对应配置文件中的节

struct uci_section
{
    struct uci_element e;
    struct uci_list options;
    struct uci_package *package;
    bool anonymous;
    char *type;
};

3.struct uci_option:选项结构体,它对应配置文件里节中的option或者list

struct uci_option
{
    struct uci_element e;
    struct uci_section *section;
    enum uci_option_type type;
    union {
        struct uci_list list;
        char *string;
    } v;
};

4.struct uci_ptr:元素位置指针结构,用来查询并保存对应位置元素

struct uci_ptr
{
    enum uci_type target;
    enum {
        UCI_LOOKUP_DONE =     (1 << 0),
        UCI_LOOKUP_COMPLETE = (1 << 1),
        UCI_LOOKUP_EXTENDED = (1 << 2),
    } flags;

    struct uci_package *p;
    struct uci_section *s;
    struct uci_option *o;
    struct uci_element *last;

    const char *package;
    const char *section;
    const char *option;
    const char *value;
};

5.struct uci_context: uci上下文结构,贯穿查询、更改配置文件全过程。

struct uci_context
{
    /* 配置文件包列表 */
    struct uci_list root;

    /* 解析上下文,只用于错误处理 */
    struct uci_parse_context *pctx;

    /* 后端导入导出 */
    struct uci_backend *backend;
    struct uci_list backends;

    /* uci 运行标识 */
    enum uci_flags flags;

    char *confdir;
    char *savedir;

    /* search path for delta files */
    struct uci_list delta_path;

    /* 私有数据 */
    int err;
    const char *func;
    jmp_buf trap;
    bool internal, nested;
    char *buf;
    int bufsz;
};

几个基本的API函数

1.uci_alloc_context:动态申请一个uci上下文结构

struct uci_context *uci_alloc_context(void);

2.uci_free_contex:释放由uci_alloc_context申请的uci上下文结构且包括它的所有数据

void uci_free_context(struct uci_context *ctx);

3.uci_lookup_ptr:由给定的元组查找元素

/**
 * uci_lookup_ptr: 分离一个uci元组字符串且查找对应元素树
 * @ctx: uci context结构体指针
 * @ptr: 存放元素查询结果的结构体指针
 * @str: 待查找的uci元组字符串
 * @extended: 允许扩展语法查询
 *
 *如果extended被设为ture,则uci_lookup_ptr支持下列扩展语法:
 * 
 *例子:
 *   network.@interface[0].ifname (‘ifname‘ option of the first interface section)
 *   network.@interface[-1]       (last interface section)
 * Note: 有必要的话uci_lookup_ptr将会自动加载配置文件包
 * @str 不能是一个const类型指针,它在使用的过程中将会被更改且用于将字符串填写到@ptr中,因此
 * 它只要@ptr还在使用,它就必须是可用的
 *
 * 这个函数在指定包元组的的字符串未被找到时返回UCI_ERR_NOTFOUND,否则返回UCI_OK
 *
 * 记住在查找其他部分失败的情况,如果它们同样被指定,包括section和option,同样会返回UCI_OK,
 * 但是ptr->flags * UCI_LOOKUP_COMPLETE标志位不会被置位
 */
int uci_lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str, bool extended);

现在我们来看一个示例来进一步了解:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <uci.h>

#define UCI_CONFIG_FILE "/etc/config/meter"
static struct uci_context * ctx = NULL; //定义一个UCI上下文的静态变量.
/*********************************************
*   载入配置文件,并遍历Section.
*/
bool load_config()
{
    struct uci_package * pkg = NULL;
    struct uci_element *e;
    char *tmp;
    const char *value;


    ctx = uci_alloc_context(); // 申请一个UCI上下文.
    if (UCI_OK != uci_load(ctx, UCI_CONFIG_FILE, &pkg))
        goto cleanup; //如果打开UCI文件失败,则跳到末尾 清理 UCI 上下文.


    /*遍历UCI的每一个节*/
    uci_foreach_element(&pkg->sections, e)
    {
        struct uci_section *s = uci_to_section(e);

    printf("section s's type is %s.\\n",s->type);

    if(!strcmp)

    if(!strcmp("meter",s->type)) //this section is a meter
    {
        printf("this seciton is a meter.\\n");
            if (NULL != (value = uci_lookup_option_string(ctx, s, "modbus_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's modbus_id is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "num_attr")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's num_attr is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "sender_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's sender_id is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "customer_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's customer_id is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "customer_name")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's customer_name is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "account_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's account_id is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "account_name")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's account_name is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "meter_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's meter_id is %s.\\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "commodity")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s's commodity is %s.\\n",s->e.name,value);
            }
    }
        // 如果您不确定是 string类型 可以先使用 uci_lookup_option() 函数得到Option 然后再判断.
        // Option 的类型有 UCI_TYPE_STRING 和 UCI_TYPE_LIST 两种.


    }
    uci_unload(ctx, pkg); // 释放 pkg 
cleanup:
    uci_free_context(ctx);
    ctx = NULL;
}
int main(int argc, char* argv[])
{

    load_config();
}

遍历一个UCI_TYPE_LIST 类型

config  "server" "webserver"  
    list    "index" "index.html"  
    list    "index" "index.php"  
    list    "index" "default.html"  

代码段:

// s 为 section.  
struct uci_option * o = uci_lookup_option(ctx, s, "index");  
if ((NULL != o) && (UCI_TYPE_LIST == o->type)) //o存在 且 类型是 UCI_TYPE_LIST则可以继续.  
{  
    struct uci_element *e;  
    uci_foreach_element(&o->v.list, e)  
    {  
        //这里会循环遍历 list  
        // e->name 的值依次是 index.html, index.php, default.html  
    }  
}  

写配置

UCI提供了一个简洁的办法来操作配置信息,例如有一个配置文件

#文件名: testconfig  
config  'servver'  
    option  'value' '123' # 我们想修改 'value' 的值为 '456'  

代码如下:

struct uci_context * ctx = uci_alloc_context(); //申请上下文  
struct uci_ptr ptr ={  
    .package = "config",  
    .section = "servver",  
    .option = "value",  
    .value = "256",  
};  
uci_set(_ctx,&ptr); //写入配置  
uci_commit(_ctx, &ptr.p, false); //提交保存更改  
uci_unload(_ctx,ptr.p); //卸载包  


uci_free_context(ctx); //释放上下文  

以上是关于Openwrt下C调用UCI API的主要内容,如果未能解决你的问题,请参考以下文章

智能路由器C代码调用uci的API读openwrt配置文件指南二

智能路由器C代码调用uci的API读openwrt配置文件指南二

智能路由器C代码调用uci的API读openwrt配置文件指南

OpenWRT UCI API的使用

如何使用C API来操作UCI

onActivityResult 未在 Android API 23 的片段上调用