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配置文件指南二