LUCI学习笔记:从源码分析openwrt Luci 构成及新增模块( 一 )

时间:2024-03-15 17:27:46

从源码分析openwrt Luci 构成

一. luci 简介

LuCI是OpenWrt上的Web管理界面,LuCI采用了MVC三层架构。

MVC(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

  • (控制器Controller)- 负责转发请求,对请求进行处理。
  • (视图View) - 界面设计人员进行图形界面设计。
  • (模型Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
    LUCI学习笔记:从源码分析openwrt Luci 构成及新增模块( 一 )

二. 源码分析

1. LUCI 在文件系统中的主要目录结构

/www/index.html
#引导页面

/cgi-bin/luci
#脚本跳转

/luci-static/*/*.css、*.js、*.gif
#静态页面css js gif 等目录

/usr/lib/lua/nixio.so、uci.so
#库文件,调用接口用

/usr/lib/lua/luci/
#mvc 层需要的脚本及库文件存放目录

/usr/lib/lua/luci/controller/*.lua
# lua 文件里,定义了菜单的显示和html 以及业务处理路径,每个文件对应一个菜单项。
# ./admin/index.lua 这个文件定义了node ,最外面的节点,最上层菜单的显示。
/usr/lib/lua/luci/model/*.lua
#对应controller 里面调用的脚本文件,下文会解析, 这里不再赘述

/usr/lib/lua/luci/view/*.lua
#对应controller 里面调用的静态页面文件
2. 从源码分析如何创建一个模块

从MVC结构可见, LUCI工作是从CONTROLLER中的脚本开始运行, 我从luci的目录结构中选用常用的系统页面来举例说明。

2.1 菜单栏入口创建

如下
LUCI学习笔记:从源码分析openwrt Luci 构成及新增模块( 一 )

module("luci.controller.admin.system", package.seeall)

function index()
	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"}, post_on({ set = true }, "action_clock_status"))

	entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
    
    .......
end
entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
-- 定义system的最外层的菜单栏 -- alias是指向别的entry的别名
entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)

在luci里面entry的全定义entry(path, target, title=nil, order=nil)

path:对应的就是菜单路径 {“admin”, “system”}或者{“admin”, “system”, “system”}

target:调用目标分为三种,分别是执行指定方法Action、访问指定页面Views以及调用CBI Module

  • 第一种可以直接调用指定的函数,直接写为call(“function_name”),然后在lua文件下编写名为function_name的函数就可以调用了。
  • 第二种可以访问指定的页面,template(“admin/system”)就可以调用/usr/lib/lua/luci/view/admin/system.htm文件。
  • 第三种为调用lua 脚本cbi(“admin/system”)就可以调用/usr/lib/lua/luci/model/cbi/admin/system.lua文件。

title:菜单栏显示名,_(“string”), 多语时调用这个来翻译对应语言

order: 同级菜单下,此菜单项的位置,从小到大,表现为从左到右,从上到下

2.2 菜单栏入口创建

2.1 当中提到过代码创建管理权菜单中,调用了cbi(“admin_system/admin”)

-- 菜单调用项
entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)

-- /usr/lib/lua/luci/model/cbi 目录下有 admin_system/admin.lua

对应实际页面中

LUCI学习笔记:从源码分析openwrt Luci 构成及新增模块( 一 )

CBI模型是Lua文件描述UCI配置文件的结构和由此产生的HTML表单来评估CBI解析器,所有CBI luci.cbi.Map类型的模型文件必须返回一个map对象,在cbi模块中定义各种控件,Luci系统会自动执行大部分处理工作。

m = Map("system", translate("Router Password"),translate("Changes the administrator password for accessing the device"))
-- 第一个参数,代表cbi 基于配置文件system建立Map, 这里的system 实际路径 /etc/config/system
-- 第二个参数, 映射页面的标题,第三个参数, 功能简介
-- 一个配置文件可以map多个文件页面

根据配置文件生成生成对应的section

s = m:section(TypedSection, "_dummy", "") 
--表示获取所有类型为空的section并生成html
-- _dummy 这里差不多和空值等价,因为密码不需要填充值
s.addremove = false
-- 设定不允许增加或删除Section
s.anonymous = true
-- 设定不显示Section的名称

pw1 = s:option(Value, "pw1", translate("Password"))
-- 生成输入框 
pw1.password = true
-- 设置输入框属性

pw2 = s:option(Value, "pw2", translate("Confirmation"))
pw2.password = true

section 函数根据配置
TypedSection表示根据类型获取section
NamedSection表示根据名字获取section

生成如下, 所示的页面
LUCI学习笔记:从源码分析openwrt Luci 构成及新增模块( 一 )


function s.cfgsections()
    return { "_pass" }
end
-- 上面显示section名函数

function m.parse(map)
    local v1 = pw1:formvalue("_pass")
    local v2 = pw2:formvalue("_pass")
    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
            --设置密码接口luci.sys.user.setpasswd, 查询接口见下面。
                m.message = translate("Password successfully changed!")
            else
                m.message = translate("Unknown Error, password not changed!")
            end
        else
            m.message = translate("Given password confirmation did not match, password not changed!")
        end
    end
    Map.parse(map)
end
-- 判断设置密码函数

luci接口手册
nixio接口手册

参考文献

[1] WIKI
[2] 部分前辈的博客