1、openwrt feeds
UCI (统一配置接口)openwrt官方说明(有中文版本):https://oldwiki.archive.openwrt.org/zh-cn/doc/uci
在Openwrt系统中,“feed”是一系列的软件包,这些软件包需要通过一个统一的接口地址进行访问。“feed”软件包中的软件包可能分布在远程服务器上、在svn上、在本地文件系统中或者其他的地方,用户可以通过一种支持feed机制的协议,通过同一个地址进行访问。(有些绕,简单来说,就是系统将一系列的软件包进行了地址映射,只能通过同一个接口进行访问)。
Feeds是OpenWRT环境所需要的软件包套件,比较重要的feeds有:
‘pacakges’:一些额外的基础路由器特性软件
‘LuCI’:OpenWRT默认的GUI
‘Xwrt’:另一种可选的GUI界面
需要能够连接互联网。
在下载之前可以通过查看’feeds.conf.default’文件,来检查哪些文件需要包含在环境中。
2、Web Interface (LuCi)
Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。
1、在mtk的mt7621发布的sdk中,Luci-app-mtk是一个插件,已经在sdk包含,也可以通过feeds安装。Luci-app-mtk主要用来操作wifi配置,用一个库/usr/lib/lua/mtkwifi.lua来直接读写wifi profile
UCI 是 Openwrt 中为实现所有系统配置的一个统一接口,英文名 Unified Configuration Interface,即统一配置接口。轻量级 LUA 语言的官方版本只包括一个精简的核心和最基本的库。这使得 LUA 体积小、启动速度快,从而适合嵌入在别的程序里。 LuCI 即是这两个项目的合体,可以实现路由的网页配置界面。
web框架luci mvc框架,通过mvc架构我们可以非常方便的添加一个模块,业务逻辑更加清晰。在luci框架中我们只需要简单的几行代码就可以实现前后端数据打通,不需要额外的数据解析,也可以自定义模板,实现深度定制。
- view 界面层,用于实现html相关的代码,包括模板
- controller 控制层,实现菜单、接口的定义
- model 数据层,实现与后台数据(uci配置)的绑定
在luci中,controller和model通过lua语言实现。我们要做的主要工作就是基于 LuCI 框架编写LUA 脚本、在 html 页面中嵌入 LUA 脚本。
2、require用法
-
加载指定的模块,相当与#include作用类似,加载了该模块,那么就可已使用模块中的全局函数和全局数据(如表等等)
如果有一个test.lua的文件,可以这样加载 require(“test”)
如果test.lua不在可执行程序A当前目录下,则加上相应路径即可
如:lua在目录X下,test.lua在X/lua/test.lua下,则这样写require(“./lua/test”)
当然还可以这样写require(“lua.test”)
注意:test.lua文件最后需要返回一个值(数字、字符、表等等)
如在test.lua行尾 return {hehe = 1} -
给加载的模块定义一个别名变量,方便调用:
local m = require(“module”)
print(m.constant)
m.func3()
3、Lua有两种执行系统命令的方法:os.execute, io.popen
例如在文件\luci-app-mtk\luasrc\controller\mtkwifi.lua中
function vif_disable(iface)os.execute("ifconfig "..iface.." down")luci.http.redirect(luci.dispatcher.build_url("admin", "mtk", "wifi"))
end
函数os.execute这一行执行了关闭网卡的作用,类似于在shell中输入下面代码中第一行:
[root@ localhost ~]# ifconfig eth0 down //关闭网卡
[root@ localhost ~]# ifconfig eth0 hw ether 00:AA:BB:CC:DD:EE //修改MAC地址
[root@ localhost ~]# ifconfig eth0 up //启动网卡
[root@ localhost ~]# ifconfig eth1 hw ether 00:1D:1C:1D:1E //关闭网卡并修改MAC地址
[root@ localhost ~]# ifconfig eth1 up //启动网卡[root@ localhost ~]# ifconfig eth0 192.168.1.56
# 给eth0网卡配置IP地址
[root@ localhost ~]# ifconfig eth0 192.168.1.56 netmask 255.255.255.0
# 给eth0网卡配置IP地址,并加上子掩码
[root@ localhost ~]# ifconfig eth0 192.168.1.56 netmask 255.255.255.0 broadcast 192.168.1.255
# 给eth0网卡配置IP地址,加上子掩码,加上个广播地址
4、luci http接口
luci是一套web框架,虽然对http协议进行了封装,开发人员可以不用关心具体http底层如何处理,但是我们还是需要用到http请求的一些接口,比如我们在自定义http请求接口时,需要获取http请求的表单参数、cookies等,有时候也需要处理上传的文件、url重定向。这一节列出http常用的一些操作接口,可以根据实际开发需要进行学习。
接口 说明
luci.http.formvalue(param) 获取表单提交的数据,支持GET和POST
luci.http.content() Return the request content if the request was of unknown type.
luci.http.getcookie(name) 获取cookie值
luci.http.getenv(name) 获取环境变量
luci.http.prepare_content() 设置content-type,如luci.http.prepare_content(“application/json”)
luci.http.source() Get the RAW HTTP input source
luci.http.write(content) 返回数据,content为字符串类型
luci.http.write_json(object) 返回json格式数据,object为对象,会自动转换成json格式
luci.http.redirect(url) 重定向到指定url
luci.http.urldecode(str) Return the URL-decoded equivalent of a string.
luci.http.urlencode(str) Return the URL-encoded equivalent of a string.
luci.http.setfilehandler (callback) Set a handler function for incoming user file uploads.
6、lua运算符
逻辑运算符
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。关系运算符
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,不相等返回 true,否则返回 false (A ~= B) 为 true。操作符
.. 连接两个字符串(两个圆点)例如:os.execute("iwpriv "..ifname.." set SSID="..SSID )
# 一元运算符,返回字符串或者表的长度
7、Lua table(表)
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,这种数组不仅可以使用数值作为索引,也可以使用字符串或其他任意类型的值作为索引( nil除外)。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string.
lua_newtable(L); /* 创建一个table */
lua_pushstring(L, "macaddr"); /* push macaddr 到table*/
lua_pushstring(L, if_hw); /* push value 到table。if_hw是一个变量*/
lua_settable(L, -3); /* 将键和值都弹出栈,即数据暴露到lua环境 */
8、Lua 和 C/C++ 的交互机制
Lua 和 C/C++ 的交互机制的基础在于 Lua 提供了一个虚拟栈,C++ 和 Lua 之间的所有类型的数据交换都通过这个栈完成。无论何时 C/C++ 想从 Lua 中调用一个值,被请求的值将会被压入栈,无论何时 C/C++ 想要传递一个值给 Lua,首先将整个值压栈,然后就可以在 Lua中 调用。栈的特点是先进后出:
堆栈索引的方式可是是正数也可以是负数,区别是:正数索引 1 永远表示栈底,负数索引 -1 永远表示栈顶。
Lua 栈可以存储数字、字符串、表、闭包等,它们最终都用 TValue 这种数据结构来保存:
TValue 结构对应于 Lua 中的所有数据类型,是一个{值, 类型} 结构,它把值和类型绑在一起,用 tt 记录 value 的类型,value 是一个联合结构,由 Value 定义,可以看到这个联合有四个成员:
p:可以存一个指针, 实际上是 Lua 中的 light userdata 结构。
n:所有的数值存在这里。
b:Boolean 值存在这里。
gc:gc 是一个指针,它可以指向的类型由联合体 GCObject 定义,诸如 table、thread、closure、string 等需要内存管理垃圾回收的类型都存在这里。
Lua API 提供了一系列的压栈函数,将 C/C++ 值入栈:
/* ** push functions (C -> stack) */
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
Lua API 提供了一系列的处理函数,处理入栈数据:
/* ** access functions (stack -> C) */
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);// 经实测,to* 函数不会出栈
LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_objlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
Lua API 提供了一系列的获取函数,获取 Lua 值并将其入栈:
/* ** get functions (Lua -> stack) */
LUA_API void (lua_gettable) (lua_State *L, int idx);
LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawget) (lua_State *L, int idx);
LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void (lua_getfenv) (lua_State *L, int idx);
Lua API 提供了一系列的获取函数,修改栈值:
/* ** set functions (stack -> Lua) */
LUA_API void (lua_settable) (lua_State *L, int idx); //这个函数会将键和值都弹出栈。
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
LUA_API int (lua_setmetatable) (lua_State *L, int obj
C/C++ 调用 Lua
Lua 和 C/C++ 通信时有这样的约定:所有的 Lua 中的值由 Lua 来管理,C/C++中产生的值 Lua 不知道。类似表达了这样一种意思:如果你(C/C++)想要什么,你告诉我(Lua),我来产生,然后放到栈上,你只能通过 API 来操作这个值,我只管我的世界。
C/C++ 可以获取 Lua 中的值,也可以调用 Lua 函数,还可以修改 Lua 文件:
C/C++ 获取 Lua 值
- 使用 lua_getglobal 来获取值并将其压栈。
- 使用 lua_toXXX 将栈中元素取出(此时元素并不会出栈)转成相应的 C/C++ 类型的值。
C/C++ 调用 Lua 函数
- 使用 lua_getglobal 来获取函数并将其压栈。
- 如果这个函数有参数的话,就需要依次将函数的参数也压入栈。
- 调用 lua_pcall 开始调用函数,调用完成以后,会将返回值压入栈中。
- 取返回值,调用完毕。
Lua 调用 C/C++
Lua 可以调用 C/C++ 的函数,步骤为:
- 将 C 的函数包装成 Lua 环境认可的函数。
- 将包装好的函数注册到 Lua 环境中。
- 像使用普通 Lua 函数那样使用注册函数。
注册 C/C++ 函数到 Lua 环境
lua_register(L,"Add2Number",add);//将 c/c++ 函数 add 注册到全局 table[Add2Number] 中
3、UCI (统一配置接口)
Openwrt中UCI配置文件被放在/etc/config目录下面,每一个配置文件设计到系统的一种配置。你可以用文本编辑器修改这个配置文件,或者用uci命令修改。当然它也可以用其他API接口来修改,比如shell、lua等,而且web接口像luci、webif也可以改变它。当配置文件被改变后,必须重启程序才能生效。
使用UCI设置的命令的结果 是断电保存的。
UCI命令配置有几种方式:
1、命令行形式:
uci set system.web.wan_connected=0
uci set system.web.wan_connected=1
2、代码形式:
work_mode = uci_getvalue(“cmcc.Plugin.WorkingMode”);
代码实现uci_api.c:
#include <uci.h>
#include <string.h>
#include <stdlib.h>#include "uci_api.h"//add by zhanglong
char *uci_getvalue(const char *key)
{
char *o = calloc(1, strlen(key)+1);if (!o)return NULL;strcpy(o, key);struct uci_context *ctx = uci_alloc_context();if (!ctx)goto error;struct uci_ptr ptr;if ((uci_lookup_ptr(ctx, &ptr, o, true) != UCI_OK) || !(ptr.flags & UCI_LOOKUP_COMPLETE)) {
goto error;}char *value = calloc(1, strlen(ptr.o->v.string)+1);if (!value)goto error;strcpy(value, ptr.o->v.string);uci_free_context(ctx);free(o);return value;error:if (ctx) uci_free_context(ctx);//MEM_FREE_IF_VALID(o);if(o) free (o);return NULL;
}
//add by yaojianchen
char *uci_getromvalue(const char *key)
{
char *o = calloc(1, strlen(key)+1);if (!o)return NULL;strcpy(o, key);struct uci_context *ctx = uci_alloc_context();if (!ctx)goto error;uci_set_confdir(ctx, "/rom/etc/config");struct uci_ptr ptr;if ((uci_lookup_ptr(ctx, &ptr, o, true) != UCI_OK) || !(ptr.flags & UCI_LOOKUP_COMPLETE)) {
goto error;}char *value = calloc(1, strlen(ptr.o->v.string)+1);if (!value)goto error;strcpy(value, ptr.o->v.string);uci_free_context(ctx);free(o);return value;error:if (ctx) uci_free_context(ctx);//MEM_FREE_IF_VALID(o);if(o) free (o);return NULL;
}int uci_setvalue(const char *key, const char *value)
{
struct uci_context *uctx = uci_alloc_context();struct uci_ptr uptr;char option_s[128] = {
0};int rc = 0;snprintf(option_s, 127, "%s=%s", key, value);if (uci_lookup_ptr(uctx, &uptr, option_s, true) != UCI_OK)rc = -1;else {
uci_set(uctx, &uptr);uci_save(uctx, uptr.p);uci_commit(uctx, &uptr.p, false);rc = 0;}uci_free_context(uctx);return rc;
}int uci_addkey(const char *conf_file,const char *key)
{
struct uci_context *ctx = uci_alloc_context();struct uci_package *p = NULL;struct uci_section *s = NULL;int ret=0;uci_load(ctx, conf_file, &p);ret = uci_add_section(ctx, p, key, &s);uci_save(ctx, p);uci_commit(ctx, &p, false);uci_free_context(ctx); return ret;
}int uci_delkey(const char *key)
{
struct uci_context *uctx = uci_alloc_context();struct uci_ptr uptr;char option_s[128] = {
0};int rc = 0;snprintf(option_s, 127, "%s", key);if (uci_lookup_ptr(uctx, &uptr, option_s, true) != UCI_OK)rc = -1;else {
uci_delete(uctx, &uptr);uci_save(uctx, uptr.p);uci_commit(uctx, &uptr.p, false);rc = 0;}uci_free_context(uctx);return rc;
}