修改Luci界面
參考 https://blog.csdn.net/hui523hui523hui523/article/details/38943693
參考 https://www.jianshu.com/p/bfb93c4e8dc9
參考 https://blog.csdn.net/weixin_43883277/article/details/98725690
參考 https://blog.csdn.net/weixin_43883277/article/details/99677581
參考 https://blog.csdn.net/weixin_43883277/article/details/98505104
參考 https://blog.csdn.net/weixin_43883277/article/details/99865510
Model 位於: /usr/lib/lua/luci/model/cbi/ 下——配置模塊實際的代碼
eg: module("luci.controller.控制器名/路徑", package.seeall) function index()
entry(路徑, 調用目標, _("顯示名稱"), 顯示順序)
end
entry(path, target, title=nil, order=nil)
調用目標分為三種,分別是執行指定方法Action、訪問指定頁面Views以及調用CBI Module
-
第一種可以直接調用指定的函數,比如點擊菜單項就直接重啟路由器等等,比如寫為call(“function_name”),然后在lua文件下編寫名為function_name的函數就可以調用了。
-
第二種可以訪問指定的頁面,比如寫為template(“myapp/mymodule”)就可以調用/usr/lib/lua/luci/view/myapp/mymodule.htm文件了
-
而如果要編寫配置頁面,那么使用第三種方法無非是最方便的,比如寫為cbi(“myapp/mymodule”)就可以調用/usr/lib/lua/luci/model/cbi/myapp/mymodule.lua文件了。
module("luci.controller.LuoYeLuCI", package.seeall)
function index()
entry({"admin", "network", "LuoYeconfig"}, cbi("LuoYeCBI"), _("LuoYeTest"), 100)
end
m = Map("配置文件文件名", "配置頁面標題", "配置頁面說明")
第一個參數即為配置文件存儲的文件名,不包含路徑.
第二與第三個參數則是用在來頁面上顯示的
Section分為兩種,NamedSection和TypedSection,前者根據配置文件中的Section名,而后者根據配置文件中的Section類型 http://luci.subsignal.org/trac/wiki/Documentation/CBI
文件需要存儲在/etc/config(如果配置文件不存在的話,訪問配置頁面將會報錯)
內容格式如下:
config login
option username ''
option password ''
option ifname 'eth0'
option domain ''
LuCI 頁面修改
簡單的文件配置,路由上路徑主要是/usr/lib/lua/luci/
下子目錄:/controller/
、/model/cbi/
、 /view/
,或者根目錄下的/www/
中.可以在路上修改查看效果.
如果想要編譯自定義LuCI頁面的固件,請嘗試修改如下OpenWRT源碼結構路徑內的LuCI文件.
xxx/package/feeds/luci/luci/luci/libs/web/root/etc/config/luci --- AA 版本pakages/feeds/luci/中 xxx\feeds\luci\luci\luci\libs\web\root\etc\config\luci --- AA 版本的feeds/luci文件夾中 xxx/feeds/luci/modules/base/root/etc/config/luci --- BB 版本中feeds中,bb版本open修改了luci配置文件路徑,並且pakages/feeds路徑中也沒有luci配置文件了.不知道這個路徑是不是正確的. 再者就是修改 xxx/dl/ 下的源碼壓縮包,或者 xxx/build_dir/$target/下源碼
注意:如果在xxx/feeds 修改可能需要執行 ./scripts/feeds install luci 更新
主題Logo替換
源碼路徑:xxx/feeds/luci/luci/luci/themes/bootstrap/htdocs/luci-static/bootstrap/logo.jpg
路由路徑:/www/luci-static/bootstrap/logo.jpg
Tips:
由於版本的更新,文件路徑可能變更,此處列出的為BB版本的例子.如果找不到可以用命令手動在/feeds/中查找:find ./ -name logo.jpg.此處為bootstrap主題Logo,其他主題的Logo修改類似.
頁面腳標信息
源碼路徑:xxx/feeds/luci/luci/luci/themes/bootstrap/luasrc/view/themes/bootstrap/footer.htm
路由路徑:/usr/lib/lua/luci/view/themes/bootstrap/footer.htm
修改位置:
Powered by <%= luci.__appname__ .. " (" .. luci.__version__ .. ")" %> <%=luci.version.distversion%>
status狀態欄信息
源碼路徑:xxx/feeds/luci/modules/admin-full/luasrc/view/admin_status/index.htm
路由路徑:/usr/lib/lua/luci/view/admin_status/index.html
修改位置:找到類似的代碼段修改.
<%:System%> <%:Hostname%><%=luci.sys.hostname() or "?"%> <%:Model%><%=pcdata(model or "?")%> <%:Firmware Version%> <%=pcdata(luci.version.distname)%> <%=pcdata(luci.version.distversion)%> / <%=pcdata(luci.version.luciname)%> (<%=pcdata(luci.version.luciversion)%>) <%:Kernel Version%><%=luci.sys.exec("uname -r")%> <%:Local Time%>- <%:Uptime%>- <%:Load Average%>
================
在使用OpenWrt路由器的過程中,經常需要根據需要改改配置文件然后重新啟動服務什么的,一般的做法是SSH登錄路由器后台,使用vi編輯器修改文件,然后使用/etc/init.d/xxxx restart 來重啟服務,次數多了就會覺得很繁瑣,光SSH輸入密碼就夠麻煩的,所以不妨自己寫一個luci界面在路由器web后台修改配置文件並完成重啟這一系列操作。下面以Pdnsd為例進行介紹
首先看一下實現效果
要實現一個luci界面至少需要如下三個文件
A: /etc/config/pdnsd
B : /usr/lib/lua/luci/controller/pdnsd.lua
C: /usr/lib/lua/luci/model/cbi/pdnsd.lua
其中
A的作用是存儲你在luci界面上的控件中填入的數值,比如用戶名密碼,是否可用,選擇的選項等等,是一個uci配置文件,可以使用shell的uci命令進行讀取
B的作用是通告OP系統你的自定義界面的顯示位置,相鄰排序,並指向C文件
C是最核心的文件,里面記錄了你luci頁面的控件布局,控件的顯示內容,和控件觸發事件的執行腳本
在創建每個文件之前,首先要了解一個概念,就是luci是MVC架構的,而且MVC的理念在luci上得到了最充分最明顯的闡釋,是理解MVC架構非常形象生動的例子,下面我就簡單介紹一下luci的MVC架構。
首先請看下面的兩個截圖,這是兩個不同OP的路由器安裝同一個luci ipk安裝包之后的界面,界面風格差別很大,不知道的還以為這是兩個不同的軟件。但其實不是,因為他們來源於同一個ipk安裝包,不僅代碼一模一樣,里面控件的類型和數量也是一致的。而且執行效果也是一樣的。
這上面左面應該是潘多拉固件或者DreamBox等等固件的截圖,而右面是OP默認界面的原圖,右面的加入了一些bootstrap樣式。
那為什么同樣的luci代碼會產生如此風格迥異的界面,並且自動的適配路由器當前固件的風格呢,這就是luci使用lua腳本使用MVC架構的奧義了。同時我們也知道,luci的開發不是像網頁一樣使用html,css,js進行開發,而是使用lua腳本和uci接口,下面就來仔細介紹一下luci的簡單開發過程。
首先我們創建上面必需的三個文件,做一個簡單的界面
/etc/config/pdnsd
config arguments
由於這個文件是記錄你配置好的參數的,由於我們現在並不需要記錄什么參數,所以寫個uci的開頭就夠,其中“arguments”是一個固定標志
/usr/lib/lua/luci/controller/pdnsd.lua
module("luci.controller.pdnsd", package.seeall) function index() if not nixio.fs.access("/etc/config/pdnsd") then return end entry({"admin", "services", "pdnsd"}, cbi("pdnsd"), _("Pdnsd")).dependent = true end
這個文件的含義是:首先檢查有沒有/etc/config/pdnsd文件,如果有就繼續向下執行,沒有就當什么都沒有發生過
第一行中,固定格式是“luci.controller.我的項目名”,因為我們所有的lua文件和/etc/config下面的配置文件都是以pdnsd來進行命名的,所以應該填“luci.controller.pdnsd”
倒數第二行是一個固定格式
entry(路徑, 調用目標, _("顯示名稱"), 顯示順序)
路徑:{"admin", "services", "pdnsd"} 意思是登錄用戶可見,“服務”主菜單下,“名字叫pdnsd的配置文件”,如果你想要放到“系統”菜單下,就把“services”改成“system”
調用目標:指向/usr/lib/lua/luci/model/cbi/pdnsd.lua這個控制文件
顯示名稱:顯示為“Pdnsd”,當然這里也可以填UTF-8的中文,用來在主菜單上顯示
顯示順序:這里沒填,使用系統默認的顯示順序
這個文件規定的規則顯示如下,就是下圖中的"Pdnsd"選項
/usr/lib/lua/luci/model/cbi/pdnsd.lua
local fs = require "nixio.fs" m=Map("pdnsd",translate("Pdnsd"),translate("Pdnsd可以實現類似ChinaDNS的效果,通過TCP協議進行DNS解析可以有效避免DNS污染,默認上游服務器為114DNS,可以在【DHCP/DNS】中設置【DNS轉發】為【127.0.0.1#5053】即可將所有DNS請求交給pdnsd進行解析,由於pdnsd自帶緩存,所以很快哦。注意SSR如果你勾選了TCP解析DNS會也會開啟一個pdnsd監聽7453接口,並與該頁面的pdnsd沖突,點擊【保存/應用】可以重啟pdnsd。另外你可以使用dig來檢測DNS解析情況,方法dig @127.0.0.1 -p 5053 www.facebook.com")) s=m:section(TypedSection,"arguments","") s.addremove=false s.anonymous=true return m
其中,第1行是引用依賴庫,這個依賴庫是用來讀取和寫入文件的,目前不寫這一行也可以,但是要實現讀寫配置文件的功能,這個庫是必不可少的
第2行是固定格式
m = Map("配置文件文件名", "配置頁面標題", "配置頁面說明")
第一個參數:上一步我們新建配置文件/etc/config/pdnsd.這里就是建立與配置文件的聯系.
第二個參數:主標題.
第三個參數:副標題,注意不能出現雙引號“”,否則會報錯,中文要使用UTF-8編碼
第3行:
m:section(類型,“arguments”,“”)
在一個配置文件中可能有很多Section,所以我們需要創建與配置文件中我們想要的Section的聯系. “arguments”就是section的uci名字,就是我們在/etc/config/pdnsd中寫的那個config arguments
有兩種方式可以選擇:NamedSection(name,type,title,description)和TypedSection(type,title,description),前者根據配置文件中的Section名,而后者根據配置文件中的Section類型.我們選用了第二種.
第4行:設定不允許增加或刪除Section,這樣能保證頁面的清爽,不然會多出一些奇奇怪怪的控件
第5行:設定是否顯示Section的名稱,建議為true
這里順帶提一句,lua腳本中使用--來作為注釋的標記,而不是常見的//或者#;另外luci可以顯示中文,但是你在編輯lua文件的時候必須指定“UTF-8”編碼,否則出來的中文是亂碼
把這三個文件通過winscp上傳到路由器對應的目錄上,無需重啟路由器,刷新一下路由器后台就可以看到效果,如下圖
這樣一個簡單的頁面就出來了,只有主標題和副標題,沒有任何其他的控件
下面我們添加一個簡單的checkbox,用來控制是否讓配置立即生效,只需修改C文件
--Alex<1886090@gmail.com> local fs = require "nixio.fs" m=Map("pdnsd",translate("Pdnsd"),translate("Pdnsd可以實現類似ChinaDNS的效果,通過TCP協議進行DNS解析可以有效避免DNS污染,默認上游服務器為114DNS,可以在【DHCP/DNS】中設置【DNS轉發】為【127.0.0.1#5053】即可將所有DNS請求交給pdnsd進行解析,由於pdnsd自帶緩存,所以很快哦。注意SSR如果你勾選了TCP解析DNS會也會開啟一個pdnsd監聽7453接口,並與該頁面的pdnsd沖突,點擊【保存/應用】可以重啟pdnsd。另外你可以使用dig來檢測DNS解析情況,方法dig @127.0.0.1 -p 5053 www.facebook.com")) s=m:section(TypedSection,"arguments","") s.addremove=false s.anonymous=true view_enable = s:option(Flag,"enabled",translate("Enable")) return m
僅添加了一行
view_enable = s:option(Flag,"enabled",translate("Enable"))
Flag是checkbox控件的意思,如果改成Value就是文本框,相當於html中的input標簽
“enabled”是/etc/config/pdnsd這個uci文件的字段名,所以我們可以順帶在這個文件里加入一個默認值,如下:
config arguments option enabled '1'
0就是默認不選,也就是進入luci界面之后checkbox不勾選,1就是默認勾選
tanslate(“Enable”)就是checkbox前面的提示文字,如果你安裝了luci-i18n等ipk,會自動翻譯成你選擇的語言,目前實現的效果如下
頁面中果然多了一個“啟用”checkbox,此時如果你點擊“保存&應用”就會把該checkbox的狀態記錄到/etc/config/pdnsd文件中,如果勾選就是1,不勾選為0或者不顯示該項
下面再來添加一個大文本框並顯示我們要修改的文本文件。
--Alex<1886090@gmail.com> local fs = require "nixio.fs" m=Map("pdnsd",translate("Pdnsd"),translate("Pdnsd可以實現類似ChinaDNS的效果,通過TCP協議進行DNS解析可以有效避免DNS污染,默認上游服務器為114DNS,可以在【DHCP/DNS】中設置【DNS轉發】為【127.0.0.1#5053】即可將所有DNS請求交給pdnsd進行解析,由於pdnsd自帶緩存,所以很快哦。注意SSR如果你勾選了TCP解析DNS會也會開啟一個pdnsd監聽7453接口,並與該頁面的pdnsd沖突,點擊【保存/應用】可以重啟pdnsd。另外你可以使用dig來檢測DNS解析情況,方法dig @127.0.0.1 -p 5053 www.facebook.com")) s=m:section(TypedSection,"arguments","") s.addremove=false s.anonymous=true view_enable = s:option(Flag,"enabled",translate("Enable")) view_cfg = s:option(TextValue, "1", nil) view_cfg.rmempty = false view_cfg.rows = 43 function view_cfg.cfgvalue() return nixio.fs.readfile("/etc/pdnsd.conf") or "" end return m
上面的代碼中TextValue就是大文本框的意思,相當於html中的text標簽,
.rmempty = false是設置該文本框不許留空,如果為空在“保存&應用”中會出現相應的提示
.rows = 43是這個大文本框的高度為43行,如果內容超出會出現滾動條
要實現進入該界面的時候該文本框自動顯示/etc/pdnsd.conf這個配置文件的內容,需要重寫大文本框自帶的.cfgvalue()方法,這樣瀏覽器進入pdnsd頁面之后luci后台就會自動調用該控件的.cfgvalue()方法,把文件內容呈現在瀏覽器上。
nixio.fs.readfile("文件路徑")會讀取一個文件的全部內容,返回值就是該文件的內容 or ""意思是如果前面那句報錯,就輸出空字符串
現在已經實現了文件的讀取功能,那么如何實現文件的寫入功能呢?我們需要實現TextValue控件的另一個自帶方法,如下
local fs = require "nixio.fs" function sync_value_to_file(value, file) value = value:gsub("\r\n?", "\n") local old_value = nixio.fs.readfile(file) if value ~= old_value then nixio.fs.writefile(file, value) end end m=Map("pdnsd",translate("Pdnsd"),translate("Pdnsd可以實現類似ChinaDNS的效果,通過TCP協議進行DNS解析可以有效避免DNS污染,默認上游服務器為114DNS,可以在【DHCP/DNS】中設置【DNS轉發】為【127.0.0.1#5053】即可將所有DNS請求交給pdnsd進行解析,由於pdnsd自帶緩存,所以很快哦。注意SSR如果你勾選了TCP解析DNS會也會開啟一個pdnsd監聽7453接口,並與該頁面的pdnsd沖突,點擊【保存/應用】可以重啟pdnsd。另外你可以使用dig來檢測DNS解析情況,方法dig @127.0.0.1 -p 5053 www.facebook.com")) s=m:section(TypedSection,"arguments","") s.addremove=false s.anonymous=true view_enable = s:option(Flag,"enabled",translate("Enable")) view_cfg = s:option(TextValue, "1", nil) view_cfg.rmempty = false view_cfg.rows = 43 function view_cfg.cfgvalue() return nixio.fs.readfile("/etc/pdnsd.conf") or "" end function view_cfg.write(self, section, value) sync_value_to_file(value, "/etc/pdnsd.conf") end return m
上面代碼中又重寫了.write(self,section,value)這個方法,其中value這個參數就是當前TextValue這個控件中顯示的文字,於是我又寫了一個函數 sync_value_to_file(文本,文件路徑)方法來將控件中的內容寫入到文件中去。
而那個信函數主要作用就是先把windows中的換行符“\r\n”換成linux的換行符“\n”
然后檢查一下新舊文本有沒有發生變化,如果發生變化才調用庫函數nixio.fs.writefile(file, value)將文本寫入到文件中去。
截至目前,我們已經實現了使用luci頁面讀寫配置文件的功能了。
可是我們還想在配置文件發生修改之后自動重啟后台的服務,應該怎么做呢,首先我們記得我們上面的checkbox記錄了啟用與否,並記在了/etc/config/pdnsd這個文件里。那么我們可以根據這個文件記錄的內容,如果是1就restart pdnsd這個服務並enable,如果是0就stop這個服務並disable。那么我們先寫一個shell腳本來讀取uci文件的記錄值並完成這一系列動作,我新建了一個/etc/pdnsd_init.sh 並添加執行權限,內容如下
echo luci for pdnsd local vt_enabled=`uci get pdnsd.@arguments[0].enabled 2>/dev/null` echo $vt_enabled if [ "$vt_enabled" = 1 ]; then logger -t alex restarting pdnsd echo "restarting pdnsd" /etc/init.d/pdnsd enable /etc/init.d/pdnsd restart else logger -t alex stopping pdnsd echo "stopping pdnsd" /etc/init.d/pdnsd disable /etc/init.d/pdnsd stop fi
一個很普通的shell腳本,其中echo語句只有使用控制台執行的時候才輸出,而logger語句會輸出到OpenWrt主界面【狀態】-【系統日志】中去,方便記錄log和分析bug
上面的uci get pdnsd.@arguments[0].enabled 就是獲取uci配置文件中enabled這個字段的值,因為一個config可以有多個section,每個section下面都可以有一個叫enabled的變量,所以要填寫arguments[0]。比如像如下這種uci文件,就會有多個seciton(uci和lua中nil就是null的意思)
config transparent_proxy option main_server 'nil' option udp_relay_server 'nil' option local_port '1234' config socks5_proxy option server 'nil' option local_port '1080' config port_forward option server 'nil' option local_port '5300' option destination '8.8.4.4:53'
然后使用終端執行/etc/pdnsd_init.sh系統就會根據/etc/config/pdnsd中記錄的值來判斷是啟動還是停止pdnsd服務了,下面我們要把這個shell文件放到luci中執行,我的方法是放在之前寫的文件寫入方法中直接執行,這樣應該是不太對的方法,不過由於我剛剛接觸luci,也沒發現別的方法,如果有大神發現可以告訴我。
function sync_value_to_file(value, file) value = value:gsub("\r\n?", "\n") local old_value = nixio.fs.readfile(file) if value ~= old_value then nixio.fs.writefile(file, value) end os.execute("/etc/pdnsd_init.sh >/dev/null") end
其中,os.excute("shell命令")就可以執行我們自定義的代碼,然后點擊”保存&應用“luci頁面會轉圈圈,提示”正在應用更改...“然后”配置已應用“,其中”正在應用更改...“取決於上面自定義shell腳本的運行時間,我試過如果在shell腳本中添加一句”sleep 10s“會讓”正在應用更改...“的時間明顯延長。
那么為什么要加上>/dev/null這個呢?其實非常必要,因為luci需要一個規定格式的輸出,如果不寫這句,你點擊”保存&應用“之后就會出現白屏錯誤,然后就沒有下文了,如下
所以必須把我們自己shell腳本的輸出全部丟棄,只需添加>/dev/null就好,注意2>/dev/null是沒有用的
至此,我們的通過luci修改配置文件並控制啟動、停止的功能就全部完成了。
參考文章:http://www.right.com.cn/forum/thread-183560-1-1.html
http://blog.csdn.net/icy_river/article/details/48179649
=========== End