move manman to site
This commit is contained in:
		
							parent
							
								
									6f81325a79
								
							
						
					
					
						commit
						59482e53da
					
				| @ -1,13 +0,0 @@ | |||||||
| include $(TOPDIR)/rules.mk |  | ||||||
| 
 |  | ||||||
| PKG_NAME:=gluon-config-mode-manman-sync |  | ||||||
| PKG_VERSION:=2 |  | ||||||
| 
 |  | ||||||
| include ../gluon.mk |  | ||||||
| 
 |  | ||||||
| define Package/gluon-config-mode-manman-sync |  | ||||||
|   TITLE:=Sync location data from manman |  | ||||||
|   DEPENDS:=+gluon-config-mode-core +gluon-manman-sync |  | ||||||
| endef |  | ||||||
| 
 |  | ||||||
| $(eval $(call BuildPackageGluon,gluon-config-mode-manman-sync)) |  | ||||||
| @ -1,36 +0,0 @@ | |||||||
| msgid "" |  | ||||||
| msgstr "" |  | ||||||
| "Project-Id-Version: \n" |  | ||||||
| "POT-Creation-Date: \n" |  | ||||||
| "PO-Revision-Date: 2021-12-15 07:33+0100\n" |  | ||||||
| "Last-Translator: Maciej Krüger <maciej@xeredo.it>\n" |  | ||||||
| "Language-Team: German\n" |  | ||||||
| "Language: de\n" |  | ||||||
| "MIME-Version: 1.0\n" |  | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" |  | ||||||
| "Content-Transfer-Encoding: 8bit\n" |  | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" |  | ||||||
| "X-Generator: Poedit 3.0\n" |  | ||||||
| 
 |  | ||||||
| msgid "Enable ManMan sync" |  | ||||||
| msgstr "ManMan synchronisierung aktivieren" |  | ||||||
| 
 |  | ||||||
| msgid "ManMan location" |  | ||||||
| msgstr "ManMan Standort" |  | ||||||
| 
 |  | ||||||
| msgid "ManMan node (optional)" |  | ||||||
| msgstr "ManMan Knoten (optional)" |  | ||||||
| 
 |  | ||||||
| msgid "Required if multiple nodes in location" |  | ||||||
| msgstr "Erforderlich wenn mehrere Knoten am Standort" |  | ||||||
| 
 |  | ||||||
| msgid "" |  | ||||||
| "Sync configuration from ManMan " |  | ||||||
| "by entering ManMan location and node name here.<br>" |  | ||||||
| "This will automatically keep name, location and ips " |  | ||||||
| "in sync with the values specified in ManMan." |  | ||||||
| msgstr "" |  | ||||||
| "Synchronisiere die Konfiguration aus ManMan, indem du hier den ManMan " |  | ||||||
| "Standort-Namen und, optional, Knoten-Namen eingibst.\n" |  | ||||||
| "Dadurch werden Name, Standort und IP-Addressen automatisch mit den in " |  | ||||||
| "ManMan angegebenen Werten synchronisiert." |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| msgid "" |  | ||||||
| msgstr "" |  | ||||||
| "Project-Id-Version: PACKAGE VERSION\n" |  | ||||||
| "PO-Revision-Date: 2015-08-12 23:30+0100\n" |  | ||||||
| "Last-Translator:Tobias Bernot <tqbs@airmail.cc>\n" |  | ||||||
| "Language-Team: French\n" |  | ||||||
| "Language: fr\n" |  | ||||||
| "MIME-Version: 1.0\n" |  | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" |  | ||||||
| "Content-Transfer-Encoding: 8bit\n" |  | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| msgid "" |  | ||||||
| msgstr "Content-Type: text/plain; charset=UTF-8" |  | ||||||
| 
 |  | ||||||
| msgid "Enable ManMan sync" |  | ||||||
| msgstr "" |  | ||||||
| 
 |  | ||||||
| msgid "ManMan location" |  | ||||||
| msgstr "" |  | ||||||
| 
 |  | ||||||
| msgid "ManMan node (optional)" |  | ||||||
| msgstr "" |  | ||||||
| 
 |  | ||||||
| msgid "Required if multiple nodes in location" |  | ||||||
| msgstr "" |  | ||||||
| 
 |  | ||||||
| msgid "" |  | ||||||
| "Sync configuration from ManMan " |  | ||||||
| "by entering ManMan location and node name here.<br>" |  | ||||||
| "This will automatically keep name, location and ips " |  | ||||||
| "in sync with the values specified in ManMan." |  | ||||||
| msgstr "" |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| local site_i18n = i18n 'gluon-site' |  | ||||||
| 
 |  | ||||||
| local uci = require('simple-uci').cursor() |  | ||||||
| 
 |  | ||||||
| local msg |  | ||||||
| 
 |  | ||||||
| if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then |  | ||||||
| 	msg = site_i18n._translate('gluon-config-mode:manman') |  | ||||||
| else |  | ||||||
| 	msg = site_i18n._translate('gluon-config-mode:no-manman') |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| if not msg then return end |  | ||||||
| 
 |  | ||||||
| renderer.render_string(msg, { |  | ||||||
| 	location_id = uci:get('gluon-manman-sync', 'sync', 'location_id') |  | ||||||
| }) |  | ||||||
| @ -1,46 +0,0 @@ | |||||||
| return function(form, uci) |  | ||||||
| 	local pkg_i18n = i18n 'gluon-config-mode-manman-sync' |  | ||||||
| 
 |  | ||||||
| 	local msg = pkg_i18n.translate( |  | ||||||
| 		'Sync configuration from ManMan ' .. |  | ||||||
| 			'by entering ManMan location and node name here.<br>' .. |  | ||||||
| 		'This will automatically keep name, location and ips ' .. |  | ||||||
| 			'in sync with the values specified in ManMan.' |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	local s = form:section(Section, nil, msg) |  | ||||||
| 
 |  | ||||||
| 	local o |  | ||||||
| 
 |  | ||||||
| 	local manman = s:option(Flag, 'manman_sync', pkg_i18n.translate('Enable ManMan sync')) |  | ||||||
| 	manman.default = uci:get_bool('gluon-manman-sync', 'sync', 'enabled') |  | ||||||
| 	function manman:write(data) |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'enabled', data) |  | ||||||
| 	end |  | ||||||
| 
 |  | ||||||
| 	local id = s:option(Value, 'manman_location', pkg_i18n.translate('ManMan location')) |  | ||||||
| 	id:depends(manman, true) |  | ||||||
| 	id.default = uci:get('gluon-manman-sync', 'sync', 'location') |  | ||||||
| 	id.datatype = 'maxlength(16)' |  | ||||||
| 	function id:write(data) |  | ||||||
| 		-- clear ID, gets fetched from manman-sync |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'location_id', nil) |  | ||||||
| 
 |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'location', data) |  | ||||||
| 	end |  | ||||||
| 
 |  | ||||||
| 	local id = s:option(Value, 'manman_node', pkg_i18n.translate('ManMan node (optional)'), pkg_i18n.translate('Required if multiple nodes in location')) |  | ||||||
| 	id:depends(manman, true) |  | ||||||
| 	id.default = uci:get('gluon-manman-sync', 'sync', 'node') |  | ||||||
| 	id.datatype = 'maxlength(16)' |  | ||||||
| 	function id:write(data) |  | ||||||
| 		-- clear ID, gets fetched from manman-sync |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'node_id', nil) |  | ||||||
| 
 |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'node', data) |  | ||||||
| 	end |  | ||||||
| 
 |  | ||||||
| 	function s:write() |  | ||||||
| 		uci:save('gluon-manman-sync') |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
| @ -1,24 +0,0 @@ | |||||||
| include $(TOPDIR)/rules.mk |  | ||||||
| 
 |  | ||||||
| PKG_NAME:=gluon-manman-sync |  | ||||||
| PKG_VERSION:=1 |  | ||||||
| 
 |  | ||||||
| include ../gluon.mk |  | ||||||
| 
 |  | ||||||
| define Package/gluon-manman-sync |  | ||||||
|   DEPENDS:=+gluon-core +micrond +luci-lib-ip +luci-lib-httpclient +gluon-lib-ecdsa |  | ||||||
|   TITLE:=Sync configuration with data from manman |  | ||||||
| endef |  | ||||||
| 
 |  | ||||||
| define Package/gluon-manman-sync/install |  | ||||||
| 	$(Gluon/Build/Install) |  | ||||||
| 
 |  | ||||||
| 	# httpclient depends on util depends on template.parser & sys.zoneinfo which is from +luci-base |  | ||||||
| 	# +luci-base is quite big and unnesesarry. stubbing the files is enough to make it work. |  | ||||||
| 	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/template |  | ||||||
| 	touch $(1)/usr/lib/lua/luci/template/parser.lua |  | ||||||
| 	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/sys |  | ||||||
| 	touch $(1)/usr/lib/lua/luci/sys/zoneinfo.lua |  | ||||||
| endef |  | ||||||
| 
 |  | ||||||
| $(eval $(call BuildPackageGluon,gluon-manman-sync)) |  | ||||||
| @ -1,2 +0,0 @@ | |||||||
| -- need_array_of(in_site({'manman', 'api'}), string(), false) |  | ||||||
| need_string(in_site({'manman', 'key'}), false) |  | ||||||
| @ -1,14 +0,0 @@ | |||||||
| package gluon-manman-sync |  | ||||||
| 
 |  | ||||||
| config sync 'sync' |  | ||||||
| 	option enabled '0' |  | ||||||
|   option location_id '' |  | ||||||
| 	option node '' |  | ||||||
|   option last_data_hash '' |  | ||||||
|   option last_data '' |  | ||||||
| 
 |  | ||||||
| config rollback 'rollback' |  | ||||||
|   option success '1' |  | ||||||
|   option checked_at '' |  | ||||||
|   option previous_hash '' |  | ||||||
|   option new_hash '' |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| #!/usr/bin/lua |  | ||||||
| 
 |  | ||||||
| -- Setup a cron for manman-sync if enabled |  | ||||||
| 
 |  | ||||||
| local uci = require('simple-uci').cursor() |  | ||||||
| 
 |  | ||||||
| local urandom = io.open('/dev/urandom', 'r') |  | ||||||
| local seed1, seed2 = urandom:read(2):byte(1, 2) |  | ||||||
| math.randomseed(seed1*0x100 + seed2) |  | ||||||
| urandom:close() |  | ||||||
| 
 |  | ||||||
| -- Perform sync at a random time each hour |  | ||||||
| local minute = math.random(0, 59) |  | ||||||
| 
 |  | ||||||
| local f = io.open('/usr/lib/micron.d/manman-sync', 'w') |  | ||||||
| -- Only setup cron if enabled |  | ||||||
| -- Write file regardless to clear old cron |  | ||||||
| if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then |  | ||||||
|   f:write(string.format('%i * * * * /usr/bin/manman-sync sync\n', minute)) |  | ||||||
| end |  | ||||||
| f:close() |  | ||||||
| @ -1,422 +0,0 @@ | |||||||
| #!/usr/bin/lua |  | ||||||
| 
 |  | ||||||
| local uci = require('simple-uci').cursor() |  | ||||||
| local ip = require 'luci.ip' -- luci-lib-ip |  | ||||||
| local fetch = require 'luci.httpclient' |  | ||||||
| local json = require 'luci.jsonc' |  | ||||||
| local ecdsa = require 'gluon.ecdsa' |  | ||||||
| local site = require 'gluon.site' |  | ||||||
| 
 |  | ||||||
| local pretty_hostname = require 'pretty_hostname' |  | ||||||
| local hostname = pretty_hostname.get(uci) |  | ||||||
| 
 |  | ||||||
| local manapi = site.manman.api() |  | ||||||
| local mankey = site.manman.key() |  | ||||||
| 
 |  | ||||||
| -- CLI lib from olsrd-cli (TODO: move to it's own lib package) |  | ||||||
| -- DO NOT EDIT HERE, JUST COPY FROM THERE |  | ||||||
| 
 |  | ||||||
| function printf(...) |  | ||||||
| 	print(string.format(...)) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| -- Print contents of `tbl`, with indentation. |  | ||||||
| -- `indent` sets the initial level of indentation. |  | ||||||
| -- src https://gist.github.com/xytis/5361405 |  | ||||||
| function tprint (tbl, indent) |  | ||||||
|   if not indent then indent = 0 end |  | ||||||
|   for k, v in pairs(tbl) do |  | ||||||
|     formatting = string.rep('  ', indent) .. k .. ': ' |  | ||||||
|     if type(v) == 'table' then |  | ||||||
|       print(formatting) |  | ||||||
|       tprint(v, indent + 1) |  | ||||||
|     else |  | ||||||
|       print(formatting .. tostring(v)) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| -- src https://stackoverflow.com/a/24823383/3990041 |  | ||||||
| function table.slice(tbl, first, last, step) |  | ||||||
|   local sliced = {} |  | ||||||
| 
 |  | ||||||
|   for i = first or 1, last or #tbl, step or 1 do |  | ||||||
|     sliced[#sliced+1] = tbl[i] |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   return sliced |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| -- CLI lib |  | ||||||
| 
 |  | ||||||
| function exec_cmd(args, sub) |  | ||||||
| 	if sub[args[1]] == nil then |  | ||||||
| 		return cmd_err('does not exist') |  | ||||||
| 	else |  | ||||||
| 		local cmd = sub[args[1]] |  | ||||||
| 		if cmd[3] ~= nil and #args > 1 then |  | ||||||
| 			exec_cmd(table.slice(args, 2), cmd[3]) |  | ||||||
| 		else |  | ||||||
| 			cmd[1](unpack(table.slice(args, 2))) |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function list_cmd(level, sub) |  | ||||||
| 	for key, cmd in pairs(sub) do |  | ||||||
| 		printf('%s%s: %s', string.rep('  ', level), key, cmd[2]) |  | ||||||
| 		if cmd[3] ~= nil then |  | ||||||
| 			list_cmd(level + 1, cmd[3]) |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function show_help() |  | ||||||
| 	printf('Usage: %s <command>', arg[0]) |  | ||||||
| 	list_cmd(1, sub) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function cmd_err(msg, no_show_help) |  | ||||||
| 	-- since argv0 is at... well... 0... even though this is lua... |  | ||||||
| 	--- ...slice just returns arg without argv0 as the for starts at 1 |  | ||||||
| 	printf('Error: Command "%s" %s', table.concat(table.slice(arg), ' '), msg) |  | ||||||
| 	if not no_show_help then |  | ||||||
| 		printf('') |  | ||||||
| 		show_help() |  | ||||||
| 	end |  | ||||||
| 	os.exit(2) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function dummy() |  | ||||||
| 	cmd_err('requires a subcommand') |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| -- /// |  | ||||||
| 
 |  | ||||||
| -- NOTE: these will have mesh_ appended for static-ip |  | ||||||
| local mappings = { |  | ||||||
|   wifi = 'radio0', |  | ||||||
|   tunnel = 'vpn', |  | ||||||
|   eth = 'other', |  | ||||||
|   lan = 'other', |  | ||||||
|   wan = 'uplink', |  | ||||||
|   single = 'uplink', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| -- https://stackoverflow.com/a/1647577/3990041 |  | ||||||
| function string:split(pat) |  | ||||||
|   pat = pat or '%s+' |  | ||||||
|   local st, g = 1, self:gmatch("()("..pat..")") |  | ||||||
|   local function getter(segs, seps, sep, cap1, ...) |  | ||||||
|     st = sep and seps + #sep |  | ||||||
|     return self:sub(segs, (seps or 0) - 1), cap1 or sep, ... |  | ||||||
|   end |  | ||||||
|   return function() if st then return getter(st, g()) end end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| -- https://gist.github.com/Uradamus/10323382 |  | ||||||
| local function shuffle(tbl) |  | ||||||
|   for i = #tbl, 2, -1 do |  | ||||||
|     local j = math.random(i) |  | ||||||
|     tbl[i], tbl[j] = tbl[j], tbl[i] |  | ||||||
|   end |  | ||||||
|   return tbl |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function fetch_signed_json(remote, url) |  | ||||||
|   print('GET ' .. url) |  | ||||||
| 
 |  | ||||||
|   local code, res, result = fetch.request_raw(remote .. url) |  | ||||||
| 
 |  | ||||||
|   if code < 1 then |  | ||||||
|     print('E: failed to fetch') |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if code == 404 then |  | ||||||
|     print('E: location does not exist') |  | ||||||
|     return 2 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if code ~= 200 then |  | ||||||
|     print('E: got status code ' .. code) |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   -- cloudflare's reverse proxies send http chunked responses with chunk sizes |  | ||||||
|   -- for whatever reasons the chunk size gets smashed into the result |  | ||||||
|   -- this is a hack to fish it out, it is irrelevant on unaffected reverse proxies |  | ||||||
|   local j_start = result:find('{') |  | ||||||
|   local j_end = (result:reverse()):find("}") |  | ||||||
|   result = string.sub(result, j_start, (1 - j_end) > 0 and (1 - j_end) or nil) |  | ||||||
| 
 |  | ||||||
|   local sig = res.headers['x-ecdsa'] |  | ||||||
|   local ts = res.headers['x-ecdsa-ts'] |  | ||||||
| 
 |  | ||||||
|   if not sig or not ts then |  | ||||||
|     print('E: provided response is not signed') |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local data = ts .. '@' .. result |  | ||||||
|   if not ecdsa.verify(data, sig, mankey) then |  | ||||||
|     print('E: signature invalid or not signed with expected key') |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local obj = json.parse(result) |  | ||||||
| 
 |  | ||||||
|   if obj == nil then |  | ||||||
|     print('E: failed to parse json data') |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   return false, obj |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function do_manman_sync() |  | ||||||
|   if not uci:get('gluon-manman-sync', 'sync', 'location_id') and not uci:get('gluon-manman-sync', 'sync', 'location') then |  | ||||||
|     print('E: manman location missing') |  | ||||||
|     return 2 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   -- check manman reachability, abort if not reachable |  | ||||||
| 
 |  | ||||||
|   local working_remote |  | ||||||
| 
 |  | ||||||
|   for _, remote in ipairs(shuffle(manapi)) do |  | ||||||
|     if not working_remote then -- don't try other remotes if we got one that works |  | ||||||
|       print('Trying remote ' .. remote) |  | ||||||
| 
 |  | ||||||
|       local success, a, b, c = pcall(function() return fetch.request_raw(remote .. '/') end) |  | ||||||
|       if not success then |  | ||||||
|         print('E: couldnt reach manman: ' .. a) |  | ||||||
|       else |  | ||||||
|         if a ~= 200 then |  | ||||||
|           print('E: couldnt reach manman - unexpected fetch result', a, b, c) |  | ||||||
|         else |  | ||||||
|           working_remote = remote |  | ||||||
|         end |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if not working_remote then |  | ||||||
|     print('E: couldnt reach any manapi server, giving up') |  | ||||||
|     return 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   -- try to fetch data |  | ||||||
|   print('Fetching manman data...') |  | ||||||
|   local location_id = uci:get('gluon-manman-sync', 'sync', 'location_id') |  | ||||||
|   local location = uci:get('gluon-manman-sync', 'sync', 'location') |  | ||||||
| 
 |  | ||||||
|   if not location_id and location then |  | ||||||
|     local err, resp = fetch_signed_json(working_remote, '/find_location_by_name/' .. location) |  | ||||||
|     if err then |  | ||||||
|       return err |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     if not resp.id then |  | ||||||
|       printf('E: location %s is invalid (404)', location) |  | ||||||
|       return 2 |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     uci:set('gluon-manman-sync', 'sync', 'location_id', resp.id) |  | ||||||
|     location_id = resp.id |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local err, location = fetch_signed_json(working_remote, '/location/show/' .. location_id) |  | ||||||
|   if err then |  | ||||||
|     return err |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   print('Syncing with location ' .. location.location.name) |  | ||||||
| 
 |  | ||||||
|   uci:set('gluon-manman-sync', 'sync', 'location', location.location.name) |  | ||||||
| 
 |  | ||||||
|   if uci:get('gluon-manman-sync', 'sync', 'last_data') and json.stringify(location) == uci:get('gluon-manman-sync', 'sync', 'last_data') then |  | ||||||
|     print('Nothing changed, skipping sync') |  | ||||||
|     return 0 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local local_router_id |  | ||||||
|   for id, _ in string.split(hostname, '-') do |  | ||||||
|     if id then |  | ||||||
|       local_router_id = id |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local local_node_name = uci:get('gluon-manman-sync', 'sync', 'node') or local_router_id |  | ||||||
|   local local_node = uci:get('gluon-manman-sync', 'sync', 'node_id') or local_router_id |  | ||||||
|   local node |  | ||||||
|   local should_hostname |  | ||||||
| 
 |  | ||||||
|   if #location.nodes > 1 then |  | ||||||
|     for _, potential_node in ipairs(location.nodes) do |  | ||||||
|       if (local_node ~= nil and tostring(potential_node.id) == local_node) or (local_node_name ~= nil and potential_node.name == local_node_name) then |  | ||||||
|         node = potential_node |  | ||||||
|         should_hostname = location.location.name .. '-' .. node.name |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|   else |  | ||||||
|     node = location.nodes[1] |  | ||||||
|     should_hostname = location.location.name |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   -- save node data to update name and persist ID |  | ||||||
|   uci:set('gluon-manman-sync', 'sync', 'node_id', node.id) |  | ||||||
|   uci:set('gluon-manman-sync', 'sync', 'node', node.name) |  | ||||||
| 
 |  | ||||||
|   if node == nil then |  | ||||||
|     print('E: unable to find matching node (selector "' .. node .. '")') |  | ||||||
|     return 2 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   print('Syncing data for node ' .. node.name) |  | ||||||
| 
 |  | ||||||
|   if hostname ~= should_hostname then |  | ||||||
|     print('Renaming node to ' .. should_hostname) |  | ||||||
|     pretty_hostname.set(uci, should_hostname) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local owner = uci:get_first('gluon-node-info', 'owner') |  | ||||||
|   uci:set('gluon-node-info', owner, 'contact', location.administrator.email) |  | ||||||
|   local _location = uci:get_first('gluon-node-info', 'location') |  | ||||||
|   uci:set('gluon-node-info', _location, 'share_location', '1') |  | ||||||
|   uci:set('gluon-node-info', _location, 'latitude', location.location.lat) |  | ||||||
|   uci:set('gluon-node-info', _location, 'longitude', location.location.long) |  | ||||||
| 
 |  | ||||||
|   -- check if anything changed since last time |  | ||||||
|   -- if yes, apply changes and do gluon-reload |  | ||||||
| 
 |  | ||||||
|   local has_changes = false |  | ||||||
| 
 |  | ||||||
|   -- Use this when changing something that needs a reload and/or rollback (not the hostname) |  | ||||||
|   local function set(a, b, c, d, isbool) |  | ||||||
|     local curval |  | ||||||
| 
 |  | ||||||
|     if isbool then |  | ||||||
|       curval = uci:get_bool(a, b, c) |  | ||||||
|     else |  | ||||||
|       curval = uci:get(a, b, c) |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     if curval ~= d then |  | ||||||
|       uci:set(a, b, c, d) |  | ||||||
|       print('  Value', a, b, c, 'changed to', d, 'was', curval) |  | ||||||
|       has_changes = true |  | ||||||
|     else |  | ||||||
|       print('  Value', a, b, c, 'unchanged', d) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   local has_tunnel = false |  | ||||||
| 
 |  | ||||||
|   for _, net in ipairs(node.interfaces) do |  | ||||||
|     local net_name = net.name |  | ||||||
|     if net_name == 'tunnel' or net_name == 'vpn' or net_name == 'mesh_vpn' then |  | ||||||
|       has_tunnel = true |  | ||||||
|     end |  | ||||||
|     local net_mapped = mappings[net_name] or net_name |  | ||||||
|     if not string.find(net_mapped, '_') then |  | ||||||
|       net_mapped = 'mesh_' .. net_mapped |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     local cidr = ip.new(net.ip, net.netmask):string() |  | ||||||
| 
 |  | ||||||
|     print('Syncing ' .. net_name .. ' as ' .. net_mapped .. ' to ' .. cidr) |  | ||||||
|     set('gluon-static-ip', net_mapped, 'ip4', cidr) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   print('Syncing mesh vpn: ' .. (has_tunnel and 'on' or 'off')) |  | ||||||
|   set('gluon', 'mesh_vpn', 'enabled', has_tunnel, true) |  | ||||||
| 
 |  | ||||||
|   uci:set('gluon-manman-sync', 'sync', 'last_data', json.stringify(location)) |  | ||||||
| 
 |  | ||||||
|   uci:save('system') |  | ||||||
|   uci:save('gluon') |  | ||||||
|   uci:save('gluon-manman-sync') |  | ||||||
|   uci:save('gluon-static-ip') |  | ||||||
|   uci:save('gluon-node-info') |  | ||||||
|   os.execute('exec uci commit') |  | ||||||
| 
 |  | ||||||
|   if has_changes then |  | ||||||
|     print('Applying changes...') |  | ||||||
|     os.execute('exec gluon-reconfigure') |  | ||||||
|     os.execute('exec gluon-reload') |  | ||||||
|   else |  | ||||||
|     print('No settings changes, no reason to reload') |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function maybe_sync() |  | ||||||
|   if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then |  | ||||||
|     do_manman_sync() |  | ||||||
|   else |  | ||||||
|     print('manman-sync not enabled, skipping') |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function enable_sync(location, node) |  | ||||||
|   if not uci:get('gluon-manman-sync', 'sync', 'location') and not uci:get('gluon-manman-sync', 'sync', 'location_id') and not location then |  | ||||||
|     cmd_err('requires at least a location (No location was found in config)', true) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if location then |  | ||||||
|     printf('Config location %s', location) |  | ||||||
|     uci:set('gluon-manman-sync', 'sync', 'location', location) |  | ||||||
| 		-- clear ID, gets fetched from manman-sync |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'location_id', nil) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if node then |  | ||||||
|     printf('Config node %s', node) |  | ||||||
|     uci:set('gluon-manman-sync', 'sync', 'node', node) |  | ||||||
| 		-- clear ID, gets fetched from manman-sync |  | ||||||
| 		uci:set('gluon-manman-sync', 'sync', 'node_id', nil) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   if not uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then |  | ||||||
|     uci:set('gluon-manman-sync', 'sync', 'enabled', true) |  | ||||||
|     print('Enabled sync.') |  | ||||||
|     print('Trigger with manman-sync sync') |  | ||||||
|   else |  | ||||||
|     print('Sync already enabled.') |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   uci:save('gluon-manman-sync') |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function disable_sync() |  | ||||||
|   if uci:get_bool('gluon-manman-sync', 'sync', 'enabled') then |  | ||||||
|     uci:set('gluon-manman-sync', 'sync', 'enabled', false) |  | ||||||
|     print('Disabled sync.') |  | ||||||
|     uci:save('gluon-manman-sync') |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function show_info() |  | ||||||
|   tprint({ |  | ||||||
|     enabled = uci:get_bool('gluon-manman-sync', 'sync', 'enabled'), |  | ||||||
|     location = string.format('%s (id %s)', |  | ||||||
|       uci:get('gluon-manman-sync', 'sync', 'location') or '[none]', |  | ||||||
|       uci:get('gluon-manman-sync', 'sync', 'location_id') or '[will autodetect]' |  | ||||||
|     ), |  | ||||||
|     node = string.format('%s (id %s)', |  | ||||||
|       uci:get('gluon-manman-sync', 'sync', 'node') or '[none - use only available or error]', |  | ||||||
|       uci:get('gluon-manman-sync', 'sync', 'node_id') or '[will autodetect]' |  | ||||||
|     ), |  | ||||||
|   }) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| sub = { |  | ||||||
| 	info = { show_info, 'Show manman-sync configuration' }, |  | ||||||
| 	help = { show_help, 'Show help' }, |  | ||||||
|   sync = { maybe_sync, 'Trigger a manman-sync' }, |  | ||||||
|   force_sync = { do_manman_sync, 'Trigger a manman-sync, despite being disabled' }, |  | ||||||
|   enable = { enable_sync, 'Enable sync. [<location>] [<node>]' }, |  | ||||||
|   disable = { disable_sync, 'Disable sync.' } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| exec_cmd(table.slice(arg), sub) |  | ||||||
| @ -1,6 +0,0 @@ | |||||||
| all: respondd.so |  | ||||||
| 
 |  | ||||||
| CFLAGS += -Wall |  | ||||||
| 
 |  | ||||||
| respondd.so: respondd.c |  | ||||||
| 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci |  | ||||||
| @ -1,78 +0,0 @@ | |||||||
| /*
 |  | ||||||
|   Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net> |  | ||||||
|   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. |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #include <respondd.h> |  | ||||||
| 
 |  | ||||||
| #include <json-c/json.h> |  | ||||||
| #include <libgluonutil.h> |  | ||||||
| 
 |  | ||||||
| #include <uci.h> |  | ||||||
| 
 |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_autoupdater(void) { |  | ||||||
| 	struct uci_context *ctx = uci_alloc_context(); |  | ||||||
| 	if (!ctx) |  | ||||||
| 		return NULL; |  | ||||||
| 	ctx->flags &= ~UCI_FLAG_STRICT; |  | ||||||
| 
 |  | ||||||
| 	struct uci_package *p; |  | ||||||
| 	if (uci_load(ctx, "gluon-manman-sync", &p)) |  | ||||||
| 		goto error; |  | ||||||
| 
 |  | ||||||
| 	struct uci_section *s = uci_lookup_section(ctx, p, "sync"); |  | ||||||
| 	if (!s) |  | ||||||
| 		goto error; |  | ||||||
| 
 |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 
 |  | ||||||
| 	json_object_object_add(ret, "location_id", gluonutil_wrap_string(uci_lookup_option_string(ctx, s, "location_id"))); |  | ||||||
| 
 |  | ||||||
| 	const char *enabled = uci_lookup_option_string(ctx, s, "enabled"); |  | ||||||
| 	json_object_object_add(ret, "enabled", json_object_new_boolean(enabled && !strcmp(enabled, "1"))); |  | ||||||
| 
 |  | ||||||
| 	uci_free_context(ctx); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| 
 |  | ||||||
|  error: |  | ||||||
| 	uci_free_context(ctx); |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * respondd_provider_nodeinfo(void) { |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 	json_object_object_add(ret, "manman", get_autoupdater()); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| const struct respondd_provider_info respondd_providers[] = { |  | ||||||
| 	{"nodeinfo", respondd_provider_nodeinfo}, |  | ||||||
| 	{} |  | ||||||
| }; |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user