LUCI 这个在百度上搜索除了一篇我的百度文库luci的介绍文章之外,前三页都是些不知所云的名词(足见百度在专业领域的搜索之烂),我却在大学毕业的大半年的大部分时间里与它纠结,由于开始的发懵到后来逐渐感觉到这家伙还很好玩的,现在就把我对luci的浅显认识介绍给大家。
官网:http://luci.subsignal.org/
有关luci的各个方面,你几乎都可以从这里获得,当然,只是浅显的获得,luci的文档写的还算比较全,但是写的稍显简略,开始看的时候会有一点不知所措。
UCI 熟悉openwrt的人都会有所了解,就是Unified Configuration Interface的简称,而luci这个openwrt上的默认web系统,是一个独立的由严谨的德国人开发的web框架,是Lua Configuration Interface的简称,如果在您的应用里,luci是对openwrt的服务,我们就有必要做一下uci的简介,我这里就不说了,见链接:
http://www.google.com.hk/url?sa=t&source=web&cd=5&ved=0CEMQFjAE&url=http%3A%2F%2Fnbd.name%2Fopenwrt-fosdem-09.pdf&ei=h52iTcXvOcrMcJ-xxOwD&usg=AFQjCNGFhumCIgS5tK_mDJ2dDFU4qsskfQ
有的时候,我们开发的luci是在自己的Linux PC上开发,在普通的linux上,一般是没有uci命令的,为了开发方便,可以手动编译一下,方法见链接:
https://forum.openwrt.org/viewtopic.php?id=15243
OK ,之前罗里罗嗦的说了很多,现在就进入正题,进入正题的前提是你已经make install正确的安装了lua ,luci,以及编译好链接了相关的so(如果你需要,比如uci.so nixio.so),以及make install正确web server,(我用的web server是thttpd,也编译过mongoose,lighttpd,在这三个之中,lighttpd是功能最完善的,mongoose是最小巧的)。
进入正题:
一:luci的启动
在web server中的cgi-bin目录下,运行luci文件(权限一般是755),luci的代码如下:
#!/usr/bin/lua --cgi的执行命令的路径require"luci.cacheloader" --导入cacheloader包require"luci.sgi.cgi" --导入sgi.cgi包 luci.dispatcher.indexcache = "/tmp/luci-indexcache" --cache缓存路径地址 luci.sgi.cgi.run() --执行run方法,此方法位于*/luci/sgi/cgi.lua中
run方法的主要任务就是在安全的环境中打开开始页面(登录页面),在run中,最主要的功能还是在dispatch.lua中完成。
运行luci之后,就会出现登录界面:
-bash-4.0# pwd /var/www/cgi-bin -bash-4.0# ./luci Status: 200 OK Content-Type: text/html; charset=utf-8 Cache-Control: no-cache Expires: 0 /*some html code*/
如果你成功的运行了luci 就说明你的luci框架成功的跑了起来。
二:LUCI的MVC
1:用户管理:
在luci的官方网站说明了luci是一个MVC架构的框架,这个MVC做的可扩展性很好,可以完全的统一的写自己的html网页,而且他对shell的支持相当的到位,(因为luci是lua写的,lua是C的儿子嘛, 与shell是兄弟)。在登录界面用户名的选择很重要, luci是一个单用户框架,公用的模块放置在*/luci/controller/下面,各个用户的模块放置在*/luci/controller/下面对应的文件夹里面,比如 admin登录,最终的页面只显示/luci/controller/admin下面的菜单。这样既有效的管理了不同管理员的权限。
2:controller文件夹下的lua文件说明:(以mini用户为例)
在mini目录下面,会有一个index.lua文件,简略的代码如下:
module("luci.controller.mini.index", package.seeall) 17 18 function index() 19 luci.i18n.loadc("admin-core") 20 local i18n = luci.i18n.translate 21 22 local root = node() 23 if not root.lock then 24 root.target = alias("mini") 25 root.index = true 26 end 27 28 entry({"about"}, template("about")).i18n = "admin-core" 29 30 local page = entry({"mini"}, alias("mini", "index"), i18n("essentials", "Essentials"), 10) 31 page.i18n = "admin-core" 32 page.sysauth = "root" 33 page.sysauth_authenticator = "htmlauth" 34 page.index = true 35 36 entry({"mini", "index"}, alias("mini", "index", "index"), i18n("overview"), 10).index = true 37 entry({"mini", "index", "index"}, form("mini/index"), i18n("general"), 1).ignoreindex = true 38 entry({"mini", "index", "luci"}, cbi("mini/luci", {autoapply=true}), i18n("settings"), 10) 39 entry({"mini", "index", "logout"}, call("action_logout"), i18n("logout")) 40 end 41 42 function action_logout() 43 luci.http.header("Set-Cookie", "sysauth=; path=/") 44 luci.http.redirect(luci.dispatcher.build_url()) 45 end
这个文件定义了node,最外面的节点,最上层菜单的显示等等。在其他的lua文件里,定义了其他菜单的显示和html以及业务处理路径。每个文件对应一个菜单相。
例如 system.lua文件
function index() 19 luci.i18n.loadc("admin-core") 20 local i18n = luci.i18n.translate 21 22 entry({"mini", "system"}, alias("mini", "system", "index"), i18n("system"), 40).index = true 23 entry({"mini", "system", "index"}, cbi("mini/system", {autoapply=true}), i18n("general"), 1) 24 entry({"mini", "system", "passwd"}, form("mini/passwd"), i18n("a_s_changepw"), 10) 25 entry({"mini", "system", "backup"}, call("action_backup"), i18n("a_s_backup"), 80) 26 entry({"mini", "system", "upgrade"}, call("action_upgrade"), i18n("a_s_flash"), 90) 27 entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100) 28 end
mudel是对应文件的,function index定义了菜单,比如这一句entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100)
第1项为菜单入口:
{"mini", "system", "reboot"},mini是最上层的菜单,即为用户项,system为一个具体的菜单,reboot为这个菜单的子菜单,如果reboot还需要加上子菜单的话,可以这样写:
entry({"mini", "system", "reboot","chreboot"}, call("action_chreboot"), i18n("chreboot"), 1),这样就会在reboot上产生一个新的子菜单,以此类推,可以产生N层菜单。
第二项为菜单对应的页面,可以是lua的源代码文件,也可以是html页面。
alias cgi form call 等定义了此菜单相应的处理方式,form和cgi对应到model/cbi相应的目录下面,那里面是对应的定制好的html和lua业务处理。
alias是等同于别的链接,call调用了相应的action_function。还有一种调用,是template,是直接链接到view相应目录下面的htm页面。(说明:luci框架下面的htm都是可以嵌入lua语句的,做业务处理,相当于jsp页面的内部的Java语句)。
问价查找对应简介:
entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100) :对应此文件的action_reboot function
entry({"mini", "system", "index"}, cbi("mini/system", {autoapply=true}), i18n("general"), 1):对应*/model/cbi/mini/system.lua {autoapply=true} 这个失传的参数。
。。。。。
第三项为i18n显示,比如entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100),菜单的名字为admin-core文件内的对应显示。此处也可以这样写, i18n("reboot","重启"),即直接做了国际化。菜单上显示的就是“重启”。
第四项为现实的顺序,这个数字越小,显示越靠前,靠上。
<%:skiplink1 Skip to navigation%> <%:skiplink2 Skip to content%>
<%=luci.version.distversion%>
<%:load%>: <%="%.2f" % load1%> <%="%.2f" % load5%> <%="%.2f" % load15%>
<%:hostname%>: <%=hostname%>
3:model业务处理和页面生成简介
我认为model的业务处理和html生成,是luci框架的精华,但是想用好它,最终扩展定义自己的页面也是最难的,但是一旦定义好了,后面的工作就会轻松高效简介统一,不失为一种好的解决方案。但是它又有缺点,就是写页面虽然统一,但是不够灵活。
下面以SimpleForm为例,讲解一下。
具体文件 */luci/model/cbi/passwd.lua
f = SimpleForm("password", translate("a_s_changepw"), translate("a_s_changepw1")) --调用SimpleForm页面 当然还是I18N从中捣乱,看上去没那么直观,不理他
pw1=f:field(Value,"pw1",translate("password")) -- SimpleForm 里面加一个field 至于SimpleForm 和 fiemd是什么,一会去看SimpleForm页面去
pw1.rmempty=false -- 把SimpleForm的rmempty为不显示 后面就不做注释了 应该看得懂了
pw2 = f:field(Value, "pw2", translate("confirmation"))
pw2.rmempty = false
function pw2.validate(self, value, section)
return pw1:formvalue(section) == value and value
end
function f.handle(self, state, data)
if state == FORM_VALID then --这个就是业务处理了 你懂得 呵呵
local stat = luci.sys.user.setpasswd("admin", data.pw1) == 0 -- root --> admin
if stat then
f.message = translate("a_s_changepw_changed")
else
f.errmessage = translate("unknownerror")
end
data.pw1 = nil
data.pw2 = nil
end
return true
end
return f
说明:( simpleForm 位于view/cbi 下面,可以研究一下,各个元素是如何定义的)
现在在给一个小例子:
以.*/luci/model /cbi/admin_system/version_manage.lua为例,介绍一下luci中web页面lua代码
6 local h = loadfile("/usr/local/luci/help.lua")
7 if h then
8 h()
9 end
10 local help_txt = help_info and help_info.version
14 versionlist = {}
15
16 function getline (s)
.........
32 end
33
34 function get_versionlist()
.........
68 end
69
70 versionlist = get_versionlist()
71 m = SimpleForm("version", translate("版本管理"))
72 m.submit = false
73 m.reset = false
74 m.help = help_txt and true or false
75 m.helptxt = help_txt or ""
t = {
row1 = {column1 = "xxx", column2 = "xxx", .... },
row2 = {column1 = "xxx", column2 = "xxx", .... },
row3 = {column1 = "xxx", column2 = "xxx", .... },
row4 = {column1 = "xxx", column2 = "xxx", .... },
}
79 enable = s:option(DummyValue, "_enabled", translate("软件状态"))
83 appid = s:option(DummyValue, "_appid", translate("软件版本"))
84 appname = s:option(DummyValue, "_appname", translate("软件名称"))
127 newinfo = up_s:option(TextValue, "_newifo", translate("新版本信息"))
128 newinfo.readonly = true
129 newinfo.rows = 11
130 newinfo.cfgvalue = function(self, section)
131 local t = string.gsub(info, "Archive:[^/n]*", "")
132 return t
133 end
88 up_s = m:section(SimpleSection)
89 up_version = up_s:option(Button, "_up_version", translate("上传新版本"))
90 up_version.onlybutton = true
91 up_version.align = "right"
92 up_version.inputstyle = "save"
93 up_version.write = function(self, section)
94 luci.http.redirect(luci.dispatcher.build_url("admin", "system", "version_manage", "upload"))
95 end
4:view下面的html简介
这个是最好理解的 例:passwd.htm
<%+header%>
<%:system%>
<%:reboot%>
<%:a_s_reboot1%>
<%-
local c = require("luci.model.uci").cursor():changes()
if c and next(c) then
-%>
<%:a_s_reboot_u%>
<%-
end
if not reboot then
-%>
<%- else -%>
<%:a_s_reboot_running%>
<%- end -%>
<%+footer%>
说明一下一些元素的意义:
<%+header%> <%+footer%> 加载公用的头部和尾部
<% lua code%>
<%:i18n%>
<%lua code%>
<%=lua 变量%>
最后:写的很乱,有些大概只有我自己看得懂,要是有高手或是同仁想交流,我的mail:[email protected]