成功将应用作为系统服务开发出来后,可以在控制台自由地restart、stop来启动或停止服务,的确是件不错的事情。但是问题来了,如果我们不仅仅能在路由器控制台里使用该应用,也可以使用openwrt的web管理界面来启动或停止该应用并且获取应用的状态(比如获取校园网余额和当前使用流量等),那岂不是更方便、更直观?所以后来我决定在luci上开发一个模块,使得我们能够在luci中设置该应用的参数,重启或停止该应用。
下面记录一下开发luci模块的一些要点,具体操作当然最好上luci的wiki去学习。
一、luci目录下几个重要目录的意义(/usr/lib/lua/luci)
1、controller,luci上各个选项卡对应的模块定义在这个目录下;
2、model/cbi,luci中一些模块的CBI文件,这些文件中定义了luci模块与uci配置文件的映射,使得luci在web界面上将config文件的各个option渲染成选择框、输入框等等,这样,点击了‘应用’后config文件就会相应地被更改,具体见下文;
3、view,luci中的html模板都放在此处,模板中还可以嵌入lua脚本。
二、luci module的新建。
new一个新的luci模块并不难,假设我们将应用放在最顶层,那么就在controller目录admin目录下下新建一个以应用名命名的lua脚本,沿用上一篇文章的假设:cnipclient。那么新建一个cnipclient.lua文件,在文件中添加如下代码:
module("luci.controller.admin.cnclient", package.seeall)
function index()
end
第一行是luci用来确保该模块的文件路径正确并生成模块的作用域的。而index函数则定义了点击该模块的行为。
三、luci模块actions的几种方法
在上述所说的index函数内,需要添要entry函数,这个函数定义了luci选项卡中各顶层选项和子选项的url和action,其原型如下:
entry(path, target, title=nil, order=nil)
path定义了url,我们这里填入{“admin”, “cnclient”},那么在url中会显示为…../cgi-bin/luci/admin/cnclient,target则定义了点击这个选项的action,title定义的是这个选项卡的标题,order则表示该选项在同一层的排列顺序,数字越小,优先级越高。
action主要有四种,拿我们的应用来举例,文件controller/cnipclient如下所示(代码中entry的属性leaf设置为true则指明该选项是一个子选项):
module("luci.controller.admin.cnclient", package.seeall)
function index()
entry({"admin", "cnclient"},
alias("admin", "cnclient", "help"),
"CNClient", 24)
entry({"admin", "cnclient", "help"},
template("cnclient/help"),
"CNClient的使用说明", 10).leaf = true
entry({"admin", "cnclient", "setting"},
cbi("cnclient/setting"),
"设置CNClient的参数", 20).leaf = true
entry({"admin", "cnclient", "restart"},
call("restart_cnclient"),
"重启CNClient服务", 30).leaf = true
end
function restart_cnclient()
luci.util.exec("/etc/init.d/cnclient restart &")
luci.http.redirect(luci.dispatcher.build_url("admin", "cnclient"))
end
1、别名:alias函数,这个用在父选项上,这些父选项底下有子选项,当我们点击父选项时,相当于点击了它所定义的path,这里是…../cgi-bin/luci/admin/cnclient/help;
2、渲染模板:template函数,参数是模板文件路径,函数将去寻找luci/view(模板目录)下该模板文件,然后渲染它,这里我们用来渲染我们的应用的帮助网页模板;
3、cbi模型:cbi函数,参数是cbi文件路径,函数将去luci/model/cbi目录下寻找该cbi文件,然后根据cbi文件(lua脚本)所返回的与uci配置文件相关联的映射类实例Map来对网页进行渲染,并且操作会影响到uci配置文件的改动,这里我们将cbi模型与我们的应用的配置文件(/etc/config/cnipclient)相关联,从而能够在web上修改配置文件;
4、调用函数:call函数,它将调用当前作用域中的函数,这里我们用来调用我们的重启应用函数。
其中,模板有自定义的模板语法,可以嵌入lua脚本,cbi则有几个需要熟悉的类,luci的github文档上都写得相当清楚。
四、显示应用的状态信息。
我们可以查看view目录下的admin_status目录下的index.htm模板的源码。一开始有一个<% %>标签包含了lua脚本,这个脚本是luci在后台获取路由器信息然后将这些信息以json格式发送给客户端;接下来是js脚本,这里是客户端接收服务器传来的json数据,使用XHR.poll不断更新页面的数据,使得客户端能够获取实时路由器数据。接下来就是html代码。当然,进路由器查看该文件比我啰嗦要好得多哈。
在这里,我们需要在lua脚本区域写上获取我们应用的状态信息(登录成功与否、余额等等),在html区域添加我们要显示的div,在js脚本区域编写使用后台传来的数据修改我们的div的显示的代码。这样我们的实时数据就可以显示在我们luci的状态页面上了。当然,也可以将其独立出来放置于我们的应用的父选项之下,只需要在controller/cnipclient.lua中的index函数添加一个entry即可。
通过以上知识,我最终将我们的应用的luci界面也编写出来了。到此,我的openwrt折腾记录告一个段落,以后有时间我也会小小修补完善一下。这个应用花了我不少的时间,但是我也从中受益匪浅,折腾才能学到东西嘛。最后附上该应用的github地址:Lua pppoe版本、Python版本