zhgaiym 发表于 2016-3-14 09:40

【资料】+openwrt后台luci界面修改

本帖最后由 奋斗丶小Z 于 2016-3-14 16:29 编辑

OpenWRT Luci界面的简单删改
主要讲述luci,还有如何删改luci界面,不涉及页面样式的改变还有,后台的交互
需要知识,lua语言,html,css,uci,linux基本操作
Luci的简单介绍
Luci是openwrt默认的web界面,但我们一开始下载的trunk或者其他版本的开发包都是不包含luci的。我在官网下载的是openwrt-master,以下简称openwrt。既然openwrt默认是没有luci的,我们需要先下载luci。根据官网luci的开发文档,我们可以在openwrt/下 执行
$ ./scripts/feeds update -a
更新列表完之后,执行
$ ./scripts/feeds install -a
也可以把-a 换成luci 这样就会只升级luci,安装luci。
!!在openwrt开发的时候必须用普通用户,不能用root用户去操作。
当我们要修改luci源码的时候 主要是在以下文件夹里面修改

而由于我们在编译的时候主要是使用默认的luci界面(luci有4种web风格去选择)
所以我们只需要修改luci-base和luci-mod-admin-full目录下面的文件。
但是我们可以先直接修改路由器后台的文件,这样就可以更快地预览修改效果。
二Luci架构的简单介绍
Luci采用的是MVC架构的web框架.M是指moduel(有的源码包是model),C指的是controller.V指的是view.Luci 其实是Lua脚本语言和UCI统一配置接口的合称.就是说原本openwrt就提供里uci的api让你可以很方便的修改openwrt的配置文件,无论是创建,读取,修改,删除.而且uci命令可以植入shell脚本和c语言或者是lua脚本.lua脚本由于比较小,运行速度是c语言的1/30左右.所以你会发现在/usr/lib/lua/luci下大多数都是lua脚本,就是这些lua脚本生成我们所看见的web页面.
我们主要修改/usr/lib/lua/luci/controller和/usr/lib/lua/luci/model/cbi下面的lua脚本就可以修改页面啦.
当我们在浏览器访问192.168.1.1的时候,我们首先是访问/www/index.htm,然后它的<meta>标签写明是立刻跳转到/www/cgi-bin/luci(luci这个是一个脚本,是用于启动/usr/lib/lua/luci下的lua脚本).这时有一个应该是dispatcher.lua或者是index.lua的脚本进行对controller目录下面的所有lua脚本进行检查入口函数并生成一个主页面,包含了导航栏header列表,logout,底部footer.而controller就是定义入口函数entry()还有调用cbi() model下的lua脚本或者是template()调用view下的htm模板.controller的作用就是进行调用控制等等.就是说model是进行业务处理,例如当用户输入帐号密码的时候,会把/etc/config下面的配置文件进行修改.而如果controller调用的是view的htm模板就直接显示出来.
Luci就是通过lua脚本进行对htm模板进行调用和组合人那后生成一个静态的html文件让我们可以访问.
三 进入路由器后台修改luci文件
首先我们用的是
$ ssh root@192.168.1.1
进行对路由器的连接,可以用有线或无线的方式连接路由器,但注意使用无线连接前要确保路由器已开启无线。
Openwrt只能有一个root用户 而且大多数路由器的默认IP地址是192.168.1.1,所以$ ssh hostname@IP 我们就写成$ ssh root@192.168.1.1。但是进行远程连接的时候有时候会出现

我们进入到 .ssh/目录下面 删除know_hosts文件或者是删除里面的内容.
当我们成功连接openwrt后台之后会出现

这就是openwrt的后台目录
而luci的文件主要放在/www下和/usr/lib/lua/luci下
/www目录下放的除了index.htm和cgi-bin/luci脚本以外,还存放着,导航栏header.htm和footer.htm还有一些css文件,javascript文件,icons,gif等图片.我们可以对/usr/lib/lua/luci/view下面的htm模板进行删改,但是如果要改样式就可以从/www下的css文件进行样式修改.
四 一步一步修改LuCi
第一步,我们先修改第一个见到的界面.如果学过前端的我们知道,当我们访问一个服务器的时候,我们先是访问index.html(由于luci使用的是htm后缀,以下都会使用htm后缀说明,这是htm版本的不一样).luci的index.htm在/www/下面.
我们看到index.htm<head>标签里面是一个这样的元素
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
这是定向到/www/cgi-bin/luci脚本,这个脚本就开始调用其他文件的lua脚本去生产我们所看到的页面.这个是htm页面,所以我们可以直接像对htm文件一样修改这个页面.
修改如下,当我们访问路由器的时候,就看见一个比较大的LCE链接
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
<style type="text/css">
body {
      width: 100%;
}
div {
      margin-left: 15%;
      text-align: center;
      text-decoration: none;
      width: 70%;
      padding-top: 200px;
}

a {
      text-decoration: none;
      font-size: 200px;
      font-family: arial,helvetica,san-serif;
      color: #808080;
}
</style>
</head>                                                                                                            
<body style="background-color: white">
<div>
<a href="/cgi-bin/luci">L C E</a>                  
</div>
</body>                                                                                                            
</html>
第二步:当页面跳转完之后,就到了登录界面.登录界面主要只有输入root用户的密码.因为openwrt是单用户系统,所以就只有root用户.通过寻找文件,我们发现登录界面调用的是/usr/lib/lua/luci/view/sysauth.htm.既然openwrt是单用户系统,其实你会发现,用户名那一栏是不能修改root用户名的,也就是说其实我们不要用户名也可以.所以为了简洁,我们把用户名一栏去掉.还有把reset按钮也去掉.reset因为我们不需要用到,所以其实可以直接把htm里面的<input><%:reset%></input>一行删掉.但是用户名不能这么做.因为你登录时是需要验证用户名跟密码的,也就是说它要验证用户名是root,密码还要正确,才能登录进去.所以,我们还要保证root这个用户名可以发送给系统进行处理.我们可以把包含用户名的<div></div>标签加上<div style=”display:none”></div>,就是把用户名给隐藏掉.
第三步:首先,我们先学习学习别人是如何添加njit client页面的,快速学习.
http://www.cnblogs.com/mayswind/p/3468124.html 学习链接,源码可以查看luci-app-h3c文件夹里面的内容.那么简单点来说,第一步需要在controller/admin/下面写一个lua脚本用于解释,你创建的页面要出现在luci页面的哪里?标题是什么?层次在哪里?你要调用什么?(函数,model下的lua,还是view的htm模板).
module("luci.controller.admin.network", package.seeall)      //这个是必须要有的,说明了你的lua脚本是在哪里,它生成页面时,是需要检查你这句话先的.然后
Function index()
Entry({“admin”, “network”, “h3c”}, cbi(“h3c”), _(title), order)
End
这里就写了,我们要在admin(一整个网页)下面的network列表下面添加一个h3c的页面.然后网页上面显示的标题,还有order顺序.这个顺序是network这个列表下面的顺序,如果把”network”删掉,这时候,h3c就出现在一级目录,这时候的顺序就是在一级目录的顺序.
而cbi的意思是调用/usr/lib/lua/luci/model/cbi/下面的lua脚本
Call()就是调用下面定义好的函数
Template()就是调用/usr/lib/lua/luci/view/下面的htm文件,但要参考其他的entry函数去修改路径.
Post()貌似跟call作用差不多,但是在breaker这个开发环境下面使用的call(),所以openwrt-master和trunk这两个版本的luci是不能直接替换breaker的luci的,不然他会提示post这个函数还没有定义.
好了,当我们搞定luci页面的目录问题,之后我们总要我们的页面显示东西吧,而我们显示的东西不是在controller/amdin下面的lua脚本定义的,而是在model/cbi/的lua脚本或者是view/htm模板定义的.
我们还是以njit client的例子来说明如何修改页面,我们看看model/下面的h3c.lua
require("luci.sys")

m = Map("h3c", translate("校园网认证"), translate("请输入你的校园网帐号和密码."))

s = m:section(TypedSection, "login", "")
s.addremove = false
s.anonymous = true

name = s:option(Value, "username", translate("Username"))
pass = s:option(Value, "password", translate("Password"))
pass.password = true

local apply = luci.http.formvalue("cbi.apply")
if apply then
      io.popen("sh /etc/init.d/njitclient")
end

return m

第一句还是要有的,因为lua语言的引用就是require(“xxx”),所以第一句话就是说包含luci/sys.lua
第二句,就是你要修改/etc/config/h3c这个文件,然后页面的h2标题是校园网认证,其实写英文也可以,不过就要在po/zh-cn添加一个翻译.不过我们一开始就弄了个默认中文页面,但是最好一开始修改还是使用英文页面,因为没有翻译过的页面可以让你更快的找到你想要的代码.
然后就是说明你要修改哪个section.

config login
      option username 'root'
      option password 'admin'
Openwrt下面的config文件的格式是这样的,
但还有的是这样的
config login h3c
      option username 'root'
      option password 'admin'
这两个有什么区别呢?区别在于s = m:section(TypedSection, "login", "")
这一句怎么写,如果是第一个section,就要用TypedSection.这就是告诉系统,你要根据type去找一个叫login的section,然后再来一个说明””,这个说明是出现在luci界面上面的.但是在config下面
Network这个config文件它里面的section是这样的
Config interface wan
Option ipadder “”

Config interface lan
Option dns “”
很明显,如果你还是用typesection,系统就会找到两个section,这样luci上面就会出现2个或多个输入框.这时候我们需要使用的是NamedSection
s = m:section(NamedSection, "wan", "")
来选择wan这个section
name = s:option(Value, "username", translate("Username"))
pass = s:option(Value, "password", translate("Password"))
pass.password = true
然后这两句就是选择对应的option进行修改,但是要把密码变成*******
local apply = luci.http.formvalue("cbi.apply")
if apply then
      io.popen("sh /etc/init.d/njitclient")
end
然后如果按啦应用保存,就触发/etc/init.d/njitclient这个脚本,这个脚本用于启动njitclient这个程序.
最后返回m,显示整个页面.
Model下面的lua修改之后是可以直接在浏览器刷新查看改变的,但是controller下面做的修改你是需要reboot路由器才能查看变化的.
最后我们,在/etc/config/下面添加一个h3c的config文件,还要在/etc/init.d/下面添加一个njitclient的脚本.于是一个njitclient的页面就添加成功了.
五 按照njitclient这个例子,继续修改其他页面
我们要隐藏一些页面,一个比较简单的方法就是把入口函数注释掉
      entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
--      entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 2)
--      entry({"admin", "system", "clock_status"}, post_on({ set = true }, "action_clock_status"))
这样及注释掉系统这个二级目录还有clock_status这个目录,lua的注释方法
行注释 --
块注释 --[[ ]]--
还有一种方法就是直接删掉controller/admin/下面的lua脚本,例如删i到status.lua,那么status整个目录就不会出现,并且还不出现错误,为什么要删掉status而不是其他呢?因为status主要显示整个路由器的各种信息,但是这些信息采集函数并不是status定义或者调用的.就是说直接删掉status.lua并不影响系统进行数据采集的.也不会影响路由器的正常功能.但是system.lua和network.lua就不太建议直接删,例如当把network.lua里面过多的入口函数给注释掉会出现一个问题,不能采集数据,这样,如果程序需要某些数据就不能正常运行.因为我们说过,entry函数里面其实是可以使用call()来调用下面的function的.
当我们注释掉一些入口函数之后,页面就会变得简单,但是最好还是注释掉,而不是直接删掉entry函数,因为有时我们还是需要查看其他entry函数是怎么写的,调用了哪些文件,调用了哪些函数.
六 把其他页面的功能移过来
我觉得network这个目录设置的东西太多太复杂,于是我把network删掉.但是我还要保留network下面的wireless下面修改SSID还有加密方式,还有密码的设置.
page = entry({"admin", "network", "wireless_join"}, post("wifi_join"), nil)
page.leaf = true

page = entry({"admin", "network", "wireless_add"}, post("wifi_add"), nil)
page.leaf = true

page = entry({"admin", "network", "wireless_delete"}, post("wifi_delete"), nil)
page.leaf = true

page = entry({"admin", "network", "wireless_status"}, call("wifi_status"), nil)
page.leaf = true

page=entry({"admin", "network", "wireless_reconnect"}, post("wifi_reconnect"), nil)
page.leaf = true

page=ntry({"admin", "network", "wireless_shutdown"}, post("wifi_shutdown"), nil)
page.leaf = true

page=entry({"admin","network","wireless"},arcombine(template("admin_network/wifi_overview"), cbi("admin_network/wifi")), _("Wifi"), 15)
page.leaf = true
page.subindex = true
分析controller/admin/network.lua下面这段代码,还有页面上面显示,我们应该需要的是wifi_add这个入口函数,调用了wifi_add这个函数,然而并没有什么用.但是我们在/model/cbi/admin_network/下面可是看到wifi这个lua脚本.我们搜索一下可以找到修改ssid还有加密方式的,还有密码的section写法,只要我们写添加到h3c的/model/cbi/h3c.lua下面,这样njitclient页面就会出现修改SSID还有加密方式的输入框.我们还是需要cat一下/etc/config/wireless发生了什么改变来确定我们要怎么写,要有验证究竟有没有修改成功.
修改完成之后,我们已经可以不需要network这个目录了,但是我们说过,如果直接删掉,它里面的一些函数就没有被调用.这样也会造成有一些数据采集不了,这样也会造成有一些程序不能正常运行.例如njitclient这个程序需要只要我们开wifi使用了那一个接口,虽然我们知道是eth.0这个接口,但是由于数据收集不到这个接口,也就没有写入到config的文件里面,所以程序就找不到我们究竟用了那一个接口,也就导致程序运行不了.
但是我们可以把network剩余的entry函数还有下面的function全部移到system的lua脚本下面.这样页面还是能够正常显示的,但是注意entry里面network要换成system,不然又会说找不到network这个目录.
七 这个有点特殊,我们需要查看njitclient的运行日志
但是我们暂时学到的都是如何用luci去修改config文件,就是说除此之外,我们似乎没什么办法查看其他文件.但是既然我们都是要查看日志文件,那么就有一个很好的例子让我们参考,那就是system log这个页面.
这个页面只是简简单单的一个标题system log然后一个文本框显示系统日志.
但是,原来还是不怎么简单的.
entry({"admin", "status", "syslog"}, call("action_syslog"), _("System Log"), 4)
这条entry函数 就是status.lua里面的system log的入口函数
Call(“action_syslog”)//调用下面一个叫action_syslog的函数
很明显就是要读取system log啦
function action_syslog()
      local syslog = luci.sys.syslog()
      luci.template.render("admin_status/syslog", {syslog=syslog})
End
但是这么少东西怎么调用呢?
我们说过,包含一个lua文件是 require “luci.sys”
那么包含之后要用啊,luci.sys.syslog()
这个就是调用了luci/sys.lua 下面的一个叫syslog()的函数





function syslog()
      return luci.util.exec("logread")
End
我们找到之后发现还是很短,因为它还是再调用函数.调用/luci/util.lua下面的exec()函数
function exec(command)
      local pp   = io.popen(command)
      local data = pp:read("*a")
      pp:close()

      return data
End
原来是输入一条命令,这么简单,好吧我们google一下发现logread原来是linux下面一个读取系统日志的命令.
那我们把logread修改成ls试试,发现luci上面system log真的显示了ls出来的内容,那就是说我们就改改那条命令就可以咯.
那有没有读取其他日志的命令呢?不好意思,日志本身就是文本,并没有这样的命令,但是我们可以修改成 cat /tmp/log/text.log
好吧,这样system log出现的就是text.log的内容.
于是我们按照system log的entry函数还有template的htm模板,我们写成一个njitclientlog.htm还有一个njitclient log的entry函数.
于是luci上面就出现一个njitclient log的页面啦.
八 总结
现在我们学会了如何添加页面,如何添加页面里面的输入框等等,还通过注释隐藏掉我们不想要的页面,还学会用一个命令读取了日志,还可以使用template()调用一个页面来添加一个我们想要显示东西的页面(不过这个页面好难和系统进行数据交互,只是我们修改起来方便很多,不需要使用luci的模板).
搞完这些如果你还想进一步修改luci界面,可以通过修改css文件,位于/www/luci-status/resources/cbi/下面的css文件,还有一些图片.
以上是添加删除luci的一些文档说明,样式的修改还是需要修改css文件还有javascript文件.
至于model下面lua脚本可以添加的东西请参看下面.
class Map (config,title,description)
–这个就是我们必须先要的加入的一个map 最后需要return的。
classNamedSection?(name,type,title,description)
–可以按照名字选择的section
classTypedSection?(type,title,description)
–根据类型选择的section
class Value (option,title,description)
–最常用的一个元素,就是普通的输入框
classListValue?(option,title,description)
–毫无疑问这是一个下拉框
class Flag (option,title,description)
–这是一个check,即一个选择框,可以勾选或者not
classMultiValue?(option,title,description)
–多个选择框
classDummyValue?(option,title,description)
–这个啥都没有,就只输出文字
classTextValue?(option,title,description)
–这个就是平常的memo了,即一个文字输入区域了。
class Button (option,title,description)
–按钮,点击就可以提交表单,可以检测该元素来执行不同动作。
classDynamicList?(option,title,description)

这些并不完整,也并不描述得准确,但是你是可以一条条尝试添加,看是有什么效果的.但是主要用到Map,NamedSection,TypeSection,Value.
你也可以参考其他的lua文件是怎么写的.
还有就是关于网页根目录,当我们启动啦/www/cgi-bin/luci这个脚本的时候,可能就生成一个网页根目录,就是说网页能访问的东西只能是网页根目录下面的东西,也就是/www这个文件夹,你会发现网页的根目录就是www,在你调用的htm里面如果要引用一张图片,那么src就不能写成src=”/www/luci-status/resources/icons/lodding.gif”
而要写成src=”/luci-status/resources/icons/lodding.gif”
https://wiki.openwrt.org/zh-cn/doc/techref/uci?s[]=luci#dokuwiki__top
https://wiki.openwrt.org/doc/devel/luci
http://www.cnblogs.com/mayswind/p/3468124.html
更多官方方面的解说请参考openwrt的官方wiki.

struggle117 发表于 2016-3-14 09:47

学习了,虽然看不懂。。

andyop 发表于 2016-3-14 14:03

额,好像以前有看到过这个,不知道楼主哪里转载的。

ks5uzy 发表于 2016-10-18 14:59

参考了,很不错

9394952 发表于 2016-12-5 13:19

慢慢学习了,现在懂一点了

viron_yjq 发表于 2018-1-16 11:33

谢谢楼主分享,学习了!
页: [1]
查看完整版本: 【资料】+openwrt后台luci界面修改