mirror of
https://github.com/nchevsky/systemrescue-zfs.git
synced 2025-12-06 07:12:01 +01:00
Before this patch sysrescue-configuration.lua was executed twice each boot: (1) during initramfs stage (2) in the regular system as sysrescue-configuration.service While in theory both runs should create the same output, this isn't guaranteed and differences could appear for example because of network issues. Finding and debugging these is difficult because the output of sysrescue-configuration.lua created during initramfs isn't available anymore when the final system is running. This patch lets sysrescue-configuration.lua just run once during initramfs. The output JSON file is then written to the new tmpfs mounted dir /run/archiso/config/. This dir is also made the new default location for sysrescue-effective-config.json. A symlink from /etc/sysrescue/sysrescue-effective-config.json to the new location is created for compatibility with previous releases.
280 lines
11 KiB
Lua
Executable file
280 lines
11 KiB
Lua
Executable file
#!/usr/bin/env lua
|
|
--
|
|
-- Author: Francois Dupoux
|
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
--
|
|
-- SystemRescue configuration processing script
|
|
--
|
|
-- This script uses the SystemRescue yaml configuration files and the options
|
|
-- passed on the boot command line to override the default configuration.
|
|
-- It processes yaml configuration files in the alphabetical order, and each option
|
|
-- found in a file override the options defined earlier. Options passed on the
|
|
-- boot command like take precedence over configuration options defined in files.
|
|
-- At the end it writes the effective configuration to a JSON file which is meant
|
|
-- to be ready by any initialisation script which needs to know the configuration.
|
|
-- Shell scripts can read values from the JSON file using a command such as:
|
|
-- jq --raw-output '.global.copytoram' /run/archiso/config/sysrescue-effective-config.json
|
|
-- This script requires the following lua packages to run on Arch Linux:
|
|
-- sudo pacman -Sy lua lua-yaml lua-dkjson lua-http
|
|
|
|
-- ==============================================================================
|
|
-- Import modules
|
|
-- ==============================================================================
|
|
local lfs = require('lfs')
|
|
local yaml = require('yaml')
|
|
local json = require("dkjson")
|
|
local request = require("http.request")
|
|
|
|
-- ==============================================================================
|
|
-- Utility functions
|
|
-- ==============================================================================
|
|
-- Read a file and return all its contents
|
|
function read_file_contents(path)
|
|
local file = io.open(path, "rb")
|
|
if not file then
|
|
return nil
|
|
end
|
|
local content = file:read("*a")
|
|
file:close()
|
|
return content
|
|
end
|
|
|
|
-- Return true if the item is present in the list or false otherwise
|
|
function item_in_list(item, list)
|
|
for _, curitem in ipairs(list) do
|
|
if (curitem == item) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Return the number of items in a table
|
|
function get_table_size(mytable)
|
|
size = 0
|
|
for _ in pairs(mytable) do
|
|
size = size + 1
|
|
end
|
|
return size
|
|
end
|
|
|
|
-- Return a list of files with a yaml extension found in the directory 'dirname'
|
|
-- If 'filenames' is an empty list then it will return all files which have been found
|
|
-- If 'filenames' is not empty then it will only return files with a name present in the list
|
|
function list_config_files(dirname, filenames)
|
|
local results = {}
|
|
for curfile in lfs.dir(dirname) do
|
|
fullpath = dirname.."/"..curfile
|
|
filetype = lfs.attributes(fullpath, "mode")
|
|
if (filetype == "file") and curfile:match(".[Yy][Aa][Mm][Ll]$") then
|
|
if (get_table_size(filenames) == 0) or item_in_list(curfile, filenames) then
|
|
table.insert(results, fullpath)
|
|
end
|
|
end
|
|
end
|
|
table.sort(results)
|
|
return results
|
|
end
|
|
|
|
-- Attempt to find the option 'optname' on the boot command line and return its value
|
|
-- If 'multiple' is false then it will return the value of the last occurence found or nil
|
|
-- If 'multiple' is true then it will return a list of all values passed or an empty list
|
|
function search_cmdline_option(optname, multiple)
|
|
local result_single = nil
|
|
local result_multiple = {}
|
|
local cmdline = read_file_contents("/proc/cmdline")
|
|
for curopt in cmdline:gmatch("%S+") do
|
|
optmatch1 = string.match(curopt, "^"..optname.."$")
|
|
_, _, optmatch2 = string.find(curopt, "^"..optname.."=([^%s]+)$")
|
|
if (optmatch1 ~= nil) or (optmatch2 == 'y') or (optmatch2 == 'yes') or (optmatch2 == 'true') then
|
|
result_single = true
|
|
table.insert(result_multiple, true)
|
|
elseif (optmatch2 == 'n') or (optmatch2 == 'no') or (optmatch2 == 'false') then
|
|
result_single = false
|
|
table.insert(result_multiple, false)
|
|
elseif (optmatch2 ~= nil) then
|
|
result_single = optmatch2
|
|
table.insert(result_multiple, optmatch2)
|
|
end
|
|
end
|
|
if multiple == true then
|
|
return result_multiple
|
|
else
|
|
return result_single
|
|
end
|
|
end
|
|
|
|
-- Process a block of yaml configuration and override the current configuration with new values
|
|
function process_yaml_config(curconfig)
|
|
if (curconfig == nil) or (type(curconfig) ~= "table") then
|
|
io.stderr:write(string.format("This is not valid yaml, it will be ignored\n"))
|
|
return false
|
|
end
|
|
for scope, entries in pairs(config) do
|
|
for key, val in pairs(entries) do
|
|
if (curconfig[scope] ~= nil) and (curconfig[scope][key] ~= nil) then
|
|
print("- Overriding config['"..scope.."']['"..key.."'] with the value from the yaml file")
|
|
config[scope][key] = curconfig[scope][key]
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Download a file over http/https and return the contents of the file or nil if it fails
|
|
function download_file(fileurl)
|
|
local req_timeout = 10
|
|
local req = request.new_from_uri(fileurl)
|
|
local headers, stream = req:go(req_timeout)
|
|
|
|
if headers == nil then
|
|
io.stderr:write(string.format("Failed to download %s: Could not connect\n", fileurl))
|
|
return nil
|
|
end
|
|
|
|
status = headers:get(":status")
|
|
if status ~= '200' then
|
|
io.stderr:write(string.format("Failed to download %s: Received HTTP code %s\n", fileurl, status))
|
|
return nil
|
|
end
|
|
|
|
local body, err = stream:get_body_as_string()
|
|
if not body and err then
|
|
io.stderr:write(string.format("Failed to download %s: Error %s\n", fileurl, tostring(err)))
|
|
return nil
|
|
end
|
|
|
|
return body
|
|
end
|
|
|
|
-- ==============================================================================
|
|
-- Initialisation
|
|
-- ==============================================================================
|
|
errcnt = 0
|
|
|
|
-- ==============================================================================
|
|
-- Define the default configuration
|
|
-- ==============================================================================
|
|
print ("====> Define the default configuration ...")
|
|
config = {
|
|
["global"] = {
|
|
['copytoram'] = false,
|
|
['checksum'] = false,
|
|
['loadsrm'] = false,
|
|
['late_load_srm'] = "",
|
|
['dostartx'] = false,
|
|
['dovnc'] = false,
|
|
['noautologin'] = false,
|
|
['nofirewall'] = false,
|
|
['rootshell'] = "",
|
|
['rootpass'] = "",
|
|
['rootcryptpass'] = "",
|
|
['setkmap'] = "",
|
|
['vncpass'] = "",
|
|
},
|
|
["autorun"] = {
|
|
['ar_disable'] = false,
|
|
['ar_nowait'] = false,
|
|
['ar_nodel'] = false,
|
|
['ar_ignorefail'] = false,
|
|
['ar_attempts'] = 1,
|
|
['ar_source'] = "",
|
|
['ar_suffixes'] = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F",
|
|
},
|
|
["sysconfig"] = {
|
|
["ca-trust"] = {},
|
|
},
|
|
}
|
|
|
|
-- ==============================================================================
|
|
-- Override the configuration with values from yaml files
|
|
-- ==============================================================================
|
|
print ("====> Overriding the default configuration with values from yaml files ...")
|
|
confdirs = {"/run/archiso/bootmnt/sysrescue.d", "/run/archiso/copytoram/sysrescue.d"}
|
|
conffiles = search_cmdline_option("sysrescuecfg", true)
|
|
|
|
-- Process local yaml configuration files
|
|
for _, curdir in ipairs(confdirs) do
|
|
if lfs.attributes(curdir, "mode") == "directory" then
|
|
print("Searching for yaml configuration files in "..curdir.." ...")
|
|
for _, curfile in ipairs(list_config_files(curdir, conffiles)) do
|
|
print(string.format("Processing local yaml configuration file: %s ...", curfile))
|
|
local curconfig = yaml.loadpath(curfile)
|
|
--print("++++++++++++++\n"..yaml.dump(curconfig).."++++++++++++++\n")
|
|
if process_yaml_config(curconfig) == false then
|
|
errcnt = errcnt + 1
|
|
end
|
|
end
|
|
else
|
|
print("Directory "..curdir.." was not found so it has been ignored")
|
|
end
|
|
end
|
|
|
|
-- Process remote yaml configuration files
|
|
print("Searching for remote yaml configuration files ...")
|
|
for _, curfile in ipairs(conffiles) do
|
|
if string.match(curfile, "^https?://") then
|
|
print(string.format("Processing remote yaml configuration file: %s ...", curfile))
|
|
local contents = download_file(curfile)
|
|
if (contents == nil) or (process_yaml_config(yaml.load(contents)) == false) then
|
|
errcnt = errcnt + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ==============================================================================
|
|
-- Override the configuration with values passed on the boot command line
|
|
-- ==============================================================================
|
|
print ("====> Overriding the configuration with options passed on the boot command line ...")
|
|
for _, scope in ipairs({"global", "autorun"}) do
|
|
for key,val in pairs(config[scope]) do
|
|
optresult = search_cmdline_option(key, false)
|
|
if optresult == true then
|
|
print("- Option '"..key.."' has been enabled on the boot command line")
|
|
config[scope][key] = optresult
|
|
elseif optresult == false then
|
|
print("- Option '"..key.."' has been disabled on the boot command line")
|
|
config[scope][key] = optresult
|
|
elseif optresult ~= nil then
|
|
print("- Option '"..key.."' has been defined as '"..optresult.."' on the boot command line")
|
|
config[scope][key] = optresult
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ==============================================================================
|
|
-- Print the effective configuration
|
|
-- ==============================================================================
|
|
print ("====> Printing the effective configuration")
|
|
local jsoncfgtxt = json.encode (config, { indent = true })
|
|
print (jsoncfgtxt)
|
|
|
|
-- ==============================================================================
|
|
-- Write the effective configuration to a JSON file
|
|
-- ==============================================================================
|
|
print ("====> Writing the effective configuration to a JSON file ...")
|
|
output_location = "/run/archiso/config"
|
|
output_filename = "sysrescue-effective-config.json"
|
|
output_fullpath = output_location.."/"..output_filename
|
|
jsoncfgfile = io.open(output_fullpath, "w")
|
|
if jsoncfgfile == nil then
|
|
io.stderr:write(string.format("ERROR: Failed to create effective configuration file in %s\n", output_fullpath))
|
|
os.exit(1)
|
|
end
|
|
jsoncfgfile:write(jsoncfgtxt)
|
|
jsoncfgfile:close()
|
|
os.execute("chmod 700 "..output_location)
|
|
os.execute("chmod 600 "..output_fullpath)
|
|
print ("Effective configuration has been written to "..output_fullpath)
|
|
|
|
-- ==============================================================================
|
|
-- Error handling
|
|
-- ==============================================================================
|
|
if errcnt == 0 then
|
|
print ("SUCCESS: Have successfully completed the processing of the configuration")
|
|
os.exit(0)
|
|
else
|
|
io.stderr:write(string.format("FAILURE: Have completed the processing of the configuration with %d errors\n", errcnt))
|
|
os.exit(1)
|
|
end
|