xref: /freebsd-14.2/stand/lua/config.lua (revision 7ffa3b0f)
1088b4f5fSWarner Losh--
24d846d26SWarner Losh-- SPDX-License-Identifier: BSD-2-Clause
372e39d71SKyle Evans--
4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <[email protected]>
5e12ff891SKyle Evans-- Copyright (c) 2018 Kyle Evans <[email protected]>
6088b4f5fSWarner Losh-- All rights reserved.
7088b4f5fSWarner Losh--
8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
10088b4f5fSWarner Losh-- are met:
11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
14088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
15088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
16088b4f5fSWarner Losh--
17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27088b4f5fSWarner Losh-- SUCH DAMAGE.
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh
30aea262bfSKyle Evanslocal hook = require("hook")
31aea262bfSKyle Evans
32aedd6be5SKyle Evanslocal config = {}
33aedd6be5SKyle Evanslocal modules = {}
34aedd6be5SKyle Evanslocal carousel_choices = {}
3564c91742SKyle Evans-- Which variables we changed
3664c91742SKyle Evanslocal env_changed = {}
3764c91742SKyle Evans-- Values to restore env to (nil to unset)
3864c91742SKyle Evanslocal env_restore = {}
3925c4c7a5SKyle Evans
4072cf7db3SKyle Evanslocal MSG_FAILDIR = "Failed to load conf dir '%s': not a directory"
41fdabb5f5SKyle Evanslocal MSG_FAILEXEC = "Failed to exec '%s'"
42fdabb5f5SKyle Evanslocal MSG_FAILSETENV = "Failed to '%s' with value: %s"
43fdabb5f5SKyle Evanslocal MSG_FAILOPENCFG = "Failed to open config: '%s'"
44fdabb5f5SKyle Evanslocal MSG_FAILREADCFG = "Failed to read config: '%s'"
45fdabb5f5SKyle Evanslocal MSG_FAILPARSECFG = "Failed to parse config: '%s'"
463cb2f5f3SKyle Evanslocal MSG_FAILEXECLUA = "Failed to execute lua conf '%s': '%s'"
4757656285SKyle Evanslocal MSG_FAILPARSEVAR = "Failed to parse variable '%s': %s"
48fdabb5f5SKyle Evanslocal MSG_FAILEXBEF = "Failed to execute '%s' before loading '%s'"
49fdabb5f5SKyle Evanslocal MSG_FAILEXAF = "Failed to execute '%s' after loading '%s'"
50fdabb5f5SKyle Evanslocal MSG_MALFORMED = "Malformed line (%d):\n\t'%s'"
51fdabb5f5SKyle Evanslocal MSG_DEFAULTKERNFAIL = "No kernel set, failed to load from module_path"
52fdabb5f5SKyle Evanslocal MSG_KERNFAIL = "Failed to load kernel '%s'"
530d7bee6aSKyle Evanslocal MSG_XENKERNFAIL = "Failed to load Xen kernel '%s'"
540d7bee6aSKyle Evanslocal MSG_XENKERNLOADING = "Loading Xen kernel..."
55fdabb5f5SKyle Evanslocal MSG_KERNLOADING = "Loading kernel..."
56fdabb5f5SKyle Evanslocal MSG_MODLOADING = "Loading configured modules..."
57532dc172SKyle Evanslocal MSG_MODBLACKLIST = "Not loading blacklisted module '%s'"
58fdabb5f5SKyle Evans
5957656285SKyle Evanslocal MSG_FAILSYN_QUOTE = "Stray quote at position '%d'"
6057656285SKyle Evanslocal MSG_FAILSYN_EOLESC = "Stray escape at end of line"
6157656285SKyle Evanslocal MSG_FAILSYN_EOLVAR = "Unescaped $ at end of line"
6257656285SKyle Evanslocal MSG_FAILSYN_BADVAR = "Malformed variable expression at position '%d'"
6357656285SKyle Evans
646b2f0a3cSKyle Evans-- MODULEEXPR should more or less allow the exact same set of characters as the
656b2f0a3cSKyle Evans-- env_var entries in the pattern table.  This is perhaps a good target for a
666b2f0a3cSKyle Evans-- little refactoring.
676b2f0a3cSKyle Evanslocal MODULEEXPR = '([%w%d-_.]+)'
6857656285SKyle Evanslocal QVALEXPR = '"(.*)"'
691ee89ab5SKyle Evanslocal QVALREPL = QVALEXPR:gsub('%%', '%%%%')
70b24872cfSKyle Evanslocal WORDEXPR = "([-%w%d][-%w%d_.]*)"
711ee89ab5SKyle Evanslocal WORDREPL = WORDEXPR:gsub('%%', '%%%%')
72058c692eSKyle Evans
73bf832717SKyle Evans-- Entries that should never make it into the environment; each one should have
74bf832717SKyle Evans-- a documented reason for its existence, and these should all be implementation
75bf832717SKyle Evans-- details of the config module.
76bf832717SKyle Evanslocal loader_env_restricted_table = {
77bf832717SKyle Evans	-- loader_conf_files should be considered write-only, and consumers
78bf832717SKyle Evans	-- should not rely on any particular value; it's a loader implementation
79bf832717SKyle Evans	-- detail.  Moreover, it's not a particularly useful variable to have in
80bf832717SKyle Evans	-- the kenv.  Save the overhead, let it get fetched other ways.
81bf832717SKyle Evans	loader_conf_files = true,
82bf832717SKyle Evans}
83bf832717SKyle Evans
8464c91742SKyle Evanslocal function restoreEnv()
8564c91742SKyle Evans	-- Examine changed environment variables
8664c91742SKyle Evans	for k, v in pairs(env_changed) do
8764c91742SKyle Evans		local restore_value = env_restore[k]
8864c91742SKyle Evans		if restore_value == nil then
8964c91742SKyle Evans			-- This one doesn't need restored for some reason
9064c91742SKyle Evans			goto continue
9164c91742SKyle Evans		end
9264c91742SKyle Evans		local current_value = loader.getenv(k)
9364c91742SKyle Evans		if current_value ~= v then
9464c91742SKyle Evans			-- This was overwritten by some action taken on the menu
9564c91742SKyle Evans			-- most likely; we'll leave it be.
9664c91742SKyle Evans			goto continue
9764c91742SKyle Evans		end
9864c91742SKyle Evans		restore_value = restore_value.value
9964c91742SKyle Evans		if restore_value ~= nil then
10064c91742SKyle Evans			loader.setenv(k, restore_value)
10164c91742SKyle Evans		else
10264c91742SKyle Evans			loader.unsetenv(k)
10364c91742SKyle Evans		end
10464c91742SKyle Evans		::continue::
10564c91742SKyle Evans	end
10664c91742SKyle Evans
10764c91742SKyle Evans	env_changed = {}
10864c91742SKyle Evans	env_restore = {}
10964c91742SKyle Evansend
11064c91742SKyle Evans
111bf832717SKyle Evans-- XXX This getEnv/setEnv should likely be exported at some point.  We can save
112bf832717SKyle Evans-- the call back into loader.getenv for any variable that's been set or
113bf832717SKyle Evans-- overridden by any loader.conf using this implementation with little overhead
114bf832717SKyle Evans-- since we're already tracking the values.
115bf832717SKyle Evanslocal function getEnv(key)
116bf832717SKyle Evans	if loader_env_restricted_table[key] ~= nil or
117bf832717SKyle Evans	    env_changed[key] ~= nil then
118bf832717SKyle Evans		return env_changed[key]
119bf832717SKyle Evans	end
120bf832717SKyle Evans
121bf832717SKyle Evans	return loader.getenv(key)
122bf832717SKyle Evansend
123bf832717SKyle Evans
12464c91742SKyle Evanslocal function setEnv(key, value)
125bf832717SKyle Evans	env_changed[key] = value
126bf832717SKyle Evans
127bf832717SKyle Evans	if loader_env_restricted_table[key] ~= nil then
128bf832717SKyle Evans		return 0
129bf832717SKyle Evans	end
130bf832717SKyle Evans
13164c91742SKyle Evans	-- Track the original value for this if we haven't already
13264c91742SKyle Evans	if env_restore[key] == nil then
13364c91742SKyle Evans		env_restore[key] = {value = loader.getenv(key)}
13464c91742SKyle Evans	end
13564c91742SKyle Evans
13664c91742SKyle Evans	return loader.setenv(key, value)
13764c91742SKyle Evansend
13864c91742SKyle Evans
139dbef5253SKyle Evans-- name here is one of 'name', 'type', flags', 'before', 'after', or 'error.'
140dbef5253SKyle Evans-- These are set from lines in loader.conf(5): ${key}_${name}="${value}" where
141dbef5253SKyle Evans-- ${key} is a module name.
142dbef5253SKyle Evanslocal function setKey(key, name, value)
143dbef5253SKyle Evans	if modules[key] == nil then
144dbef5253SKyle Evans		modules[key] = {}
145dbef5253SKyle Evans	end
146dbef5253SKyle Evans	modules[key][name] = value
147dbef5253SKyle Evansend
148dbef5253SKyle Evans
149deb8c8f5SKyle Evans-- Escapes the named value for use as a literal in a replacement pattern.
150deb8c8f5SKyle Evans-- e.g. dhcp.host-name gets turned into dhcp%.host%-name to remove the special
151deb8c8f5SKyle Evans-- meaning.
152deb8c8f5SKyle Evanslocal function escapeName(name)
153deb8c8f5SKyle Evans	return name:gsub("([%p])", "%%%1")
154deb8c8f5SKyle Evansend
155deb8c8f5SKyle Evans
156deb8c8f5SKyle Evanslocal function processEnvVar(value)
15757656285SKyle Evans	local pval, vlen = '', #value
15857656285SKyle Evans	local nextpos, vdelim, vinit = 1
15957656285SKyle Evans	local vpat
16057656285SKyle Evans	for i = 1, vlen do
16157656285SKyle Evans		if i < nextpos then
16257656285SKyle Evans			goto nextc
163deb8c8f5SKyle Evans		end
16457656285SKyle Evans
16557656285SKyle Evans		local c = value:sub(i, i)
16657656285SKyle Evans		if c == '\\' then
16757656285SKyle Evans			if i == vlen then
16857656285SKyle Evans				return nil, MSG_FAILSYN_EOLESC
169deb8c8f5SKyle Evans			end
17057656285SKyle Evans			nextpos = i + 2
17157656285SKyle Evans			pval = pval .. value:sub(i + 1, i + 1)
17257656285SKyle Evans		elseif c == '"' then
17357656285SKyle Evans			return nil, MSG_FAILSYN_QUOTE:format(i)
17457656285SKyle Evans		elseif c == "$" then
17557656285SKyle Evans			if i == vlen then
17657656285SKyle Evans				return nil, MSG_FAILSYN_EOLVAR
17757656285SKyle Evans			else
17857656285SKyle Evans				if value:sub(i + 1, i + 1) == "{" then
17957656285SKyle Evans					-- Skip ${
18057656285SKyle Evans					vinit = i + 2
18157656285SKyle Evans					vdelim = '}'
18257656285SKyle Evans					vpat = "^([^}]+)}"
18357656285SKyle Evans				else
18457656285SKyle Evans					-- Skip the $
18557656285SKyle Evans					vinit = i + 1
18657656285SKyle Evans					vdelim = nil
187b24872cfSKyle Evans					vpat = "^([%w][-%w%d_.]*)"
18857656285SKyle Evans				end
18957656285SKyle Evans
19057656285SKyle Evans				local name = value:match(vpat, vinit)
19157656285SKyle Evans				if not name then
19257656285SKyle Evans					return nil, MSG_FAILSYN_BADVAR:format(i)
19357656285SKyle Evans				else
19457656285SKyle Evans					nextpos = vinit + #name
19557656285SKyle Evans					if vdelim then
19657656285SKyle Evans						nextpos = nextpos + 1
19757656285SKyle Evans					end
19857656285SKyle Evans
19957656285SKyle Evans					local repl = loader.getenv(name) or ""
20057656285SKyle Evans					pval = pval .. repl
20157656285SKyle Evans				end
20257656285SKyle Evans			end
20357656285SKyle Evans		else
20457656285SKyle Evans			pval = pval .. c
20557656285SKyle Evans		end
20657656285SKyle Evans		::nextc::
20757656285SKyle Evans	end
20857656285SKyle Evans
20957656285SKyle Evans	return pval
210deb8c8f5SKyle Evansend
211deb8c8f5SKyle Evans
2121ee89ab5SKyle Evanslocal function checkPattern(line, pattern)
2131ee89ab5SKyle Evans	local function _realCheck(_line, _pattern)
2141ee89ab5SKyle Evans		return _line:match(_pattern)
2151ee89ab5SKyle Evans	end
2161ee89ab5SKyle Evans
2171ee89ab5SKyle Evans	if pattern:find('$VALUE') then
2181ee89ab5SKyle Evans		local k, v, c
2191ee89ab5SKyle Evans		k, v, c = _realCheck(line, pattern:gsub('$VALUE', QVALREPL))
2201ee89ab5SKyle Evans		if k ~= nil then
2211ee89ab5SKyle Evans			return k,v, c
2221ee89ab5SKyle Evans		end
2231ee89ab5SKyle Evans		return _realCheck(line, pattern:gsub('$VALUE', WORDREPL))
2241ee89ab5SKyle Evans	else
2251ee89ab5SKyle Evans		return _realCheck(line, pattern)
2261ee89ab5SKyle Evans	end
2271ee89ab5SKyle Evansend
2281ee89ab5SKyle Evans
229058c692eSKyle Evans-- str in this table is a regex pattern.  It will automatically be anchored to
230058c692eSKyle Evans-- the beginning of a line and any preceding whitespace will be skipped.  The
231058c692eSKyle Evans-- pattern should have no more than two captures patterns, which correspond to
232058c692eSKyle Evans-- the two parameters (usually 'key' and 'value') that are passed to the
2331ee89ab5SKyle Evans-- process function.  All trailing characters will be validated.  Any $VALUE
2341ee89ab5SKyle Evans-- token included in a pattern will be tried first with a quoted value capture
2351ee89ab5SKyle Evans-- group, then a single-word value capture group.  This is our kludge for Lua
2361ee89ab5SKyle Evans-- regex not supporting branching.
2379a16e110SKyle Evans--
2389a16e110SKyle Evans-- We have two special entries in this table: the first is the first entry,
2399a16e110SKyle Evans-- a full-line comment.  The second is for 'exec' handling.  Both have a single
2409a16e110SKyle Evans-- capture group, but the difference is that the full-line comment pattern will
2419a16e110SKyle Evans-- match the entire line.  This does not run afoul of the later end of line
2429a16e110SKyle Evans-- validation that we'll do after a match.  However, the 'exec' pattern will.
2439a16e110SKyle Evans-- We document the exceptions with a special 'groups' index that indicates
2449a16e110SKyle Evans-- the number of capture groups, if not two.  We'll use this later to do
2459a16e110SKyle Evans-- validation on the proper entry.
2461ee89ab5SKyle Evans--
247e1a8835aSKyle Evanslocal pattern_table = {
248e1a8835aSKyle Evans	{
2493cb2f5f3SKyle Evans		luaexempt = true,
250058c692eSKyle Evans		str = "(#.*)",
251e1a8835aSKyle Evans		process = function(_, _)  end,
2529a16e110SKyle Evans		groups = 1,
253088b4f5fSWarner Losh	},
254088b4f5fSWarner Losh	--  module_load="value"
255e1a8835aSKyle Evans	{
2563cb2f5f3SKyle Evans		name = MODULEEXPR .. "_load%s*",
2573cb2f5f3SKyle Evans		val = "%s*$VALUE",
258088b4f5fSWarner Losh		process = function(k, v)
2599f71d421SKyle Evans			if modules[k] == nil then
260aedd6be5SKyle Evans				modules[k] = {}
261088b4f5fSWarner Losh			end
262aedd6be5SKyle Evans			modules[k].load = v:upper()
263e9eee0f2SWarner Losh			setEnv(k .. "_load", v:upper())
264e1a8835aSKyle Evans		end,
265088b4f5fSWarner Losh	},
266088b4f5fSWarner Losh	--  module_name="value"
267e1a8835aSKyle Evans	{
2683cb2f5f3SKyle Evans		name = MODULEEXPR .. "_name%s*",
2693cb2f5f3SKyle Evans		val = "%s*$VALUE",
270088b4f5fSWarner Losh		process = function(k, v)
271dbef5253SKyle Evans			setKey(k, "name", v)
272e9eee0f2SWarner Losh			setEnv(k .. "_name", v)
273e1a8835aSKyle Evans		end,
274088b4f5fSWarner Losh	},
275088b4f5fSWarner Losh	--  module_type="value"
276e1a8835aSKyle Evans	{
2773cb2f5f3SKyle Evans		name = MODULEEXPR .. "_type%s*",
2783cb2f5f3SKyle Evans		val = "%s*$VALUE",
279088b4f5fSWarner Losh		process = function(k, v)
280dbef5253SKyle Evans			setKey(k, "type", v)
281e9eee0f2SWarner Losh			setEnv(k .. "_type", v)
282e1a8835aSKyle Evans		end,
283088b4f5fSWarner Losh	},
284088b4f5fSWarner Losh	--  module_flags="value"
285e1a8835aSKyle Evans	{
2863cb2f5f3SKyle Evans		name = MODULEEXPR .. "_flags%s*",
2873cb2f5f3SKyle Evans		val = "%s*$VALUE",
288088b4f5fSWarner Losh		process = function(k, v)
289dbef5253SKyle Evans			setKey(k, "flags", v)
290e9eee0f2SWarner Losh			setEnv(k .. "_flags", v)
291e1a8835aSKyle Evans		end,
292088b4f5fSWarner Losh	},
293088b4f5fSWarner Losh	--  module_before="value"
294e1a8835aSKyle Evans	{
2953cb2f5f3SKyle Evans		name = MODULEEXPR .. "_before%s*",
2963cb2f5f3SKyle Evans		val = "%s*$VALUE",
297088b4f5fSWarner Losh		process = function(k, v)
298dbef5253SKyle Evans			setKey(k, "before", v)
299e9eee0f2SWarner Losh			setEnv(k .. "_before", v)
300e1a8835aSKyle Evans		end,
301088b4f5fSWarner Losh	},
302088b4f5fSWarner Losh	--  module_after="value"
303e1a8835aSKyle Evans	{
3043cb2f5f3SKyle Evans		name = MODULEEXPR .. "_after%s*",
3053cb2f5f3SKyle Evans		val = "%s*$VALUE",
306088b4f5fSWarner Losh		process = function(k, v)
307dbef5253SKyle Evans			setKey(k, "after", v)
308e9eee0f2SWarner Losh			setEnv(k .. "_after", v)
309e1a8835aSKyle Evans		end,
310088b4f5fSWarner Losh	},
311088b4f5fSWarner Losh	--  module_error="value"
312e1a8835aSKyle Evans	{
3133cb2f5f3SKyle Evans		name = MODULEEXPR .. "_error%s*",
3143cb2f5f3SKyle Evans		val = "%s*$VALUE",
315088b4f5fSWarner Losh		process = function(k, v)
316dbef5253SKyle Evans			setKey(k, "error", v)
317e9eee0f2SWarner Losh			setEnv(k .. "_error", v)
318e1a8835aSKyle Evans		end,
319088b4f5fSWarner Losh	},
320088b4f5fSWarner Losh	--  exec="command"
321e1a8835aSKyle Evans	{
3223cb2f5f3SKyle Evans		luaexempt = true,
3233cb2f5f3SKyle Evans		name = "exec%s*",
3243cb2f5f3SKyle Evans		val = "%s*" .. QVALEXPR,
325e2df27e3SKyle Evans		process = function(k, _)
3269ab2d3c5SKyle Evans			if cli_execute_unparsed(k) ~= 0 then
327fdabb5f5SKyle Evans				print(MSG_FAILEXEC:format(k))
328088b4f5fSWarner Losh			end
329e1a8835aSKyle Evans		end,
3309a16e110SKyle Evans		groups = 1,
331088b4f5fSWarner Losh	},
33257656285SKyle Evans	--  env_var="value" or env_var=[word|num]
333e1a8835aSKyle Evans	{
3343cb2f5f3SKyle Evans		name = "([%w][%w%d-_.]*)%s*",
3353cb2f5f3SKyle Evans		val = "%s*$VALUE",
336088b4f5fSWarner Losh		process = function(k, v)
33757656285SKyle Evans			local pv, msg = processEnvVar(v)
33857656285SKyle Evans			if not pv then
33957656285SKyle Evans				print(MSG_FAILPARSEVAR:format(k, msg))
34057656285SKyle Evans				return
341088b4f5fSWarner Losh			end
34257656285SKyle Evans			if setEnv(k, pv) ~= 0 then
34357656285SKyle Evans				print(MSG_FAILSETENV:format(k, v))
3443cb2f5f3SKyle Evans			else
3453cb2f5f3SKyle Evans				return pv
346088b4f5fSWarner Losh			end
347e1a8835aSKyle Evans		end,
348e1a8835aSKyle Evans	},
349aedd6be5SKyle Evans}
350088b4f5fSWarner Losh
351dbef5253SKyle Evanslocal function isValidComment(line)
352dbef5253SKyle Evans	if line ~= nil then
353dbef5253SKyle Evans		local s = line:match("^%s*#.*")
354dbef5253SKyle Evans		if s == nil then
355dbef5253SKyle Evans			s = line:match("^%s*$")
356dbef5253SKyle Evans		end
357dbef5253SKyle Evans		if s == nil then
358dbef5253SKyle Evans			return false
359dbef5253SKyle Evans		end
360dbef5253SKyle Evans	end
361dbef5253SKyle Evans	return true
362dbef5253SKyle Evansend
363dbef5253SKyle Evans
364532dc172SKyle Evanslocal function getBlacklist()
365e1f1ddebSKyle Evans	local blacklist = {}
366532dc172SKyle Evans	local blacklist_str = loader.getenv('module_blacklist')
367532dc172SKyle Evans	if blacklist_str == nil then
368e1f1ddebSKyle Evans		return blacklist
369532dc172SKyle Evans	end
370532dc172SKyle Evans
371b24872cfSKyle Evans	for mod in blacklist_str:gmatch("[;, ]?([-%w_]+)[;, ]?") do
372532dc172SKyle Evans		blacklist[mod] = true
373532dc172SKyle Evans	end
374532dc172SKyle Evans	return blacklist
375532dc172SKyle Evansend
376532dc172SKyle Evans
377dbef5253SKyle Evanslocal function loadModule(mod, silent)
378dbef5253SKyle Evans	local status = true
379532dc172SKyle Evans	local blacklist = getBlacklist()
380dbef5253SKyle Evans	local pstatus
381dbef5253SKyle Evans	for k, v in pairs(mod) do
3828d21763eSKyle Evans		if v.load ~= nil and v.load:lower() == "yes" then
383532dc172SKyle Evans			local module_name = v.name or k
3844634bb1fSKyle Evans			if not v.force and blacklist[module_name] ~= nil then
385532dc172SKyle Evans				if not silent then
386532dc172SKyle Evans					print(MSG_MODBLACKLIST:format(module_name))
387532dc172SKyle Evans				end
388532dc172SKyle Evans				goto continue
389532dc172SKyle Evans			end
3903078173cSKyle Evans			if not silent then
3913078173cSKyle Evans				loader.printc(module_name .. "...")
3923078173cSKyle Evans			end
393dbef5253SKyle Evans			local str = "load "
394dbef5253SKyle Evans			if v.type ~= nil then
395dbef5253SKyle Evans				str = str .. "-t " .. v.type .. " "
396dbef5253SKyle Evans			end
397532dc172SKyle Evans			str = str .. module_name
39835beb928SKyle Evans			if v.flags ~= nil then
39935beb928SKyle Evans				str = str .. " " .. v.flags
40035beb928SKyle Evans			end
401dbef5253SKyle Evans			if v.before ~= nil then
402dbef5253SKyle Evans				pstatus = cli_execute_unparsed(v.before) == 0
403dbef5253SKyle Evans				if not pstatus and not silent then
404dbef5253SKyle Evans					print(MSG_FAILEXBEF:format(v.before, k))
405dbef5253SKyle Evans				end
406dbef5253SKyle Evans				status = status and pstatus
407dbef5253SKyle Evans			end
408dbef5253SKyle Evans
409dbef5253SKyle Evans			if cli_execute_unparsed(str) ~= 0 then
410*7ffa3b0fSWarner Losh				-- XXX Temporary shim: don't break the boot if
411*7ffa3b0fSWarner Losh				-- loader hadn't been recompiled with this
412*7ffa3b0fSWarner Losh				-- function exposed.
413*7ffa3b0fSWarner Losh				if loader.command_error then
4143078173cSKyle Evans					print(loader.command_error())
415*7ffa3b0fSWarner Losh				end
416dbef5253SKyle Evans				if not silent then
4173078173cSKyle Evans					print("failed!")
418dbef5253SKyle Evans				end
419dbef5253SKyle Evans				if v.error ~= nil then
420dbef5253SKyle Evans					cli_execute_unparsed(v.error)
421dbef5253SKyle Evans				end
422dbef5253SKyle Evans				status = false
4233078173cSKyle Evans			elseif v.after ~= nil then
424dbef5253SKyle Evans				pstatus = cli_execute_unparsed(v.after) == 0
425dbef5253SKyle Evans				if not pstatus and not silent then
426dbef5253SKyle Evans					print(MSG_FAILEXAF:format(v.after, k))
427dbef5253SKyle Evans				end
4283078173cSKyle Evans				if not silent then
4293078173cSKyle Evans					print("ok")
4303078173cSKyle Evans				end
431dbef5253SKyle Evans				status = status and pstatus
432dbef5253SKyle Evans			end
433dbef5253SKyle Evans		end
434532dc172SKyle Evans		::continue::
435dbef5253SKyle Evans	end
436dbef5253SKyle Evans
437dbef5253SKyle Evans	return status
438dbef5253SKyle Evansend
439dbef5253SKyle Evans
440322a2dddSKyle Evanslocal function readFile(name, silent)
441164b58fdSKyle Evans	local f = io.open(name)
442164b58fdSKyle Evans	if f == nil then
443164b58fdSKyle Evans		if not silent then
444fdabb5f5SKyle Evans			print(MSG_FAILOPENCFG:format(name))
445164b58fdSKyle Evans		end
446164b58fdSKyle Evans		return nil
447164b58fdSKyle Evans	end
448164b58fdSKyle Evans
449164b58fdSKyle Evans	local text, _ = io.read(f)
450164b58fdSKyle Evans	-- We might have read in the whole file, this won't be needed any more.
451164b58fdSKyle Evans	io.close(f)
452164b58fdSKyle Evans
4538d21763eSKyle Evans	if text == nil and not silent then
454fdabb5f5SKyle Evans		print(MSG_FAILREADCFG:format(name))
455164b58fdSKyle Evans	end
456164b58fdSKyle Evans	return text
457164b58fdSKyle Evansend
458164b58fdSKyle Evans
459322a2dddSKyle Evanslocal function checkNextboot()
460be2050daSKyle Evans	local nextboot_file = loader.getenv("nextboot_conf")
461e307eb94SToomas Soome	local nextboot_enable = loader.getenv("nextboot_enable")
462e307eb94SToomas Soome
463ddfae7e3SKyle Evans	if nextboot_file == nil then
464ddfae7e3SKyle Evans		return
465ddfae7e3SKyle Evans	end
466ddfae7e3SKyle Evans
467e307eb94SToomas Soome	-- is nextboot_enable set in nvstore?
468e307eb94SToomas Soome	if nextboot_enable == "NO" then
469e307eb94SToomas Soome		return
470e307eb94SToomas Soome	end
471e307eb94SToomas Soome
472322a2dddSKyle Evans	local text = readFile(nextboot_file, true)
473ddfae7e3SKyle Evans	if text == nil then
474ddfae7e3SKyle Evans		return
475ddfae7e3SKyle Evans	end
476ddfae7e3SKyle Evans
477e307eb94SToomas Soome	if nextboot_enable == nil and
478e307eb94SToomas Soome	    text:match("^nextboot_enable=\"NO\"") ~= nil then
479ddfae7e3SKyle Evans		-- We're done; nextboot is not enabled
480ddfae7e3SKyle Evans		return
481ddfae7e3SKyle Evans	end
482ddfae7e3SKyle Evans
483ddfae7e3SKyle Evans	if not config.parse(text) then
4842f3ecc87SKyle Evans		print(MSG_FAILPARSECFG:format(nextboot_file))
485ddfae7e3SKyle Evans	end
486ddfae7e3SKyle Evans
487ddfae7e3SKyle Evans	-- Attempt to rewrite the first line and only the first line of the
488ddfae7e3SKyle Evans	-- nextboot_file. We overwrite it with nextboot_enable="NO", then
48967eae503SKyle Evans	-- check for that on load.
490ddfae7e3SKyle Evans	-- It's worth noting that this won't work on every filesystem, so we
491ddfae7e3SKyle Evans	-- won't do anything notable if we have any errors in this process.
492ddfae7e3SKyle Evans	local nfile = io.open(nextboot_file, 'w')
493ddfae7e3SKyle Evans	if nfile ~= nil then
494ddfae7e3SKyle Evans		-- We need the trailing space here to account for the extra
495ddfae7e3SKyle Evans		-- character taken up by the string nextboot_enable="YES"
496ddfae7e3SKyle Evans		-- Or new end quotation mark lands on the S, and we want to
497ddfae7e3SKyle Evans		-- rewrite the entirety of the first line.
498ddfae7e3SKyle Evans		io.write(nfile, "nextboot_enable=\"NO\" ")
499ddfae7e3SKyle Evans		io.close(nfile)
500ddfae7e3SKyle Evans	end
501e307eb94SToomas Soome	loader.setenv("nextboot_enable", "NO")
502ddfae7e3SKyle Evansend
503ddfae7e3SKyle Evans
5043cb2f5f3SKyle Evanslocal function processEnv(k, v)
5053cb2f5f3SKyle Evans	for _, val in ipairs(pattern_table) do
5063cb2f5f3SKyle Evans		if not val.luaexempt and val.name then
5073cb2f5f3SKyle Evans			local matched = k:match(val.name)
5083cb2f5f3SKyle Evans
5093cb2f5f3SKyle Evans			if matched then
5103cb2f5f3SKyle Evans				return val.process(matched, v)
5113cb2f5f3SKyle Evans			end
5123cb2f5f3SKyle Evans		end
5133cb2f5f3SKyle Evans	end
5143cb2f5f3SKyle Evansend
5153cb2f5f3SKyle Evans
516b5746545SKyle Evans-- Module exports
517f0b03262SKyle Evansconfig.verbose = false
518b5746545SKyle Evans
51925c4c7a5SKyle Evans-- The first item in every carousel is always the default item.
52025c4c7a5SKyle Evansfunction config.getCarouselIndex(id)
5218d21763eSKyle Evans	return carousel_choices[id] or 1
52225c4c7a5SKyle Evansend
52325c4c7a5SKyle Evans
52425c4c7a5SKyle Evansfunction config.setCarouselIndex(id, idx)
525aedd6be5SKyle Evans	carousel_choices[id] = idx
52625c4c7a5SKyle Evansend
52725c4c7a5SKyle Evans
528fb7275beSKyle Evans-- Returns true if we processed the file successfully, false if we did not.
529fb7275beSKyle Evans-- If 'silent' is true, being unable to read the file is not considered a
530fb7275beSKyle Evans-- failure.
531ddfae7e3SKyle Evansfunction config.processFile(name, silent)
532062d62c9SKyle Evans	if silent == nil then
533062d62c9SKyle Evans		silent = false
534062d62c9SKyle Evans	end
535088b4f5fSWarner Losh
536322a2dddSKyle Evans	local text = readFile(name, silent)
5379f71d421SKyle Evans	if text == nil then
538fb7275beSKyle Evans		return silent
539088b4f5fSWarner Losh	end
5403dcb7648SKyle Evans
5413cb2f5f3SKyle Evans	if name:match(".lua$") then
5423cb2f5f3SKyle Evans		local cfg_env = setmetatable({}, {
5433cb2f5f3SKyle Evans			indices = {},
5443cb2f5f3SKyle Evans			__index = function(env, key)
5453cb2f5f3SKyle Evans				if getmetatable(env).indices[key] then
5463cb2f5f3SKyle Evans					return rawget(env, key)
5473cb2f5f3SKyle Evans				end
5483cb2f5f3SKyle Evans
5493cb2f5f3SKyle Evans				return loader.getenv(key)
5503cb2f5f3SKyle Evans			end,
5513cb2f5f3SKyle Evans			__newindex = function(env, key, val)
5523cb2f5f3SKyle Evans				getmetatable(env).indices[key] = true
5533cb2f5f3SKyle Evans				rawset(env, key, val)
5543cb2f5f3SKyle Evans			end,
5553cb2f5f3SKyle Evans		})
5563cb2f5f3SKyle Evans
5573cb2f5f3SKyle Evans		-- Give local modules a chance to populate the config
5583cb2f5f3SKyle Evans		-- environment.
5593cb2f5f3SKyle Evans		hook.runAll("config.buildenv", cfg_env)
5603cb2f5f3SKyle Evans		local res, err = pcall(load(text, name, "t", cfg_env))
5613cb2f5f3SKyle Evans		if res then
5623cb2f5f3SKyle Evans			for k, v in pairs(cfg_env) do
5633cb2f5f3SKyle Evans				local t = type(v)
5643cb2f5f3SKyle Evans				if t ~= "function" and t ~= "table" then
5653cb2f5f3SKyle Evans					if t ~= "string" then
5663cb2f5f3SKyle Evans						v = tostring(v)
5673cb2f5f3SKyle Evans					end
5683cb2f5f3SKyle Evans					local pval = processEnv(k, v)
5693cb2f5f3SKyle Evans					if pval then
5703cb2f5f3SKyle Evans						setEnv(k, pval)
5713cb2f5f3SKyle Evans					end
5723cb2f5f3SKyle Evans				end
5733cb2f5f3SKyle Evans			end
5743cb2f5f3SKyle Evans		else
5753cb2f5f3SKyle Evans			print(MSG_FAILEXECLUA:format(name, err))
5763cb2f5f3SKyle Evans		end
5773cb2f5f3SKyle Evans
5783cb2f5f3SKyle Evans		return res
5793cb2f5f3SKyle Evans	else
5804adde50dSKyle Evans		return config.parse(text)
5814adde50dSKyle Evans	end
5823cb2f5f3SKyle Evansend
5834adde50dSKyle Evans
5844adde50dSKyle Evans-- silent runs will not return false if we fail to open the file
5854adde50dSKyle Evansfunction config.parse(text)
586aedd6be5SKyle Evans	local n = 1
587aedd6be5SKyle Evans	local status = true
588088b4f5fSWarner Losh
58971049173SKyle Evans	for line in text:gmatch("([^\n]+)") do
5909f71d421SKyle Evans		if line:match("^%s*$") == nil then
591e2df27e3SKyle Evans			for _, val in ipairs(pattern_table) do
5923cb2f5f3SKyle Evans				if val.str == nil then
5933cb2f5f3SKyle Evans					val.str = val.name .. "=" .. val.val
5943cb2f5f3SKyle Evans				end
595058c692eSKyle Evans				local pattern = '^%s*' .. val.str .. '%s*(.*)';
5969a16e110SKyle Evans				local cgroups = val.groups or 2
5971ee89ab5SKyle Evans				local k, v, c = checkPattern(line, pattern)
5989f71d421SKyle Evans				if k ~= nil then
5999a16e110SKyle Evans					-- Offset by one, drats
6009a16e110SKyle Evans					if cgroups == 1 then
6019a16e110SKyle Evans						c = v
6029a16e110SKyle Evans						v = nil
6039a16e110SKyle Evans					end
604088b4f5fSWarner Losh
605dbef5253SKyle Evans					if isValidComment(c) then
606aedd6be5SKyle Evans						val.process(k, v)
6079a16e110SKyle Evans						goto nextline
608088b4f5fSWarner Losh					end
609088b4f5fSWarner Losh
610aedd6be5SKyle Evans					break
611088b4f5fSWarner Losh				end
612088b4f5fSWarner Losh			end
613088b4f5fSWarner Losh
614fdabb5f5SKyle Evans			print(MSG_MALFORMED:format(n, line))
615aedd6be5SKyle Evans			status = false
616088b4f5fSWarner Losh		end
6179a16e110SKyle Evans		::nextline::
618aedd6be5SKyle Evans		n = n + 1
619088b4f5fSWarner Losh	end
620088b4f5fSWarner Losh
621aedd6be5SKyle Evans	return status
622088b4f5fSWarner Loshend
623088b4f5fSWarner Losh
6243fe0ac6aSKyle Evansfunction config.readConf(file, loaded_files)
6253fe0ac6aSKyle Evans	if loaded_files == nil then
6263fe0ac6aSKyle Evans		loaded_files = {}
6277a5c6c8bSKyle Evans	end
6287a5c6c8bSKyle Evans
6293fe0ac6aSKyle Evans	if loaded_files[file] ~= nil then
6303fe0ac6aSKyle Evans		return
6313fe0ac6aSKyle Evans	end
6323fe0ac6aSKyle Evans
633a25531dbSStéphane Rochoy	local top_level = next(loaded_files) == nil -- Are we the top-level readConf?
6343fe0ac6aSKyle Evans	print("Loading " .. file)
6353fe0ac6aSKyle Evans
6363fe0ac6aSKyle Evans	-- The final value of loader_conf_files is not important, so just
6373fe0ac6aSKyle Evans	-- clobber it here.  We'll later check if it's no longer nil and process
6383fe0ac6aSKyle Evans	-- the new value for files to read.
6393fe0ac6aSKyle Evans	setEnv("loader_conf_files", nil)
6403fe0ac6aSKyle Evans
6417a5c6c8bSKyle Evans	-- These may or may not exist, and that's ok. Do a
6427a5c6c8bSKyle Evans	-- silent parse so that we complain on parse errors but
6437a5c6c8bSKyle Evans	-- not for them simply not existing.
6443fe0ac6aSKyle Evans	if not config.processFile(file, true) then
6453fe0ac6aSKyle Evans		print(MSG_FAILPARSECFG:format(file))
6467a5c6c8bSKyle Evans	end
6477a5c6c8bSKyle Evans
6483fe0ac6aSKyle Evans	loaded_files[file] = true
6493fe0ac6aSKyle Evans
6503fe0ac6aSKyle Evans	-- Going to process "loader_conf_files" extra-files
6513fe0ac6aSKyle Evans	local loader_conf_files = getEnv("loader_conf_files")
6523fe0ac6aSKyle Evans	if loader_conf_files ~= nil then
6533fe0ac6aSKyle Evans		for name in loader_conf_files:gmatch("[%w%p]+") do
6543fe0ac6aSKyle Evans			config.readConf(name, loaded_files)
6557a5c6c8bSKyle Evans		end
6567a5c6c8bSKyle Evans	end
65772cf7db3SKyle Evans
658a25531dbSStéphane Rochoy	if top_level then
65972cf7db3SKyle Evans		local loader_conf_dirs = getEnv("loader_conf_dirs")
6603eb3a802SStéphane Rochoy
6613eb3a802SStéphane Rochoy		-- If product_vars is set, it must be a list of environment variable names
6623eb3a802SStéphane Rochoy		-- to walk through to guess product information. The order matters as
6633eb3a802SStéphane Rochoy		-- reading a config files override the previously defined values.
6643eb3a802SStéphane Rochoy		--
6653eb3a802SStéphane Rochoy		-- If product information can be guessed, for each product information
6663eb3a802SStéphane Rochoy		-- found, also read config files found in /boot/loader.conf.d/PRODUCT/.
6673eb3a802SStéphane Rochoy		local product_vars = getEnv("product_vars")
6683eb3a802SStéphane Rochoy		if product_vars then
6693eb3a802SStéphane Rochoy			local product_conf_dirs = ""
6703eb3a802SStéphane Rochoy			for var in product_vars:gmatch("%S+") do
6713eb3a802SStéphane Rochoy				local product = getEnv(var)
6723eb3a802SStéphane Rochoy				if product then
6733eb3a802SStéphane Rochoy					product_conf_dirs = product_conf_dirs .. " /boot/loader.conf.d/" .. product
6743eb3a802SStéphane Rochoy				end
6753eb3a802SStéphane Rochoy			end
6763eb3a802SStéphane Rochoy
6773eb3a802SStéphane Rochoy			if loader_conf_dirs then
6783eb3a802SStéphane Rochoy				loader_conf_dirs = loader_conf_dirs .. product_conf_dirs
6793eb3a802SStéphane Rochoy			else
6803eb3a802SStéphane Rochoy				loader_conf_dirs = product_conf_dirs
6813eb3a802SStéphane Rochoy			end
6823eb3a802SStéphane Rochoy		end
6833eb3a802SStéphane Rochoy
684a25531dbSStéphane Rochoy		-- Process "loader_conf_dirs" extra-directories
68572cf7db3SKyle Evans		if loader_conf_dirs ~= nil then
68672cf7db3SKyle Evans			for name in loader_conf_dirs:gmatch("[%w%p]+") do
68772cf7db3SKyle Evans				if lfs.attributes(name, "mode") ~= "directory" then
68872cf7db3SKyle Evans					print(MSG_FAILDIR:format(name))
68972cf7db3SKyle Evans					goto nextdir
69072cf7db3SKyle Evans				end
6913eb3a802SStéphane Rochoy
69272cf7db3SKyle Evans				for cfile in lfs.dir(name) do
69372cf7db3SKyle Evans					if cfile:match(".conf$") then
69472cf7db3SKyle Evans						local fpath = name .. "/" .. cfile
69572cf7db3SKyle Evans						if lfs.attributes(fpath, "mode") == "file" then
69672cf7db3SKyle Evans							config.readConf(fpath, loaded_files)
69772cf7db3SKyle Evans						end
69872cf7db3SKyle Evans					end
69972cf7db3SKyle Evans				end
70072cf7db3SKyle Evans				::nextdir::
70172cf7db3SKyle Evans			end
70272cf7db3SKyle Evans		end
703a25531dbSStéphane Rochoy
704a25531dbSStéphane Rochoy		-- Always allow overriding with local config files, e.g.,
705a25531dbSStéphane Rochoy		-- /boot/loader.conf.local.
706a25531dbSStéphane Rochoy		local local_loader_conf_files = getEnv("local_loader_conf_files")
707a25531dbSStéphane Rochoy		if local_loader_conf_files then
708a25531dbSStéphane Rochoy			for name in local_loader_conf_files:gmatch("[%w%p]+") do
709a25531dbSStéphane Rochoy				config.readConf(name, loaded_files)
710a25531dbSStéphane Rochoy			end
711a25531dbSStéphane Rochoy		end
71272cf7db3SKyle Evans	end
7137a5c6c8bSKyle Evansend
7147a5c6c8bSKyle Evans
71518c286a0SKyle Evans-- other_kernel is optionally the name of a kernel to load, if not the default
71618c286a0SKyle Evans-- or autoloaded default from the module_path
717322a2dddSKyle Evansfunction config.loadKernel(other_kernel)
718aedd6be5SKyle Evans	local flags = loader.getenv("kernel_options") or ""
719aedd6be5SKyle Evans	local kernel = other_kernel or loader.getenv("kernel")
720088b4f5fSWarner Losh
721322a2dddSKyle Evans	local function tryLoad(names)
722088b4f5fSWarner Losh		for name in names:gmatch("([^;]+)%s*;?") do
72380eb81f6SKyle Evans			local r = loader.perform("load " .. name ..
72480eb81f6SKyle Evans			     " " .. flags)
7259f71d421SKyle Evans			if r == 0 then
726aedd6be5SKyle Evans				return name
727088b4f5fSWarner Losh			end
728088b4f5fSWarner Losh		end
729aedd6be5SKyle Evans		return nil
73018c286a0SKyle Evans	end
731088b4f5fSWarner Losh
7325dd1b834SKyle Evans	local function getModulePath()
7335dd1b834SKyle Evans		local module_path = loader.getenv("module_path")
7345dd1b834SKyle Evans		local kernel_path = loader.getenv("kernel_path")
7355dd1b834SKyle Evans
7365dd1b834SKyle Evans		if kernel_path == nil then
7375dd1b834SKyle Evans			return module_path
7385dd1b834SKyle Evans		end
7395dd1b834SKyle Evans
7405dd1b834SKyle Evans		-- Strip the loaded kernel path from module_path. This currently assumes
7415dd1b834SKyle Evans		-- that the kernel path will be prepended to the module_path when it's
7425dd1b834SKyle Evans		-- found.
7435dd1b834SKyle Evans		kernel_path = escapeName(kernel_path .. ';')
7445dd1b834SKyle Evans		return module_path:gsub(kernel_path, '')
7455dd1b834SKyle Evans	end
7465dd1b834SKyle Evans
747322a2dddSKyle Evans	local function loadBootfile()
748aedd6be5SKyle Evans		local bootfile = loader.getenv("bootfile")
749088b4f5fSWarner Losh
750088b4f5fSWarner Losh		-- append default kernel name
7519f71d421SKyle Evans		if bootfile == nil then
752aedd6be5SKyle Evans			bootfile = "kernel"
753088b4f5fSWarner Losh		else
754aedd6be5SKyle Evans			bootfile = bootfile .. ";kernel"
755088b4f5fSWarner Losh		end
756088b4f5fSWarner Losh
757322a2dddSKyle Evans		return tryLoad(bootfile)
75824a1bd54SKyle Evans	end
759088b4f5fSWarner Losh
760088b4f5fSWarner Losh	-- kernel not set, try load from default module_path
7619f71d421SKyle Evans	if kernel == nil then
762322a2dddSKyle Evans		local res = loadBootfile()
763088b4f5fSWarner Losh
7649f71d421SKyle Evans		if res ~= nil then
765d4591301SKyle Evans			-- Default kernel is loaded
766aedd6be5SKyle Evans			config.kernel_loaded = nil
767aedd6be5SKyle Evans			return true
768088b4f5fSWarner Losh		else
769fdabb5f5SKyle Evans			print(MSG_DEFAULTKERNFAIL)
770aedd6be5SKyle Evans			return false
771088b4f5fSWarner Losh		end
772088b4f5fSWarner Losh	else
77315d8e03cSKyle Evans		-- Use our cached module_path, so we don't end up with multiple
77415d8e03cSKyle Evans		-- automatically added kernel paths to our final module_path
7755dd1b834SKyle Evans		local module_path = getModulePath()
776e2df27e3SKyle Evans		local res
777088b4f5fSWarner Losh
7789f71d421SKyle Evans		if other_kernel ~= nil then
779aedd6be5SKyle Evans			kernel = other_kernel
78018c286a0SKyle Evans		end
781088b4f5fSWarner Losh		-- first try load kernel with module_path = /boot/${kernel}
782088b4f5fSWarner Losh		-- then try load with module_path=${kernel}
783aedd6be5SKyle Evans		local paths = {"/boot/" .. kernel, kernel}
784088b4f5fSWarner Losh
785e2df27e3SKyle Evans		for _, v in pairs(paths) do
786aedd6be5SKyle Evans			loader.setenv("module_path", v)
787322a2dddSKyle Evans			res = loadBootfile()
788088b4f5fSWarner Losh
78915d8e03cSKyle Evans			-- succeeded, add path to module_path
7909f71d421SKyle Evans			if res ~= nil then
791aedd6be5SKyle Evans				config.kernel_loaded = kernel
7929f71d421SKyle Evans				if module_path ~= nil then
793c990f0a9SKyle Evans					loader.setenv("module_path", v .. ";" ..
794aedd6be5SKyle Evans					    module_path)
7955dd1b834SKyle Evans					loader.setenv("kernel_path", v)
796c990f0a9SKyle Evans				end
797aedd6be5SKyle Evans				return true
798088b4f5fSWarner Losh			end
799088b4f5fSWarner Losh		end
800088b4f5fSWarner Losh
801088b4f5fSWarner Losh		-- failed to load with ${kernel} as a directory
802088b4f5fSWarner Losh		-- try as a file
803322a2dddSKyle Evans		res = tryLoad(kernel)
8049f71d421SKyle Evans		if res ~= nil then
805aedd6be5SKyle Evans			config.kernel_loaded = kernel
806aedd6be5SKyle Evans			return true
807088b4f5fSWarner Losh		else
808fdabb5f5SKyle Evans			print(MSG_KERNFAIL:format(kernel))
809aedd6be5SKyle Evans			return false
810088b4f5fSWarner Losh		end
811088b4f5fSWarner Losh	end
812088b4f5fSWarner Loshend
813088b4f5fSWarner Losh
814322a2dddSKyle Evansfunction config.selectKernel(kernel)
815aedd6be5SKyle Evans	config.kernel_selected = kernel
816fa4a2394SKyle Evansend
817088b4f5fSWarner Losh
8187aba5b2fSKyle Evansfunction config.load(file, reloading)
8199f71d421SKyle Evans	if not file then
820aedd6be5SKyle Evans		file = "/boot/defaults/loader.conf"
821088b4f5fSWarner Losh	end
822088b4f5fSWarner Losh
8233fe0ac6aSKyle Evans	config.readConf(file)
824088b4f5fSWarner Losh
825322a2dddSKyle Evans	checkNextboot()
8263dcb7648SKyle Evans
8278d21763eSKyle Evans	local verbose = loader.getenv("verbose_loading") or "no"
828f0b03262SKyle Evans	config.verbose = verbose:lower() == "yes"
8297aba5b2fSKyle Evans	if not reloading then
8307aba5b2fSKyle Evans		hook.runAll("config.loaded")
8317aba5b2fSKyle Evans	end
832fa4a2394SKyle Evansend
833fa4a2394SKyle Evans
834fa4a2394SKyle Evans-- Reload configuration
835fa4a2394SKyle Evansfunction config.reload(file)
836aedd6be5SKyle Evans	modules = {}
83764c91742SKyle Evans	restoreEnv()
8387aba5b2fSKyle Evans	config.load(file, true)
839aea262bfSKyle Evans	hook.runAll("config.reloaded")
840fa4a2394SKyle Evansend
841fa4a2394SKyle Evans
842fa4a2394SKyle Evansfunction config.loadelf()
8430d7bee6aSKyle Evans	local xen_kernel = loader.getenv('xen_kernel')
844aedd6be5SKyle Evans	local kernel = config.kernel_selected or config.kernel_loaded
84579c20f22SRyan Moeller	local status
84615d8e03cSKyle Evans
8470d7bee6aSKyle Evans	if xen_kernel ~= nil then
8480d7bee6aSKyle Evans		print(MSG_XENKERNLOADING)
8490d7bee6aSKyle Evans		if cli_execute_unparsed('load ' .. xen_kernel) ~= 0 then
8500d7bee6aSKyle Evans			print(MSG_XENKERNFAIL:format(xen_kernel))
8513078173cSKyle Evans			return false
8520d7bee6aSKyle Evans		end
8530d7bee6aSKyle Evans	end
854fdabb5f5SKyle Evans	print(MSG_KERNLOADING)
85579c20f22SRyan Moeller	if not config.loadKernel(kernel) then
8563078173cSKyle Evans		return false
857fa4a2394SKyle Evans	end
85879c20f22SRyan Moeller	hook.runAll("kernel.loaded")
859088b4f5fSWarner Losh
860fdabb5f5SKyle Evans	print(MSG_MODLOADING)
8610db2ca0cSKyle Evans	status = loadModule(modules, not config.verbose)
8620db2ca0cSKyle Evans	hook.runAll("modules.loaded")
8630db2ca0cSKyle Evans	return status
864088b4f5fSWarner Loshend
865088b4f5fSWarner Losh
8664634bb1fSKyle Evansfunction config.enableModule(modname)
8674634bb1fSKyle Evans	if modules[modname] == nil then
8684634bb1fSKyle Evans		modules[modname] = {}
8694634bb1fSKyle Evans	elseif modules[modname].load == "YES" then
8704634bb1fSKyle Evans		modules[modname].force = true
8714634bb1fSKyle Evans		return true
8724634bb1fSKyle Evans	end
8734634bb1fSKyle Evans
8744634bb1fSKyle Evans	modules[modname].load = "YES"
8754634bb1fSKyle Evans	modules[modname].force = true
8764634bb1fSKyle Evans	return true
8774634bb1fSKyle Evansend
8784634bb1fSKyle Evans
8794634bb1fSKyle Evansfunction config.disableModule(modname)
8804634bb1fSKyle Evans	if modules[modname] == nil then
8814634bb1fSKyle Evans		return false
8824634bb1fSKyle Evans	elseif modules[modname].load ~= "YES" then
8834634bb1fSKyle Evans		return true
8844634bb1fSKyle Evans	end
8854634bb1fSKyle Evans
8864634bb1fSKyle Evans	modules[modname].load = "NO"
8874634bb1fSKyle Evans	modules[modname].force = nil
8884634bb1fSKyle Evans	return true
8894634bb1fSKyle Evansend
8904634bb1fSKyle Evans
8914634bb1fSKyle Evansfunction config.isModuleEnabled(modname)
8924634bb1fSKyle Evans	local mod = modules[modname]
8934634bb1fSKyle Evans	if not mod or mod.load ~= "YES" then
8944634bb1fSKyle Evans		return false
8954634bb1fSKyle Evans	end
8964634bb1fSKyle Evans
8974634bb1fSKyle Evans	if mod.force then
8984634bb1fSKyle Evans		return true
8994634bb1fSKyle Evans	end
9004634bb1fSKyle Evans
9014634bb1fSKyle Evans	local blacklist = getBlacklist()
90210aeb6cdSKyle Evans	return not blacklist[modname]
9034634bb1fSKyle Evansend
9044634bb1fSKyle Evans
9057ed84fa1SKyle Evansfunction config.getModuleInfo()
9067ed84fa1SKyle Evans	return {
9077ed84fa1SKyle Evans		modules = modules,
9087ed84fa1SKyle Evans		blacklist = getBlacklist()
9097ed84fa1SKyle Evans	}
9107ed84fa1SKyle Evansend
9117ed84fa1SKyle Evans
9123cb2f5f3SKyle Evanshook.registerType("config.buildenv")
9137aba5b2fSKyle Evanshook.registerType("config.loaded")
914aea262bfSKyle Evanshook.registerType("config.reloaded")
9154bee6189SRyan Moellerhook.registerType("kernel.loaded")
9160db2ca0cSKyle Evanshook.registerType("modules.loaded")
917aedd6be5SKyle Evansreturn config
918