UCI简介
- UCI是Unified Configuration Interface的缩写,翻译成中文就是统一配置接口,用途就是为OpenWrt提供一个集中控制的接口。
- 配置接口启动流程:
- ① 启动脚本 /etc/init.d/xxx;
- ② 启动脚本通过UCI分析库从 /etc/config/xxx 获得启动参数;
- ③ 启动脚本完成正常启动。
UCI的文件
- UCI的配置文件全部存储在
/etc/config
目录下 - 常见UCI配置文件
文件 | 作用 |
---|---|
dhcp | 面向LAN口提供的IP地址分配服务配置 |
dropbear | SSH服务配置 |
firewall | 路由转发,端口转发,防火墙规则 |
network | 自身网络接口配置 |
system | 时间服务器时区配置 |
wireless | 无线网络配置 |
uhttpd | Web服务器选项配置 |
luci | 基本的LuCI配置 |
- 每个文件都涉及到系统配置的一部分。可以用VI编辑器或用UCI命令行修改配置文件。也可以通过各种编程API(如shell、lua、C)来修改,这也是Web接口例如LuCI修改UCI文件的方式。
注:在通过上述方法改变一个UCI配置文件后,受影响的服务或可执行程序必须由init.d进行重启,这样更新UCI配置才生效。
UCI文件语法
- 语法:
config 'section_type' 'section'
option 'key' 'value'
list 'list_key' 'list_value'
123
config节点:以关键字 config 开始的一行用来代表当前节点
- section_type: 节点类型 ————-对应section中的title 参数
- section: 节点名称 —————在UCI中自定义,可以在cbi中用anonymous=true 隐匿
- UCI允许只有节点类型的匿名节点存在
- 节点名字建议使用单引号包含
- 节点可以包含多个option或list
- 节点遇到文件结束或遇到下一个节点代表完成
option选项:表示节点中的一个元素
- key:键 //对应option中的name 参数
- value:值
- 选项value建议使用单引号包含
- 相同的选项key存在于同一个节点,只有一个生效
list 列表项:表示列表形式的一组参数
- list_key: 列表键
- list_value:列表值
- 列表key的名字如果相同,则相同键的值将会被当作数组传递给相应软件
- 示例:参考上一节 Openwrt:LuCI之CBI(二)
config typedsection1_title 'typedsection1'
option Value_option_name 'Value'
option TextValue_option_name 'TextValue'
option DummyValue_option_name 'DummyValue'
option Listvalue_option_name '1'
list DynamicList_option_name 'DynamicList'
list DynamicList_option_name '123123'
option Flag_option_name '1'
option MultiValue_option_name '0 1 2 3'
list StaticList_option_name '0'
list StaticList_option_name '1'
list StaticList_option_name '2'
list StaticList_option_name '3'
config typedsection2_title 'typedsection2'
option tab1_option1 '1'
option tab2_option2 '2'
二、UCI配置修改
UCI命令
UCI命令语法格式
uci [<option>] <command> [<arguments>]
规则
- UCI读取总是先读取内存中的缓存,然后再读取文件中的。
- 进行过增加、修改、删除操作后要执行生效指令
commit
,否则所做修改只留存在缓存中。- 当使用UCI命令工具写入配置文件,配置文件都是整个重写并且不需要确认命令。这意味着在文件中任何多余的注释行和空行均会被删除。
- option:
选项 | 含义 |
---|---|
-c |
设置配置文件的搜索路径(默认值:/etc/config) |
-d |
在 uci 显示中为列表值设置分隔符 |
-f |
使用 |
-m | 导入时,将数据合并到现有包中 |
-n | 导出时,命名未命名的节(默认) |
-N | 不命名未命名的节 |
-p |
为配置更改文件添加一个搜索路径 |
-P |
为配置更改文件添加一个搜索路径,并将其用作默认 |
-q | 静音模式(不打印错误消息) |
-s | 强制strict 模式(停止解析器错误,默认) |
-S | 禁用strict 模式 |
-x | 不在"显示"中使用扩展语法 |
- command:
命令 | 含义 |
---|---|
add | 增加指定配置文件的类型为section-type 的匿名区段。 |
add_list | 对已存在的list选项增加字符串。 |
commit | 对给定的配置文件写入修改,如果没有指定参数则将所有的配置文件写入文件系统。 |
export | 导出一个机器可读格式的配置。它是作为操作配置文件的shell脚本而在内部使用,导出配置内容时会在前面加“package”和文件名。 |
import | 以UCI语法导入配置文件。 |
changes | 列出配置文件分阶段修改的内容,即未使用“uci commit”提交的修改。如果没有指定配置文件,则指所有的配置文件的修改部分。 |
show | 显示指定的选项、配置节或配置文件。以精简的方式输出,即key=value的方式输出。 |
get | 获取指令区域选项的值。 |
set | 设置指定配置节选项的值,或者是增加一个配置节,类型设置为指定的值。 |
delete | 删除指定的配置或选项。 |
rename | 对指定的选项或配置节重命名为指定的名字。 |
revert | 恢复指定的选项,配置节点或配置文件。 |
常见UCI命令操作
- UCI 文件示例:
读取类操作
- 显示指定文件配置
uci show <config>
注:省略则显示全部文件配置 - 取得节点类型
uci get <config>.<section>
- 取得一个值
uci get <config>.<section>.<option>
- 显示指定节点名字配置
uci show <config>.<section>
- 显示指定节点名字选项配置
uci show <config>.<section>.<option>
- 显示尚未生效的修改记录
uci changes <config>
- 匿名节点显示(如果所显示内容有匿名节点,使用-X参数可以显示出匿名节点的ID)
uci show -X <config>.<section>.<option>
写入类操作
- 在文件中增加一个匿名节点
uci add <config> <section-type>
- 在文件中增加一个节点或修改一个节点的类型(节点存在则修改,不存在则增加)
uci set <config>.<section>=<section-type>
- 在节点中增加一个选项和值或修改一个选项的值(同上)
uci set <config>.<section>.<option>=<value>
- 在列表中增加一个选项和值
uci add_list <config>.<section>.<option>=<value>
- 删除指定名字的节点
uci delete <config>.<section>
- 删除指定选项
uci delete <config>.<section>.<option>
- 删除列表
uci delete <config>.<section>.<list>
- 删除列表中的一个值
uci delete <config>.<section>.<option>=<string>
- 生效修改(任何写入类的语法,最终都要执行生效修改,否则所做修改只在缓存中!!!不会保存在文件中!!!)
uci commit <config>
UCI API
环境安装
- UCI不仅提供命令接口供脚本开发者开发,而且提供了C语言调用接口。下面将在普通桌面操作系统Ubuntu(非板子上)下来说明API的使用。准备UCI编程接口的使用环境
Cmake 安装
- Cmake是跨平台的产生Makefile的命令行工具,它应用于在脚本文件中配置工程。Libubox和UCI均使用Cmake命令来产生目标平台的构建系统命令。因此首先安装Cmake。
sudo apt-get install cmake
Libubox库 安装
- Libubox是Openwrt的一个必备的基础库,包含大小端转换、链表、MD5等实用工具基础库,采用Cmake来编译。UCI软件依赖Libubox。
- 安装Libubox:安装包获取—-在编译完后的
openwrt源码/dl/
目录下或执行命令got clone https://github.com/yubo/libubox.git
,并执行下面命令
tar -xzf libubox-2015-11-08-10429bccd0dc5d204635e110a7a8fae7b80d16cb.tar.gz
cd libubox-2015-11-08
cmake -D BUILD_LUA:BOOL=OFF -D BUILD_EXAMPLES:BOLL=OFF .
make
sudo make install
- 头文件默认安装在
/usr/local/include/libubox/
目录下,动态链接库libubox.so和libubox.a安装在/usr/local/lib/
目录下
UCI库 安装
- 完成Libubox安装就可以编译安装UCI,同样的
openwrt源码/dl/
目录下或执行命令git clone https://github.com/jkjuopperi/uci.git
,执行下面命令
tar -xzf uci-2015-08-27.1.tar.gz
cd uci-2015-08-27.1
cmake -D BUILD_LUA:BOOL=OFF .
make
sudo make install
sduo ldconfig
- UCI库的头文件安装在
/usr/local/include/
目录下,动态链接库安装在/usr/local/lib/libuci.so
,可执行程序为/usr/local/bin/uci
。 - 运行
ldconfig
命令是因为系统还不知道动态链接库已经安装,运行该命令会告诉系统重新加载动态链接库,这样UCI动态链接库就可以使用了。编译时使用gcc test.c -o test -luci
来链接UCI库。
API 接口
- API 接口详情都在
/usr/local/include/uci.h
头文件中
API相关结构体:
- uci_context:uci上下文结构,贯彻查询、更改配置文件全过程
struct uci_context {
/* list of config packages */
struct uci_list root;
/* parser context, use for error handling only */
struct uci_parse_context *pctx;
/* backend for import and export */
struct uci_backend *backend;
struct uci_list backends;
/* uci runtime flags */
enum uci_flags flags;
char *confdir;
char *savedir;
/* search path for delta files */
struct uci_list delta_path;
/* private: */
int err;
const char *func;
jmp_buf trap;
bool internal, nested;
char *buf;
int bufsz;
};
- 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;
};
- uci_section:对应配置文件中的节
struct uci_section {
struct uci_element e;
struct uci_list options;
struct uci_package *package;
bool anonymous;
char *type;
};
- 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;
};
- 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;
};
全部API函数功能:
- 动态分配一块内存用于struct uci_context结构
extern struct uci_context * uci_alloc_context(void);
- 释放struct uci_context结构内存,以及为其成员申请的所有内存
extern void uci_free_context(structuci_context *ctx);
- 打印最后一条出错信息,如果在打印出错信息前想打印其他信息,则传入str即可
extern void uci_perror(struct uci_context*ctx, const char *str);
- 获取最后一个uci错误的错误字符串
extern void uci_get_errorstr(structuci_context *ctx, char **dest, const char *str);
- 从文件流中导入uci的配置数据
extern int uci_import(struct uci_context * ctx, FILE * stream, const char *name, struct uci_package **package, boolsingle);
- 导出uci的配置文件数据到文件流stream
extern int uci_export(struct uci_context*ctx, FILE *stream, struct uci_package *package, bool header);
- 解析一个uci配置文件并把它存到ctx中
extern int uci_load(struct uci_context*ctx, const char *name, struct uci_package **package);
- 从ctx中卸载一个配置文件包
extern int uci_unload(struct uci_context*ctx, struct uci_package *p);
- 分离一个uci类型的字符串查找对应的元素
extern int uci_lookup_ptr(structuci_context *ctx, struct uci_ptr *ptr, char *str, bool extended);
- 添加一个匿名节点
extern int uci_add_section(structuci_context *ctx, struct uci_package *p, const char *type, struct uci_section**res);
- 设置一个元素值,必要的话新建这个元素
extern int uci_set(struct uci_context *ctx,struct uci_ptr *ptr);
- 附加一个字符串到一个元素列表
extern int uci_add_list(struct uci_context*ctx, struct uci_ptr *ptr);
- 从一个元素列表中删除一个元素
extern int uci_del_list(struct uci_context*ctx, struct uci_ptr *ptr);
- 改变一个节的(顺序)位置
extern int uci_reorder_section(structuci_context *ctx, struct uci_section *s, int pos);
- 重命名一个元素
extern int uci_rename(struct uci_context*ctx, struct uci_ptr *ptr);
- 删除一个节或选项
extern int uci_delete(struct uci_context*ctx, struct uci_ptr *ptr);
- 为一个package保存改变的delta
extern int uci_save(struct uci_context*ctx, struct uci_package *p);
- 提交改动到一个package
extern int uci_commit(struct uci_context*ctx, struct uci_package **p, bool overwrite);
- 列出可用的uci配置文件
extern int uci_list_configs(structuci_context *ctx, char ***list);
- 覆盖默认的delta保存的目录
extern int uci_set_savedir(structuci_context *ctx, const char *dir);
- 覆盖默认的配置文件存储目录
extern int uci_set_confdir(structuci_context *ctx, const char *dir);
- 为detal文件添加一个目录到搜索路径
extern int uci_add_delta_path(structuci_context *ctx, const char *dir);
- 恢复一个配置项的所有变更
extern int uci_revert(struct uci_context*ctx, struct uci_ptr *ptr);
- 解析一个shell风格的参数
extern int uci_parse_argument(structuci_context *ctx, FILE *stream, char **str, char **result);
- 更改默认后端
extern int uci_set_backend(structuci_context *ctx, const char *name);
- 验证uci options中的一个字符串值
extern bool uci_validate_text(const char*str);
- 解析一个uci字符串到uci_prt结构中
int uci_parse_ptr(struct uci_context *ctx,struct uci_ptr *ptr, char *str);
- 查找子元素
int uci_lookup_next(struct uci_context*ctx, struct uci_element **e, struct uci_list *list, const char *name);
- 查找一组选项
void uci_parse_section(struct uci_section*s, const struct uci_parse_option *opts, int n_opts, struct uci_option **tb);
- 在选项列表上构建一个散列
uint32_t uci_hash_options(struct uci_option**tb, int n_opts);
- 分配一个通用的uci_element,保留缓冲区和属性
#define uci_alloc_element(ctx, type, name,datasize) \
uci_to_## type (uci_alloc_generic(ctx, uci_type_ ## type, name, sizeof(struct uci_ ##type) + datasize))
#define uci_dataptr(ptr) \
(((char*) ptr) + sizeof(*ptr))
- 查找package
static inline struct uci_package *
uci_lookup_package (struct uci_context *ctx,const char *name)
{
structuci_element *e = NULL;
if(uci_lookup_next(ctx, &e, &ctx->root, name) == 0)
return uci_to_package(e);
else
return NULL;
}
- 查找section
static inline struct uci_section *
uci_lookup_section(struct uci_context *ctx,struct uci_package *p, const char *name)
{
structuci_element *e = NULL;
if(uci_lookup_next(ctx, &e, &p->sections, name) == 0)
returnuci_to_section(e);
else
returnNULL;
}
- 查找option
static inline struct uci_option *
uci_lookup_option(struct uci_context *ctx,struct uci_section *s, const char *name)
{
structuci_element *e = NULL;
if(uci_lookup_next(ctx, &e, &s->options, name) == 0)
returnuci_to_option(e);
else
returnNULL;
}
static inline const char *
uci_lookup_option_string(struct uci_context*ctx, struct uci_section *s, const char *name)
{
structuci_option *o;
o= uci_lookup_option(ctx, s, name);
if(!o || o->type != UCI_TYPE_STRING)
returnNULL;
returno->v.string;
}
操作实例(在板子上实现):
(1)UCI 配置文件内容 (2)UCI配置文件简单读取:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uci.h"
int main()
{
struct uci_context *c;
struct uci_ptr p;
char *a = strdup("test_file.typedsection1.MultiValue_option_name");
c = uci_alloc_context();
if (UCI_OK != uci_lookup_ptr(c, &p, a, true)) {
uci_perror(c, "no found!\n");
return -1;
}
printf("%s\n", p.o->v.string);
uci_free_context(c);
free(a);
return(0);
}
- 在顶层Makefile中添加如下:
- 在src/Makefile中添加编译链接uci库
-luci
- 然后像平常编译安装IPK包程序一样,得到IPK包,然后将IPK安装到开发板中
opkg install xxx.ipk
如何生成IPK,详情见 Openwrt:IPK软件包
- 执行结果如下: (2)UCI配置文件简单设置:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uci.h"
int main()
{
struct uci_context *ctx;
struct uci_ptr ptr;
char *a = strdup("test_file.typedsection1.Value_option_name=hello world");
ctx = uci_alloc_context();
if(UCI_OK != uci_lookup_ptr(ctx, &ptr, a, true)) {
uci_perror(ctx, "no found!\n");
return -1;
}
uci_set(ctx, &ptr);
uci_save(ctx, ptr.p);
uci_commit(ctx, &ptr.p, false);
uci_free_context(ctx);
free(a);
return(0);
}
- 执行结果
- 网页显示结果