Browse Source

drop ffho-status-page and ffho-site-auto-select

ffho-site-auto-select my be re-added later if needed
Karsten Böddeker 6 years ago
parent
commit
3f3aa29be4

+ 0 - 43
ffho/ffho-site-auto-select/Makefile

@@ -1,43 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=ffho-site-auto-select
-PKG_VERSION:=1
-PKG_RELEASE:=$(GLUON_VERSION).$(GLUON_SITE_CODE)-$(GLUON_RELEASE).$(GLUON_CONFIG_VERSION)
-
-PFG_BUILD_DEPENDS := lua-cjson/host
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(TOPDIR)/../package/gluon.mk
-
-define Package/ffho-site-auto-select
-  SECTION:=ffho
-  CATEGORY:=Site-select
-  TITLE:=Toolset to autodetect the site.
-  DEPENDS:=+gluon-core +micrond +gluon-neighbour-info +ffho-site-generate
-  MAINTAINER:=Freifunk Hochstift <kontakt@hochstift.freifunk.net>
-endef
-
-define Package/ffho-site-auto-select/description
-  Toolset to autodetect the site by geo and gluon-neighbour-info.
-endef
-
-define Build/Prepare
-	mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-	lua -e 'print(require("cjson").encode(require("cjson").decode(io.open("$(call qstrip,$(CONFIG_GLUON_SITEDIR))/extra/geo.json"):read("*a"))))' > $(PKG_BUILD_DIR)/geo.json
-	$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/)
-endef
-
-define Package/ffho-site-auto-select/install
-	$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/
-	$(INSTALL_DIR) $(1)/lib/gluon/site-select
-	$(INSTALL_DATA) $(PKG_BUILD_DIR)/geo.json $(1)/lib/gluon/site-select/
-endef
-
-$(eval $(call BuildPackage,ffho-site-auto-select))

+ 0 - 67
ffho/ffho-site-auto-select/ReadMe.md

@@ -1,67 +0,0 @@
-ffho-site-auto-select
-=====================
-
-This Package tries to autoselect the correct site-code of gluon nodes.
-
-If geocoordinates are set it matchs them to the areas provided by geo.json file.
-If there is no match, the *geo-default-site*, specified in site.conf, is selected.
-If no coordinates are set, it tries to discover the site-code via
-`gluon-neighbour-info` hourly.
-
-site.conf
----------
-
-**site_select.geo_default_site: optional**
-- default site_code if geolocation is set but does not match to defined areas.
-
-### example
-```lua
-{
-  site_select = {
-    default_site = 'ffho_uml',
-  },
-  ...
-},
-```
-
-site/extra/geo.json
--------------------
-
-File with geojson format. The corresponding `site_code` has to be defined within
-`"properties"` section as `"site_code": "ffho_abn"`.
-
-### example
-```json
-{
-  "type": "FeatureCollection",
-  "features": [
-    {
-      "type": "Feature",
-      "geometry": {
-        "type": "Polygon",
-        "coordinates": [
-          [
-            [
-              8.8845,
-              51.69
-            ],
-            [
-              8.8765,
-              51.6925
-            ],
-            [
-              8.8815,
-              51.7105
-            ],
-            ...
-          ]
-        ]
-      },
-      "properties": {
-        "site_code": "ffho_abn"
-      }
-    },
-    ...
-  ]
-}
-```

+ 0 - 74
ffho/ffho-site-auto-select/luasrc/lib/gluon/upgrade/003-site-auto-select

@@ -1,74 +0,0 @@
-#!/usr/bin/lua
-
-local uci = require('luci.model.uci').cursor()
-local json = require 'luci.json'
-local tools = require 'gluon.site_generate'
-local shape = require 'gluon.pointwithinshape'
-local site_conf = require 'gluon.site_config'
-
-function get_config(file)
-  local f = io.open(file)
-  if f then
-    local config = json.decode(f:read('*a'))
-    f:close()
-    return config
-  end
-  return nil
-end
-
-function set_currentsite(site_code)
-  if site_code and tools.validate_site(site_code) then
-    uci:set('currentsite', 'current', 'name', site_code)
-    uci:save('currentsite')
-    return true
-  end
-  return false
-end
-
-function get_site_by_geo(latitude, longitude)
-  if not latitude or not longitude then
-    return nil
-  end
-
-  local sites = tools.get_config('/lib/gluon/site-select/geo.json').features
-  for _,site in ipairs(sites) do
-    if site.geometry and site.geometry.coordinates then
-      local tmp1 = {}
-      for _, val in ipairs(site.geometry.coordinates[1]) do
-        local tmp2 = {}
-        tmp2.x=val[2]
-        tmp2.y=val[1]
-        table.insert(tmp1, tmp2)
-      end
-      if shape.PointWithinShape(tmp1, tonumber(latitude), tonumber(longitude)) then
-        return site.properties.site_code
-      end
-    end
-  end
-
-  if (site_conf.site_select or {}).geo_default_site then
-     return site_conf.site_select.geo_default_site
-  end
-
-  return nil
-end
-
-local currentsite = uci:get('currentsite', 'current', 'name')
-local configured = tools.validate_site(currentsite)
-
-if not configured then
-  local latitude = uci:get_first('gluon-node-info', 'location', 'latitude')
-  local longitude = uci:get_first('gluon-node-info', 'location', 'longitude')
-  if latitude and longitude then
-    currentsite = get_site_by_geo(latitude, longitude)
-    configured = set_currentsite(currentsite)
-  end
-end
-
-if not configured then
-  local minute = math.random(0, 59)
-  local f = io.open('/usr/lib/micron.d/ffho-site-auto-select', 'w')
-  f:write(string.format('%i * * * * /usr/sbin/ffho-site-auto-select\n', minute))
-  f:close()
-end
-

+ 0 - 126
ffho/ffho-site-auto-select/luasrc/usr/lib/lua/gluon/pointwithinshape.lua

@@ -1,126 +0,0 @@
-#!/usr/bin/lua
-
-module('gluon.pointwithinshape', package.seeall)
-
--- Begin https://love2d.org/wiki/PointWithinShape
-function PointWithinShape(shape, tx, ty)
-	if #shape == 0 then
-		return false
-	elseif #shape == 1 then
-		return shape[1].x == tx and shape[1].y == ty
-	elseif #shape == 2 then
-		return PointWithinLine(shape, tx, ty)
-	else
-		return CrossingsMultiplyTest(shape, tx, ty)
-	end
-end
-
-function BoundingBox(box, tx, ty)
-	return	(box[2].x >= tx and box[2].y >= ty)
-		and (box[1].x <= tx and box[1].y <= ty)
-		or  (box[1].x >= tx and box[2].y >= ty)
-		and (box[2].x <= tx and box[1].y <= ty)
-end
-
-function colinear(line, x, y, e)
-	e = e or 0.1
-	m = (line[2].y - line[1].y) / (line[2].x - line[1].x)
-	local function f(x) return line[1].y + m*(x - line[1].x) end
-	return math.abs(y - f(x)) <= e
-end
-
-function PointWithinLine(line, tx, ty, e)
-	e = e or 0.66
-	if BoundingBox(line, tx, ty) then
-		return colinear(line, tx, ty, e)
-	else
-		return false
-	end
-end
-
--------------------------------------------------------------------------
--- The following function is based off code from
--- [ http://erich.realtimerendering.com/ptinpoly/ ]
---
---[[
- ======= Crossings Multiply algorithm ===================================
- * This version is usually somewhat faster than the original published in
- * Graphics Gems IV; by turning the division for testing the X axis crossing
- * into a tricky multiplication test this part of the test became faster,
- * which had the additional effect of making the test for "both to left or
- * both to right" a bit slower for triangles than simply computing the
- * intersection each time.  The main increase is in triangle testing speed,
- * which was about 15% faster; all other polygon complexities were pretty much
- * the same as before.  On machines where division is very expensive (not the
- * case on the HP 9000 series on which I tested) this test should be much
- * faster overall than the old code.  Your mileage may (in fact, will) vary,
- * depending on the machine and the test data, but in general I believe this
- * code is both shorter and faster.  This test was inspired by unpublished
- * Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
- * Related work by Samosky is in:
- *
- * Samosky, Joseph, "SectionView: A system for interactively specifying and
- * visualizing sections through three-dimensional medical image data",
- * M.S. Thesis, Department of Electrical Engineering and Computer Science,
- * Massachusetts Institute of Technology, 1993.
- *
- --]]
-
---[[ Shoot a test ray along +X axis.  The strategy is to compare vertex Y values
- * to the testing point's Y and quickly discard edges which are entirely to one
- * side of the test ray.  Note that CONVEX and WINDING code can be added as
- * for the CrossingsTest() code; it is left out here for clarity.
- *
- * Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
- * _point_, returns 1 if inside, 0 if outside.
- --]]
-function CrossingsMultiplyTest(pgon, tx, ty)
-	local i, yflag0, yflag1, inside_flag
-	local vtx0, vtx1
-
-	local numverts = #pgon
-
-	vtx0 = pgon[numverts]
-	vtx1 = pgon[1]
-
-	-- get test bit for above/below X axis
-	yflag0 = ( vtx0.y >= ty )
-	inside_flag = false
-
-	for i=2,numverts+1 do
-		yflag1 = ( vtx1.y >= ty )
-
-		--[[ Check if endpoints straddle (are on opposite sides) of X axis
-		 * (i.e. the Y's differ); if so, +X ray could intersect this edge.
-		 * The old test also checked whether the endpoints are both to the
-		 * right or to the left of the test point.  However, given the faster
-		 * intersection point computation used below, this test was found to
-		 * be a break-even proposition for most polygons and a loser for
-		 * triangles (where 50% or more of the edges which survive this test
-		 * will cross quadrants and so have to have the X intersection computed
-		 * anyway).  I credit Joseph Samosky with inspiring me to try dropping
-		 * the "both left or both right" part of my code.
-		 --]]
-		if ( yflag0 ~= yflag1 ) then
-			--[[ Check intersection of pgon segment with +X ray.
-			 * Note if >= point's X; if so, the ray hits it.
-			 * The division operation is avoided for the ">=" test by checking
-			 * the sign of the first vertex wrto the test point; idea inspired
-			 * by Joseph Samosky's and Mark Haigh-Hutchinson's different
-			 * polygon inclusion tests.
-			 --]]
-			if ( ((vtx1.y - ty) * (vtx0.x - vtx1.x) >= (vtx1.x - tx) * (vtx0.y - vtx1.y)) == yflag1 ) then
-				inside_flag =  not inside_flag
-			end
-		end
-
-		-- Move to the next pair of vertices, retaining info as possible.
-		yflag0  = yflag1
-		vtx0    = vtx1
-		vtx1    = pgon[i]
-	end
-
-	return  inside_flag
-end
--- End https://love2d.org/wiki/PointWithinShape
-

+ 0 - 57
ffho/ffho-site-auto-select/luasrc/usr/sbin/ffho-site-auto-select

@@ -1,57 +0,0 @@
-#!/usr/bin/lua
-
-local util = require("luci.util")
-local json = require("luci.json")
-local uci = require('luci.model.uci').cursor()
-local site_code = require('gluon.site_config').site_code
-local tools = require 'gluon.site_generate'
-
-function neighbours(ifname)
-  local info = util.exec("gluon-neighbour-info -d ff02::2:1001 -p 1001 -r nodeinfo -t 2 -i " .. ifname)
-  local macs = {}
-  for _, line in ipairs(util.split(info)) do
-    local data = json.decode(line)
-    if data and data["network"] then
-      local mac = data["network"]["mac"]
-      if mac then
-        macs[mac] = data
-      end
-    end
-  end
-
-  return macs
-end
-
-function get_neighbour_site()
-  local interfaces = util.split(util.trim(util.exec("batctl if | grep active | grep -v mesh-vpn | cut -d':' -f1")))
-  for _, ifname in ipairs(interfaces) do
-    local macs = neighbours(ifname)
-    for _, node in pairs(macs) do
-      if node["system"] then
-        local node_site = node["system"]["site_code"]
-        if node_site and tools.validate_site(node_site) then
-          return node_site
-        end
-      end
-    end
-  end
-
-  return nil
-end
-
-local currentsite = uci:get('currentsite', 'current', 'name')
-local configured = tools.validate_site(currentsite)
-
-if not configured then
-  currentsite = get_neighbour_site()
-  configured = tools.set_site_code(currentsite)
-end
-
-if configured then
-  os.remove("/usr/lib/micron.d/ffho-site-auto-select")
-  if site_code ~= currentsite then
-    os.execute("/lib/gluon/site-select/site-upgrade >/dev/null 2>&1")
-    os.execute("reboot >/dev/null 2>&1")
-  end
-end
-

+ 0 - 26
ffho/ffho-status-page/LICENSE

@@ -1,26 +0,0 @@
-The code of Project Gluon may be distributed under the following terms, unless
-noted otherwise in individual files or subtrees.
-
-Copyright (c) 2013, Project Gluon
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-  1. Redistributions of source code must retain the above copyright notice,
-     this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright notice,
-     this list of conditions and the following disclaimer in the documentation
-     and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-

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

@@ -1,40 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=ffho-status-page
-PKG_VERSION:=1
-PKG_RELEASE:=$(GLUON_VERSION).$(GLUON_SITE_CODE)-$(GLUON_RELEASE).$(GLUON_CONFIG_VERSION)
-
-PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
-
-include $(TOPDIR)/../package/gluon.mk
-
-define Package/ffho-status-page
-  SECTION:=ffho
-  CATEGORY:=Gluon
-  TITLE:=Adds a status page showing information about the node.
-  DEPENDS:=+gluon-core +uhttpd +gluon-status-page-api +libiwinfo-lua +luci-lib-jsonc
-  MAINTAINER:=Freifunk Hochstift <kontakt@hochstift.freifunk.net>
-endef
-
-define Package/ffho-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
-	$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/)
-endef
-
-define Package/ffho-status-page/install
-	$(CP) ./files/* $(1)/
-	$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/
-endef
-
-$(eval $(call BuildPackage,ffho-status-page))

+ 0 - 24
ffho/ffho-status-page/ReadMe.md

@@ -1,24 +0,0 @@
-ffho-status-page
-================
-
-This is a customized version of the *old* `gluon-status-page`.
-
-A link to a (geographical) map can be specified within the `site.conf` and it
-will be shown together with the koordinates. To identify the node, its nodeid
-will be added at the end.
-
-site.conf
----------
-
-**status_page.location_link: optional**
-- Link to a (geographical) map
-
-### example
-```lua
-{
-  status_page = {
-    location_link = 'https://map.hochstift.freifunk.net/#!v:m;n:',
-  },
-  ...
-},
-```

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

@@ -1,12 +0,0 @@
-<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>

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

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

+ 0 - 250
ffho/ffho-status-page/luasrc/lib/gluon/status-page/www/cgi-bin/status

@@ -1,250 +0,0 @@
-#!/usr/bin/lua
-
-local util = require("luci.util")
-local fs = require("nixio.fs")
-local ltn12 = require 'luci.ltn12'
-local sys = require("luci.sys")
-local json = require("luci.jsonc")
-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 version = util.trim(fs.readfile("/lib/gluon/gluon-version") or "")
-local primary_mac = require('gluon.sysconfig').primary_mac
-local nodeid = require('gluon.util').node_id()
-
-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] == 'local-node' 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
-
-local function location()
-  local text = 'none'
-  local locationid = uci:get_first('gluon-node-info', 'location')
-  if locationid then
-    local location = uci:get_all('gluon-node-info', locationid)
-    if uci:get_bool('gluon-node-info', locationid, 'share_location') and location.latitude and location.longitude then
-      text = location.latitude .. ', ' .. location.longitude
-    end
-  end
-
-  if (site.status_page or {}).location_link then
-    text = '<a href=' .. site.status_page.location_link .. nodeid .. '>' .. text .. '</a>'
-  end
-  return text
-end
-
-local function escape_html(s)
-  return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'):gsub('"', '&quot;'))
-end
-
-local 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.parse(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
-
-local function ip_proto(address)
-  if address:match("%.") then
-    return "IPv4"
-  end
-  if address:match(":") then
-    return "IPv6"
-  end
-  return "???"
-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("Community:   " .. escape_html(site.site_name) .. "\n")
-io.write("Model:       " .. escape_html(model) .. "\n")
-io.write("Firmware:    " .. escape_html(release) .. " / " .. escape_html(version) .. "\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>Mesh Interfaces</h2>")
-io.write("<pre>")
-local iface = false
-for _, line in ipairs(util.split(util.exec("batctl if"))) do
-  if not line:match("^primary0") then
-    io.write(escape_html(line) .. '\n')
-    iface = true
-  end
-end
-if not iface then
-  io.write("none")
-end
-io.write("</pre>")
-
-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.new()
-    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 via %s\n", peer.connection.established/1000, ip_proto(peer.address)))
-    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>")