opnsense-src/stand/lua/config.lua
Kyle Evans 25c4c7a5f3 lualoader: Move carousel storage out into config
Carousel storage doesn't need to happen in the menu module, and indeed
storing it there introduces a circular reference between drawer and menu
that only works because of global pollution in loader.lua.

Carousel choices generally map to config entries anyways, making it as good
of place as any to store these. Move {get,set}CarouselIndex functionality
out into config so that drawer and menu may both use it. If we had more
carousel functionality, it might make sense to create a carousel module, but
this is not the case.
2018-02-20 18:04:08 +00:00

455 lines
10 KiB
Lua

--
-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--
-- $FreeBSD$
--
local config = {};
local modules = {};
local pattern_table;
local carousel_choices = {};
pattern_table = {
[1] = {
str = "^%s*(#.*)",
process = function(k, v) end
},
-- module_load="value"
[2] = {
str = "^%s*([%w_]+)_load%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
if (modules[k] == nil) then
modules[k] = {};
end
modules[k].load = v:upper();
end
},
-- module_name="value"
[3] = {
str = "^%s*([%w_]+)_name%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "name", v);
end
},
-- module_type="value"
[4] = {
str = "^%s*([%w_]+)_type%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "type", v);
end
},
-- module_flags="value"
[5] = {
str = "^%s*([%w_]+)_flags%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "flags", v);
end
},
-- module_before="value"
[6] = {
str = "^%s*([%w_]+)_before%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "before", v);
end
},
-- module_after="value"
[7] = {
str = "^%s*([%w_]+)_after%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "after", v);
end
},
-- module_error="value"
[8] = {
str = "^%s*([%w_]+)_error%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
config.setKey(k, "error", v);
end
},
-- exec="command"
[9] = {
str = "^%s*exec%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
if (loader.perform(k) ~= 0) then
print("Failed to exec '" .. k .. "'");
end
end
},
-- env_var="value"
[10] = {
str = "^%s*([%w%p]+)%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
process = function(k, v)
if (config.setenv(k, v) ~= 0) then
print("Failed to set '" .. k ..
"' with value: " .. v .. "");
end
end
},
-- env_var=num
[11] = {
str = "^%s*([%w%p]+)%s*=%s*(%d+)%s*(.*)",
process = function(k, v)
if (config.setenv(k, v) ~= 0) then
print("Failed to set '" .. k ..
"' with value: " .. v .. "");
end
end
}
};
-- Module exports
-- Which variables we changed
config.env_changed = {};
-- Values to restore env to (nil to unset)
config.env_restore = {};
-- The first item in every carousel is always the default item.
function config.getCarouselIndex(id)
local val = carousel_choices[id];
if (val == nil) then
return 1;
end
return val;
end
function config.setCarouselIndex(id, idx)
carousel_choices[id] = idx;
end
function config.restoreEnv()
for k, v in pairs(config.env_changed) do
local restore_value = config.env_restore[k];
if (restore_value ~= nil) then
loader.setenv(k, restore_value);
else
loader.unsetenv(k);
end
end
config.env_changed = {};
config.env_restore = {};
end
function config.setenv(k, v)
-- Do we need to track this change?
if (config.env_changed[k] == nil) then
config.env_changed[k] = true;
config.env_restore[k] = loader.getenv(k);
end
return loader.setenv(k, v);
end
function config.setKey(k, n, v)
if (modules[k] == nil) then
modules[k] = {};
end
modules[k][n] = v;
end
function config.lsModules()
print("== Listing modules");
for k, v in pairs(modules) do
print(k, v.load);
end
print("== List of modules ended");
end
function config.isValidComment(c)
if (c ~= nil) then
local s = c:match("^%s*#.*");
if (s == nil) then
s = c:match("^%s*$");
end
if (s == nil) then
return false;
end
end
return true;
end
function config.loadmod(mod, silent)
local status = true;
for k, v in pairs(mod) do
if (v.load == "YES") then
local str = "load ";
if (v.flags ~= nil) then
str = str .. v.flags .. " ";
end
if (v.type ~= nil) then
str = str .. "-t " .. v.type .. " ";
end
if (v.name ~= nil) then
str = str .. v.name;
else
str = str .. k;
end
if (v.before ~= nil) then
if (loader.perform(v.before) ~= 0) then
if (not silent) then
print("Failed to execute '" ..
v.before ..
"' before loading '" .. k ..
"'");
end
status = false;
end
end
if (loader.perform(str) ~= 0) then
if (not silent) then
print("Failed to execute '" .. str ..
"'");
end
if (v.error ~= nil) then
loader.perform(v.error);
end
status = false;
end
if (v.after ~= nil) then
if (loader.perform(v.after) ~= 0) then
if (not silent) then
print("Failed to execute '" ..
v.after ..
"' after loading '" .. k ..
"'");
end
status = false;
end
end
else
-- if not silent then
-- print("Skiping module '". . k .. "'");
-- end
end
end
return status;
end
function config.parse(name, silent)
local f = io.open(name);
if (f == nil) then
if (not silent) then
print("Failed to open config: '" .. name .. "'");
end
return false;
end
local text;
local r;
text, r = io.read(f);
if (text == nil) then
if (not silent) then
print("Failed to read config: '" .. name .. "'");
end
return false;
end
local n = 1;
local status = true;
for line in text:gmatch("([^\n]+)") do
if (line:match("^%s*$") == nil) then
local found = false;
for i, val in ipairs(pattern_table) do
local k, v, c = line:match(val.str);
if (k ~= nil) then
found = true;
if (config.isValidComment(c)) then
val.process(k, v);
else
print("Malformed line (" .. n ..
"):\n\t'" .. line .. "'");
status = false;
end
break;
end
end
if (found == false) then
print("Malformed line (" .. n .. "):\n\t'" ..
line .. "'");
status = false;
end
end
n = n + 1;
end
return status;
end
-- other_kernel is optionally the name of a kernel to load, if not the default
-- or autoloaded default from the module_path
function config.loadkernel(other_kernel)
local flags = loader.getenv("kernel_options") or "";
local kernel = other_kernel or loader.getenv("kernel");
local try_load = function (names)
for name in names:gmatch("([^;]+)%s*;?") do
r = loader.perform("load " .. flags .. " " .. name);
if (r == 0) then
return name;
end
end
return nil;
end
local load_bootfile = function()
local bootfile = loader.getenv("bootfile");
-- append default kernel name
if (bootfile == nil) then
bootfile = "kernel";
else
bootfile = bootfile .. ";kernel";
end
return try_load(bootfile);
end
-- kernel not set, try load from default module_path
if (kernel == nil) then
local res = load_bootfile();
if (res ~= nil) then
-- Default kernel is loaded
config.kernel_loaded = nil;
return true;
else
print("No kernel set, failed to load from module_path");
return false;
end
else
-- Use our cached module_path, so we don't end up with multiple
-- automatically added kernel paths to our final module_path
local module_path = config.module_path;
local res = nil;
if (other_kernel ~= nil) then
kernel = other_kernel;
end
-- first try load kernel with module_path = /boot/${kernel}
-- then try load with module_path=${kernel}
local paths = {"/boot/" .. kernel, kernel};
for k,v in pairs(paths) do
loader.setenv("module_path", v);
res = load_bootfile();
-- succeeded, add path to module_path
if (res ~= nil) then
config.kernel_loaded = kernel;
if (module_path ~= nil) then
loader.setenv("module_path", v .. ";" ..
module_path);
end
return true;
end
end
-- failed to load with ${kernel} as a directory
-- try as a file
res = try_load(kernel);
if (res ~= nil) then
config.kernel_loaded = kernel;
return true;
else
print("Failed to load kernel '" .. kernel .. "'");
return false;
end
end
end
function config.selectkernel(kernel)
config.kernel_selected = kernel;
end
function config.load(file)
if (not file) then
file = "/boot/defaults/loader.conf";
end
if (not config.parse(file)) then
-- print("Failed to parse configuration: '" .. file .. "'");
end
local f = loader.getenv("loader_conf_files");
if (f ~= nil) then
for name in f:gmatch("([%w%p]+)%s*") do
if (not config.parse(name)) then
-- print("Failed to parse configuration: '" ..
-- name .. "'");
end
end
end
-- Cache the provided module_path at load time for later use
config.module_path = loader.getenv("module_path");
end
-- Reload configuration
function config.reload(file)
modules = {};
config.restoreEnv();
config.load(file);
end
function config.loadelf()
local kernel = config.kernel_loaded or config.kernel_selected;
local loaded = false;
print("Loading kernel...");
loaded = config.loadkernel(kernel);
if (not loaded) then
loaded = config.loadkernel();
end
if (not loaded) then
-- Ultimately failed to load kernel
print("Failed to load any kernel");
return;
end
print("Loading configured modules...");
if (not config.loadmod(modules)) then
print("Could not load one or more modules!");
end
end
return config;