【OpenWrt】(Luci)OpenWrt Web GUI 开发之 Luci 框架粗解

本博文全部内容在 GitHub 仓库上同步,可以在 👉 GitHub 🔗 上找到。
本博文是 GitHub 上的 README 内容,故本文内部分链接是以 GitHub 上相对路径放置的,有需要请在 GitHub 中下载/查看。

因为个人的水平和精力是有限的,如果本目录下的内容存在错误,疏忽之处,欢迎指出:可创建 Issue 或者 fork 修改后向本仓库做 pull request


理解 Luci 架构

注:

下文“几个元素”中,属于“总结”。

是在编写本文是为了理清思路,边整理思路边写下的。

若有不明之处可以在看完本目录下的内容之后再回过头来看一遍。

几个元素:

  • 在 OpenWrt 使用 Luci – Web。

  • Luci 使用 lua 语言作为后台。

  • Luci 使用 lua 通过 uci 库读取和修改 OpenWrt 协定的 UCI 配置文件。

  • OpenWrt 内协定 UCI 配置文件,并提供了不同的接口操作它,其中之一是实现了 lua 语言的 uci 库。

  • Luci 框架内基于 lua + uci 库编写了 CBI 框架 – CBI 框架是 Luci 的子框架。

  • CBI 框架加载入 uci 配置文件相应的 lua 模块,对于 HTTP GET 能够以 CBI 框架的运行逻辑将 UCI 配置文件转化渲染成用于 Web 前端显示的 HTML 做 HTTP Response;同样对 HTTP POST 也以 CBI 框架运行的逻辑将 form 表单修改写入到 UCI 配置文件中(和生效)。

  • 由于 UCI 有着规范的格式,以及 CBI 框架的 OOP 实现,我们不需要从头如 读取 UCI 配置文件,写出如何渲染成 HTML 的代码再 Response 到 Client(浏览器);这些操作是可以抽象出来的一套可重用的参数和运行逻辑(方法),用户(promgramer)只需要编写针对细分的不同的配置文件内容的 CBI 模块代码即可(即,OOP 中的 class 已经被实现好了,只需要根据不同的实体内容实例和调整实例内容即可)。

  • 因为 CBI 框架的高度可重用性(配置文件大同小异,用户(user)管理网页风格统一),所以我们可以设想只要在编写的 CBI 模块代码中指明该模块关联的 UCI 配置文件,那么 CBI 框架就能够将该 UCI 配置文件以写好的有限的规则读取出来并显示在浏览器上 – 但是因为我们需要控制配置文件中的哪些部分需要显示,哪些部分不需要显示;哪些部分以何种方式显示,另一些部分又以另外的什么方式显示;所以我们除了声明配置文件外,还需要声明这些内容 – 这很类似“声明式”编程,后面具体会感觉到。

    (另外一提,这点和 Web 开发的后台管理网站没有本质区别,后台管理网站也是通过编写大量通用的代码涵盖大范围的内容类型(数据库,数据 type)以做到对大部分编写的数据模型无需额外编写管理页面,直接能够以通用的模板显示出来并且能够操作)

  • Luci 框架亦是 MVC 模式,其中 CBI 即是 model,因为用户(user)管理网页(路由器的 Web GUI)没有很多花样,所以基本上需要的前端模板 luci 都提供了 – 即用户编写了 CBI 模块即可,CBI 框架渲染时的运行逻辑能够使用既有的模板为浏览器提供 HTML 显示。当然,在一些需要以特殊方式显示或者提供更丰富的功能的地方,我们仍然可以自己编写模板(view),然后在 CBI 内指定哪些 UCI 内容使用该模板显示。

  • Luci 框架的控制器是理解 Luci 框架运行逻辑的关键,它主要将用户编写的控制器规则生成 URI(路由)(并且提供了反向解析的接口),用户(user)只需要在浏览器访问 URL,Luci 就能够通过控制器执行程序中定义的处理代码(比如 CBI 模块)得到处理代码(方法)的返回内容作为 HTTP 响应(View),即 controller -> model -> view(view 中又包含 controller URL)。

详解 Luci 框架 – 将分以下步骤进行(可以仔细看一下,有助于理清思路):

  1. 软件结构:源码结构和运行结构
    1. 源码分布介绍和“安装/运行”文件分布
    2. 源码编译/打包方式和上传安装
  2. HTTP Server
    1. HTTP Server 和 Luci 交互基本原理
    2. HTTP Server 简单介绍
  3. Luci 用户系统
    1. 多用户支持
    2. 状态保持原理(HTTP 是无状态协议)
    3. 登录系统
  4. Luci 框架
    1. hello world 级 demo – 接触与感受
    2. MVC 简介
    3. Controller 和 URI
    4. View – 模板简介和基本使用/语法
    5. Luci 框架前半部运行逻辑
      1. 从 HTTP Server 到执行 Luci
      2. 从 Luci 入口到 Controller
      3. 从 Controller 到执行 target(函数)
    6. 非 CBI target – call 与 template 实例
    7. Model(模型 )-- CBI 框架
      1. CBI 的 hello world 级 demo
      2. 了解 OOP 基本知识和作用
        1. 基本知识
        2. 基本作用 -> 可实现的声明式编程
        3. 加速编程的好处,隐藏大量细节的坏处
        4. lua OOP 语法与动态语法
        5. 类与实例 – 不容模糊的 self
      3. UCI 协议(约定)
        1. 配置文件格式
        2. 常规对应 Web 页面显示的控件
        3. lua 中的 uci 库与 API
      4. CBI 模块实例 - example 页和 example 配置文件
      5. 处理表单 – 探究 CBI 框架运行逻辑
        1. 模块,节,tab 与 option
        2. 基本类型和动态绑定(组合模式)
        3. lua 脚本代码在什么时候运行?以及 require 和 loader 区别
        4. 运行方法(代码执行逻辑)和重写
        5. 可选的定制化 – 钩子函数
        6. 配置更新的生效和重定向
    8. 模板扩展 – javascript 异步请求
  5. 节选 - 编程关键词与理解
    1. 类,类型,实例,对象
    2. API,框架,模块,(数据)模型
    3. 钩子,重写
    4. 强类型和弱类型,动态和静态
    5. 面向过程,面向对象,面向声明
    6. UML

注:

因为本目录的内容并没有按照上文“步骤”写完,所以只称得上“粗解”。

因为 PPT 是先于本文做完的,虽然 PPT 内容本身可能不够精细,但是根据 PPT 内的文字说明,讲解,结合 Luci 源码查看,想必对框架理解会有一定的帮助。

另外在写 PPT 的时候是为了团队现场讲解,会配置打开的源代码文件讲,所以在 PPT 中主要以概括性、解释性、总结性内容为主,读者需要结合源码看。此处内容后面可能会不定期更新。

本着不等将事情做到完美的那一刻再分享出来的理由,

因为一是这样会过很久之后才分享出来,二是有可能做不到心目中的完美而一直不能分享出来。

若是有需要的读者早一些看到,并且可能能起到一定的帮助,再由读者自身通过结合源码以及翻阅资料补充便可以理解 Luci 框架。那么本目录下分享出来的内容便是有用的。

通过我也期望于即使目前此处的内容还很粗糙,但是若有读者能够 fork > 编辑补重小节小段或者是几句话,几行代码解释介绍,那也是甚好的。

粗解 Luci 框架:

目录

  1. 软件结构:源码结构和运行结构
    1. 源码分布介绍和“安装/运行”文件分布
    2. 源码编译/打包方式和上传安装
  2. HTTP Server
    1. HTTP Server 和 Luci 交互基本原理
    2. HTTP Server 简单介绍
  3. Luci 用户系统 《== 未完成
    1. 多用户支持
    2. 状态保持原理(HTTP 是无状态协议)
    3. 登录系统
  4. Luci 框架
    1. hello world 级 demo – 接触与感受
    2. MVC 简介
    3. Controller 和 URI
    4. View – 模板简介和基本使用/语法
    5. Luci 框架前半部运行逻辑《== 参见 luci框架代码”逻辑”流程图.pdf
      1. 从 HTTP Server 到执行 Luci
      2. 从 Luci 入口到 Controller
      3. 从 Controller 到执行 target(函数)
    6. 非 CBI target – call 与 template 实例
    7. Model(模型 )-- CBI 框架
      1. CBI 的 hello world 级 demo
      2. 了解 OOP 基本知识和作用《== 未完成
        1. 基本知识
        2. 基本作用 -> 可实现的声明式编程
        3. 加速编程的好处,隐藏大量细节的坏处
        4. lua OOP 语法与动态语法
        5. 类与实例 – 不容模糊的 self
      3. UCI 协议(约定)
        1. 配置文件格式
        2. 常规对应 Web 页面显示的控件
        3. lua 中的 uci 库与 API《== 未完成
      4. CBI 模块实例 - example 页和 example 配置文件
        1. 模块,节,tab 与 option
        2. 基本类型和动态绑定(组合模式)《== 未完成
        3. input-Value,select-ListValue,checkbox-Flag
      5. 处理表单 – 探究 CBI 框架运行逻辑
        1. lua 脚本代码在什么时候运行?以及 require 和 loader 区别《== 未完成
        2. 运行方法(代码执行逻辑)和重写
        3. 可选的定制化 – 钩子函数《== 未完成
        4. 配置更新的生效和重定向
    8. 模板扩展 – javascript 异步请求《== 未完成
  5. 节选 - 编程关键词与理解《== 未完成
    1. 类,类型,实例,对象
    2. API,框架,模块,(数据)模型
    3. 钩子,重写
    4. 强类型和弱类型,动态和静态
    5. 面向过程,面向对象,面向声明
    6. UML

正文

参阅 luci-web(GUI)-for-develop.pptx

以下为 PPT 幻灯片截图:
n/a
n/a
n/a
n/a
n/a
n/a
n/a
n/a
n/a

添加一个导航栏 tab,需要在 controller/ 下增加一个相应名称的 *.lua 脚本文件。
框架会读取 controller/ 下的所有 *.lua 脚本文件生成框架内部使用的结构树。每个 *.lua 脚本文件对应一个导航栏 tab。
框架读取 *.lua 内的 function index() 函数,其函数内的 entry 会在页面显示上作为导航栏 tab 的子页标签(页面入口)。

entry(path, target, title, order)
path : router – url
target: Target function to call when dispatched
title : 显示的名称
order : 顺序

source code(dispatch.lua)

function entry(path, target, title, order)
    local c = node(unpack(path))

    c.target = target
    c.title  = title
    c.order  = order
    c.module = getfenv(2)._NAME

    return c
end

n/a
n/a
n/a
n/a

luci 框架本身也就是 lua 脚本代码。
访问 url 并非访问某个特定的 *.lua 脚本文件。
而是通过 luci 框架执行相应的 lua 代码。

n/a
n/a
n/a

Client端和serv端采用cgi方式交互,uhttpd服务器的cgi方式中,fork出一个子进程,
子进程利用execl替换为luci进程空间,并通过setenv环境变量的方式,传递一些固定格式的数据(如PATH_INFO)给luci。

另外一些非固定格式的数据(post-data)则由父进程通过一个w_pipe写给luci的stdin,
而luci的返回数据则写在stdout上,由父进程通过一个r_pipe读取。

n/a
n/a

  1. 首次运行时,是以普通的file方式获得docroot/index.html,该文件中以meta的方式自动跳转到cgi的url,这是web服务器的一般做法。

  2. 然后第一次执行luci,path_info=‘/’,会alise到’/admin’(‘/‘会索引到 tree.rootnode,并执行其target方法,即alise(’/admin’),即重新去索引adminnode,这在后面会详细描述),
    该节点需要认证,所以返回一个登录界面。

  3. 第3次交互,过程同上一次的,只是这时已post来了登录信息,所以serv端会生成一个session值,然后执行’/admin’的target(它的target为firstchild,即索引第一个子节点),
    最终返回/admin/status.html,同时会把session值以cookie的形式发给client。这就是从原始状态到得到显示页面的过程,之后主要就是点击页面上的连接,产生新的request。

  4. 每个链接的url中都会带有一个stok值(它是serv生成的,并放在html中的url里),并且每个新request都要带有session值,它和stok值一起供serv端联合认证。

  5. 先介绍 luci 如何生成 router(路由)。
    然后关于 luci 框架如何加载模板,渲染数据后 response 到 client——先介绍 MVC(和 CBI),然后介绍连接 MVC 和处理请求接口的 dispatcher.lua。
    最后 dispatcher.lua 的其它代码,如何调用执行 controller 中的代码。

n/a

luci 框架会对解析得到的数据结构缓存在 /tmp/ 目录下
function 是 *.lua -> index -> entry 内的 target

luci框架代码“逻辑”流程图.vsdx == luci框架代码”逻辑”流程图.pdf

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

n/a
n/a
n/a

HowTo: Write Modules: https://github.com/openwrt/luci/wiki/ModulesHowTo

module <> Tab
action <
> page

n/a
n/a

Writing variables and function values
Syntax:
<% write (value) %>
Short-markup:
<%=value%>

Note: index 内部的另外一种写法!!!!!!!!!

n/a
n/a

通过 API 接口“声明式”编程关联配置文件 – 像 html, css 由浏览器读取 html, css 代码,然后执行浏览器内部的代码最后“显示”。

n/a

on_commit, on_apply 等方法在源码中以“钩子”的含义运行。

n/a

亦:CBI 模块也可以自行编写 template 模板然后指定使用。

n/a

类与 API 源码位置:qsdk\qca\feeds\luci\modules\luci-base\luasrc\cbi.lua

n/a

Abstract 即说明这是抽象类型;不能直接使用该类,需要继承出类型。
AbstractSection, AbstractValue 即两种不同的类,从效果上看,AbstractValue 的继承类在 CBI 模块中会根据配置文件需要而实例化绑定在继承 AbstractSection 类的实例下;这一点也可以从 AbstractSection 中的 option 方法内的代码看出。

parse 函数决定了框架内运行逻辑。

n/a

“uci"是"Unified Configuration Interface”(统一配置界面)的缩写,意在OpenWrt整个系统的配置集中化。
系统配置应容易,更直接且在此有文档描述,从而使你的生活更轻松!
(它是White Russian系列OpenWrt基于nvram的配置的后继改进。)
许多程序在系统某处拥有自己的配置文件,
比如/etc/network/interfaces, /etc/exports, /etc/dnsmasq.conf或者 /etc/samba/samba.conf,
有时它们还使用稍有不同的语法。
在OpenWrt中你无需为此烦恼,我们只需更改UCI配置文件!
你不需要为了某个更改起效而重启系统!参阅下文中的命令行实用工具以了解如何做到这点。
还有不要忘了官方程序包(official binaries)里包含了很多后台程序,但默认情况下并未启用!
比如cron后台程序默认并未激活,因而只编辑crontab并无作用。
你需要用/etc/init.d/crond start起动它或用/etc/init.d/crond enable激活它。 大部分后台程序都可以disable(禁用),stop(停止)和restart(重起)。 还有一些非UCI配置你可以参阅。

共同原则
OpenWrt的所有配置文件皆位于/etc/config/目录下。每个文件大致与它所配置的那部分系统相关。可用文本编辑器、“uci” 命令行实用程序或各种编程API(比如 Shell, Lua and C)来编辑/修改这些配置文件。

n/a

参考:
https://lvii.gitbooks.io/outman/content/openwrt.uci.html

实例:

# cat /etc/config/wireless 

config wifi-device 'wifi0'
        option type 'qcawificfg80211'
        option macaddr '00:03:7f:12:20:df'
        option hwmode '11a'
        option channel 'auto'

config wifi-iface
        option device 'wifi0'
        option mode 'ap'
        option ssid 'AAAAAAATest-5G'
        option bssid 'admin'
        option network 'lan'
        option key '12345678'
        option wps_pushbutton '0'
        option encryption 'none'

config wifi-device 'wifi1'
        。。。略。。。

# cat /etc/config/ddns 

config ddns 'global'
        option ddns_dateformat '%F %R'
        option ddns_loglines '250'
        option upd_privateip '0'

config service 'myddns_ipv4'
        option lookup_host 'yourhost.example.com'
        option domain 'yourhost.example.com'
        option username 'your_username'
        option password 'your_password'
        option interface 'wan'
        option ip_source 'network'
        option ip_network 'wan'
        option service_name 'dyn.com'

config service 'myddns_ipv6'
        。。。略。。。

n/a

参考:
https://blog.csdn.net/rainforest_c/article/details/70139962

  • package 'example’中的’example’实际上就是UCI文件的文件名,例如/etc/config/network对应 package ‘network’,但是这个语句不会存在文件中,需要通过命令uci export network查看。
  • config ‘example’ 'test’语句定义了一个type为example,名字为test的section。section可以只有type而没有名字,这类section称为匿名的section,后文会有说明。
  • option ‘string’ 'some value’语句定义了section下的一个option,该option标识为string,值为some value。
  • list ‘collection’ 'first item’语句定义了section下的一个list,list与option不同之处在于list可以有多个值,该例子中的list collection有first item和second item两个值。

n/a

此处直接在 OpenWrt 运行系统中直接修改。

n/a
n/a
n/a
n/a

s <= m:section()
taboption() 指定了该 Option 类在指定的 section->tab 下,并返回 option 实例。
option:write 方法重写定义了该方法的内容。write 方法在 post form 时被调用 – 参考前面幻灯片和源码。
m.uci:set, m.uci:delete 这些即使用 lua 的 uci 模块操作 UCI 配置文件 – 命令行操作 UCI 配置文件,Web(Lua)操作 UCI 配置文件两种方式的后一种。
面向对象编程中,m.uci 使用的是 lua 的 uci 模块,通过 m.uci 对象绑定(将 m 实例以 self 的形式传递给 uci 模块)即操作的是 m 指定的 UCI 配置文件。

n/a

  • 16
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值