Browse Source

ffho-status-page: backport from master
based on gluon-status-page

Karsten Böddeker 8 years ago
parent
commit
8e7d0473a1

+ 37 - 0
ffho/ffho-status-page/Makefile

@@ -0,0 +1,37 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-status-page
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-status-page
+  SECTION:=gluon
+  CATEGORY:=Gluon
+  TITLE:=Adds a status page showing information about the node.
+  DEPENDS:=+gluon-core +gluon-neighbour-info +uhttpd
+endef
+
+define Package/gluon-status-page/description
+	Adds a status page showing information about the node.
+	Especially useful in combination with the next-node feature.
+endef
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-status-page/install
+	$(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-status-page))

+ 217 - 0
ffho/ffho-status-page/files/lib/gluon/status-page/www/cgi-bin/status

@@ -0,0 +1,217 @@
+#!/usr/bin/lua
+
+local util = require("luci.util")
+local fs = require("luci.fs")
+local ltn12 = require 'luci.ltn12'
+local sys = require("luci.sys")
+local json = require("luci.json")
+local nixio = require 'nixio'
+local platform_info = require("platform_info")
+local site = require 'gluon.site_config'
+local ip = require 'luci.ip'
+local uci = require('luci.model.uci').cursor()
+
+local hostname = sys.hostname()
+local model = platform_info.get_model()
+local release = util.trim(fs.readfile("/lib/gluon/release") or "")
+local primary_mac = require('gluon.sysconfig').primary_mac
+local nodeid = require('gluon.util').node_id()
+local latitude = uci:get_first('gluon-node-info', 'location', 'latitude')
+local longitude = uci:get_first('gluon-node-info', 'location', 'longitude')
+local location = ""
+if latitude and longitude then
+  location = "<a href=https://map.hochstift.freifunk.net/#!v:m;n:" .. nodeid .. ">" .. latitude .. ", " .. longitude .. "</a>"
+else
+  location = "<a href=https://map.hochstift.freifunk.net/#!v:m;n:" .. nodeid .. ">none</a>"
+end
+
+local contact = uci:get_first('gluon-node-info', 'owner', 'contact', '')
+if contact == '' then
+  contact = "none"
+end
+local autoupdater = uci:get('autoupdater', 'settings', 'branch')
+if uci:get_bool('autoupdater', 'settings', 'enabled') == false then
+  autoupdater = "disabled (" .. autoupdater .. ")"
+end
+
+local addresses = ""
+for line in io.lines('/proc/net/if_inet6') do
+  local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 4) .. '%s+([^%s]+)$') }
+  if matches[9] == 'br-client' then
+    addresses = addresses .. "             " .. ip.IPv6(string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches))):string():lower() .. "\n"
+  end
+end
+
+local data = io.open('/proc/meminfo'):read('*a')
+local fields = {}
+for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do
+        fields[k] = tonumber(v)
+end
+
+function escape_html(s)
+  return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'):gsub('"', '&quot;'))
+end
+
+function neighbours(ifname)
+  local info = util.exec("gluon-neighbour-info -d ff02::2:1001 -p 1001 -r nodeinfo -t 3 -i " .. ifname)
+  local macs = {}
+  for _, line in ipairs(util.split(info)) do
+    local data = json.decode(line)
+    if data then
+      local function add_macs(list)
+        if list then
+          for _, mac in ipairs(list) do
+            macs[mac] = data
+          end
+        end
+      end
+
+      if data["network"] then
+        add_macs(data["network"]["mesh_interfaces"])
+
+        if data["network"]["mesh"] and data["network"]["mesh"]["bat0"] and
+           data["network"]["mesh"]["bat0"]["interfaces"] then
+          local interfaces = data["network"]["mesh"]["bat0"]["interfaces"]
+          add_macs(interfaces["other"])
+          add_macs(interfaces["wireless"])
+          add_macs(interfaces["tunnel"])
+        end
+      end
+    end
+  end
+
+  return macs
+end
+
+io.write("Content-type: text/html\n\n")
+io.write("<!DOCTYPE html>\n")
+io.write("<html>")
+io.write("<head>")
+io.write("<meta charset=\"utf-8\"/>")
+io.write("<script src=\"/status.js\"></script>")
+io.write("<title>" .. escape_html(hostname) .. "</title>")
+io.write("</head>")
+io.write("<body>")
+
+io.write("<h1>" .. escape_html(hostname) .. "</h1>")
+io.write("<pre>")
+
+io.write("Region:      " .. escape_html(site.site_name) .. "\n")
+io.write("Model:       " .. escape_html(model) .. "\n")
+io.write("Firmware:    " .. escape_html(release) .. "\n")
+io.write("MAC:         " .. escape_html(primary_mac) .. "\n")
+io.write("Contact:     " .. escape_html(contact) .. "\n")
+io.write("Uptime:      " .. escape_html(util.trim(sys.exec("uptime | sed 's/^ \+//'"))) .. "\n")
+io.write("Autoupdater: " .. escape_html(autoupdater) .. "\n")
+io.write("Location:    " .. location .. "\n")
+io.write("IPs:         " .. escape_html(util.trim(addresses)) .. "\n")
+io.write("Memory:      " .. string.format("%.1f %% used, %.1f %% free",(fields.MemTotal-fields.MemFree)/fields.MemTotal*100,fields.MemFree/fields.MemTotal*100) .. "\n")
+io.write("</pre>")
+
+io.write("<h2>Neighbours</h2>")
+
+local interfaces = util.split(util.trim(util.exec("iw dev | egrep 'type IBSS|type mesh' -B 5 | grep Interface | cut -d' ' -f2")))
+
+for _, ifname in ipairs(interfaces) do
+  io.write("<h3>" .. escape_html(ifname) .. "</h3>")
+  io.write("<pre>")
+
+  local peer=false
+  for _, line in ipairs(util.split(util.exec("iw dev " .. ifname .. " station dump"))) do
+    local mac = line:match("^Station (.*) %(on ")
+    if mac then
+      io.write("Station <a id=\"" .. escape_html(ifname) .. "-" .. mac .. "\">" .. mac .. "</a> (on " .. escape_html(ifname) .. ")\n")
+      peer = true
+    else
+      io.write(escape_html(line) .. "\n")
+    end
+  end
+
+  if peer == false then
+    io.write("no peers connected")
+  end
+
+  io.write("</pre>")
+end
+
+io.write("<h2>VPN status</h2>")
+io.write("<pre>")
+
+if string.len(util.exec("ip -f inet address show dev br-wan | grep global")) >= 2 then
+  io.write("IPv4 configured\n")
+else
+  io.write("IPv4 not configured\n")
+end
+
+if string.len(util.exec("ip -f inet6 address show dev br-wan | grep global")) >= 2 then
+  io.write("IPv6 configured\n")
+else
+  io.write("IPv6 not configured\n")
+end
+
+local stat, fastd_status = pcall(
+  function()
+    local fastd_sock = nixio.socket('unix', 'stream')
+    assert(fastd_sock:connect('/var/run/fastd.mesh_vpn.socket'))
+
+    decoder = json.Decoder()
+    ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink())
+    return decoder:get()
+  end
+)
+
+if stat then
+  io.write(string.format("fastd running for %.3f seconds\n\n", fastd_status.uptime/1000))
+
+  local peers = 0
+  local connections = 0
+
+  for key, peer in pairs(fastd_status.peers) do
+    peers = peers+1
+
+    if peer.connection then
+      connections = connections+1
+    end
+  end
+
+  io.write(string.format("There are %i peers configured, of which %i are connected:\n", peers, connections))
+
+  for key, peer in pairs(fastd_status.peers) do
+    io.write(string.format("%s: ", escape_html(peer.name)))
+
+    if peer.connection then
+      io.write(string.format("connected for %.3f seconds\n", peer.connection.established/1000))
+    else
+      io.write("not connected\n")
+    end
+  end
+
+else
+  io.write("fastd not running")
+end
+
+io.write("</pre>")
+
+io.write("<script>")
+for _, ifname in ipairs(interfaces) do
+  local macs = neighbours(ifname)
+  for mac, node in pairs(macs) do
+    local hostname = node["hostname"]
+    local ip
+    if node["network"] and node["network"]["addresses"] then
+      for _, myip in ipairs(node["network"]["addresses"]) do
+        if ip == nil and myip:sub(1, 5) ~= "fe80:" then
+          ip = myip
+        end
+      end
+    end
+
+    if ip and hostname then
+      io.write("update_node(\"" .. escape_html(ifname) .. "-" .. mac .. "\", \"" .. escape_html(ip) .. "\", \"" .. escape_html(hostname) .. "\");")
+    end
+  end
+end
+
+io.write("</script>")
+io.write("</body>")
+io.write("</html>")

+ 12 - 0
ffho/ffho-status-page/files/lib/gluon/status-page/www/index.html

@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <meta http-equiv="refresh" content="0; URL=/cgi-bin/status">
+    <meta http-equiv="cache-control" content="no-cache">
+    <meta http-equiv="expires" content="0">
+    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
+    <meta http-equiv="pragma" content="no-cache">
+  </head>
+  <body>
+    <a href="/cgi-bin/status">Redirecting...</a>
+  </body>
+</html>

+ 9 - 0
ffho/ffho-status-page/files/lib/gluon/status-page/www/status.js

@@ -0,0 +1,9 @@
+function update_node(id, ip, hostname) {
+  var el = document.getElementById(id);
+
+  if (!el)
+    return;
+
+  el.href = "http://[" + ip + "]/";
+  el.textContent += " (" + hostname + ")";
+}

+ 13 - 0
ffho/ffho-status-page/files/lib/gluon/upgrade/500-status-page

@@ -0,0 +1,13 @@
+#!/bin/sh
+
+uci batch <<-EOF
+	delete uhttpd.main.listen_http
+	add_list uhttpd.main.listen_http=0.0.0.0:80
+	add_list uhttpd.main.listen_http=[::]:80
+
+	delete uhttpd.main.listen_https
+
+	set uhttpd.main.home=/lib/gluon/status-page/www
+
+	commit uhttpd
+EOF