From 90baebc2b7dd8e6602d0ea3799bf50c7017876df Mon Sep 17 00:00:00 2001
From: Jan-Tarek Butt <tarek@ring0.de>
Date: Sat, 12 May 2018 17:48:32 +0200
Subject: [PATCH] add gluon-hoodselector: Integrate geolocation mode

This MR includs only the VPN MODE of the hoodselector whitch simply set
hoods base on their geopositions.

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

check_site.lua: fix language syntax

muss -> must
rage -> range
at lease -> at least
coordiantes -> coordinates
realaise -> realised

gluon-hoodselector: fix language syntax in hoodselector

can not -> can't
routers -> router's
continure -> continue
to next -> to the next
TMP -> temporary
for current -> for the current
continure -> continue
with next -> with the next
thier -> there
provides -> provide
possition -> position
therfore -> therefore

gluon-hoodselector: fix language syntax in util.lua

realaise -> realised

gluon-hoodselector: fix language syntax and use autoupdate lock mechanism.

gluon-hoodselector: fix spelling/grammar

gluon-hoodselector: automatically set SECTION and CATEGORY for Gluon packages

gluon-hoodselector-add-VPN-MODE: add micrond & libjson-c dependency

gluon-hoodselector-add-VPN-MODE: check running hoodselector before loading lua

gluon-hoodselector-add-VPN-MODE: remove nixio dependency from hoodselector util

Revert "gluon-hoodselector-add-VPN-MODE: check running hoodselector before loading lua"

This reverts commit 535b0a1b2fb73e563bf6a44b568a796440bd307f.

add luaposix and luabitop to pakage dependency

sbin/hoodselector: remove nixio requiemend

sbin/hoodselector: load hoods only if necessary

gluon-hoodselector: use VPN abstraction layer. the hoodselectore does
not need to know about all individual VPN protocols.

gluon-hoodselector: Makefile add gluon-mesh-vpn-core as dependency

gluon-hoodselector: apply changes of mesh vpn lib

gluon-hoodselector: remove outdated comments

package/gluon-hoodselector: check_site.lua rm domain seed check thus its already checked by gluon-core

package/gluon-hoodselector: util.lua code cleanup and refactoring

package/gluon-hoodselector: hoodselector code cleanup and refactoring

gluon-hoodselector: util.lua, use taps instead of spaces. Use posix.unistd.access instead of io.open

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

gluon-hoodselector: hoodselector, use taps instead of spaces.

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

gluon-hoodselector: check_site.lua: replace hood with domain

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

gluon-hoodselector: drop VPN mode and rename hood to domain. Furthermore implement geolocator mode as neorayder way

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: rm duplicated print output

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector util: fix wrong function signature

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

small typo fixes

small typo fixes

Update util.lua

processes are really restarted now. new (old) problem: nodes will not forget their former ipv6-addresses. watchdog could here with that.

gluon-hoodselector util.lua: replace i iterator with _

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

Update util.lua

now polygons with holes are recognized correctly. also a mix of nested polygons and boxes should be possible as shapes[]

package/gluon-hoodselector: hoodselector use gluon-reload for daemon restarts/reloads

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: util.lua use math-polygon lib and rm restart_services function. Rectengles will be converted into polygons now

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: Makefile rewrite description update depends list

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: check_site.lua reduce complexity

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: use : for gluon_version Val

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: fix if equal syntax

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

luasrc/usr/lib/lua/hoodselector/util.lua: check_site.lua simplify checksite script and fix if logic

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: set space after comma, rm unnecessary error handling

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: use only brackes on require function no mixup

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: check_site.lua rm unuse variables and fix non std global function

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: util.lua rm unuse include

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: rm comment return nil in function get_geolocation()

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>

package/gluon-hoodselector: Makefile refactor pkg description

Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>
---
 package/gluon-hoodselector/Makefile           | 24 ++++++
 package/gluon-hoodselector/check_site.lua     | 26 ++++++
 .../files/usr/lib/micron.d/hoodselector       |  1 +
 .../luasrc/usr/lib/lua/hoodselector/util.lua  | 79 +++++++++++++++++++
 .../luasrc/usr/sbin/hoodselector              | 56 +++++++++++++
 5 files changed, 186 insertions(+)
 create mode 100644 package/gluon-hoodselector/Makefile
 create mode 100644 package/gluon-hoodselector/check_site.lua
 create mode 100644 package/gluon-hoodselector/files/usr/lib/micron.d/hoodselector
 create mode 100644 package/gluon-hoodselector/luasrc/usr/lib/lua/hoodselector/util.lua
 create mode 100755 package/gluon-hoodselector/luasrc/usr/sbin/hoodselector

diff --git a/package/gluon-hoodselector/Makefile b/package/gluon-hoodselector/Makefile
new file mode 100644
index 000000000..394deeae3
--- /dev/null
+++ b/package/gluon-hoodselector/Makefile
@@ -0,0 +1,24 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-hoodselector
+
+GLUON_VERSION:=3
+PKG_VERSION:=2
+
+include ../gluon.mk
+
+define Package/gluon-hoodselector
+  TITLE:=Automatically migrate nodes between domains.
+  DEPENDS:=+luaposix +libgluonutil +lua-math-polygon +libjson-c +gluon-site +micrond +luabitop @GLUON_MULTIDOMAIN
+  CONFLICTS:=+gluon-config-mode-domain-select
+endef
+
+define Package/gluon-hoodselector/description
+  Hoodselector automatically detects in which domain the node is
+  located based on its geolocation settings. Domains require
+  bounding boxes defined as polygons or rectangles. Hoodselector
+  selects a domain from the list of known domains and migrate
+  towards it without requiring a reboot.
+endef
+
+$(eval $(call BuildPackageGluon,gluon-hoodselector))
diff --git a/package/gluon-hoodselector/check_site.lua b/package/gluon-hoodselector/check_site.lua
new file mode 100644
index 000000000..81b9e39e0
--- /dev/null
+++ b/package/gluon-hoodselector/check_site.lua
@@ -0,0 +1,26 @@
+local function check_lat_lon_range(pos, range, label)
+	need({'hoodselector', 'shapes'}, function()
+		if (type(pos) ~= "number") then
+			return false
+		end
+		if pos > range or pos < -range then
+			return false
+		end
+		return true
+	end, true, label.." must match a range +/-"..range)
+end
+
+if this_domain() ~= need_string(in_site({'default_domain'})) then
+	for _, shape in pairs(need_table(in_domain({'hoodselector', 'shapes'}))) do
+		need({'hoodselector', 'shapes'}, function()
+			if #shape < 2 then
+				return false
+			end
+			for _, v in ipairs(shape) do
+				check_lat_lon_range(v.lat, 90.0, "lat")
+				check_lat_lon_range(v.lon, 180.0, "lon")
+			end
+			return true
+		end, true, "needs to have at least 2 coordinates for rectangular shapes.")
+	end
+end
diff --git a/package/gluon-hoodselector/files/usr/lib/micron.d/hoodselector b/package/gluon-hoodselector/files/usr/lib/micron.d/hoodselector
new file mode 100644
index 000000000..6e793151d
--- /dev/null
+++ b/package/gluon-hoodselector/files/usr/lib/micron.d/hoodselector
@@ -0,0 +1 @@
+*/2 * * * * /usr/sbin/hoodselector
diff --git a/package/gluon-hoodselector/luasrc/usr/lib/lua/hoodselector/util.lua b/package/gluon-hoodselector/luasrc/usr/lib/lua/hoodselector/util.lua
new file mode 100644
index 000000000..b93097a66
--- /dev/null
+++ b/package/gluon-hoodselector/luasrc/usr/lib/lua/hoodselector/util.lua
@@ -0,0 +1,79 @@
+local util = require ('gluon.util')
+local math_polygon = require('math-polygon')
+local json = require ('jsonc')
+local uci = require('simple-uci').cursor()
+local site = require ('gluon.site')
+local logger = require('posix.syslog')
+local M = {}
+
+function M.log(msg)
+	io.stdout:write(msg..'\n')
+	logger.openlog(msg, logger.LOG_PID)
+end
+
+function M.get_domains()
+	local list = {}
+	for _, domain_path in ipairs(util.glob('/lib/gluon/domains/*.json')) do
+		table.insert(list, {
+			domain_code = domain_path:match('([^/]+)%.json$'),
+			domain = assert(json.load(domain_path)),
+		})
+	end
+	return list
+end
+
+-- Return the default domain from the domain list.
+-- This method can return the following data:
+-- * default domain
+function M.get_default_domain(jdomains)
+	for _, domain in pairs(jdomains) do
+		if domain.domain_code == site.default_domain() then
+			return domain
+		end
+	end
+end
+
+-- Get Geoposition.
+-- This method can return the following data:
+-- * table {lat, lon}
+function M.get_geolocation()
+	return {
+		lat = tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'latitude')),
+		lon = tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'longitude'))
+	}
+end
+
+-- Return domain from the domain list based on geo position or nil if no geo based domain could be
+-- determined.
+function M.get_domain_by_geo(jdomains, geo)
+	for _, domain in pairs(jdomains) do
+		if domain.domain_code ~= site.default_domain() then
+			-- Keep record of how many nested shapes we are in, e.g. a polyon with holes.
+			local nesting = 1
+			for _, area in pairs(domain.domain.hoodselector.shapes) do
+				-- Convert rectangle, defined by to points, into polygon
+				if #area == 2 then
+					area = math_polygon.two_point_rec_to_poly(area)
+				end
+				if (math_polygon.point_in_polygon(area, geo) == 1) then
+					nesting = nesting * (-1)
+				end
+			end
+			if nesting == -1 then return domain end
+		end
+	end
+	return nil
+end
+
+function M.set_domain_config(domain)
+	if uci:get('gluon', 'core', 'domain') ~= domain.domain_code then
+		uci:set('gluon', 'core', 'domain', domain.domain_code)
+		uci:commit('gluon')
+		os.execute('gluon-reconfigure')
+		M.log('Set domain "'..domain.domain.domain_names[domain.domain_code]..'"')
+		return true
+	end
+	return false
+end
+
+return M
diff --git a/package/gluon-hoodselector/luasrc/usr/sbin/hoodselector b/package/gluon-hoodselector/luasrc/usr/sbin/hoodselector
new file mode 100755
index 000000000..12125da76
--- /dev/null
+++ b/package/gluon-hoodselector/luasrc/usr/sbin/hoodselector
@@ -0,0 +1,56 @@
+#!/usr/bin/lua
+
+local bit = require('bit')
+local unistd = require('posix.unistd')
+local fcntl = require('posix.fcntl')
+local hoodutil = require('hoodselector.util')
+
+-- PID file to ensure the hoodselector isn't running parallel
+local lockfile = '/var/lock/hoodselector.lock'
+local lockfd, err = fcntl.open(lockfile, bit.bor(fcntl.O_WRONLY, fcntl.O_CREAT), 384) -- mode 0600
+
+if not lockfd then
+	hoodutil.log(err, '\n')
+	os.exit(1)
+end
+
+local ok, _ = fcntl.fcntl(lockfd, fcntl.F_SETLK, {
+	l_start = 0,
+	l_len = 0,
+	l_type = fcntl.F_WRLCK,
+	l_whence = unistd.SEEK_SET,
+})
+
+if not ok then
+	io.stderr:write(string.format(
+		"Unable to lock file %s. Make sure there is no other instance of the hoodselector running.\n",
+		lockfile
+	))
+	os.exit(1)
+end
+
+-- geolocation mode
+-- If we have a location we will try to select the domain corresponding to this location.
+-- If no domain for the location has been defined or if we can't determine the node's location,
+-- we will select the default domain as last fallback instance.
+local geo = hoodutil.get_geolocation()
+if geo.lat ~= nil and geo.lon ~= nil then
+	io.stdout:write('Position found. Enter "geolocation mode" ...\n')
+	local jdomains = hoodutil.get_domains()
+	local geo_base_domain = hoodutil.get_domain_by_geo(jdomains, geo)
+	if geo_base_domain ~= nil then
+		if hoodutil.set_domain_config(geo_base_domain) then
+			os.execute("gluon-reload")
+			hoodutil.log('Domain set by geolocation mode.\n')
+		end
+		return
+	end
+	io.stdout:write('No domain has been defined for the current position. Continue with default domain mode\n')
+else
+	io.stdout:write('No position found. Continue with default domain mode\n')
+end
+
+-- default domain mode
+if hoodutil.set_domain_config(hoodutil.get_default_domain(hoodutil.get_domains())) then
+	os.execute("gluon-reload")
+end
-- 
GitLab