Openwrt:LuCI之UCI(三)

一、UCI简介

  • UCI是Unified Configuration Interface的缩写,翻译成中文就是统一配置接口,用途就是为OpenWrt提供一个集中控制的接口。
  • 配置接口启动流程:
  • ① 启动脚本 /etc/init.d/xxx;
  • ② 启动脚本通过UCI分析库从 /etc/config/xxx 获得启动参数;
  • ③ 启动脚本完成正常启动。

Openwrt:LuCI之UCI(三)_第1张图片

1.1 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配置才生效。

1.2 UCI文件语法

  • 语法:
config 	'section_type'		'section'
		option		'key'			'value'
		list		'list_key'		'list_value'

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配置修改

2.1 UCI命令

2.1.1 UCI命令语法格式
uci  []	 <command>  []

规则

  • UCI读取总是先读取内存中的缓存,然后再读取文件中的。
  • 进行过增加、修改、删除操作后要执行生效指令 commit,否则所做修改只留存在缓存中。
  • 当使用UCI命令工具写入配置文件,配置文件都是整个重写并且不需要确认命令。这意味着在文件中任何多余的注释行和空行均会被删除。
  • option:
选项 含义
-c 设置配置文件的搜索路径(默认值:/etc/config)
-d 在 uci 显示中为列表值设置分隔符
-f 使用作为输入来替代标准 stdin
-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 恢复指定的选项,配置节点或配置文件。
2.1.2 常见UCI命令操作
  • UCI 文件示例:
    Openwrt:LuCI之UCI(三)_第2张图片
2.1.2.1 读取类操作
  • 显示指定文件配置
    uci show 注:省略则显示全部文件配置
    Openwrt:LuCI之UCI(三)_第3张图片

  • 取得节点类型
    uci get .


    在这里插入图片描述

  • 取得一个值
    uci get .

    .
    在这里插入图片描述

  • 显示指定节点名字配置
    uci show .


    Openwrt:LuCI之UCI(三)_第4张图片

  • 显示指定节点名字选项配置
    uci show .

    .
    在这里插入图片描述

  • 显示尚未生效的修改记录
    uci changes

  • 匿名节点显示(如果所显示内容有匿名节点,使用-X参数可以显示出匿名节点的ID)
    uci show -X .

    .

2.1.2.2 写入类操作
  • 在文件中增加一个匿名节点
    uci add
  • 在文件中增加一个节点或修改一个节点的类型(节点存在则修改,不存在则增加)
    uci set .
    =
  • 在节点中增加一个选项和值或修改一个选项的值(同上)
    uci set .
    .
  • 在列表中增加一个选项和值
    uci add_list .
    .
  • 删除指定名字的节点
    uci delete .
  • 删除指定选项
    uci delete .
    .
  • 删除列表
    uci delete .
    .
  • 删除列表中的一个值
    uci delete .
    .
  • 生效修改(任何写入类的语法,最终都要执行生效修改,否则所做修改只在缓存中!!!不会保存在文件中!!!)
    uci commit

2.2 UCI API

2.2.1 环境安装
  • UCI不仅提供命令接口供脚本开发者开发,而且提供了C语言调用接口。下面将在普通桌面操作系统Ubuntu(非板子上)下来说明API的使用。准备UCI编程接口的使用环境
2.2.1.1 Cmake 安装
  • Cmake是跨平台的产生Makefile的命令行工具,它应用于在脚本文件中配置工程。Libubox和UCI均使用Cmake命令来产生目标平台的构建系统命令。因此首先安装Cmake。
  • sudo apt-get install cmake
2.2.1.2 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/ 目录下
2.2.1.3 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库。
2.2.2 API 接口
  • API 接口详情都在 /usr/local/include/uci.h 头文件中
2.2.2.1 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;
};
2.2.2.2 全部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;
}
2.2.2.3 操作实例(在板子上实现):

(1)UCI 配置文件内容
Openwrt:LuCI之UCI(三)_第5张图片
(2)UCI配置文件简单读取:

#include 
#include 
#include 
#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中添加如下:
    Openwrt:LuCI之UCI(三)_第6张图片
  • 在src/Makefile中添加编译链接uci库 -luci
    Openwrt:LuCI之UCI(三)_第7张图片
  • 然后像平常编译安装IPK包程序一样,得到IPK包,然后将IPK安装到开发板中 opkg install xxx.ipk

如何生成IPK,详情见 Openwrt:IPK软件包

  • 执行结果如下:
    在这里插入图片描述
    (2)UCI配置文件简单设置:
#include 
#include 
#include 
#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);
}
  • 执行结果
    Openwrt:LuCI之UCI(三)_第8张图片
  • 网页显示结果
    在这里插入图片描述

三、结尾

3.1 参考博文

  • https://www.cnblogs.com/tfanalysis/p/3688080.html
  • https://blog.csdn.net/u012819339/article/details/50752157
  • https://blog.csdn.net/jf_xu/article/details/72629302

跟随大师的脚步,模仿大师的行为,感受大师的意境,成为真正的大师。

你可能感兴趣的:(OpenWrt)