关于OpenWrt的一知半解

1 篇文章 1 订阅
1 篇文章 0 订阅
OpenWrt开发学习总结:
一.学习准备:
a.学习lua脚本语言.
b.了解openwrt的文件架构,主要是luci的.
静态的js,css,图标文件,html文件放在路由器根目录的www文件夹下
luci对应的controler,model,view的文件放在/usr/lib/lua/luci文件夹下,
源码路径为openwrt-sdk\package\ramips\ui\luci-mtk\src
c.开发方式为:利用mtk自带samba功能,对路由器文件进行相应修改
(注意不可使用DW进行修改,否则会导致路由器无法启动,推荐NotePad,
编译时做make-menuconfig中开启samba功能,然后再网页页面中添加,在菜单服务,
网络共享中,Path设置为"/"根目录,勾选Allow guests,Create Mask设置为777,
Directiory mask设置为777,然后运行中填入"\\+IP"即可访问路由器源文件,
进行修改);
注意:down下源码后,需要对其进行更新,安装luci等插件,执行两个脚本即可:
./scripts/feeds update -a
./scripts/feeds install -a
利用samba功能修改代码时,注意需要修改的文件将权限修改为777.


二.开发学习总结:
1.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文件让我们可以访问.




2.uhttpd:
这个是LuCI所在的Web Server。docroot在/www下边,index-html指向了/cgi-bin/luci,注意这是相
对于docroot而言的路径。
openwrt中利用它作为web服务器,实现客户端web页面配置功能。对于request处理方式,采用的是cgi,而所用
的cgi程序就是luci。




3.模块的初始化与调用.(以status模块为例)
模块入口文件status.lua在目录lua\luci\controller\admin下,
在index()函数中,使用entry函数来完成每个模块函数的注册:
--- entry(path, target, title=nil, order=nil) ---
第一个参数path是定义菜单的显示(Virtual path);
第三个参数title是菜单的文本,_(“string”),国际化;
第四个参数order是是同级菜单下,此菜单项的位置,从小到大;
第二个target参数定义相应的处理方式(target)。处理方式如下:


a.alias是指向别的entry的别名
比如两个entry需要指向同一个路径的文件,只需要一个指明路径,然后另一个指向该entry即可
eg:
entry({"admin", "status"}, alias("admin", "status", "overview"), _("Status"), 2).index = true
entry({"admin", "status", "overview"}, template("admin_status/index"), _("Overview"), 1)


b.from调用的某一个view,即lua\luci\view文件夹下的html文件,
eg:
entry({"admin", "system", "startup"}, form("admin_system/startup"), _("Startup"), 45)


c.cbi调用某一个model,即lua\luci\model\cbi下的文件
eg:entry({"admin", "status", "processes"}, cbi("admin_status/processes"), _("Processes"), 6)


d.call直接调用函数,即当前.lua文件下可使用的lua函数,
eg:
entry({"admin", "status", "syslog"}, call("action_syslog"), _("System Log"), 4)


function action_syslog()
local syslog = luci.sys.syslog()
luci.template.render("admin_status/syslog", {syslog=syslog})
end




4.关于CBI的调用:
 CBI模型是Lua文件描述UCI配置文件的结构和由此产生的HTML表单来评估CBI解析器,所有CBI luci.cbi.Map类型
的模型文件必须返回一个map对象,在cbi模块中定义各种控件,Luci系统会自动执行大部分处理工作。其链接目录
在lua\luci\model\cbi下
eg:
entry({"admin", "status", "processes"}, cbi("admin_status/processes"), _("Processes"), 6)
调用\lua\luci\model\cbi\admin_status\processes.lua来实现模块。


流程为以下:
a、映射UCI文件
b、生成section
c、生成option
其实质为利用lua脚本生成对应的网页控件,类似js生产html.这样做方便脚本获取网页页面的对应值
同时完成对应的操作,但是不利于界面的修改.
后面我们将介绍在html界面上使用lua的方法.二者各有优缺点.可自行选择使用.
eg:
require("luci.sys")
--[[
 Map("配置文件文件名", "配置页面标题", "配置页面说明"),对应到配置文件/etc/config/testclient
]]--
m = Map("testclient", "the title of testclient", "the shuoming of testclient")
--[[
获取所有类型为login的section并生成html section
class可以是TypedSection表示根据类型获取section
NamedSection表示根据名字获取section
]]--
s = m:section(TypedSection, "login", "")
s.addremove = false
s.anonymous = true
--[[
生成option
class可以是:
Value:input控件
ListValue:下拉列表
Flag:选择框
MultiValue:
DummyValue:纯文本
TextValue:多行input
Button:按钮
StaticList:
DynamicList:
下代码为每个section生成可选项控件,映射到proto字段
p= s:option(ListValue, “proto”, “Protocol”)
p:value(“static”, “static”)
p:value(“dhcp”, “DHCP”)
p.default = “static”
]]--
enable = s:option(Flag, "enable", translate("Enable"))
name = s:option(Value, "username", translate("Username"))
pass = s:option(Value, "password", translate("Password"))
pass.password = true
domain = s:option(Value, "domain", translate("Domain"))
ifname = s:option(ListValue, "ifname", translate("Interfaces"))
for k, v in ipairs(luci.sys.net.devices()) do
    if v ~= "lo" then
        ifname:value(v)
    end
end
local apply = luci.http.formvalue("cbi.apply")
if apply then
    io.popen("/etc/init.d/njitclient restart")
end


return m


控件对应的html文件在luasrc\view\cbi目录下


5.XHR类
关于XHR的说明,我们可以通过网上的两篇文章进行一些了解:
https://segmentfault.com/a/1190000002782175
https://segmentfault.com/a/1190000002789203
主要是用于实现ajax的http请求,包括post和get.
OpenWrt中已经使用js封装好了该类的使用.
主要方法有:
//XHR.get(url, data, callback);
XHR.poll(interval, url, data, callback)
(new XHR()).post(url, data, callback);
通过以上方法,可以实现ajax的免刷新获取数据.


6.利用XHR,完成js与luci的交互.
以修改管理员密码为例:
a.在html页面使用lua编写对应请求的处理.(往后将探索将lua分开单独一个文件进行处理)
这部分代码置于header之前
<%-
    local uci  = require("luci.model.uci").cursor() 
    --retieve data from xhr http request.
    if luci.http.formvalue("status") == "1" then      
        local v1 = luci.http.formvalue("pw1")
local v2 = luci.http.formvalue("pw2")
local applist = ""
local count = ""
if v1 and v2 and #v1 > 0 and #v2 > 0 then
if v1 == v2 then
if luci.sys.user.setpasswd(luci.dispatcher.context.authuser, v1) == 0 then
applist = "suc"
count = 1
else
applist = "fail"
count = 0
end
else
applist = "fail"
count = -1
end
end

        rv = {
            list=applist,
            appnum=count
        }


-- return with json type
        luci.http.prepare_content("application/json")
        luci.http.write_json(rv)


        return
    end
%>


b.使用xhr类,发起修改密码的post请求
<script type="text/javascript">
var url = '<%=REQUEST_URI%>';
console.log("*****************"+url);
new XHR().post('<%=REQUEST_URI%>', { status: 1 ,pw1:"123",pw2:"123"},
function(xhr) {
            console.log(xhr.responseText);//you will see the xhr.responseText is a json string.
console.log("xhr:"+xhr);
        });
/*************************
使用XHR.poll方法,interval为刷新频率(单位s).
********************************
XHR.poll(25,'<%=REQUEST_URI%>', { status: 1 ,pw1:"123",pw2:"123"},
function(x,json){
console.log("x-->"+x.responseText);
console.log("json-->"+json.list);
}
);
************/
</script>


测试结果返回的JSON字符串为: { "appnum": 1, "list": "suc" }


c.自定义界面的基本思路:
通过查看openwrt源码,查找到对应功能所需要用到的lua函数,从而通过在html界面调用对应的lua函数实现对应功能的
操作,以达到自定义界面的目的.此方法适用于擅长HTML以及js而刚刚入门开发openwort的开发人员.


参考文章:
http://blog.csdn.net/luanjinlu/article/details/46429241
http://blog.csdn.net/zbffff/article/details/40868463
http://blog.csdn.net/ccwwff/article/details/40790675
http://blog.csdn.net/qq_21949217/article/details/42192627
http://www.jianshu.com/p/bfb93c4e8dc9




7.OpenWrt的UCI系统.
UCI是Openwrt的统一配置接口,通过\etc\config中的配置文件内容的修改,对路由器配置进行修改,如路由器的网络接口设置,
无线参数设置,logging设置等等.许多第三方程序是根据它自己对应于/etc/config下的UCI配置文件的选项去设置程序的原
始配置文件,这样就实现了程序对UCI配置的兼容,然后执行一次/etc/init.d脚本完成一次配置。因而当你启动一个某个程
序的UCI兼容的进程脚本时,该脚本应该不只是修改/etc/config下对应的UCI配置文件,同时也应该覆盖程序自己的原配置文
件。比如Samba/CIFS程序,其原配置文件是在/etc/samba/smb.conf,而对应的UCI文件是/etc/config/samba,当/etc/config
/samba文件被修改了之后,需要运行一次之后UCI文件中的设置才会更新到原配置文件中去。
UCI的配置文件被分割成/etc/config下的多个独立的文件,各个文件按名字含义对应系统的不同的功能配置。你可以通过文本
编译器或者uci实用程序去修改这些配置文件,同时uci还提供了C语言/脚本/Lua等语言的应用程序接口,WEB配置页面例如Luci
就是利用了uci所提供的lua的API而实现对UCI配置文件的修改的。


那么如何通过UCI系统进行配置的修改呢?
首先我们需要了解UCI系统配置文件的编写规则,这里不做过多介绍,我们可以参考一下文章
http://developer.t-firefly.com/thread-1035-1-1.html
接下来我们来了解一下关于UCI系统的指令,一般使用shell或者是lua;
a.shell指令集,笔者使用的是lua,因此关于shell的说明请读者自行参考刚刚提到的UIC系统配置文件编写规则的文章
http://developer.t-firefly.com/thread-1035-1-1.html


b.lua指令集,Openwrt很方便的给我们提供了一套lua操作UCI的API,封装于文件lua.model.uci当中,
--因此首先我们应该获取OpenWrt封装的操作UCI的API句柄,去定义为
local x = luci.model.uci.cursor()
--接下来我们介绍使用LUA的api进行配置文件内容的添,删,查,改


--添加/修改:
x:set("config","name","type") --增加一个section(修改不需要使用此函数)
x:set("config","sectionname","option","exp") --在section下增加配置
参数说明
config -- 配置文件的名字,配置文件位于/etc/config/下
name -- 配置文件中某个类型的具体名字
name/sectionname --具体某一个section的名称这里需要注意一点,部分section
--的名称可能不会直接体现到配置文件当中,因此需要使用先查询后操作的
--方式(后面会提到查询的API),一般使用type进行查询.
type -- 配置文件中类型(type)
option -- 具体配置的option的内容
exp --配置文件中具体option的值


--删除:
x:delete("config","section") --删除section
x:delete("config,"section","option") -- 在section下删除option


--位置插入函数
x:reorder("config","sectionname",position)
将某个section放到postion位置(配置的section是从0开始计数


--查询:
x:foreach("config","type","function(s) ... end") -- 遍历整个config文件
x:get("config","sectionname","option") ---获得option的值
在foreach中有个两个变量
s[".type"] -->section 类型
s[".name"] -->section名称,此处为上文提到的,根据类型查询名称的时候需要获取
--的section名称sectionName
其中s[".name"] 就是x:get的第二个参数
例:有如下一个配置文件
config globals '0'
     option hostname 'iphone'
     option ip '192.168.0.1'
     option mac '00:11:22:33:44:55:66'
config globals '1'
     option hostname 'iphone1'
     option ip '192.168.0.2'
     option mac '00:11:22:33:44:55:77'
遍历并且打印每一个option
x:foreach("wificonfig","globals",function(s)
     local lcName = s[".name"]
     local lcHostname = x:get("wificonfig",lcName,"hostname")
     local lcIp = x:get("wificonfig",lcName,"ip")
     local lcMac = x:get("wificonfig",lcName,"mac")
     print("hostname = " .. lcHostname .. ",ip = " .. lcIp .. ",mac= " .. lcMac)
end
)




--[[
需要注意的是:每一次修改完配置后,需要调用x:commit("config")函数,才会修改到配置文件
另外,要使得配置在系统中生效,需要重启一次机器
以下是以修改wifi的ssid为例的具体例子:
]]--
--html文件下添加lua脚本
<%-
    local uci  = require("luci.model.uci").cursor()  
if luci.http.formvalue("status") == "2" then 
local ssidModify=  luci.http.formvalue("ssid")
local ssid = ""
local sectionName = ""
local allMesg = ""
uci:foreach("wireless","wifi-iface",function(s)
allMesg=s
sectionName = s[".name"]
end)
uci:set("wireless",sectionName,"ssid",ssidModify)
uci:commit("wireless")
ssid = uci:get("wireless",sectionName,"ssid")
rv = {
            listSsid=ssid,
listAll=allMesg,
listName=sectionName
        }


        luci.http.prepare_content("application/json")
        luci.http.write_json(rv)
    end

%>
--使用XHR的poll或者是post函数发起请求
XHR.poll(25,'<%=REQUEST_URI%>', { status: 2,ssid:"MyWifiTest"},
function(x,info){
console.log("x-->"+x);
console.log("x-->"+x.responseText);
console.log("info-->"+info.list);
}
);


--[[
以上便是使用LUA的API对openwrt的UCI配置文件进行操作,以达到修改路由器配置
的目的的具体操作步骤.
参考文章:http://blog.csdn.net/dai_xiangjun/article/details/40322539
]]--




  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值