aboutsummaryrefslogtreecommitdiff
path: root/stand/lua/config.lua
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2023-05-11 04:10:53 +0000
committerKyle Evans <kevans@FreeBSD.org>2023-05-11 04:10:53 +0000
commit3cb2f5f369ecfd6d15aeff2b47e21866551a20de (patch)
tree126063038758313ebebf542da571bbd321557fbc /stand/lua/config.lua
parentb6d3a9644d5c73a1903dc39f79fe306b42b86b35 (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.lua90
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")