第9章 OpenWrt-UCI的使用(二)
本章主要讲述了UCI API的使用,关于UCI指令的使用请参考上一篇文章《OpenWrt-UCI的使用(一)》。
UCI API 编程接口
UCI接口都是以小写的uci开头并放在uci.h头文件中。大多数函数的第一个参数均为uci_context的指针变量。这个变量在程序初始化时调用uci_alloc_context函数分配空间并设置初始值。通过调用uci_free_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;
};
- 配置文件(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;
};
- 节(section)对应的结构体:
struct uci_section
{
struct uci_element e;
struct uci_list options;
struct uci_package *package;
bool anonymous;
char *type;
};
- 元素位置指针结构体
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;
};
常用的函数如下,可以参考uci.h文件来查看下表。
函数 | 含义 |
---|---|
uci_alloc_context | 分配UCI上下文环境对象 |
uci_free_context | 释放UCI上下文环境对象 |
uci_load | 解析UCI配置文件,并存储到UCI对象中。@name:配置文件名,相对于配置目录,@package:在这个变量中存储装载的配置包 |
uci_unload | 从UCI上下文环境对象中unload配置文件 |
uci_loop_ptr | 分割字符串并查找 |
uci_set | 设置元素值,更新或创建的元素将存储在ptr->last中 |
uci_add_list | 向list中增加一个元素值 |
uci_delete | 删除一个元素,配置节或者选项 |
uci_save | 保存一个package修改的delta |
uci_commit | 提交更新到package文件 |
uci_set_confdir | 修改UCI配置文件的存储位置,默认为/etc/config |
快速使用示例1
如下代码
目录结构如下:
atlas@atlas-virtual-machine:$ cd package/utils/uci_api_demo/
atlas@atlas-virtual-machine:$ ls
files Makefile src
atlas@atlas-virtual-machine:$ tree
.
├── files
│ └── uci_api_demo.conf
├── Makefile
└── src
├── Makefile
└── uci_api_demo.c
2 directories, 4 files
我们创建了一个名为“uci_api_demo.conf”的配置文件,内容如下,ipk安装到OpenWrt后,该文件会对应成“/etc/config/uci_api_demo”。
config my_server web_server
option ip '127.0.0.1'
option port '8090'
"src/uci_api_demo.c”内容如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uci.h"
int main()
{
struct uci_context *context;
struct uci_ptr ptr;
char *str = strdup("uci_api_demo.web_server.ip");
context = uci_alloc_context();
if (UCI_OK != uci_lookup_ptr(context, &ptr, str, true)) {
uci_perror(context, "no found!\n");
return -1;
}
printf("%s\n", ptr.o->v.string);
uci_free_context(context);
free(str);
return 0;
}
“src/Makefile”内容如下:
# CFLAGS +=
LDFLAGS += -luci #依赖uci.so
uci_api_demo : uci_api_demo.o
$(CC) uci_api_demo.o $(LDFLAGS) -o uci_api_demo
uci_api_demo.o:uci_api_demo.c
$(CC) $(CFLAGS) $(LDLIBS) -c uci_api_demo.c
clean:
rm -rf *.o uci_api_demo
根目录的“Makefile”内容如下:
include $(TOPDIR)/rules.mk
PKG_NAME:=uci_api_demo
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/uci_api_demo
SECTION:=utils
CATEGORY:=Utilities
DEPENDS:= +uci +libuci
TITLE:=uci_api_demo --learn uci api
endef
define Package/uci_api_demo/description
This is a uci api demo
endef
define Build/Prepare
echo "Here is Build/Prepare"
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/uci_api_demo/install
echo "Here is Package/install"
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/uci_api_demo.conf $(1)/etc/config/uci_api_demo
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/uci_api_demo $(1)/bin/
endef
$(eval $(call BuildPackage,uci_api_demo))
将源代码编程成ipk并完成安装(可参考《第3章 OpenWrt应用层ipk编写及编译》)。
从执行结果可以看到,可执行程序uci_api_demo读取到uci_api_demo这个配置文件中ip这个option的值
root@(none):~# cat /etc/config/uci_api_demo
config my_server web_server
option ip '127.0.0.1'
option port '8090'
root@(none):~# uci_api_demo
127.0.0.1
root@(none):~#
快速使用示例2
目录结构如下,
atlas@atlas-virtual-machine:$ cd package/utils/uci_api_demo_2/
atlas@atlas-virtual-machine:$ tree
.
├── files
│ └── uci_api_demo.conf
├── include
│ └── uci_utils.h
├── Makefile
└── src
├── Makefile
├── uci_api_demo.c
└── uci_utils.c
3 directories, 6 files
atlas@atlas-virtual-machine:$
“include/uci_utils.h”内容如下:
int set_uci(const char *package, const char *section, const char *option, const char *value);
int resolve_uci(const char *package);
“src/uci_utils.c”内容如下:
#include <string.h>
#include "uci_utils.h"
#include "uci.h"
int set_uci(const char *package, const char *section, const char *option, const char *value)
{
struct uci_ptr ptr;
memset(&ptr, 0, sizeof(ptr));
struct uci_context * _ctx = uci_alloc_context(); //申请上下文
if (UCI_OK != uci_load(_ctx, package, &ptr.p)) {
printf("open set_uci UCI_CONFIG_FILE fail exit\n");
goto set_cleanup; //如果打开UCI文件失败,则跳到末尾 清理 UCI 上下文.
}
ptr.package = "config";
ptr.section = section;
ptr.option = option;
ptr.value = value;
uci_set(_ctx, &ptr); //写入配置
uci_commit(_ctx, &ptr.p, false); //提交保存更改
uci_unload(_ctx, ptr.p); //卸载包
set_cleanup:
//printf("uci set_uci exit\n");
uci_free_context(_ctx); //释放上下文
return 0;
}
int resolve_uci(const char *package)
{
int rc = -1;
struct uci_package * pkg = NULL;
struct uci_element *e, *e1;
struct uci_ptr ptr;
char *value = NULL;
struct uci_option *o;
struct uci_context *ctx = uci_alloc_context(); // 申请一个UCI>上下文.
if (UCI_OK != uci_load(ctx, package, &pkg)) {
printf("open file UCI_CONFIG_FILE fail exit\n");
goto cleanup; //如果打开UCI文件失败,则跳到末尾 清理 UCI 上下文.
}
/*遍历UCI的每一个节*/
uci_foreach_element(&pkg->sections, e) {
struct uci_section *s = uci_to_section(e);
// 此时通过 s->e.name 来获取.
printf("---- section name: %s ---- \n", s->e.name);
uci_foreach_element(&s->options, e1) {
o = uci_to_option(e1);
printf(" --- option name: %s --, value: %s \n", o->e.name, o->v.string);
}
}
cleanup:
uci_free_context(ctx);
ctx = NULL;
return 0;
}
“src/uci_api_demo.c”内容如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uci_utils.h"
int main()
{
const char *pkg_file = "/etc/config/uci_api_demo";
const char *sec_name = "web_server";
const char *opt_type = "backup_port";
const char *opt_value = "8888";
printf("---- before change ----");
resolve_uci(pkg_file);
printf("---- after change ----");
set_uci(pkg_file, sec_name, opt_type, opt_value);
resolve_uci(pkg_file);
return 0;
}
“src/Makefile”内容如下:
LDFLAGS += -luci -I./include
uci_api_demo_2 : uci_api_demo_2.o uci_utils.o
$(CC) $(LDFLAGS) uci_api_demo_2.o uci_utils.o -o uci_api_demo_2
uci_api_demo_2.o:uci_api_demo.c
$(CC) $(CFLAGS) $(LDLIBS) $(LDFLAGS) -c uci_api_demo.c -o uci_api_demo_2.o
uci_utils.o:uci_utils.c
$(CC) $(CFLAGS) $(LDLIBS) $(LDFLAGS) -c uci_utils.c
clean:
rm -rf *.o uci_api_demo_2
“files/uci_api_demo.conf”内容如下:
config my_server web_server
option ip '127.0.0.1'
option port '8090'
将ipk编译并进行安装,测试结果如下:
root@(none):~# opkg install /tmp/uci_api_demo_2_1_i386_pentium4.ipk
Package uci_api_demo_2 (1) installed in root is up to date.
root@(none):~# opkg remove uci_api_demo_2
Removing package uci_api_demo_2 from root...
root@(none):~# opkg install /tmp/uci_api_demo_2_1_i386_pentium4.ipk
Installing uci_api_demo_2 (1) to root...
Configuring uci_api_demo_2.
root@(none):~#
root@(none):~# uci_api_demo_2
---- before change -------- section name: web_server ----
--- option name: ip --, value: 127.0.0.1
--- option name: port --, value: 8090
---- after change -------- section name: web_server ----
--- option name: ip --, value: 127.0.0.1
--- option name: port --, value: 8090
--- option name: backup_port --, value: 8888
root@(none):~# cat /etc/config/uci_api_demo
config my_server 'web_server'
option ip '127.0.0.1'
option port '8090'
option backup_port '8888
点解连接阅读《OpenWrt-UCI的使用(一)》。