当前位置: 代码迷 >> 综合 >> 无线专题 openwrt feeds、web框架luci(lua语言)、UCI (统一配置接口)
  详细解决方案

无线专题 openwrt feeds、web框架luci(lua语言)、UCI (统一配置接口)

热度:48   发布时间:2023-10-27 10:29:39.0

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;
}