diff options
| author | Kyle Evans <kevans@FreeBSD.org> | 2023-05-11 04:10:53 +0000 |
|---|---|---|
| committer | Kyle Evans <kevans@FreeBSD.org> | 2023-05-11 04:10:53 +0000 |
| commit | 3cb2f5f369ecfd6d15aeff2b47e21866551a20de (patch) | |
| tree | 126063038758313ebebf542da571bbd321557fbc /stand/lua/config.lua | |
| parent | b6d3a9644d5c73a1903dc39f79fe306b42b86b35 (diff) | |
lualoader: add support for .lua configuration files
If a file is specified in loader_conf_files that ends in '.lua', lualoader
will now load and execute that file. These may be used in place of a
traditional loader.conf to use more complicated logic, where some values
may be set based on others or based on the environment that the C bits has
left us with.
Lua scripts are run in a limited environment. In particular, it does not get
access to any modules or, in-fact, anything except environment variable.
A config.buildenv hook has been added so that a local module can add
whatever it may need to to the environment.
When a global var is set in the lua script, it does not immediately alter
the loader environment. Instead, the script's environment is initially
empty and processed only if the whole script executes successfully.
Effectively, a lua configuration file either takes effect or it does not,
an error will not leave it in a half-baked state.
Reviewed by: bcr (manpages), imp
Differential Revision: https://reviews.freebsd.org/D28450
Diffstat (limited to 'stand/lua/config.lua')
| -rw-r--r-- | stand/lua/config.lua | 90 |
1 files changed, 80 insertions, 10 deletions
diff --git a/stand/lua/config.lua b/stand/lua/config.lua index 04d5cda1e704..36be2b6b121e 100644 --- a/stand/lua/config.lua +++ b/stand/lua/config.lua @@ -45,6 +45,7 @@ local MSG_FAILSETENV = "Failed to '%s' with value: %s" local MSG_FAILOPENCFG = "Failed to open config: '%s'" local MSG_FAILREADCFG = "Failed to read config: '%s'" local MSG_FAILPARSECFG = "Failed to parse config: '%s'" +local MSG_FAILEXECLUA = "Failed to execute lua conf '%s': '%s'" local MSG_FAILPARSEVAR = "Failed to parse variable '%s': %s" local MSG_FAILEXBEF = "Failed to execute '%s' before loading '%s'" local MSG_FAILEXAF = "Failed to execute '%s' after loading '%s'" @@ -244,13 +245,15 @@ end -- local pattern_table = { { + luaexempt = true, str = "(#.*)", process = function(_, _) end, groups = 1, }, -- module_load="value" { - str = MODULEEXPR .. "_load%s*=%s*$VALUE", + name = MODULEEXPR .. "_load%s*", + val = "%s*$VALUE", process = function(k, v) if modules[k] == nil then modules[k] = {} @@ -261,7 +264,8 @@ local pattern_table = { }, -- module_name="value" { - str = MODULEEXPR .. "_name%s*=%s*$VALUE", + name = MODULEEXPR .. "_name%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "name", v) setEnv(k .. "_name", v) @@ -269,7 +273,8 @@ local pattern_table = { }, -- module_type="value" { - str = MODULEEXPR .. "_type%s*=%s*$VALUE", + name = MODULEEXPR .. "_type%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "type", v) setEnv(k .. "_type", v) @@ -277,7 +282,8 @@ local pattern_table = { }, -- module_flags="value" { - str = MODULEEXPR .. "_flags%s*=%s*$VALUE", + name = MODULEEXPR .. "_flags%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "flags", v) setEnv(k .. "_flags", v) @@ -285,7 +291,8 @@ local pattern_table = { }, -- module_before="value" { - str = MODULEEXPR .. "_before%s*=%s*$VALUE", + name = MODULEEXPR .. "_before%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "before", v) setEnv(k .. "_before", v) @@ -293,7 +300,8 @@ local pattern_table = { }, -- module_after="value" { - str = MODULEEXPR .. "_after%s*=%s*$VALUE", + name = MODULEEXPR .. "_after%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "after", v) setEnv(k .. "_after", v) @@ -301,7 +309,8 @@ local pattern_table = { }, -- module_error="value" { - str = MODULEEXPR .. "_error%s*=%s*$VALUE", + name = MODULEEXPR .. "_error%s*", + val = "%s*$VALUE", process = function(k, v) setKey(k, "error", v) setEnv(k .. "_error", v) @@ -309,7 +318,9 @@ local pattern_table = { }, -- exec="command" { - str = "exec%s*=%s*" .. QVALEXPR, + luaexempt = true, + name = "exec%s*", + val = "%s*" .. QVALEXPR, process = function(k, _) if cli_execute_unparsed(k) ~= 0 then print(MSG_FAILEXEC:format(k)) @@ -319,7 +330,8 @@ local pattern_table = { }, -- env_var="value" or env_var=[word|num] { - str = "([%w][%w%d-_.]*)%s*=%s*$VALUE", + name = "([%w][%w%d-_.]*)%s*", + val = "%s*$VALUE", process = function(k, v) local pv, msg = processEnvVar(v) if not pv then @@ -328,6 +340,8 @@ local pattern_table = { end if setEnv(k, pv) ~= 0 then print(MSG_FAILSETENV:format(k, v)) + else + return pv end end, }, @@ -486,6 +500,18 @@ local function checkNextboot() loader.setenv("nextboot_enable", "NO") end +local function processEnv(k, v) + for _, val in ipairs(pattern_table) do + if not val.luaexempt and val.name then + local matched = k:match(val.name) + + if matched then + return val.process(matched, v) + end + end + end +end + -- Module exports config.verbose = false @@ -511,7 +537,47 @@ function config.processFile(name, silent) return silent end - return config.parse(text) + if name:match(".lua$") then + local cfg_env = setmetatable({}, { + indices = {}, + __index = function(env, key) + if getmetatable(env).indices[key] then + return rawget(env, key) + end + + return loader.getenv(key) + end, + __newindex = function(env, key, val) + getmetatable(env).indices[key] = true + rawset(env, key, val) + end, + }) + + -- Give local modules a chance to populate the config + -- environment. + hook.runAll("config.buildenv", cfg_env) + local res, err = pcall(load(text, name, "t", cfg_env)) + if res then + for k, v in pairs(cfg_env) do + local t = type(v) + if t ~= "function" and t ~= "table" then + if t ~= "string" then + v = tostring(v) + end + local pval = processEnv(k, v) + if pval then + setEnv(k, pval) + end + end + end + else + print(MSG_FAILEXECLUA:format(name, err)) + end + + return res + else + return config.parse(text) + end end -- silent runs will not return false if we fail to open the file @@ -522,6 +588,9 @@ function config.parse(text) for line in text:gmatch("([^\n]+)") do if line:match("^%s*$") == nil then for _, val in ipairs(pattern_table) do + if val.str == nil then + val.str = val.name .. "=" .. val.val + end local pattern = '^%s*' .. val.str .. '%s*(.*)'; local cgroups = val.groups or 2 local k, v, c = checkPattern(line, pattern) @@ -805,6 +874,7 @@ function config.getModuleInfo() } end +hook.registerType("config.buildenv") hook.registerType("config.loaded") hook.registerType("config.reloaded") hook.registerType("kernel.loaded") |
