openwrt使用靜態html做web界面

目前官網openwrt系統集成的web界面,使用luci和luci2,關於luci和luci2的不同,可參見下面鏈接,作者介紹的很詳細:
http://blog.csdn.net/wdsfup/article/details/51024150?locationNum=11&fps=1

本人也是使用luci2實現前後端數據交互的,只是界面使用的是html寫成的界面,這樣界面製作會更靈活,界面風格能更自由;
luci2的實現原理如下圖所示:ubus使用cs模型進行通訊,可以參考下面的作者寫的:
http://blog.csdn.net/flexman09/article/details/51722582?locationNum=10&fps=1

這裏寫圖片描述

1.後臺包源碼
(1).編寫C源碼後,編譯生成.so放在目錄/usr/lib/rpcd/.so(加粗斜線表示在固件中的路徑,下同)*中,rpcd會將方法註冊到ubus,使用ubus list,查看接口是否註冊到ubus; (可以參考luci2.so中各方法的實現)
(2).對ubus的接口,需要在/usr/share/rpcd/acl.d/.json*賦予接口被調用的讀寫權限;(可以參考luci2.json文件)
(3).可以在後臺使用ubus call調用註冊的方法,檢測使用能使用,排除後臺方法調用的錯誤;
下面是自己編寫c代碼和編譯的CMakeLists.txt後,生成.so文件的過程,還有就是,將.so放到固件中(包的路徑package/luci2/;文件路徑/packge/luci2/src/rpcd/下面的文件均在此路徑下)
C代碼文件:lepton.c如下

 1 #include <fcntl.h>
  2 #include <errno.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <string.h>
  6 #include <ctype.h>
  7 #include <sys/wait.h>
  8 #include <sys/stat.h>
  9 #include <sys/types.h>
 10 #include <sys/statvfs.h>
 11 #include <dirent.h>
 12 #include <arpa/inet.h>
 13 #include <signal.h>
 14 #include <glob.h>
 15 #include <libubox/blobmsg_json.h>
 16 #include <libubox/avl-cmp.h>
 17 #include <libubus.h>
 18
 19 #include <rpcd/plugin.h>
 20
 21 static const struct rpc_daemon_ops *ops;
 22 static struct blob_buf buf;
 23
 24 enum {
 25         RPC_P_D_DATA,
 26         RPC_P_D_COUNT,
 27         __RPC_P_D_MAX
 28 };
 29
 30 static const struct blobmsg_policy rpc_ping_data_policy[__RPC_P_D_MAX] = {
 31         [RPC_P_D_DATA]   = { .name = "data",  .type = BLOBMSG_TYPE_STRING },
 32         [RPC_P_D_COUNT]   = { .name = "count",  .type = BLOBMSG_TYPE_STRING },
 33 };
 34
 35 static int
 36 rpc_luci2_network_ping(struct ubus_context *ctx, struct ubus_object *obj,
 37                        struct ubus_request_data *req, const char *method,
 38                        struct blob_attr *msg)
 39 {
 40         char *arg;
 41         char *count = "5";
 42
 43         struct blob_attr *tb_ping[__RPC_P_D_MAX];
 44         blobmsg_parse(rpc_ping_data_policy, __RPC_P_D_MAX, tb_ping,
 45                       blob_data(msg), blob_len(msg));
 46
 47         if (!tb_ping[RPC_P_D_DATA] && !tb_ping[RPC_P_D_COUNT])
 48                 return UBUS_STATUS_INVALID_ARGUMENT;
 49
 50         arg = blobmsg_get_string(tb_ping[RPC_P_D_DATA]);
 51         count = blobmsg_get_string(tb_ping[RPC_P_D_COUNT]);
 52
 53         const char *cmds[7] = { "ping", "-c", count, "-W", "1", arg, NULL };
 54
 55         return ops->exec(cmds, NULL, NULL, NULL, NULL, NULL, ctx, req);
 56 }
 57
 58 static int
 59 rpc_luci2_print_lepton(struct ubus_context *ctx, struct ubus_object *obj,
 60                          struct ubus_request_data *req, const char *method,
 61                          struct blob_attr *msg)
 62 {
 63         char conf[10] = "lepton";
 64         blob_buf_init(&buf, 0);
 65         blobmsg_add_string(&buf, "name", conf);
 66
 67         ubus_send_reply(ctx, req, buf.head);
 68         return 0;
 69 }
 70
 71 static int
 72 rpc_luci2_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
 73 {
 74         int rv = 0;
 75
 76         static const struct ubus_method luci2_network_methods[] = {
 77                 UBUS_METHOD_NOARG("lepton",          rpc_luci2_print_lepton), //無參操作,ubus接口lepton.network對應的方法名
 78                 UBUS_METHOD("ping1",                  rpc_luci2_network_ping, //有參操作,ubus接口lepton.network對應的方法名
 79                                                      rpc_ping_data_policy)
 80         };
 81
 82         static struct ubus_object_type luci2_network_type =
 83                 UBUS_OBJECT_TYPE("luci-rpc-luci2-lepton", luci2_network_methods);
 84
 85         static struct ubus_object network_obj = {
 86                 .name = "lepton.network", //接口名
 87                 .type = &luci2_network_type,
 88                 .methods = luci2_network_methods,
 89                 .n_methods = ARRAY_SIZE(luci2_network_methods),
 90         };
 91
 92         ops = o;
 93
 94         rv |= ubus_add_object(ctx, &network_obj);
 95
 96         return rv;
 97 }
 98
 99 struct rpc_plugin rpc_plugin = {
100         .init = rpc_luci2_api_init
101 };

對應的CMakeLists.txt,此處還有別的C文件也在此編譯,看lepton的部分即可:

1 cmake_minimum_required(VERSION 2.6)
  2
  3
  4 PROJECT(luci2-plugin C)
  5
  6 ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations -Iinclude)
  7
  8 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
  9
 10 IF(APPLE)
 11   INCLUDE_DIRECTORIES(/opt/local/include)
 12   LINK_DIRECTORIES(/opt/local/lib)
 13 ENDIF()
 14
 15 FIND_LIBRARY(crypt NAMES crypt)
 16 IF(crypt STREQUAL "LIBS-NOTFOUND")
 17   SET(crypt "")
 18 ENDIF()
 19
 20 ADD_LIBRARY(luci2-plugin MODULE luci2.c)
 21 TARGET_LINK_LIBRARIES(luci2-plugin ubox ubus ${crypt})
 22 SET_TARGET_PROPERTIES(luci2-plugin PROPERTIES OUTPUT_NAME luci2 PREFIX "")
 23
 24 ADD_LIBRARY(bwmon-plugin MODULE bwmon.c)
 25 SET_TARGET_PROPERTIES(bwmon-plugin PROPERTIES OUTPUT_NAME bwmon PREFIX "")
 26
 27 ADD_LIBRARY(firewall-plugin MODULE firewall.c)
 28 SET_TARGET_PROPERTIES(firewall-plugin PROPERTIES OUTPUT_NAME firewall PREFIX "")
 29
 30 ADD_LIBRARY(wireless-plugin MODULE wireless.c)
 31 SET_TARGET_PROPERTIES(wireless-plugin PROPERTIES OUTPUT_NAME wireless PREFIX "")
 32
 33 ADD_LIBRARY(qos-plugin MODULE qos.c)
 34 SET_TARGET_PROPERTIES(qos-plugin PROPERTIES OUTPUT_NAME qos PREFIX "")
 35
 36 ADD_LIBRARY(lepton-plugin MODULE lepton.c)#此處編譯本人寫的C源碼
 37 SET_TARGET_PROPERTIES(lepton-plugin PROPERTIES OUTPUT_NAME lepton PREFIX "")
 38
 39 SET( MY_DIR sub_source/my_string)
 40 SET(SRC_LIST wuxian.c;${MY_DIR}/my_string.c;${MY_DIR}/mylist.c)
 41 ADD_LIBRARY(wuxian-plugin MODULE ${SRC_LIST})
 42
 43 include_directories(sub_source/my_string)
 44 SET_TARGET_PROPERTIES(wuxian-plugin PROPERTIES OUTPUT_NAME wuxian PREFIX "")
 45
 46 INSTALL(TARGETS luci2-plugin bwmon-plugin firewall-plugin wireless-plugin qos-plugin wuxian-plugin lepton-plugin     LIBRARY DESTINATION lib)

以上兩個文件寫完後,編譯後在build_dir/target_XXX/luci2_xXX/rpcd路徑下,可以看到編譯生成的.so文件,但此時,該.so還沒有編譯到固件中,需要在引導Makefile中,寫明它的安裝路徑
這裏寫圖片描述

Makefile文件,引導.so編譯到固件,如下注釋部分

  1 #
  2 # Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
  3 #
  4 # Licensed under the Apache License, Version 2.0.
  5 #
  6
  7 include $(TOPDIR)/rules.mk
  8
  9 PKG_NAME:=luci2
 10 #PKG_VERSION:=$(shell git --git-dir=$(CURDIR)/../.git log -1 --pretty="%ci %h" | awk '{ print $$1 "-" $$4 }')
 11 PKG_VERSION:=2015-02-14-e452ca6
 12 PKG_MAINTAINER:=Jo-Philipp Wich <jow@openwrt.org>
 13
 14 PKG_LICENSE:=Apache-2.0
 15 PKG_LICENSE_FILES:=
 16
 17 PKG_BUILD_PARALLEL:=1
 18
 19 include $(INCLUDE_DIR)/package.mk
 20 include $(INCLUDE_DIR)/cmake.mk
 21
 22 define Build/Prepare
 23         $(INSTALL_DIR) $(PKG_BUILD_DIR)
 24         $(CP) ./src/* $(PKG_BUILD_DIR)/
 25 endef
 26
 27 define Package/luci2
 28   SECTION:=luci2
 29   CATEGORY:=LuCI2
 30   TITLE:=LuCI2 UI
 31   DEPENDS:=+rpcd +rpcd-mod-iwinfo +uhttpd +uhttpd-mod-ubus +libubox +libubus
 32 endef
 33
 34 define Package/luci2/description
 35  Provides the LuCI2 web interface with standard functionality.
 36 endef
 37
 38 define Package/luci2/install
 39         $(INSTALL_DIR) $(1)/www
 40         $(CP) ./htdocs/* $(1)/www/
 41         $(INSTALL_DIR) $(1)/usr/share/rpcd
 42         $(CP) ./share/* $(1)/usr/share/rpcd/
 43         $(INSTALL_DIR) $(1)/usr/lib/rpcd
 44         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/luci2.so $(1)/usr/lib/rpcd/
 45         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/bwmon.so $(1)/usr/lib/rpcd/
 46         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/firewall.so $(1)/usr/lib/rpcd/
 47         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/wireless.so $(1)/usr/lib/rpcd/
 48         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/qos.so $(1)/usr/lib/rpcd/
 49         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/wuxian.so $(1)/usr/lib/rpcd/
 50         $(INSTALL_BIN) $(PKG_BUILD_DIR)/rpcd/lepton.so $(1)/usr/lib/rpcd/    #安裝在固件中的路徑是/usr/lib/rpcd
 51         $(INSTALL_DIR) $(1)/usr/libexec $(1)/www/cgi-bin
 52         $(INSTALL_BIN) $(PKG_BUILD_DIR)/io/luci2-io $(1)/usr/libexec/
 53         $(LN) /usr/libexec/luci2-io $(1)/www/cgi-bin/luci-upload
 54         $(LN) /usr/libexec/luci2-io $(1)/www/cgi-bin/luci-backup
 55         $(INSTALL_DIR) $(1)/etc
 56         $(CP) ./script/etc/* $(1)/etc/
 57         $(INSTALL_DIR) $(1)/usr/bin
 58         $(CP) ./script/usr/bin/* $(1)/usr/bin/
 59         $(INSTALL_DIR) $(1)/etc/config
 60         $(CP) ./uci_file/* $(1)/etc/config/
 61 endef
 62
 63 define Package/luci2/postinst
 64 #!/bin/sh
 65
 66 if [ "$$(uci -q get uhttpd.main.ubus_prefix)" != "/ubus" ]; then
 67         uci set uhttpd.main.ubus_prefix="/ubus"
 68         uci commit uhttpd
 69 fi
 70
 71 exit 0
 72 endef
 73
 74 $(eval $(call BuildPackage,luci2))

經過以上的部分,編譯後,能在固件中調用到註冊的ubus接口,但不能通過web界面調用到註冊的接口,因爲web界面調用註冊的ubus接口,還需要權限,下面在(package/luci2/share/acl.d)路徑下,添加lepton.json文件,使註冊的接口或得訪問權限:

 1 {
  2         "test":{
  3                 "description": "for learn luci2",
  4                 "read":{
  5                         "ubus":{
  6                                 "lepton.network":[
  7                                         "ping1",
  8                                         "lepton"
  9                                 ]
 10                         }
 11                 }
 12         },
 13 }

2.使用putty,進入路由系統驗證註冊的方法
經下面驗證,方法均可成功調用:

這裏寫圖片描述

3.前端對ubus接口調用
在官網集成的luci2源碼中有對接口的封裝,源碼路徑(package/luci2/htdocs/),此路徑下放着web前端界面的文件,生成固件後在目錄*www下,查看/etc/config/uhttpd可以知道web服務器默認訪問的路徑就是www,我們的html文件就放在www下,下面是前端的源碼實例解析(一個登錄界面index.html,一個測試界面test.html)
這裏寫圖片描述

原luci2的www中文件替換如下:(編譯前替換到package/luci2/htdocs/下,或使用工具WinSCP替換)
這裏寫圖片描述

下面是luci2文件夾下的文件:
文件說明:bootstrap.js, jquery.peity.js, jquery-1.9.1.js保留這三個文件是因爲我的html要加載js代碼,如果開發時,有js代碼庫加載,這三個文件可以不要; rpc.js封裝了ubus接口發送的函數,不可少; session.js文件中是界面心跳機制的實現,不可少; luci2.js中封裝了很多方法,不可少;
注:此處沒有加載uci.js,項目中最好保留,因爲它對ubus調用uci方法的封裝做的很好。

這裏寫圖片描述

添加index.js和test.js後,需要在luci2.js文件中加載,加載代碼如下面截圖
這裏寫圖片描述

下面是前端新增的5個文件的代碼:
1.index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title>luci2</title>

    <script type="text/javascript" src="/luci2/jquery-1.9.1.js"></script>
    <script type="text/javascript" src="/luci2/jquery.peity.js"></script>
    <script type="text/javascript" src="/luci2/bootstrap.js"></script>
    <script type="text/javascript" src="luci2/luci2.js"></script>
    <script type="text/javascript" src="luci.js"></script>

    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
     <meta http-equiv="content-type" content="text/html; charset=utf-8" />

</head>
<body>

    <p>
        login&nbsp;</p>
    <p>
        username:<input id="username" type="text" value="root"/></p>
    <p>
        password:<input id="password" type="text" /></p>
    <p>
        <input id="login" type="button" value="login" /></p>
    </body>

</html>

2.test.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title>luci2</title>

    <script type="text/javascript" src="/luci2/jquery-1.9.1.js"></script>
    <script type="text/javascript" src="/luci2/jquery.peity.js"></script>
    <script type="text/javascript" src="/luci2/bootstrap.js"></script>
    <script type="text/javascript" src="luci2/luci2.js"></script>
    <script type="text/javascript" src="luci.js"></script>

    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
     <meta http-equiv="content-type" content="text/html; charset=utf-8" />

</head>
<body>

    <p>
        name:
        <input id="name" type="text" /></p>
    <p>
        &nbsp;</p>
    <p>
        &nbsp;</p>
    <p>
        ping:</p>
    <p>
        addr:&nbsp;
        <input id="ping_addr" type="text" /></p>
    <p>
        count: <input id="ping_count" type="text" /></p>
    <p>
        <textarea id="ping_text">result:</textarea></p>
    <p>
        <input id="ping_btn" type="button" value="start" /></p>
    </body>

</html>

3.luci.js

$(function () {
    var hr = window.location.href;

    var g_sid = hr.substring(hr.lastIndexOf(':') + 1, hr.length);

    var ln_name = hr.substring(hr.lastIndexOf('/') + 1, hr.lastIndexOf('.'));//使用鏈接傳遞權限id值

    var L;
    if (g_sid && g_sid.match(/^[a-f0-9]{32}$/)) {
        if (ln_name == "test") {
            L = new LuCI2("test");
            L.globals.sid = g_sid;    //有這個值後,才能獲取到調用後臺ubus接口的權限
            L.setHash('id', L.globals.sid);
            L.session.startHeartbeat();
            L.test.load();
        } else {
            var hr = ('%s/test.html').format(window.location.origin);
            window.location.href = hr;
        }
    } else {
            L = new LuCI2("index");
    }

    $("#ping_btn").click(function () {
        L.test.ping_opt();
    });

    $("#login").click(function () {
        L.index.load();
    });
});


4.luci2/index.js

Class.extend({
    load: function () {
        var u = document.getElementById("username").value;
        var p = document.getElementById("password").value;

        L.globals.sid = '00000000000000000000000000000000';
        L.session.login(u, p).then(function (response) {
            L.globals.sid = response.ubus_rpc_session; //此處獲取到後臺rpcd發送的訪問ubus接口的權限了
            L.setHash('id', L.globals.sid);
            L.session.startHeartbeat();

            var hr = ('%s/test.html#id:%s').format(window.location.origin, L.globals.sid);
            window.location.href = hr;
        });
    },
});

5.luci2/test.js

Class.extend({
    get_name: L.rpc.declare({
        object: 'lepton.network',
        method: 'lepton',
        expect: { '': {} }
    }),     //後臺ubus接口在前端調用的格式函數,這個是無參,有返回值

    runPing: L.rpc.declare({
        object: 'lepton.network',
        method: 'ping1',
        params: ['data', 'count'],
        expect: { '': { code: -1 } }
    }),     //有參,有返回值

    load: function () {
        L.test.get_name().then(function (info) { //接口函數的調用
            document.getElementById("name").value = info.name; //通過ID使靜態界面,獲取到固件動態值
        });
    },

    ping_opt: function () {
        var ip_ping = document.getElementById("ping_addr").value;
        var ping_count = document.getElementById("ping_count").value;

        if (ping_count == "") {
            ping_count = "1";
        }

        var ping_v = parseInt(ping_count);
        if (ping_v > 0 && ping_v <= 10) {
            ping_count = ("%s").format(ping_v);
        } else {
            ping_count = "1";
        }

        $("#ping_text").text(("ping : %s ...").format(ip_ping));
        L.test.runPing(ip_ping, ping_count).then(function (rv) {
            $("#ping_text").text(rv.stdout);
        });
    },
});

界面效果如下:
登錄界面index.html
這裏寫圖片描述

鏈接中的id就是每次登錄web後,rpcd返回的隨機值,多html時,我是使用鏈接傳遞這個值得,只要new的luci2中全局變量L.globals.sid 獲取到這個值後,就有權限在前端調用ubus接口方法了。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章