Merge 33765c7334
into 12103d9638
This commit is contained in:
commit
4f81d55ddf
@ -18,7 +18,7 @@
|
||||
-- 32 bytes of random data, encoded in hexadecimal
|
||||
-- Must be the same for all nodes in one mesh domain
|
||||
-- Can be generated using: echo $(hexdump -v -n 32 -e '1/1 "%02x"' </dev/urandom)
|
||||
site_seed = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
domain_seed = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
|
||||
-- Prefixes used within the mesh.
|
||||
-- prefix6 is required, prefix4 can be omitted if next_node.ip4
|
||||
|
@ -21,7 +21,7 @@ site_code
|
||||
The code of your community. It is good practice to use the TLD of
|
||||
your community here.
|
||||
|
||||
site_seed
|
||||
domain_seed
|
||||
32 bytes of random data, encoded in hexadecimal, used to seed other random
|
||||
values specific to the mesh domain. It must be the same for all nodes of one
|
||||
mesh, but should be different for firmwares that are not supposed to mesh with
|
||||
|
@ -1 +1 @@
|
||||
need_string_array 'authorized_keys'
|
||||
need_string_array(in_site('authorized_keys'))
|
||||
|
@ -1,14 +1,14 @@
|
||||
need_string 'autoupdater.branch'
|
||||
need_string(in_site('autoupdater.branch'))
|
||||
|
||||
local function check_branch(k, _)
|
||||
assert_uci_name(k)
|
||||
local function check_branch(k, conf_name)
|
||||
assert_uci_name(k, conf_name)
|
||||
|
||||
local prefix = string.format('autoupdater.branches[%q].', k)
|
||||
|
||||
need_string(prefix .. 'name')
|
||||
need_string(in_site(prefix .. 'name'))
|
||||
need_string_array_match(prefix .. 'mirrors', '^http://')
|
||||
need_number(prefix .. 'good_signatures')
|
||||
need_string_array_match(prefix .. 'pubkeys', '^%x+$')
|
||||
need_number(in_site(prefix .. 'good_signatures'))
|
||||
need_string_array_match(in_site(prefix .. 'pubkeys'), '^%x+$')
|
||||
end
|
||||
|
||||
need_table('autoupdater.branches', check_branch)
|
||||
|
@ -1,10 +1,10 @@
|
||||
need_string_match('next_node.mac', '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
|
||||
need_string_match(in_domain('next_node.mac'), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
|
||||
|
||||
if need_string_match('next_node.ip4', '^%d+.%d+.%d+.%d+$', false) then
|
||||
need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$')
|
||||
if need_string_match(in_domain('next_node.ip4'), '^%d+.%d+.%d+.%d+$', false) then
|
||||
need_string_match(in_domain('prefix4'), '^%d+.%d+.%d+.%d+/%d+$')
|
||||
end
|
||||
|
||||
need_string_match('next_node.ip6', '^[%x:]+$', false)
|
||||
need_string_match(in_domain('next_node.ip6'), '^[%x:]+$', false)
|
||||
|
||||
|
||||
for _, config in ipairs({'wifi24', 'wifi5'}) do
|
||||
|
@ -1,3 +1,3 @@
|
||||
if need_table('config_mode', nil, false) and need_table('config_mode.owner', nil, false) then
|
||||
need_boolean('config_mode.owner.obligatory', false)
|
||||
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.owner'), nil, false) then
|
||||
need_boolean(in_site('config_mode.owner.obligatory'), false)
|
||||
end
|
||||
|
@ -1,3 +1,3 @@
|
||||
if need_table('config_mode', nil, false) and need_table('config_mode.geo_location', nil, false) then
|
||||
need_boolean('config_mode.geo_location.show_altitude', false)
|
||||
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.geo_location'), nil, false) then
|
||||
need_boolean(in_site('config_mode.geo_location.show_altitude'), false)
|
||||
end
|
||||
|
@ -1,36 +1,50 @@
|
||||
need_string 'site_code'
|
||||
need_string 'site_name'
|
||||
need_string_match('site_seed', '^' .. ('%x'):rep(64) .. '$')
|
||||
need_string(in_site('site_code'))
|
||||
need_string(in_site('site_name'))
|
||||
need_string_match(in_domain('domain_seed'), '^' .. ('%x'):rep(64) .. '$')
|
||||
|
||||
need_string(in_site('default_domain_code'))
|
||||
|
||||
need_string(in_domain('domain_name'))
|
||||
|
||||
function check_alias(k, conf_name)
|
||||
assert_uci_name(k, conf_name)
|
||||
|
||||
local path = string.format('domain_aliases[%q]', k)
|
||||
need_string(in_domain(path))
|
||||
end
|
||||
|
||||
need_table(in_domain('domain_aliases'), check_alias, false)
|
||||
|
||||
if need_table('opkg', nil, false) then
|
||||
need_string('opkg.lede', false)
|
||||
|
||||
function check_repo(k, _)
|
||||
function check_repo(k, conf_name)
|
||||
-- this is not actually a uci name, but using the same naming rules here is fine
|
||||
assert_uci_name(k)
|
||||
assert_uci_name(k, conf_name)
|
||||
|
||||
need_string(string.format('opkg.extra[%q]', k))
|
||||
local path = string.format('opkg.extra[%q]', k)
|
||||
need_string(path)
|
||||
end
|
||||
|
||||
need_table('opkg.extra', check_repo, false)
|
||||
end
|
||||
|
||||
need_string('hostname_prefix', false)
|
||||
need_string 'timezone'
|
||||
need_string(in_site('hostname_prefix'), false)
|
||||
need_string(in_site('timezone'))
|
||||
|
||||
need_string_array('ntp_servers', false)
|
||||
|
||||
need_string_match('prefix6', '^[%x:]+/64$')
|
||||
need_string_match(in_domain('prefix6'), '^[%x:]+/64$')
|
||||
|
||||
|
||||
for _, config in ipairs({'wifi24', 'wifi5'}) do
|
||||
if need_table(config, nil, false) then
|
||||
need_string('regdom') -- regdom is only required when wifi24 or wifi5 is configured
|
||||
need_string(in_site('regdom')) -- regdom is only required when wifi24 or wifi5 is configured
|
||||
|
||||
need_number(config .. '.channel')
|
||||
|
||||
local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000}
|
||||
local supported_rates = need_array_of(config .. '.supported_rates', rates, false)
|
||||
local supported_rates = need_array_of(in_site(config .. '.supported_rates'), rates, false)
|
||||
if supported_rates then
|
||||
need_array_of(config .. '.basic_rate', supported_rates, true)
|
||||
else
|
||||
@ -39,36 +53,36 @@ for _, config in ipairs({'wifi24', 'wifi5'}) do
|
||||
end
|
||||
end
|
||||
|
||||
need_boolean('poe_passthrough', false)
|
||||
need_boolean(in_site('poe_passthrough'), false)
|
||||
if need_table('dns', nil, false) then
|
||||
need_number('dns.cacheentries', false)
|
||||
need_string_array_match('dns.servers', '^[%x:]+$', true)
|
||||
end
|
||||
|
||||
if need_table('next_node', nil, false) then
|
||||
need_string_match('next_node.ip6', '^[%x:]+$', false)
|
||||
need_string_match('next_node.ip4', '^%d+.%d+.%d+.%d+$', false)
|
||||
need_string_match(in_domain('next_node.ip6'), '^[%x:]+$', false)
|
||||
need_string_match(in_domain('next_node.ip4'), '^%d+.%d+.%d+.%d+$', false)
|
||||
end
|
||||
|
||||
for _, config in ipairs({'wifi24', 'wifi5'}) do
|
||||
local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000}
|
||||
rates = need_array_of(config .. '.supported_rates', rates, false) or rates
|
||||
rates = need_array_of(in_site(config .. '.supported_rates'), rates, false) or rates
|
||||
|
||||
if need_table(config .. '.ibss', nil, false) then
|
||||
need_string(config .. '.ibss.ssid')
|
||||
need_string_match(config .. '.ibss.bssid', '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$')
|
||||
need_string(in_domain(config .. '.ibss.ssid'))
|
||||
need_string_match(in_domain(config .. '.ibss.bssid'), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$')
|
||||
need_one_of(config .. '.ibss.mcast_rate', rates, false)
|
||||
need_number(config .. '.ibss.vlan', false)
|
||||
need_boolean(config .. '.ibss.disabled', false)
|
||||
end
|
||||
|
||||
if need_table(config .. '.mesh', nil, false) then
|
||||
need_string(config .. '.mesh.id')
|
||||
need_string(in_domain(config .. '.mesh.id'))
|
||||
need_one_of(config .. '.mesh.mcast_rate', rates, false)
|
||||
need_boolean(config .. '.mesh.disabled', false)
|
||||
end
|
||||
end
|
||||
|
||||
need_boolean('mesh_on_wan', false)
|
||||
need_boolean('mesh_on_lan', false)
|
||||
need_boolean('single_as_lan', false)
|
||||
need_boolean(in_site('mesh_on_wan'), false)
|
||||
need_boolean(in_site('mesh_on_lan'), false)
|
||||
need_boolean(in_site('single_as_lan'), false)
|
||||
|
1
package/gluon-core/files/etc/config/gluon
Normal file
1
package/gluon-core/files/etc/config/gluon
Normal file
@ -0,0 +1 @@
|
||||
config system system
|
10
package/gluon-core/files/lib/gluon/domain_changed.sh
Executable file
10
package/gluon-core/files/lib/gluon/domain_changed.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
domain_code=$(uci get gluon.system.domain_code)
|
||||
|
||||
[ -f /lib/gluon/domains/${domain_code}.json ] || (echo "file not found: /lib/gluon/domains/${domain_code}.json" >&2; exit 1) || exit 1
|
||||
|
||||
for s in /lib/gluon/upgrade/*; do
|
||||
echo -n ${s}:
|
||||
(${s} && echo " ok") || echo " error"
|
||||
done
|
@ -44,7 +44,7 @@ proto_gluon_wired_setup() {
|
||||
json_add_string tunlink "$config"
|
||||
json_add_string ip6addr "$(interface_linklocal "$ifname")"
|
||||
json_add_string peer6addr 'ff02::15c'
|
||||
json_add_int vid "$(lua -lgluon.util -e 'print(tonumber(gluon.util.site_seed_bytes("gluon-mesh-vxlan", 3), 16))')"
|
||||
json_add_int vid "$(lua -lgluon.util -e 'print(tonumber(gluon.util.domain_seed_bytes("gluon-mesh-vxlan", 3), 16))')"
|
||||
json_close_object
|
||||
ubus call network add_dynamic "$(json_dump)"
|
||||
fi
|
||||
|
5
package/gluon-core/luasrc/usr/bin/gluon-print-site
Executable file
5
package/gluon-core/luasrc/usr/bin/gluon-print-site
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/lua
|
||||
local jsonc = require 'luci.jsonc'
|
||||
local site = require 'gluon.site'
|
||||
|
||||
print(jsonc.stringify(site(), true))
|
@ -1,11 +1,10 @@
|
||||
local site = (function()
|
||||
local config = '/lib/gluon/site.json'
|
||||
local function read_json(path)
|
||||
|
||||
local json = require 'luci.jsonc'
|
||||
local decoder = json.new()
|
||||
local sink = decoder:sink()
|
||||
|
||||
local file = assert(io.open(config))
|
||||
local file = assert(io.open(path))
|
||||
|
||||
while true do
|
||||
local chunk = file:read(2048)
|
||||
@ -16,11 +15,40 @@ local site = (function()
|
||||
file:close()
|
||||
|
||||
return assert(decoder:get())
|
||||
end)()
|
||||
end
|
||||
|
||||
local site = read_json('/lib/gluon/site.json')
|
||||
local domain = (function(site)
|
||||
local uci = require('simple-uci').cursor()
|
||||
local fs = require "nixio.fs"
|
||||
local sname = uci:get_first('gluon', 'system')
|
||||
|
||||
local dc = uci:get('gluon', sname, 'domain_code') or ''
|
||||
|
||||
if fs.stat('/lib/gluon/domains/'..dc..'.json', 'type')~='reg' then
|
||||
dc = site['default_domain_code']
|
||||
end
|
||||
|
||||
local domain = read_json('/lib/gluon/domains/'..dc..'.json')
|
||||
if domain['domain_aliases'] and domain['domain_aliases'][dc] then
|
||||
domain['domain_name'] = domain['domain_aliases'][dc]
|
||||
end
|
||||
return domain
|
||||
end)(site)
|
||||
|
||||
|
||||
local wrap
|
||||
|
||||
local function merge(t1, t2)
|
||||
for k, v in pairs(t2) do
|
||||
if (type(v) == "table") and (type(t1[k] or false) == "table") then
|
||||
merge(t1[k], t2[k])
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
end
|
||||
return t1
|
||||
end
|
||||
|
||||
local function index(t, k)
|
||||
local v = getmetatable(t).value
|
||||
@ -58,4 +86,4 @@ end
|
||||
|
||||
module 'gluon.site'
|
||||
|
||||
return wrap(site, _M)
|
||||
return wrap(merge(site, domain), _M)
|
||||
|
@ -122,7 +122,7 @@ function node_id()
|
||||
return string.gsub(sysconfig.primary_mac, ':', '')
|
||||
end
|
||||
|
||||
function site_seed_bytes(key, length)
|
||||
function domain_seed_bytes(key, length)
|
||||
local ret = ''
|
||||
local v = ''
|
||||
local i = 0
|
||||
@ -131,7 +131,7 @@ function site_seed_bytes(key, length)
|
||||
-- cryptographic strength
|
||||
while ret:len() < 2*length do
|
||||
i = i + 1
|
||||
v = hash.md5(v .. key .. site.site_seed():lower() .. i)
|
||||
v = hash.md5(v .. key .. site.domain_seed():lower() .. i)
|
||||
ret = ret .. v
|
||||
end
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$', false)
|
||||
need_string_array_match('extra_prefixes6', '^[%x:]+/%d+$', false)
|
||||
need_string_match(in_domain('prefix4'), '^%d+.%d+.%d+.%d+/%d+$', false)
|
||||
need_string_array_match(in_domain('extra_prefixes6'), '^[%x:]+/%d+$', false)
|
||||
|
@ -1,23 +1,23 @@
|
||||
local fastd_methods = {'salsa2012+gmac', 'salsa2012+umac', 'null+salsa2012+gmac', 'null+salsa2012+umac', 'null'}
|
||||
need_array_of('mesh_vpn.fastd.methods', fastd_methods)
|
||||
need_boolean('mesh_vpn.fastd.configurable', false)
|
||||
need_boolean(in_site('mesh_vpn.fastd.configurable'), false)
|
||||
|
||||
need_one_of('mesh_vpn.fastd.syslog_level', {'error', 'warn', 'info', 'verbose', 'debug', 'debug2'}, false)
|
||||
need_one_of(in_site('mesh_vpn.fastd.syslog_level'), {'error', 'warn', 'info', 'verbose', 'debug', 'debug2'}, false)
|
||||
|
||||
local function check_peer(prefix)
|
||||
return function(k, _)
|
||||
assert_uci_name(k)
|
||||
return function(k, conf_name)
|
||||
assert_uci_name(k, conf_name)
|
||||
|
||||
local table = string.format('%s[%q].', prefix, k)
|
||||
|
||||
need_string_match(table .. 'key', '^%x+$')
|
||||
need_string_array(table .. 'remotes')
|
||||
need_string_array(in_domain(table .. 'remotes'))
|
||||
end
|
||||
end
|
||||
|
||||
local function check_group(prefix)
|
||||
return function(k, _)
|
||||
assert_uci_name(k)
|
||||
return function(k, conf_name)
|
||||
assert_uci_name(k, conf_name)
|
||||
|
||||
local table = string.format('%s[%q].', prefix, k)
|
||||
|
||||
|
@ -1 +1 @@
|
||||
need_string('roles.default', false)
|
||||
need_string(in_site('roles.default'), false)
|
||||
|
@ -67,6 +67,11 @@ static struct json_object * get_site_code(void) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct json_object * get_domain_code(void) {
|
||||
char * domain_code = gluonutil_get_selected_domain_code(NULL);
|
||||
return gluonutil_wrap_and_free_string(domain_code);
|
||||
}
|
||||
|
||||
static struct json_object * get_hostname(void) {
|
||||
struct json_object *ret = NULL;
|
||||
|
||||
@ -123,6 +128,7 @@ static struct json_object * respondd_provider_nodeinfo(void) {
|
||||
|
||||
struct json_object *system = json_object_new_object();
|
||||
json_object_object_add(system, "site_code", get_site_code());
|
||||
json_object_object_add(system, "domain_code", get_domain_code());
|
||||
json_object_object_add(ret, "system", system);
|
||||
|
||||
return ret;
|
||||
|
@ -1,2 +1 @@
|
||||
need_boolean('setup_mode.skip', false)
|
||||
|
||||
need_boolean(in_site('setup_mode.skip'), false)
|
||||
|
@ -8,7 +8,7 @@ PKG_VERSION:=$(if $(DUMP),x,$(GLUON_SITE_VERSION))
|
||||
|
||||
|
||||
PKG_CONFIG_DEPENDS := CONFIG_GLUON_RELEASE CONFIG_GLUON_SITEDIR
|
||||
PKG_FILE_DEPENDS := $(GLUON_SITEDIR)/site.conf $(GLUON_SITEDIR)/i18n/
|
||||
PKG_FILE_DEPENDS := $(GLUON_SITEDIR)/site.conf $(GLUON_SITEDIR)/domains/ $(GLUON_SITEDIR)/i18n/
|
||||
PKG_BUILD_DEPENDS := lua-cjson/host
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
@ -38,6 +38,7 @@ endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
mkdir -p $(PKG_BUILD_DIR)/domains
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
@ -45,12 +46,26 @@ endef
|
||||
|
||||
define Build/Compile
|
||||
GLUON_SITEDIR='$(call qstrip,$(CONFIG_GLUON_SITEDIR))' lua -e 'print(require("cjson").encode(assert(dofile("../../scripts/site_config.lua"))))' > $(PKG_BUILD_DIR)/site.json
|
||||
ls $(call qstrip,$(CONFIG_GLUON_SITEDIR))/domains/*.conf > /dev/null # at least one domain cfg has to exist
|
||||
GLUON_SITEDIR='$(call qstrip,$(CONFIG_GLUON_SITEDIR))' lua -e 'print(assert(dofile("../../scripts/site_config.lua")).default_domain_code)' > $(PKG_BUILD_DIR)/default_domain_code
|
||||
ls '$(call qstrip,$(CONFIG_GLUON_SITEDIR))'/domains/$$$$(cat $(PKG_BUILD_DIR)/default_domain_code).conf # ensure default_domain_code exists
|
||||
rm -f $(PKG_BUILD_DIR)/domains/*.json
|
||||
for domain_cfg in `find $(call qstrip,$(CONFIG_GLUON_SITEDIR))/domains/ -iname \*.conf -printf "%f\n"`; do \
|
||||
dc=$$$${domain_cfg%.conf}; \
|
||||
GLUON_SITEDIR='$(call qstrip,$(CONFIG_GLUON_SITEDIR))' lua -e 'print(require("cjson").encode(assert(dofile("../../scripts/domain_config.lua")("'$$$${dc}'"))))' > $(PKG_BUILD_DIR)/domains/$$$${dc}.json; \
|
||||
aliases=$$$$(GLUON_SITEDIR='$(call qstrip,$(CONFIG_GLUON_SITEDIR))' lua -e 'for alias_name, _ in pairs(dofile("../../scripts/domain_config.lua")("'$$$${dc}'")["domain_aliases"] or {}) do print(alias_name.." ") end'); \
|
||||
for alias in $$$${aliases}; do \
|
||||
ln -s $$$${dc}.json $(PKG_BUILD_DIR)/domains/$$$${alias}.json; \
|
||||
done; \
|
||||
done
|
||||
$(call GluonBuildI18N,gluon-site,$(GLUON_SITEDIR)/i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-site/install
|
||||
$(INSTALL_DIR) $(1)/lib/gluon
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/site.json $(1)/lib/gluon/
|
||||
$(INSTALL_DIR) $(1)/lib/gluon/domains
|
||||
$(CP) $(PKG_BUILD_DIR)/domains/*.json $(1)/lib/gluon/domains/
|
||||
echo '$(GLUON_SITE_VERSION)' > $(1)/lib/gluon/site-version
|
||||
echo '$(call qstrip,$(CONFIG_GLUON_RELEASE))' > $(1)/lib/gluon/release
|
||||
|
||||
|
@ -53,6 +53,7 @@ define(["lib/helper"], function (Helper) {
|
||||
dlEntry(list, nodeInfo, "software.fastd.enabled", Helper._("Mesh VPN"), enabledDisabled)
|
||||
dlEntry(list, nodeInfo, "software.autoupdater.enabled", Helper._("Automatic updates"), enabledDisabled)
|
||||
dlEntry(list, nodeInfo, "software.autoupdater.branch", Helper._("Branch"))
|
||||
dlEntry(list, nodeInfo, "system.domain_code", Helper._("Domain"))
|
||||
|
||||
el.appendChild(list)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
if need_table('config_mode', nil, false) and need_table('config_mode.remote_login', nil, false) then
|
||||
need_boolean('config_mode.remote_login.show_password_form', false)
|
||||
need_number('config_mode.remote_login.min_password_length', false)
|
||||
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.remote_login'), nil, false) then
|
||||
need_boolean(in_site('config_mode.remote_login.show_password_form'), false)
|
||||
need_number(in_site('config_mode.remote_login.min_password_length'), false)
|
||||
end
|
||||
|
@ -1,2 +1,2 @@
|
||||
assert(need_boolean('mesh_vpn.fastd.configurable') == true,
|
||||
assert(need_boolean(in_site('mesh_vpn.fastd.configurable')) == true,
|
||||
"site.conf error: expected `mesh_vpn.fastd.configurable' to be true")
|
||||
|
@ -1,2 +1,2 @@
|
||||
need_string 'roles.default'
|
||||
need_string_array 'roles.list'
|
||||
need_string(in_site('roles.default'))
|
||||
need_string_array(in_site('roles.list'))
|
||||
|
@ -11,7 +11,24 @@ local site_json = f:read('*a')
|
||||
f:close()
|
||||
|
||||
site = require('cjson').decode(site_json)
|
||||
$(shell cat '$(TOPDIR)/../scripts/check_site_lib.lua' '$(1)' | sed -ne '1h; 1!H; $$ {g; s/@/+@/g; s/\n/-@/g; p}')
|
||||
|
||||
function check_domain(domain_code, domain)
|
||||
$(shell cat '$(TOPDIR)/../scripts/check_site_lib.lua' '$(1)' | sed -ne '1h; 1!H; $$ {g; s/@/+@/g; s/\n/-@/g; p}')
|
||||
end
|
||||
|
||||
local dir = os.getenv('IPKG_INSTROOT') .. '/lib/gluon/domains/'
|
||||
local pfile = io.popen('find '..dir..' -iname \\*.json')
|
||||
for domain_path in pfile:lines() do
|
||||
local domain_code = string.gmatch(domain_path, '([^/]+).json$$')()
|
||||
local f = assert(io.open(domain_path))
|
||||
local domain_json = f:read('*a')
|
||||
f:close()
|
||||
|
||||
domain = require('cjson').decode(domain_json)
|
||||
|
||||
check_domain(domain_code, domain)
|
||||
end
|
||||
pfile:close()
|
||||
END__GLUON__CHECK__SITE
|
||||
endef
|
||||
|
||||
|
@ -16,7 +16,7 @@ define Package/libgluonutil
|
||||
SECTION:=libs
|
||||
CATEGORY:=Libraries
|
||||
TITLE:=Gluon utility library
|
||||
DEPENDS:=+libjson-c
|
||||
DEPENDS:=+libjson-c +libuci
|
||||
endef
|
||||
|
||||
CMAKE_OPTIONS += \
|
||||
|
@ -7,14 +7,15 @@ project(libgluonutil C)
|
||||
set(LIBDIR "lib${LIB_SUFFIX}")
|
||||
|
||||
find_package(JSON_C REQUIRED)
|
||||
find_package(UCI REQUIRED)
|
||||
|
||||
set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE)
|
||||
|
||||
add_library(gluonutil SHARED libgluonutil.c)
|
||||
set_property(TARGET gluonutil PROPERTY COMPILE_FLAGS "-Wall -std=c99 ${JSON_C_CFLAGS_OTHER}")
|
||||
set_property(TARGET gluonutil PROPERTY LINK_FLAGS "${JSON_C_LDFLAGS_OTHER}")
|
||||
set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR})
|
||||
target_link_libraries(gluonutil ${JSON_C_LIBRARIES})
|
||||
set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR} ${UCI_INCLUDE_DIR})
|
||||
target_link_libraries(gluonutil ${JSON_C_LIBRARIES} ${UCI_LIBRARIES})
|
||||
install(TARGETS gluonutil
|
||||
ARCHIVE DESTINATION ${LIBDIR}
|
||||
LIBRARY DESTINATION ${LIBDIR}
|
||||
|
21
package/libgluonutil/src/FindUCI.cmake
Normal file
21
package/libgluonutil/src/FindUCI.cmake
Normal file
@ -0,0 +1,21 @@
|
||||
# UCI_FOUND - true if library and headers were found
|
||||
# UCI_INCLUDE_DIRS - include directories
|
||||
# UCI_LIBRARIES - library directories
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_UCI QUIET uci)
|
||||
|
||||
find_path(UCI_INCLUDE_DIR uci.h
|
||||
HINTS ${PC_UCI_INCLUDEDIR} ${PC_UCI_INCLUDE_DIRS})
|
||||
|
||||
find_library(UCI_LIBRARY NAMES uci libuci
|
||||
HINTS ${PC_UCI_LIBDIR} ${PC_UCI_LIBRARY_DIRS})
|
||||
|
||||
set(UCI_LIBRARIES ${UCI_LIBRARY})
|
||||
set(UCI_INCLUDE_DIRS ${UCI_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(UCI DEFAULT_MSG UCI_LIBRARY UCI_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(UCI_INCLUDE_DIR UCI_LIBRARY)
|
@ -31,7 +31,41 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <uci.h>
|
||||
|
||||
/**
|
||||
* Merges two JSON objects
|
||||
*
|
||||
* On conflicts, object a will be preferred.
|
||||
*
|
||||
* Internally, this functions merges all entries from object a into object b,
|
||||
* so merging a small object a with a big object b is faster than vice-versa.
|
||||
*/
|
||||
static struct json_object * merge_json(struct json_object *a, struct json_object *b) {
|
||||
if (!json_object_is_type(a, json_type_object) || !json_object_is_type(b, json_type_object)) {
|
||||
json_object_put(b);
|
||||
return a;
|
||||
}
|
||||
|
||||
json_object_object_foreach(a, key, val_a) {
|
||||
struct json_object *val_b;
|
||||
|
||||
json_object_get(val_a);
|
||||
|
||||
if (!json_object_object_get_ex(b, key, &val_b)) {
|
||||
json_object_object_add(b, key, val_a);
|
||||
continue;
|
||||
}
|
||||
|
||||
json_object_get(val_b);
|
||||
|
||||
json_object_object_add(b, key, merge_json(val_a, val_b));
|
||||
}
|
||||
|
||||
json_object_put(a);
|
||||
return b;
|
||||
}
|
||||
|
||||
char * gluonutil_read_line(const char *filename) {
|
||||
FILE *f = fopen(filename, "r");
|
||||
@ -140,7 +174,139 @@ bool gluonutil_get_node_prefix6(struct in6_addr *prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char * get_selected_domain_code(struct json_object * base) {
|
||||
char * domain_path_fmt = "/lib/gluon/domains/%s.json";
|
||||
char domain_path[strlen(domain_path_fmt) + 256];
|
||||
const char * domain_code;
|
||||
|
||||
struct uci_context *ctx = uci_alloc_context();
|
||||
if (!ctx)
|
||||
goto uci_fail;
|
||||
|
||||
ctx->flags &= ~UCI_FLAG_STRICT;
|
||||
|
||||
struct uci_package *p;
|
||||
if (uci_load(ctx, "gluon", &p))
|
||||
goto uci_fail;
|
||||
|
||||
struct uci_section *s = uci_lookup_section(ctx, p, "system");
|
||||
if (!s)
|
||||
goto uci_fail;
|
||||
|
||||
domain_code = uci_lookup_option_string(ctx, s, "domain_code");
|
||||
|
||||
if (!domain_code)
|
||||
goto uci_fail;
|
||||
|
||||
snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code);
|
||||
|
||||
if (access(domain_path, R_OK) != -1) {
|
||||
// ${domain_code}.conf exists and is accessible
|
||||
char * domain_code_cpy = strndup(domain_code, 256); // copy before free
|
||||
uci_free_context(ctx);
|
||||
return domain_code_cpy;
|
||||
}
|
||||
|
||||
uci_fail:
|
||||
if (ctx)
|
||||
uci_free_context(ctx);
|
||||
|
||||
json_object * default_domain_code;
|
||||
|
||||
// it's okay to pass base == NULL to json_object_object_get_ex()
|
||||
if (!json_object_object_get_ex(base, "default_domain_code", &default_domain_code))
|
||||
return NULL;
|
||||
|
||||
domain_code = json_object_get_string(default_domain_code);
|
||||
|
||||
if (!domain_code)
|
||||
return NULL;
|
||||
|
||||
// the gluon build environment should ensure, that this filename exists,
|
||||
// but to be sure, we check here again.
|
||||
snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code);
|
||||
|
||||
if (access(domain_path, R_OK) == -1)
|
||||
return NULL;
|
||||
|
||||
// create a copy so site could be freed before domain_code
|
||||
return strndup(domain_code, 256);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected domain code
|
||||
*
|
||||
* - If NULL is passed to the site parameter, internally only the base part
|
||||
* (without domain config) is loaded, which is more efficient than calling
|
||||
* gluonutil_load_site_config() for this job only. Nevertheless if you already
|
||||
* have an instance of a site object then you should pass it here.
|
||||
* - Returned domain code string has to be freed after use
|
||||
* - Returns NULL in case of error
|
||||
* - If a domain code is returned, it's ensured that the corresponding config
|
||||
* in /lib/gluon/domains/ exists.
|
||||
*/
|
||||
char * gluonutil_get_selected_domain_code(struct json_object * site) {
|
||||
if (site)
|
||||
// If the user already has allocated a whole site object, it makes no sense
|
||||
// to load the base object. Taking the site object (merged from domain and
|
||||
// base) should be fine here.
|
||||
return get_selected_domain_code(site);
|
||||
|
||||
// load base
|
||||
struct json_object * base = json_object_from_file("/lib/gluon/site.json");
|
||||
|
||||
if (!base)
|
||||
return NULL;
|
||||
|
||||
return get_selected_domain_code(base);
|
||||
}
|
||||
|
||||
struct json_object * gluonutil_load_site_config(void) {
|
||||
return json_object_from_file("/lib/gluon/site.json");
|
||||
// load base
|
||||
struct json_object * base = json_object_from_file("/lib/gluon/site.json");
|
||||
|
||||
if (!base)
|
||||
return NULL;
|
||||
|
||||
// load domain
|
||||
char * domain_path_fmt = "/lib/gluon/domains/%s.json";
|
||||
char domain_path[strlen(domain_path_fmt) + 256];
|
||||
char * domain_code = get_selected_domain_code(base);
|
||||
|
||||
if (!domain_code) {
|
||||
// something went horribly wrong here
|
||||
json_object_put(base); // free base
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code);
|
||||
|
||||
|
||||
struct json_object * domain = json_object_from_file(domain_path);
|
||||
|
||||
if (!domain) {
|
||||
json_object_put(base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_object * aliases;
|
||||
|
||||
// it's okay to pass base == NULL to json_object_object_get_ex()
|
||||
if (!json_object_object_get_ex(domain, "domain_aliases", &aliases))
|
||||
goto skip_name_replacement;
|
||||
|
||||
json_object * aliased_domain_name;
|
||||
|
||||
if (!json_object_object_get_ex(aliases, domain_code, &aliased_domain_name))
|
||||
goto skip_name_replacement;
|
||||
|
||||
// freeing of old value is done inside json_object_object_add()
|
||||
json_object_object_add(domain, "domain_name", json_object_get(aliased_domain_name));
|
||||
|
||||
skip_name_replacement:
|
||||
|
||||
free(domain_code);
|
||||
|
||||
// finally merge them
|
||||
return merge_json(domain, base);
|
||||
}
|
||||
|
@ -42,4 +42,18 @@ struct json_object * gluonutil_wrap_and_free_string(char *str);
|
||||
|
||||
struct json_object * gluonutil_load_site_config(void);
|
||||
|
||||
/**
|
||||
* Get selected domain code
|
||||
*
|
||||
* - If NULL is passed to the site parameter, internally only the base part
|
||||
* (without domain config) is loaded, which is more efficient than calling
|
||||
* gluonutil_load_site_config() for this job only. Nevertheless if you already
|
||||
* have an instance of a site object then you should pass it here.
|
||||
* - Returned domain code string has to be freed after use
|
||||
* - Returns NULL in case of error
|
||||
* - If a domain code is returned, it's ensured that the corresponding config
|
||||
* in /lib/gluon/domains/ exists.
|
||||
*/
|
||||
char * gluonutil_get_selected_domain_code(struct json_object * site);
|
||||
|
||||
#endif /* _LIBGLUON_LIBGLUON_H_ */
|
||||
|
@ -1,9 +1,32 @@
|
||||
local function loadvar(varname)
|
||||
local ok, val = pcall(assert(loadstring('return site.' .. varname)))
|
||||
if ok then
|
||||
return val
|
||||
local ok, val = pcall(assert(loadstring('return domain.' .. varname)))
|
||||
if ok and val ~= nil then
|
||||
return val, 'domains/'..domain_code..'.conf'
|
||||
end
|
||||
|
||||
ok, val = pcall(assert(loadstring('return site.' .. varname)))
|
||||
if ok and val ~= nil then
|
||||
return val, 'site.conf'
|
||||
else
|
||||
return nil
|
||||
return nil, 'site.conf or domains/'..domain_code..'.conf'
|
||||
end
|
||||
end
|
||||
|
||||
local function loadvar_domain(varname)
|
||||
local ok, val = pcall(assert(loadstring('return domain.' .. varname)))
|
||||
if ok then
|
||||
return val, 'domains/'..domain_code..'.conf'
|
||||
else
|
||||
return nil, 'domains/'..domain_code..'.conf'
|
||||
end
|
||||
end
|
||||
|
||||
local function loadvar_site(varname)
|
||||
ok, val = pcall(assert(loadstring('return site.' .. varname)))
|
||||
if ok then
|
||||
return val, 'site.conf'
|
||||
else
|
||||
return nil, 'site.conf'
|
||||
end
|
||||
end
|
||||
|
||||
@ -32,68 +55,87 @@ local function assert_type(var, t, msg)
|
||||
assert(type(var) == t, msg)
|
||||
end
|
||||
|
||||
-- returns an unique keys in keys of returned table
|
||||
function keys_merged(a, b)
|
||||
keys_table = {}
|
||||
for k, _ in pairs(a or {}) do
|
||||
keys_table[k] = 1
|
||||
end
|
||||
for k, _ in pairs(b or {}) do
|
||||
keys_table[k] = 1
|
||||
end
|
||||
return keys_table
|
||||
end
|
||||
|
||||
function assert_uci_name(var)
|
||||
function forbid_in_domain(varname)
|
||||
local ok, val = pcall(assert(loadstring('return domain.' .. varname)))
|
||||
assert(not ok or val == nil, "domains/"..domain_code..".conf error: `"..varname.."` is not allowed in domain specific config.")
|
||||
end
|
||||
|
||||
function forbid_in_site(varname)
|
||||
local ok, val = pcall(assert(loadstring('return site.' .. varname)))
|
||||
assert(not ok or val == nil, "site.conf error: `"..varname.."` is not allowed in site config.")
|
||||
end
|
||||
|
||||
function assert_uci_name(var, conf_name)
|
||||
-- We don't use character classes like %w here to be independent of the locale
|
||||
assert(var:match('^[0-9a-zA-Z_]+$'), "site.conf error: `" .. var .. "' is not a valid config section name (only alphanumeric characters and the underscore are allowed)")
|
||||
assert(var:match('^[0-9a-zA-Z_]+$'), conf_name.." error: `" .. var .. "' is not a valid config section name (only alphanumeric characters and the underscore are allowed)")
|
||||
end
|
||||
|
||||
|
||||
function need_string(varname, required)
|
||||
local var = loadvar(varname)
|
||||
local var, conf_name = loadvar(varname)
|
||||
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
return nil, conf_name
|
||||
end
|
||||
|
||||
assert_type(var, 'string', "site.conf error: expected `" .. varname .. "' to be a string")
|
||||
return var
|
||||
assert_type(var, 'string', conf_name .. " error: expected `" .. varname .. "' to be a string")
|
||||
return var, conf_name
|
||||
end
|
||||
|
||||
function need_string_match(varname, pat, required)
|
||||
local var = need_string(varname, required)
|
||||
local var, conf_name = need_string(varname, required)
|
||||
|
||||
if not var then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert(var:match(pat), "site.conf error: expected `" .. varname .. "' to match pattern `" .. pat .. "'")
|
||||
assert(var:match(pat), conf_name.." error: expected `" .. varname .. "' to match pattern `" .. pat .. "'")
|
||||
|
||||
return var
|
||||
end
|
||||
|
||||
function need_number(varname, required)
|
||||
local var = loadvar(varname)
|
||||
local var, conf_name = loadvar(varname)
|
||||
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert_type(var, 'number', "site.conf error: expected `" .. varname .. "' to be a number")
|
||||
assert_type(var, 'number', conf_name.." error: expected `" .. varname .. "' to be a number")
|
||||
|
||||
return var
|
||||
end
|
||||
|
||||
function need_boolean(varname, required)
|
||||
local var = loadvar(varname)
|
||||
local var, conf_name = loadvar(varname)
|
||||
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert_type(var, 'boolean', "site.conf error: expected `" .. varname .. "' to be a boolean")
|
||||
assert_type(var, 'boolean', conf_name.." error: expected `" .. varname .. "' to be a boolean")
|
||||
|
||||
return var
|
||||
end
|
||||
|
||||
function need_array(varname, subcheck, required)
|
||||
local var = loadvar(varname)
|
||||
|
||||
local function __need_array_from_var(var, varname, subcheck, required, conf_name)
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert_type(var, 'table', "site.conf error: expected `" .. varname .. "' to be an array")
|
||||
assert_type(var, 'table', conf_name.." error: expected `" .. varname .. "' to be an array")
|
||||
|
||||
for _, e in ipairs(var) do
|
||||
subcheck(e)
|
||||
@ -102,18 +144,27 @@ function need_array(varname, subcheck, required)
|
||||
return var
|
||||
end
|
||||
|
||||
function need_array(varname, subcheck, required)
|
||||
local var, conf_name = loadvar(varname)
|
||||
return __need_array_from_var(var, varname, subcheck, required, conf_name)
|
||||
end
|
||||
|
||||
|
||||
function need_table(varname, subcheck, required)
|
||||
local var = loadvar(varname)
|
||||
local var, conf_name = loadvar(varname)
|
||||
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert_type(var, 'table', "site.conf error: expected `" .. varname .. "' to be a table")
|
||||
assert_type(var, 'table', conf_name.." error: expected `" .. varname .. "' to be a table")
|
||||
|
||||
local dvar = loadvar_domain(varname)
|
||||
local svar = loadvar_site(varname)
|
||||
|
||||
if subcheck then
|
||||
for k, v in pairs(var) do
|
||||
subcheck(k, v)
|
||||
for k, _ in pairs(keys_merged(dvar, svar)) do
|
||||
subcheck(k, conf_name)
|
||||
end
|
||||
end
|
||||
|
||||
@ -121,31 +172,44 @@ function need_table(varname, subcheck, required)
|
||||
end
|
||||
|
||||
function need_one_of(varname, array, required)
|
||||
local var = loadvar(varname)
|
||||
local var, conf_name = loadvar(varname)
|
||||
|
||||
if required == false and var == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
assert_one_of(var, array, "site.conf error: expected `" .. varname .. "' to be one of given array: " .. array_to_string(array))
|
||||
assert_one_of(var, array, conf_name.." error: expected `" .. varname .. "' to be one of given array: " .. array_to_string(array))
|
||||
|
||||
return var
|
||||
end
|
||||
|
||||
function need_string_array(varname, required)
|
||||
local ok, var = pcall(need_array, varname, function(e) assert_type(e, 'string') end, required)
|
||||
assert(ok, "site.conf error: expected `" .. varname .. "' to be a string array")
|
||||
local var, conf_name = loadvar(varname)
|
||||
local ok, var = pcall(__need_array_from_var, var, varname, function(e) assert_type(e, 'string') end, required, conf_name)
|
||||
assert(ok, conf_name.." error: expected `" .. varname .. "' to be a string array")
|
||||
return var
|
||||
end
|
||||
|
||||
function need_string_array_match(varname, pat, required)
|
||||
local ok, var = pcall(need_array, varname, function(e) assert(e:match(pat)) end, required)
|
||||
assert(ok, "site.conf error: expected `" .. varname .. "' to be a string array matching pattern `" .. pat .. "'")
|
||||
local var, conf_name = loadvar(varname)
|
||||
local ok, var = pcall(__need_array_from_var, var, varname, function(e) assert(e:match(pat)) end, required, conf_name)
|
||||
assert(ok, conf_name.." error: expected `" .. varname .. "' to be a string array matching pattern `" .. pat .. "'")
|
||||
return var
|
||||
end
|
||||
|
||||
function need_array_of(varname, array, required)
|
||||
local ok, var = pcall(need_array, varname, function(e) assert_one_of(e, array) end,required)
|
||||
assert(ok, "site.conf error: expected `" .. varname .. "' to be a subset of given array: " .. array_to_string(array))
|
||||
local var, conf_name = loadvar(varname)
|
||||
local ok, var = pcall(__need_array_from_var, var, varname, function(e) assert_one_of(e, array) end, required, conf_name)
|
||||
assert(ok, conf_name.." error: expected `" .. varname .. "' to be a subset of given array: " .. array_to_string(array))
|
||||
return var
|
||||
end
|
||||
|
||||
function in_domain(var)
|
||||
forbid_in_site(var)
|
||||
return var
|
||||
end
|
||||
|
||||
function in_site(var)
|
||||
forbid_in_domain(var)
|
||||
return var
|
||||
end
|
||||
|
13
scripts/domain_config.lua
Normal file
13
scripts/domain_config.lua
Normal file
@ -0,0 +1,13 @@
|
||||
local function load_domain(domain_code)
|
||||
local config = os.getenv('GLUON_SITEDIR')
|
||||
|
||||
local function loader()
|
||||
coroutine.yield('return ')
|
||||
coroutine.yield(io.open(config .. '/domains/' .. domain_code .. '.conf'):read('*a'))
|
||||
end
|
||||
|
||||
-- setfenv doesn't work with Lua 5.2 anymore, but we're using 5.1
|
||||
return setfenv(assert(load(coroutine.wrap(loader), 'domains/' .. domain_code .. '.conf')), {})()
|
||||
end
|
||||
|
||||
return load_domain
|
Loading…
Reference in New Issue
Block a user