lualoader: improve loader.conf var processing

lualoader was previously not processing \ as escapes; this commit fixes
that and does better error checking on the value as well.

Additionally, loader.conf had some odd restrictions on values that make
little sense. Previously, lines like:

kernel=foo

Would simply be discarded with a malformed line complaint you might not
see unless you disable beastie.

lualoader tries to process these as well as it can and manipulates the
environment, while forthloader did minimal processing and constructed a
`set` command to do the heavy lifting instead. The lua approach was
re-envisioned from building a `set` command so that we can appropriately
reset the environment when, for example, boot environments change.

Lift the previous restrictions to allow unquoted values on the right hand
side of an expression.  Note that an unquoted value is effectively:

[A-Za-z0-9-][A-Za-z0-9-_.]*

This commit also stops trying to weirdly limit what it can handle in a
quoted value. Previously it only allowed spaces, alphanumeric, and
punctuation, which is kind of weird. Change it here to grab as much as it
can between two sets of quotes, then let processEnvVar() do the needful and
complain if it finds something malformed looking.

My extremely sophisticated test suite is as follows:

<<EOF
X_01_simple_string="simple"
X_02_escaped_string="s\imple"

X_03_unquoted_val=3
X_04_unquoted_strval=simple_test

X_05_subval="${X_03_unquoted_val}"
X_06_escaped_subval="\${X_03_unquoted_val}"

X_07_embedded="truth${X_03_unquoted_val}"
X_08_escaped_embedded="truth\${X_03_unquoted_val}"

X_09_unknown="${unknown_val}"
X_10_unknown_embedded="truth${unknown_val}"

X_11_crunchy="crunch$unknown_val crunch"
X_12_crunchy="crunch${unknown_val}crunch"

Y_01_badquote="te"lol"
Y_02_eolesc="lol\"
Y_02_noteolesc="lol\\"
Y_03_eolvar="lol$"
Y_03_noteolvar="lol\$"
Y_04_badvar="lol${"

exec="echo Done!"
EOF

Future work may provide a stub loader module in userland so that we can
formally test the loader scripts rather than sketchy setups like the above
in conjunction with the lua-* tools in ^/tools/boot.
This commit is contained in:
Kyle Evans 2021-01-24 13:25:34 -06:00
parent 5fe433a6e4
commit 576562856e

View File

@ -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_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'"
local MSG_MALFORMED = "Malformed line (%d):\n\t'%s'"
@ -56,10 +57,15 @@ local MSG_KERNLOADING = "Loading kernel..."
local MSG_MODLOADING = "Loading configured modules..."
local MSG_MODBLACKLIST = "Not loading blacklisted module '%s'"
local MSG_FAILSYN_QUOTE = "Stray quote at position '%d'"
local MSG_FAILSYN_EOLESC = "Stray escape at end of line"
local MSG_FAILSYN_EOLVAR = "Unescaped $ at end of line"
local MSG_FAILSYN_BADVAR = "Malformed variable expression at position '%d'"
local MODULEEXPR = '([%w-_]+)'
local QVALEXPR = "\"([%w%s%p]-)\""
local QVALEXPR = '"(.*)"'
local QVALREPL = QVALEXPR:gsub('%%', '%%%%')
local WORDEXPR = "([%w]+)"
local WORDEXPR = "([%w%d-][%w%d-_.]*)"
local WORDREPL = WORDEXPR:gsub('%%', '%%%%')
-- Entries that should never make it into the environment; each one should have
@ -146,15 +152,59 @@ local function escapeName(name)
end
local function processEnvVar(value)
for name in value:gmatch("${([^}]+)}") do
local replacement = loader.getenv(name) or ""
value = value:gsub("${" .. escapeName(name) .. "}", replacement)
local pval, vlen = '', #value
local nextpos, vdelim, vinit = 1
local vpat
for i = 1, vlen do
if i < nextpos then
goto nextc
end
local c = value:sub(i, i)
if c == '\\' then
if i == vlen then
return nil, MSG_FAILSYN_EOLESC
end
nextpos = i + 2
pval = pval .. value:sub(i + 1, i + 1)
elseif c == '"' then
return nil, MSG_FAILSYN_QUOTE:format(i)
elseif c == "$" then
if i == vlen then
return nil, MSG_FAILSYN_EOLVAR
else
if value:sub(i + 1, i + 1) == "{" then
-- Skip ${
vinit = i + 2
vdelim = '}'
vpat = "^([^}]+)}"
else
-- Skip the $
vinit = i + 1
vdelim = nil
vpat = "^([%w][%w%d-_.]*)"
end
local name = value:match(vpat, vinit)
if not name then
return nil, MSG_FAILSYN_BADVAR:format(i)
else
nextpos = vinit + #name
if vdelim then
nextpos = nextpos + 1
end
local repl = loader.getenv(name) or ""
pval = pval .. repl
end
end
else
pval = pval .. c
end
::nextc::
end
for name in value:gmatch("$([%w%p]+)%s*") do
local replacement = loader.getenv(name) or ""
value = value:gsub("$" .. escapeName(name), replacement)
end
return value
return pval
end
local function checkPattern(line, pattern)
@ -260,21 +310,17 @@ local pattern_table = {
end,
groups = 1,
},
-- env_var="value"
-- env_var="value" or env_var=[word|num]
{
str = "([%w%p]+)%s*=%s*$VALUE",
str = "([%w][%w%d-_.]*)%s*=%s*$VALUE",
process = function(k, v)
if setEnv(k, processEnvVar(v)) ~= 0 then
print(MSG_FAILSETENV:format(k, v))
local pv, msg = processEnvVar(v)
if not pv then
print(MSG_FAILPARSEVAR:format(k, msg))
return
end
end,
},
-- env_var=num
{
str = "([%w%p]+)%s*=%s*(-?%d+)",
process = function(k, v)
if setEnv(k, processEnvVar(v)) ~= 0 then
print(MSG_FAILSETENV:format(k, tostring(v)))
if setEnv(k, pv) ~= 0 then
print(MSG_FAILSETENV:format(k, v))
end
end,
},