OPENWRT 教程第二章 Openwrt Luci 初探(WEB)

 

转自:Openwrt Luci 初探(WEB) 这博客博文题目看起来挺有意思的,不过博文加密了;

追溯到原文是:【玩转开源】BananaPi R2 —— 第四篇 Openwrt Luci 初探 这个博客也很有意思,看下来很有收获


什么是Luci呢?先直观的感受一下,打开web浏览器的网关地址,然后出现了一个web登录界面,这个就是Openwrt Luci的应用。

 

 

 

概述:

OpenWRT的web采取的是luci框架, 在luci的官方网站说明了luci是一个MVC架构的框架,是一个单用户框架,
公用的模块放置在*/luci/controller/下面,
各个用户的模块放置在*/luci/controller/下面对应的文件夹里面,
比如admin登录,最终的页面只显示/luci/controller/admin下面的菜单。这样既有效的管理了不同管理员的权限。

 

基础知识:

Luci = lua + uci
lua : 脚本语言
uci :(Unified Configuration Interface)是Openwrt的配置框架

 

 

Openwrt 的 web 服务器: uhttpd

uhttpd:是一个轻量级的web服务器,由于其可以和Openwrt的配置框架UCI结合到一起,因此默认被用于OpenWrt的Web管理接口LuCI。我们都知道,网站都是被部署在一台台服务器,PC等设备上的,我们的设备访问网站时,先是通过网络访问到部署这个网站的服务器,然后服务器的web服务再返回页面给我们;也就是说如果服务器没有web服务,我们是访问不了网页的哦。

说明:
1) lua单行注释使用“--”,类似于C语言的“//”,多行注释时,“--[[”类似C语言中的“/*”,“]]--”类似C语言中的“*/”

 

一:luci的目录

contoller:逻辑控制文件(主要是注册页面)

model :业务上的处理

view : 存放 html 文件

controller在luci框架中的作用是逻辑上的组织,编码时主要分为2块

1 模块的注册 :

module("luci.controller.admin.system", package.seeall)		//在luci/controller/admin/下注册一个system模块

2 节点的注册 :表示添加一个新的模块入口

local fs = require "nixio.fs"
entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)
entry({"admin", "system", "clock_status"}, call("action_clock_status"))
entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
entry({"admin", "system", "reboot"}, call("action_reboot"), _("Reboot"), 90)

  

函数原型 : entry(path, target, title=nil, order=nil)

 

path :是访问的路径,路径是按字符串数组给定的,
	比如路径按如下方式写"{"admin", "loogson", "control"}",那么就可以在浏览器里访问"http://192.168.1.1/cgi-bin/luci/admin/loogson/control"来访问这个脚本。
	其中的“admin”表示为管理员添加脚本,"loogson"即为一级菜单名,"control"为菜单项名。系统会自动在对应的菜单中生成菜单项。比如想在"System"菜单下创建一个菜单项,那么一级菜单名可以写为
"system"。

  

target : 为调用目标,调用目标分为三种,分别是执行指定方法(Action)、访问指定页面(Views)以及调用CBI Module。
  call:第一种可以直接调用指定的函数,比如点击菜单项就直接重启路由器等等,比如写为"call("function_name")",然后在该lua文件下编写名为function_name的函数就可以调用了。

  template:第二种可以访问指定的页面,比如写为"template("myapp/mymodule")"就可以调用/usr/lib/lua/luci/view/myapp/mymodule.htm文件了。

  cbi: 第三种主要应用在配置界面,比如写为"cbi("myapp/mymodule")"就可以调用/usr/lib/lua/luci/model/cbi/myapp/mymodule.lua文件了。

  title和order: 是针对管理员菜单的,其中的title即是显示在网页上的内容。这里我们创建"/usr/lib/lua/luci/controller/loogson.lua"文件,定义我们的入口为"loogson"。
  alias :表示连接到其他某个节点

 

用户管理:
/luci/controller/admin下面的菜单,一共7个文件

root@OpenWrt:/usr/lib/lua/luci/controller/admin# ls -l
-rw-rw-r--    1 root     root           319 Mar 31 07:19 filebrowser.lua
-rw-rw-r--    1 root     root          1140 Mar 31 07:19 index.lua
-rw-rw-r--    1 root     root         11355 Mar 31 07:19 network.lua
-rw-rw-r--    1 root     root          4403 Mar 31 07:19 status.lua
-rw-rw-r--    1 root     root         10235 Mar 31 07:19 system.lua
-rw-rw-r--    1 root     root          1769 Mar 31 07:19 uci.lua

-rw-rw-r--    1 root     root          1167 Mar 31 07:19 servicectl.lua

前面6个来至Feeds/luci/modules/luci-mod-admin-full/luasrc/controller/admin目录,最后一个来自luci-base目录

 

 二:脚本函数

2.1 Map 函数

m = Map("配置文件存储的文件名,不包含路径", "配置页面标题", "配置页面说明")

  

2.2 

local m, s, o //定义全局变量

  

2.3 

Section : 创建与配置文件中对应的Section , Section分为两种,NamedSection 和 TypedSection
NamedSection :根据配置文件中的Section名
TypedSection:根据配置文件中的Section类型

 

s = m:section(TypedSection, "_dummy", "")
s.addremove = false	//不允许增加或删除Section
s.anonymous = true	//设定不显示Section的名称

2.4 定义:

选项:tab 
s:tab("led", translate("Control LED"))

文本框:value
o:value(0, translate("LED0"))
o:value(1, translate("LED1"))
o:value(2, translate("LED2"))

下拉框:ListValue
o = s:taboption("led", ListValue, "lednum", translate("LED NUM:"))

选择框:Flag

 

三:web 访问流程

 3.1 首先只有当web 服务器起来后,才可能访问到页面,所以首先看一下web 服务器的配置,前文说过,openwrt 的web 服务器使用的是uhttpd

root@OpenWrt:/etc/config# cat uhttpd 	#也可以通过uci 命令查看 :uci show uhttpd

config uhttpd 'main'
        list listen_http '0.0.0.0:8080'	#http协议IPV4的监听端口80
        list listen_http '[::]:8080' 	#http协议IPV6的监听端口80
        list listen_https '0.0.0.0:443'		#https协议的监听端口为443
        list listen_https '[::]:443'		
        option redirect_https '1'
        option home '/www'				#指定根路径
        option rfc1918_filter '1'
        option max_requests '3'			#最大请求数
        option max_connections '100'	#最大TCP连接数
        option cert '/etc/uhttpd.crt'	#HTTPS连接的证书路径
        option key '/etc/uhttpd.key'	#HTTPS连接的私钥路径
        option cgi_prefix '/cgi-bin'	#cgi脚本的路径,这个路径又是home的相对路径,即/www/cgi-bin
        option script_timeout '60'
        option network_timeout '30'
        option http_keepalive '20'
        option tcp_keepalive '1'
        option ubus_prefix '/ubus'

config cert 'px5g'
        option days '730'
        option bits '1024'
        option country 'ZZ'
        option state 'Somewhere'
        option location ''
        option commonname 'OpenWrt'

 

3.2 由上面uhttpd的配置来看,当我们通过web的方式访问时,uhttpd会导向"/www"的路径,那么我们来看看"/www"里面有什么。

root@OpenWrt:/www# ls
cgi-bin      index.html   luci-static

  

root@OpenWrt:/www# cat index.html 
<?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" />
</head>
<body style="background-color: white">
<a style="color: black; font-family: arial, helvetica, sans-serif;" href="/cgi-bin/luci">LuCI - Lua Configuration Interface</a>
</body>
</html>
root@OpenWrt:/www#

  从 index.html 可以看到这个内容“href="/cgi-bin/luci”,原来是这里把网关导向了“/cgi-bin/luci”;那么我们再来看看这个路径里面又有什么?

root@OpenWrt:/www/cgi-bin# ls         #这里有个luci的脚本
luci

  

root@OpenWrt:/www/cgi-bin# cat luci 
#!/usr/bin/lua
require "luci.cacheloader"
require "luci.sgi.cgi"
luci.dispatcher.indexcache = "/tmp/luci-indexcache"
luci.sgi.cgi.run()

root@OpenWrt:/www/cgi-bin#

  这个路径下面放着一个lua的脚本,脚本里面调用了这个接口“luci.sgi.cgi.run()”,那么这个接口执行的函数在哪里呢,在这里:

root@OpenWrt:/usr/lib/lua/luci/sgi# ls
cgi.lua     uhttpd.lua

  

root@OpenWrt:/usr/lib/lua/luci/sgi# cat cgi.lua 
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.

exectime = os.clock()
module("luci.sgi.cgi", package.seeall)
local ltn12 = require("luci.ltn12")
require("nixio.util")
require("luci.http")
require("luci.sys")
require("luci.dispatcher")

-- Limited source to avoid endless blocking
local function limitsource(handle, limit)
        limit = limit or 0
        local BLOCKSIZE = ltn12.BLOCKSIZE

        return function()
                if limit < 1 then
                        handle:close()
                        return nil
                else
                        local read = (limit > BLOCKSIZE) and BLOCKSIZE or limit
                        limit = limit - read

                        local chunk = handle:read(read)
                        if not chunk then handle:close() end
                        return chunk
                end
        end
end

function run()
        local r = luci.http.Request(
                luci.sys.getenv(),
                limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
                ltn12.sink.file(io.stderr)
        )

        local x = coroutine.create(luci.dispatcher.httpdispatch)
        local hcache = ""
        local active = true

        while coroutine.status(x) ~= "dead" do
                local res, id, data1, data2 = coroutine.resume(x, r)

                if not res then
                        print("Status: 500 Internal Server Error")
                        print("Content-Type: text/plain\n")
                        print(id)
                        break;
                end

                if active then
                        if id == 1 then
                                io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
                        elseif id == 2 then
                                hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
                        elseif id == 3 then
                                io.write(hcache)
                                io.write("\r\n")
                        elseif id == 4 then
                                io.write(tostring(data1 or ""))
                        elseif id == 5 then
                                io.flush()
                                io.close()
                                active = false
                        elseif id == 6 then
                                data1:copyz(nixio.stdout, data2)
                                data1:close()
                        end
                end
        end
end
root@OpenWrt:/usr/lib/lua/luci/sgi#

  

现在我们可以初步了解到,lua语言就是这样被Luci使用的。

那么我们再整体看一下整个访问流程:

web(输入网关地址)==> uhttpd调用"/www/index.html" ==> index.html重定向到"/cgi-bin/luci" ==> luci被启动。

讲到这里可能大家对Luci有一个初步的认识了,由于Luci涉及lua语言,这门语言不像Java,C普及率那么高,我这里暂时也点到为止,更多Luci的知识后续有时间我会继续和大家一起研究。

  Luci Github:https://github.com/openwrt/luci

  • 10
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenWrt 中,ubus 是一种进程间通信系统,它允许应用程序之间进行简单的、安全的通信。它基于 Unix 套接字和 JSON-RPC 协议,并且是 OpenWrt 的一个重要组成部分,因为它提供了许多 OpenWrt 的核心功能。 在本教程中,我们将介绍如何使用 ubus 在 OpenWrt 中进行进程间通信。 ## 安装 ubus 首先,我们需要在 OpenWrt 中安装 ubus。可以通过以下命令来安装: ``` opkg update opkg install ubus ``` ## 使用 ubus ### ubus 命令行工具 ubus 命令行工具提供了一个简单的方式来与 ubus 服务进行交互。以下是一些常用的 ubus 命令: - `ubus call`:调用一个 ubus 方法。 - `ubus list`:列出所有可用的 ubus 方法。 - `ubus monitor`:监听 ubus 事件。 例如,要列出所有可用的 ubus 方法,可以运行以下命令: ``` ubus list ``` 你将看到一份所有可用的 ubus 方法的列表。 ### ubus 库 如果你希望在应用程序中使用 ubus,可以使用 ubus 库。ubus 库是一个 C 库,允许应用程序通过编程方式调用 ubus 方法。 要使用 ubus 库,需要包含 `libubus.h` 头文件,并链接 `libubus` 库。以下是一个简单的例子: ```c #include <libubus.h> int main() { struct ubus_context *ctx = ubus_connect(NULL); if (!ctx) { printf("Failed to connect to ubus\n"); return 1; } struct blob_buf buf; blob_buf_init(&buf, 0); blobmsg_add_string(&buf, "message", "Hello, world!"); struct ubus_request req; ubus_request_init(ctx, &req, "example_method", &buf.head); if (ubus_invoke(ctx, &req)) { printf("Failed to invoke ubus method\n"); return 1; } return 0; } ``` 在这个例子中,我们连接到 ubus 服务,并调用了一个名为 `example_method` 的 ubus 方法,传递了一个包含 `message` 字段的 blob 对象。当 ubus 方法被调用时,它会收到这个包含消息的 blob 对象,并且可以进行相应的操作。 ### ubus 事件 除了调用 ubus 方法之外,ubus 还支持事件。应用程序可以向 ubus 注册事件,并在事件发生时接收通知。以下是一个简单的例子: ```c #include <libubus.h> static void event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg) { printf("Received event: %s\n", type); } int main() { struct ubus_context *ctx = ubus_connect(NULL); if (!ctx) { printf("Failed to connect to ubus\n"); return 1; } struct ubus_event_handler handler = { .cb = event_handler, }; ubus_register_event_handler(ctx, &handler, "example_event"); while (1) { ubus_handle_event(ctx); } return 0; } ``` 在这个例子中,我们向 ubus 注册了一个名为 `example_event` 的事件,并在事件发生时打印出了事件的类型。 ## 总结 在本教程中,我们介绍了 OpenWrt 中的 ubus 进程间通信系统,并且演示了如何使用 ubus 命令行工具和 ubus 库进行进程间通信。此外,我们还展示了如何使用 ubus 事件来接收通知。ubus 是 OpenWrt 的一个重要组成部分,可以让应用程序之间进行简单的、安全的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值