UCI简介
UCI (Unified Configuration Interface) 是 Openwrt 中的统一配置接口。官方文档参考
每一个程序的配置文件都保存在/etc/config 目录,可以通过文本编辑器、uci(一个可执行程序)以及各种 API(Shell、Lua 和C)来修改这些配置文件。
UCI 文件格式
UCI 配置文件由一个或多个 config 语句组成,每一个 config 语句伴随着一个或多个 option 语句。这样的由一个 config 语句以及伴随的几个 option 语句组成的段落就叫做一个 section。
例如:在/etc/config 目录下创建一个配置文件 example,其内容如下
root@OpenWrt:~# vi /etc/config/example
config example 'test'
option name 'value'
list collection 'first item'
list collection 'second item'
- config example ‘test’ 中的 example 表示这个 section 的 type ,'test’表示这个 section 的name,为空时称为匿名section 。name 方便后续直接使用,如果是匿名则需要用
@type[0] @type[1]
代表文件中先后的通type的section,@type[-1] @type[-2]
文件末尾开始。
- option name ‘value’ 定义了一个“名称-值”对 name=value
- list 定义了一个列表,表示一个名称有多个值,比如这里的 collection 就有 2 个值’first item’和’second item
#下面的例子都是正确的 UCI 语法
option example value
option 'example' value
option example "value"
option "example" 'value'
option 'example' "value"
#下面的例子都是错误的 UCI 语法
option 'example" "value' (引号不对称)
option example some value with space (缺少引号,引起歧义,不知道哪几个单词相结合)
Vim 语法高亮插件: Vim Editor plugins
UCI 命令
UCI 提供了一个命令行工具 uci,它可以对配置文件中的内容进行修改、添加、删除和读取。
直接输入uci可以查看uci的帮助信息和具体参数
#注:下面说名中 config是配置文件名;section是 对应的name或者id或者@type[0]
# uci
Usage: uci [<options>] <command> [<arguments>]
Commands:
batch
export [<config>]
import [<config>]
changes [<config>]
commit [<config>]
add <config> <section-type>
add_list <config>.<section>.<option>=<string>
del_list <config>.<section>.<option>=<string>
show [<config>[.<section>[.<option>]]]
get <config>.<section>[.<option>]
set <config>.<section>[.<option>]=<value>
delete <config>.<section>[.<option>]
rename <config>.<section>[.<option>]=<name>
revert <config>[.<section>[.<option>]]
reorder <config>.<section>=<position>
Options:
-c <path> set the search path for config files (default: /etc/config)
-d <str> set the delimiter for list values in uci show
-f <file> use <file> as input instead of stdin
-m when importing, merge data into an existing package
-n name unnamed sections on export (default)
-N don't name unnamed sections
-p <path> add a search path for config change files
-P <path> add a search path for config change files and use as default
-q quiet mode (don't print error messages)
-s force strict mode (stop on parser errors, default)
-S disable strict mode
-X do not use extended syntax on 'show'
UCI命令含义:
batch
批处理uci命令,需要把每条命令前的字符串”uci”去掉。
#例如在 cfg 配置文件中增加一个section以及option,然后修改section的type与name
uci set cfg.section_name=section_type
uci set cfg.section_name.key=value
uci set cfg.@section_type[0]=section_new_type
uci rename cfg.@section_type[0]=section_new_name
uci export cfg
#只需要把这两个命令去掉uci后放到文档a里面
uci batch << EOI
set cfg.section_name=section_type
set cfg.section_name.key=value
set cfg.@section_type[0]=section_new_type
rename cfg.section_name=section_new_name
export cfg
EOI
#撤销操作
uci revert cfg
#提交 写入cfg 文件中
uci commit cfg
export
uci export [<config>]
显示UCI可读取到的某配置文件的信息(包括已修改但未提交内容)。
# uci export cfg
package cfg
config section_new_type 'section_new_name'
option key 'value'
import
uci import [<config>]
导入配置文件为UCI格式
# uci import
package cfg
config section_type section_name
option key value
list l 1
list l 2
#ctrl + D
#执行上述命令后会得到文件 cfg
# cat cfg
config section_type 'section_name'
option key 'value'
list l '1'
list l '2'
changes
uci changes [<config>]
# uci changes cfg
cfg.section_name='section_type'
cfg.section_name.key='value'
追踪修改但未提交的动作,类似git。
commit
uci commit [<config>]
提交uci修改,当命令uci changes有返回结果时表示有uci修改待提交,可以通过命令uci commit完成提交,即写入到配置文件。
uci commit可以提交具体的package,例如uci commit network表示只提交network部分的修改。
提交后需要执行相应的命令来重新加载配置,例如:
uci set uhttpd.main.listen_http='8080'
uci commit uhttpd
/etc/init.d/uhttpd restart
通过 uci 写配置文件后,整个配置会被重新写入配置文件,这意味着配置文件中任何无关行都会被删除,包括注释(#开头的内容)。因此如果你要保留自己写的注释,就只能通过文本编辑器修改配置文件。
add
uci add <config> <section-type>
在某个配置文件中添加一条匿名的 section
# uci add cfg section_another_type
cfg02620b #这里是匿名section的id 相当于相当于一个别名,也等同于 @section_another_type[0]
# uci export cfg
package cfg
config section_new_type 'section_new_name'
option key 'value'
config section_another_type
add_list
uci add_list <config>.<section>.<option>=<string>
添加一条list
# uci add_list cfg.@section_another_type[0].l1=0
# uci add_list cfg.cfg02620b.l1=1
# uci add_list cfg.cfg02620b.l1=2
# uci export cfg
package cfg
config section_new_type 'section_new_name'
option key 'value'
config section_another_type
list l1 '0'
list l1 '1'
list l1 '2'
del_list
uci del_list <config>.<section>.<option>=<string>
删除一条list,由于list可以存在多个值,所以必须指定删除的值
# uci del_list cfg.@section_another_type[0].l1=1
# uci export cfg
package cfg
config section_new_type 'section_new_name'
option key 'value'
config section_another_type
list l1 '0'
list l1 '2'
show
uci show [<config>[.<section>[.<option>]]]
用压缩格式显示all, package, section或option的值,依参数而定
# uci show #--- show all
...
# uci show cfg #--- show package
cfg.section_new_name=section_new_type
cfg.section_new_name.key='value'
cfg.@section_another_type[0]=section_another_type
cfg.@section_another_type[0].l1='0' '2'
# uci show cfg.section_new_name #--- show section
cfg.section_new_name=section_new_type
cfg.section_new_name.key='value'
# uci show cfg.@section_another_type[0] #--- show section
cfg.cfg02620b=section_another_type
cfg.cfg02620b.l1='0' '2'
# uci show cfg.section_new_name.key #--- show option
cfg.section_new_name.key='value'
get
uci get <config>.<section>[.<option>]
获取指定 section 的type 或option的值,与uci show等号两边对应
# uci get cfg.cfg02620b
section_another_type
# uci get cfg.@section_another_type[0]
section_another_type
# uci get cfg.section_new_name.key
value
set
uci set <config>.<section>[.<option>]=<value>
设置指定section 的 type 或option的值(不存在则创建该对象),格式可以参考uci show的结果
# uci set cfg.section_new_name.key=val
# uci set cfg.@section_another_type[0]=section_type1
#or
# uci set cfg.cfg02620b=section_type1
# uci export cfg
package cfg
config section_new_type 'section_new_name'
option key 'val'
config section_type1
list l1 '0'
list l1 '2'
delete
uci delete <config>.<section>[.<option>]
删除指定section或option
# uci delete cfg.@section_type1[0].l1
# uci delete cfg.section_new_name
# uci export cfg
package cfg
config section_type1
# 循环删除
while uci -q delete umdns.@umdns[0]; do :; done
rename
uci rename <config>.<section>[.<option>]=<name>
重命名指定section的name或option的key。
重命名section的name后id就会消失,因为id本质就是别名。
# uci show cfg.@section_type1[0]
cfg.cfg02800c=section_type1
# uci rename cfg.@section_type1[0]=section_name1
# uci show cfg.@section_type1[0]
cfg.section_name1=section_type1
# id 被删除
# uci show cfg.cfg02800c
uci: Entry not found
# uci show cfg.section_name1
cfg.section_name1=section_type1
revert
uci revert <config>[.<section>[.<option>]]
撤销修改(执行uci commit之前)。
# uci changes
-cfg.cfg02800c.l1
-cfg.section_new_name
cfg.cfg02800c='section_name1'
# uci revert cfg
# uci changes cfg
#
reorder
uci reorder <config>.<section>=<position>
修改配置项config在文件中的位置;优先级0最大,数字越大越排后。
# uci batch << EOI
set cfg.name1=type1
set cfg.name2=type2
set cfg.name3=type3
export cfg
EOI
package cfg
config type1 'name1'
config type2 'name2'
config type3 'name3'
# uci reorder cfg.name3=0
# uci export cfg
package cfg
config type3 'name3'
config type1 'name1'
config type2 'name2'
# uci reorder cfg.name1=99
# uci export cfg
package cfg
config type3 'name3'
config type2 'name2'
config type1 'name1'
常用功能配置文件含义(OpenWrt Configuration files):
/etc/config/dhcp // dnsmasq软件包配置,包含dhcp和dns设置
/etc/config/dropbear // SSH服务器选项
/etc/config/firewall // 防火墙设置,包含网络地址转换、包过滤、端口转发等
/etc/config/network // 网络配置,包含桥接、接口、路由配置
/etc/config/system // 系统配置,包含主机名称、网络时间同步等
/etc/config/timeserver // rdate的时间服务列表
/etc/config/luci // 基本的LuCI配置
/etc/config/wireless // 无限设置和wifi网络定义
/etc/config/uhttpd // web服务器选项配置
/etc/config/upnpd // miniupnpd UPnP服务设置
/etc/config/qos // 网络服务质量的配置文件定义
通过 shell 脚本操作 UCI 配置
Openwrt 提供了一些 shell 脚本函数,这些函数使得操作 UCI 配置文件变得非常的高效。参考官方
/etc/init.d/
目录下的启动脚本基本都会用到(通常脚本开头为#!/bin/sh /etc/rc.common
, /etc/rc.common
包含了 /lib/functions.sh
)。
要使用这些 shell 函数,首先需要加载 /lib/functions.sh
,然后实现 config_cb()、 option_cb()、 list_cb()
这些 shell 函数,当我们调用 Openwrt 提供的 shell 函数 config_load 来解析配置文件时,程序就会回调我们自己定义的 config_cb()、 option_cb()、 list_cb()
这些 shell 函数.
下面创建一个 shell 脚本 test.sh,其内容如下:
#!/bin/sh
. /lib/functions.sh
reset_cb
config_cb() {
local type="$1"
local name="$2"
# commands to be run for every section
if [ -n "$type" ];then
echo "$name=$type"
fi
}
option_cb() {
local name="$1"
local value="$2"
# commands to be run for every option
echo $name=$value
}
list_cb() {
local name="$1"
local value="$2"
# commands to be run for every list item
echo $name=$value
}
config_load $1
reset_cb 表示将 xxx_cb 这 3 个函数初始化为没有任何操作。config_load
首先从绝对路径文件名加载配置,如果失败再从 /etc/config
路径加载配置文件。xxx_cb 那 3 个函数必须在包含 /lib/functions.sh
和调用 config_load
之间定义。
给 test.sh 添加可执行权限chmod +x test.sh
,执行 test.sh
root@OpenWrt:/# ./test.sh dhcp
cfg02411c=dnsmasq
domainneeded=1
boguspriv=1
filterwin2k=0
localise_queries=1
rebind_protection=1
rebind_localhost=1
local=/lan/
domain=lan
expandhosts=1
nonegcache=0
authoritative=1
readethers=1
leasefile=/tmp/dhcp.leases
resolvfile=/tmp/resolv.conf.auto
lan=dhcp
interface=lan
start=100
limit=150
leasetime=12h
wan=dhcp
interface=wan
ignore=1
cfg06fe63=host
ip=192.168.10.104
mac=44:8A:5B:EC:49:27
可以看到,当 config_load
解析配置文件时,解析到 section
时,就会回调 config_cb
,解析 option
时,就会回调 option_cb
,解析 list
时,就会回调 list_cb
。我们可以在对应的函数里面添加自己的相关命令。
另一种方式是使用 config_foreach
针对每一个 section
调用一个指定的自定义的函数,看下面这个 shell 脚本 test.sh
#!/bin/sh
. /lib/functions.sh
reset_cb
handle_interface() {
local config="$1"
local custom="$2"
# run commands for every interface section
if [ "$config" = "$custom" ];then
echo $custom
fi
}
config_load $1
config_foreach handle_interface interface $2
这里定义了一个函数 handle_interface
,然后以 handle_interface
、interface
和 $2
这 3 个参数调用 config_foreach
。
config_foreach
会遍历以 $1
指定的配置文件中的所有的 section
,一旦其类型与参数 interface
相等时,则回调
handle_interface
,在 config_foreach
函数内部,会以当前正在解析的 section
的 ID 和调用 config_foreach
时的第 3 个参数作为调用 handle_interface
的参数。
root@OpenWrt:/# ./test.sh network wan
wan
这里以参数 network 和 wan 调用 test.sh,然后程序首先调用 config_load
加载配置文件 network
,然后 config_foreach
在配置文件 network 中搜寻类型为 interface
的 section
,一旦找到就以当前 section
的 ID 和 wan 作为参数调用 handle_interface
,在 handle_interface
函数中,得到 config=wan,custom=wan
,最终打印出 wan。
使用 config_get 读取配置选项的值,该函数需要至少 3 个参数:
- 一个存储返回值的变量
- 要读取的 section 的 ID(如果是有名称的 section 就是其名称)
- 要读取的 option 的名称
在 handle_interface
中使用 config_get
和 config_set
来读取和设置当前的 section
。
#!/bin/sh
. /lib/functions.sh
reset_cb
handle_interface() {
local config="$1"
local custom="$2"
# run commands for every interface section
if [ "$config" = "$custom" ];then
config_get prot $config proto
echo $prot
fi
}
config_load $1
config_foreach handle_interface interface $2
root@OpenWrt:/# ./test.sh network lan
static
root@OpenWrt:/# ./test.sh network wan
dhcp
如果明确知道 section 的名称,可以直接调用 config_get
读取配置选项的值。
config_set
用来设置配置选项的值,它需要 3 个参数:
- section 的 ID(如果有名称就是其名称)
- option 的名称 与 要设置的值
更多操作参考
Lua API
lua_bindings_for_uci