1088b4f5fSWarner Losh-- 272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 372e39d71SKyle Evans-- 4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <[email protected]> 521d5bcbeSKyle 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-- $FreeBSD$ 30088b4f5fSWarner Losh-- 31088b4f5fSWarner Losh 32aea262bfSKyle Evanslocal hook = require("hook") 33aea262bfSKyle Evans 34aedd6be5SKyle Evanslocal config = {} 35aedd6be5SKyle Evanslocal modules = {} 36aedd6be5SKyle Evanslocal carousel_choices = {} 3764c91742SKyle Evans-- Which variables we changed 3864c91742SKyle Evanslocal env_changed = {} 3964c91742SKyle Evans-- Values to restore env to (nil to unset) 4064c91742SKyle Evanslocal env_restore = {} 4125c4c7a5SKyle Evans 42fdabb5f5SKyle Evanslocal MSG_FAILEXEC = "Failed to exec '%s'" 43fdabb5f5SKyle Evanslocal MSG_FAILSETENV = "Failed to '%s' with value: %s" 44fdabb5f5SKyle Evanslocal MSG_FAILOPENCFG = "Failed to open config: '%s'" 45fdabb5f5SKyle Evanslocal MSG_FAILREADCFG = "Failed to read config: '%s'" 46fdabb5f5SKyle Evanslocal MSG_FAILPARSECFG = "Failed to parse config: '%s'" 47fdabb5f5SKyle Evanslocal MSG_FAILEXBEF = "Failed to execute '%s' before loading '%s'" 48fdabb5f5SKyle Evanslocal MSG_FAILEXAF = "Failed to execute '%s' after loading '%s'" 49fdabb5f5SKyle Evanslocal MSG_MALFORMED = "Malformed line (%d):\n\t'%s'" 50fdabb5f5SKyle Evanslocal MSG_DEFAULTKERNFAIL = "No kernel set, failed to load from module_path" 51fdabb5f5SKyle Evanslocal MSG_KERNFAIL = "Failed to load kernel '%s'" 520d7bee6aSKyle Evanslocal MSG_XENKERNFAIL = "Failed to load Xen kernel '%s'" 530d7bee6aSKyle Evanslocal MSG_XENKERNLOADING = "Loading Xen kernel..." 54fdabb5f5SKyle Evanslocal MSG_KERNLOADING = "Loading kernel..." 55fdabb5f5SKyle Evanslocal MSG_MODLOADING = "Loading configured modules..." 56532dc172SKyle Evanslocal MSG_MODBLACKLIST = "Not loading blacklisted module '%s'" 57fdabb5f5SKyle Evans 58058c692eSKyle Evanslocal MODULEEXPR = '([%w-_]+)' 591ee89ab5SKyle Evanslocal QVALEXPR = "\"([%w%s%p]-)\"" 601ee89ab5SKyle Evanslocal QVALREPL = QVALEXPR:gsub('%%', '%%%%') 611ee89ab5SKyle Evanslocal WORDEXPR = "([%w]+)" 621ee89ab5SKyle Evanslocal WORDREPL = WORDEXPR:gsub('%%', '%%%%') 63058c692eSKyle Evans 6464c91742SKyle Evanslocal function restoreEnv() 6564c91742SKyle Evans -- Examine changed environment variables 6664c91742SKyle Evans for k, v in pairs(env_changed) do 6764c91742SKyle Evans local restore_value = env_restore[k] 6864c91742SKyle Evans if restore_value == nil then 6964c91742SKyle Evans -- This one doesn't need restored for some reason 7064c91742SKyle Evans goto continue 7164c91742SKyle Evans end 7264c91742SKyle Evans local current_value = loader.getenv(k) 7364c91742SKyle Evans if current_value ~= v then 7464c91742SKyle Evans -- This was overwritten by some action taken on the menu 7564c91742SKyle Evans -- most likely; we'll leave it be. 7664c91742SKyle Evans goto continue 7764c91742SKyle Evans end 7864c91742SKyle Evans restore_value = restore_value.value 7964c91742SKyle Evans if restore_value ~= nil then 8064c91742SKyle Evans loader.setenv(k, restore_value) 8164c91742SKyle Evans else 8264c91742SKyle Evans loader.unsetenv(k) 8364c91742SKyle Evans end 8464c91742SKyle Evans ::continue:: 8564c91742SKyle Evans end 8664c91742SKyle Evans 8764c91742SKyle Evans env_changed = {} 8864c91742SKyle Evans env_restore = {} 8964c91742SKyle Evansend 9064c91742SKyle Evans 9164c91742SKyle Evanslocal function setEnv(key, value) 9264c91742SKyle Evans -- Track the original value for this if we haven't already 9364c91742SKyle Evans if env_restore[key] == nil then 9464c91742SKyle Evans env_restore[key] = {value = loader.getenv(key)} 9564c91742SKyle Evans end 9664c91742SKyle Evans 9764c91742SKyle Evans env_changed[key] = value 9864c91742SKyle Evans 9964c91742SKyle Evans return loader.setenv(key, value) 10064c91742SKyle Evansend 10164c91742SKyle Evans 102dbef5253SKyle Evans-- name here is one of 'name', 'type', flags', 'before', 'after', or 'error.' 103dbef5253SKyle Evans-- These are set from lines in loader.conf(5): ${key}_${name}="${value}" where 104dbef5253SKyle Evans-- ${key} is a module name. 105dbef5253SKyle Evanslocal function setKey(key, name, value) 106dbef5253SKyle Evans if modules[key] == nil then 107dbef5253SKyle Evans modules[key] = {} 108dbef5253SKyle Evans end 109dbef5253SKyle Evans modules[key][name] = value 110dbef5253SKyle Evansend 111dbef5253SKyle Evans 112deb8c8f5SKyle Evans-- Escapes the named value for use as a literal in a replacement pattern. 113deb8c8f5SKyle Evans-- e.g. dhcp.host-name gets turned into dhcp%.host%-name to remove the special 114deb8c8f5SKyle Evans-- meaning. 115deb8c8f5SKyle Evanslocal function escapeName(name) 116deb8c8f5SKyle Evans return name:gsub("([%p])", "%%%1") 117deb8c8f5SKyle Evansend 118deb8c8f5SKyle Evans 119deb8c8f5SKyle Evanslocal function processEnvVar(value) 120deb8c8f5SKyle Evans for name in value:gmatch("${([^}]+)}") do 121deb8c8f5SKyle Evans local replacement = loader.getenv(name) or "" 122deb8c8f5SKyle Evans value = value:gsub("${" .. escapeName(name) .. "}", replacement) 123deb8c8f5SKyle Evans end 124deb8c8f5SKyle Evans for name in value:gmatch("$([%w%p]+)%s*") do 125deb8c8f5SKyle Evans local replacement = loader.getenv(name) or "" 126deb8c8f5SKyle Evans value = value:gsub("$" .. escapeName(name), replacement) 127deb8c8f5SKyle Evans end 128deb8c8f5SKyle Evans return value 129deb8c8f5SKyle Evansend 130deb8c8f5SKyle Evans 1311ee89ab5SKyle Evanslocal function checkPattern(line, pattern) 1321ee89ab5SKyle Evans local function _realCheck(_line, _pattern) 1331ee89ab5SKyle Evans return _line:match(_pattern) 1341ee89ab5SKyle Evans end 1351ee89ab5SKyle Evans 1361ee89ab5SKyle Evans if pattern:find('$VALUE') then 1371ee89ab5SKyle Evans local k, v, c 1381ee89ab5SKyle Evans k, v, c = _realCheck(line, pattern:gsub('$VALUE', QVALREPL)) 1391ee89ab5SKyle Evans if k ~= nil then 1401ee89ab5SKyle Evans return k,v, c 1411ee89ab5SKyle Evans end 1421ee89ab5SKyle Evans return _realCheck(line, pattern:gsub('$VALUE', WORDREPL)) 1431ee89ab5SKyle Evans else 1441ee89ab5SKyle Evans return _realCheck(line, pattern) 1451ee89ab5SKyle Evans end 1461ee89ab5SKyle Evansend 1471ee89ab5SKyle Evans 148058c692eSKyle Evans-- str in this table is a regex pattern. It will automatically be anchored to 149058c692eSKyle Evans-- the beginning of a line and any preceding whitespace will be skipped. The 150058c692eSKyle Evans-- pattern should have no more than two captures patterns, which correspond to 151058c692eSKyle Evans-- the two parameters (usually 'key' and 'value') that are passed to the 1521ee89ab5SKyle Evans-- process function. All trailing characters will be validated. Any $VALUE 1531ee89ab5SKyle Evans-- token included in a pattern will be tried first with a quoted value capture 1541ee89ab5SKyle Evans-- group, then a single-word value capture group. This is our kludge for Lua 1551ee89ab5SKyle Evans-- regex not supporting branching. 1569a16e110SKyle Evans-- 1579a16e110SKyle Evans-- We have two special entries in this table: the first is the first entry, 1589a16e110SKyle Evans-- a full-line comment. The second is for 'exec' handling. Both have a single 1599a16e110SKyle Evans-- capture group, but the difference is that the full-line comment pattern will 1609a16e110SKyle Evans-- match the entire line. This does not run afoul of the later end of line 1619a16e110SKyle Evans-- validation that we'll do after a match. However, the 'exec' pattern will. 1629a16e110SKyle Evans-- We document the exceptions with a special 'groups' index that indicates 1639a16e110SKyle Evans-- the number of capture groups, if not two. We'll use this later to do 1649a16e110SKyle Evans-- validation on the proper entry. 1651ee89ab5SKyle Evans-- 166e1a8835aSKyle Evanslocal pattern_table = { 167e1a8835aSKyle Evans { 168058c692eSKyle Evans str = "(#.*)", 169e1a8835aSKyle Evans process = function(_, _) end, 1709a16e110SKyle Evans groups = 1, 171088b4f5fSWarner Losh }, 172088b4f5fSWarner Losh -- module_load="value" 173e1a8835aSKyle Evans { 1741ee89ab5SKyle Evans str = MODULEEXPR .. "_load%s*=%s*$VALUE", 175088b4f5fSWarner Losh process = function(k, v) 1769f71d421SKyle Evans if modules[k] == nil then 177aedd6be5SKyle Evans modules[k] = {} 178088b4f5fSWarner Losh end 179aedd6be5SKyle Evans modules[k].load = v:upper() 180e1a8835aSKyle Evans end, 181088b4f5fSWarner Losh }, 182088b4f5fSWarner Losh -- module_name="value" 183e1a8835aSKyle Evans { 1841ee89ab5SKyle Evans str = MODULEEXPR .. "_name%s*=%s*$VALUE", 185088b4f5fSWarner Losh process = function(k, v) 186dbef5253SKyle Evans setKey(k, "name", v) 187e1a8835aSKyle Evans end, 188088b4f5fSWarner Losh }, 189088b4f5fSWarner Losh -- module_type="value" 190e1a8835aSKyle Evans { 1911ee89ab5SKyle Evans str = MODULEEXPR .. "_type%s*=%s*$VALUE", 192088b4f5fSWarner Losh process = function(k, v) 193dbef5253SKyle Evans setKey(k, "type", v) 194e1a8835aSKyle Evans end, 195088b4f5fSWarner Losh }, 196088b4f5fSWarner Losh -- module_flags="value" 197e1a8835aSKyle Evans { 1981ee89ab5SKyle Evans str = MODULEEXPR .. "_flags%s*=%s*$VALUE", 199088b4f5fSWarner Losh process = function(k, v) 200dbef5253SKyle Evans setKey(k, "flags", v) 201e1a8835aSKyle Evans end, 202088b4f5fSWarner Losh }, 203088b4f5fSWarner Losh -- module_before="value" 204e1a8835aSKyle Evans { 2051ee89ab5SKyle Evans str = MODULEEXPR .. "_before%s*=%s*$VALUE", 206088b4f5fSWarner Losh process = function(k, v) 207dbef5253SKyle Evans setKey(k, "before", v) 208e1a8835aSKyle Evans end, 209088b4f5fSWarner Losh }, 210088b4f5fSWarner Losh -- module_after="value" 211e1a8835aSKyle Evans { 2121ee89ab5SKyle Evans str = MODULEEXPR .. "_after%s*=%s*$VALUE", 213088b4f5fSWarner Losh process = function(k, v) 214dbef5253SKyle Evans setKey(k, "after", v) 215e1a8835aSKyle Evans end, 216088b4f5fSWarner Losh }, 217088b4f5fSWarner Losh -- module_error="value" 218e1a8835aSKyle Evans { 2191ee89ab5SKyle Evans str = MODULEEXPR .. "_error%s*=%s*$VALUE", 220088b4f5fSWarner Losh process = function(k, v) 221dbef5253SKyle Evans setKey(k, "error", v) 222e1a8835aSKyle Evans end, 223088b4f5fSWarner Losh }, 224088b4f5fSWarner Losh -- exec="command" 225e1a8835aSKyle Evans { 2261ee89ab5SKyle Evans str = "exec%s*=%s*" .. QVALEXPR, 227e2df27e3SKyle Evans process = function(k, _) 2289ab2d3c5SKyle Evans if cli_execute_unparsed(k) ~= 0 then 229fdabb5f5SKyle Evans print(MSG_FAILEXEC:format(k)) 230088b4f5fSWarner Losh end 231e1a8835aSKyle Evans end, 2329a16e110SKyle Evans groups = 1, 233088b4f5fSWarner Losh }, 234088b4f5fSWarner Losh -- env_var="value" 235e1a8835aSKyle Evans { 2361ee89ab5SKyle Evans str = "([%w%p]+)%s*=%s*$VALUE", 237088b4f5fSWarner Losh process = function(k, v) 238deb8c8f5SKyle Evans if setEnv(k, processEnvVar(v)) ~= 0 then 239fdabb5f5SKyle Evans print(MSG_FAILSETENV:format(k, v)) 240088b4f5fSWarner Losh end 241e1a8835aSKyle Evans end, 242088b4f5fSWarner Losh }, 243088b4f5fSWarner Losh -- env_var=num 244e1a8835aSKyle Evans { 245058c692eSKyle Evans str = "([%w%p]+)%s*=%s*(-?%d+)", 246088b4f5fSWarner Losh process = function(k, v) 247deb8c8f5SKyle Evans if setEnv(k, processEnvVar(v)) ~= 0 then 248fdabb5f5SKyle Evans print(MSG_FAILSETENV:format(k, tostring(v))) 249088b4f5fSWarner Losh end 250e1a8835aSKyle Evans end, 251e1a8835aSKyle Evans }, 252aedd6be5SKyle Evans} 253088b4f5fSWarner Losh 254dbef5253SKyle Evanslocal function isValidComment(line) 255dbef5253SKyle Evans if line ~= nil then 256dbef5253SKyle Evans local s = line:match("^%s*#.*") 257dbef5253SKyle Evans if s == nil then 258dbef5253SKyle Evans s = line:match("^%s*$") 259dbef5253SKyle Evans end 260dbef5253SKyle Evans if s == nil then 261dbef5253SKyle Evans return false 262dbef5253SKyle Evans end 263dbef5253SKyle Evans end 264dbef5253SKyle Evans return true 265dbef5253SKyle Evansend 266dbef5253SKyle Evans 267532dc172SKyle Evanslocal function getBlacklist() 268*fdc0a80dSKyle Evans local blacklist = {} 269532dc172SKyle Evans local blacklist_str = loader.getenv('module_blacklist') 270532dc172SKyle Evans if blacklist_str == nil then 271*fdc0a80dSKyle Evans return blacklist 272532dc172SKyle Evans end 273532dc172SKyle Evans 274532dc172SKyle Evans for mod in blacklist_str:gmatch("[;, ]?([%w-_]+)[;, ]?") do 275532dc172SKyle Evans blacklist[mod] = true 276532dc172SKyle Evans end 277532dc172SKyle Evans return blacklist 278532dc172SKyle Evansend 279532dc172SKyle Evans 280dbef5253SKyle Evanslocal function loadModule(mod, silent) 281dbef5253SKyle Evans local status = true 282532dc172SKyle Evans local blacklist = getBlacklist() 283dbef5253SKyle Evans local pstatus 284dbef5253SKyle Evans for k, v in pairs(mod) do 2858d21763eSKyle Evans if v.load ~= nil and v.load:lower() == "yes" then 286532dc172SKyle Evans local module_name = v.name or k 287532dc172SKyle Evans if blacklist[module_name] ~= nil then 288532dc172SKyle Evans if not silent then 289532dc172SKyle Evans print(MSG_MODBLACKLIST:format(module_name)) 290532dc172SKyle Evans end 291532dc172SKyle Evans goto continue 292532dc172SKyle Evans end 293*fdc0a80dSKyle Evans if not silent then 294*fdc0a80dSKyle Evans loader.printc(module_name .. "...") 295*fdc0a80dSKyle Evans end 296dbef5253SKyle Evans local str = "load " 297dbef5253SKyle Evans if v.type ~= nil then 298dbef5253SKyle Evans str = str .. "-t " .. v.type .. " " 299dbef5253SKyle Evans end 300532dc172SKyle Evans str = str .. module_name 30135beb928SKyle Evans if v.flags ~= nil then 30235beb928SKyle Evans str = str .. " " .. v.flags 30335beb928SKyle Evans end 304dbef5253SKyle Evans if v.before ~= nil then 305dbef5253SKyle Evans pstatus = cli_execute_unparsed(v.before) == 0 306dbef5253SKyle Evans if not pstatus and not silent then 307dbef5253SKyle Evans print(MSG_FAILEXBEF:format(v.before, k)) 308dbef5253SKyle Evans end 309dbef5253SKyle Evans status = status and pstatus 310dbef5253SKyle Evans end 311dbef5253SKyle Evans 312dbef5253SKyle Evans if cli_execute_unparsed(str) ~= 0 then 313*fdc0a80dSKyle Evans -- XXX Temporary shim: don't break the boot if 314*fdc0a80dSKyle Evans -- loader hadn't been recompiled with this 315*fdc0a80dSKyle Evans -- function exposed. 316*fdc0a80dSKyle Evans if loader.command_error then 317*fdc0a80dSKyle Evans print(loader.command_error()) 318*fdc0a80dSKyle Evans end 319dbef5253SKyle Evans if not silent then 320*fdc0a80dSKyle Evans print("failed!") 321dbef5253SKyle Evans end 322dbef5253SKyle Evans if v.error ~= nil then 323dbef5253SKyle Evans cli_execute_unparsed(v.error) 324dbef5253SKyle Evans end 325dbef5253SKyle Evans status = false 326*fdc0a80dSKyle Evans elseif v.after ~= nil then 327dbef5253SKyle Evans pstatus = cli_execute_unparsed(v.after) == 0 328dbef5253SKyle Evans if not pstatus and not silent then 329dbef5253SKyle Evans print(MSG_FAILEXAF:format(v.after, k)) 330dbef5253SKyle Evans end 331*fdc0a80dSKyle Evans if not silent then 332*fdc0a80dSKyle Evans print("ok") 333*fdc0a80dSKyle Evans end 334dbef5253SKyle Evans status = status and pstatus 335dbef5253SKyle Evans end 336dbef5253SKyle Evans end 337532dc172SKyle Evans ::continue:: 338dbef5253SKyle Evans end 339dbef5253SKyle Evans 340dbef5253SKyle Evans return status 341dbef5253SKyle Evansend 342dbef5253SKyle Evans 3434072dcb3SKyle Evanslocal function readConfFiles(loaded_files) 3444072dcb3SKyle Evans local f = loader.getenv("loader_conf_files") 3454072dcb3SKyle Evans if f ~= nil then 3464072dcb3SKyle Evans for name in f:gmatch("([%w%p]+)%s*") do 3474072dcb3SKyle Evans if loaded_files[name] ~= nil then 3484072dcb3SKyle Evans goto continue 3494072dcb3SKyle Evans end 3504072dcb3SKyle Evans 3514072dcb3SKyle Evans local prefiles = loader.getenv("loader_conf_files") 3524072dcb3SKyle Evans 3534072dcb3SKyle Evans print("Loading " .. name) 3544072dcb3SKyle Evans -- These may or may not exist, and that's ok. Do a 3554072dcb3SKyle Evans -- silent parse so that we complain on parse errors but 3564072dcb3SKyle Evans -- not for them simply not existing. 3574072dcb3SKyle Evans if not config.processFile(name, true) then 3584072dcb3SKyle Evans print(MSG_FAILPARSECFG:format(name)) 3594072dcb3SKyle Evans end 3604072dcb3SKyle Evans 3614072dcb3SKyle Evans loaded_files[name] = true 3624072dcb3SKyle Evans local newfiles = loader.getenv("loader_conf_files") 3634072dcb3SKyle Evans if prefiles ~= newfiles then 3644072dcb3SKyle Evans readConfFiles(loaded_files) 3654072dcb3SKyle Evans end 3664072dcb3SKyle Evans ::continue:: 3674072dcb3SKyle Evans end 3684072dcb3SKyle Evans end 3694072dcb3SKyle Evansend 370dbef5253SKyle Evans 371322a2dddSKyle Evanslocal function readFile(name, silent) 372164b58fdSKyle Evans local f = io.open(name) 373164b58fdSKyle Evans if f == nil then 374164b58fdSKyle Evans if not silent then 375fdabb5f5SKyle Evans print(MSG_FAILOPENCFG:format(name)) 376164b58fdSKyle Evans end 377164b58fdSKyle Evans return nil 378164b58fdSKyle Evans end 379164b58fdSKyle Evans 380164b58fdSKyle Evans local text, _ = io.read(f) 381164b58fdSKyle Evans -- We might have read in the whole file, this won't be needed any more. 382164b58fdSKyle Evans io.close(f) 383164b58fdSKyle Evans 3848d21763eSKyle Evans if text == nil and not silent then 385fdabb5f5SKyle Evans print(MSG_FAILREADCFG:format(name)) 386164b58fdSKyle Evans end 387164b58fdSKyle Evans return text 388164b58fdSKyle Evansend 389164b58fdSKyle Evans 390322a2dddSKyle Evanslocal function checkNextboot() 391be2050daSKyle Evans local nextboot_file = loader.getenv("nextboot_conf") 392ddfae7e3SKyle Evans if nextboot_file == nil then 393ddfae7e3SKyle Evans return 394ddfae7e3SKyle Evans end 395ddfae7e3SKyle Evans 396322a2dddSKyle Evans local text = readFile(nextboot_file, true) 397ddfae7e3SKyle Evans if text == nil then 398ddfae7e3SKyle Evans return 399ddfae7e3SKyle Evans end 400ddfae7e3SKyle Evans 401ddfae7e3SKyle Evans if text:match("^nextboot_enable=\"NO\"") ~= nil then 402ddfae7e3SKyle Evans -- We're done; nextboot is not enabled 403ddfae7e3SKyle Evans return 404ddfae7e3SKyle Evans end 405ddfae7e3SKyle Evans 406ddfae7e3SKyle Evans if not config.parse(text) then 4072f3ecc87SKyle Evans print(MSG_FAILPARSECFG:format(nextboot_file)) 408ddfae7e3SKyle Evans end 409ddfae7e3SKyle Evans 410ddfae7e3SKyle Evans -- Attempt to rewrite the first line and only the first line of the 411ddfae7e3SKyle Evans -- nextboot_file. We overwrite it with nextboot_enable="NO", then 41267eae503SKyle Evans -- check for that on load. 413ddfae7e3SKyle Evans -- It's worth noting that this won't work on every filesystem, so we 414ddfae7e3SKyle Evans -- won't do anything notable if we have any errors in this process. 415ddfae7e3SKyle Evans local nfile = io.open(nextboot_file, 'w') 416ddfae7e3SKyle Evans if nfile ~= nil then 417ddfae7e3SKyle Evans -- We need the trailing space here to account for the extra 418ddfae7e3SKyle Evans -- character taken up by the string nextboot_enable="YES" 419ddfae7e3SKyle Evans -- Or new end quotation mark lands on the S, and we want to 420ddfae7e3SKyle Evans -- rewrite the entirety of the first line. 421ddfae7e3SKyle Evans io.write(nfile, "nextboot_enable=\"NO\" ") 422ddfae7e3SKyle Evans io.close(nfile) 423ddfae7e3SKyle Evans end 424ddfae7e3SKyle Evansend 425ddfae7e3SKyle Evans 426b5746545SKyle Evans-- Module exports 427f0b03262SKyle Evansconfig.verbose = false 428b5746545SKyle Evans 42925c4c7a5SKyle Evans-- The first item in every carousel is always the default item. 43025c4c7a5SKyle Evansfunction config.getCarouselIndex(id) 4318d21763eSKyle Evans return carousel_choices[id] or 1 43225c4c7a5SKyle Evansend 43325c4c7a5SKyle Evans 43425c4c7a5SKyle Evansfunction config.setCarouselIndex(id, idx) 435aedd6be5SKyle Evans carousel_choices[id] = idx 43625c4c7a5SKyle Evansend 43725c4c7a5SKyle Evans 438fb7275beSKyle Evans-- Returns true if we processed the file successfully, false if we did not. 439fb7275beSKyle Evans-- If 'silent' is true, being unable to read the file is not considered a 440fb7275beSKyle Evans-- failure. 441ddfae7e3SKyle Evansfunction config.processFile(name, silent) 442062d62c9SKyle Evans if silent == nil then 443062d62c9SKyle Evans silent = false 444062d62c9SKyle Evans end 445088b4f5fSWarner Losh 446322a2dddSKyle Evans local text = readFile(name, silent) 4479f71d421SKyle Evans if text == nil then 448fb7275beSKyle Evans return silent 449088b4f5fSWarner Losh end 4503dcb7648SKyle Evans 4514adde50dSKyle Evans return config.parse(text) 4524adde50dSKyle Evansend 4534adde50dSKyle Evans 4544adde50dSKyle Evans-- silent runs will not return false if we fail to open the file 4554adde50dSKyle Evansfunction config.parse(text) 456aedd6be5SKyle Evans local n = 1 457aedd6be5SKyle Evans local status = true 458088b4f5fSWarner Losh 45971049173SKyle Evans for line in text:gmatch("([^\n]+)") do 4609f71d421SKyle Evans if line:match("^%s*$") == nil then 461e2df27e3SKyle Evans for _, val in ipairs(pattern_table) do 462058c692eSKyle Evans local pattern = '^%s*' .. val.str .. '%s*(.*)'; 4639a16e110SKyle Evans local cgroups = val.groups or 2 4641ee89ab5SKyle Evans local k, v, c = checkPattern(line, pattern) 4659f71d421SKyle Evans if k ~= nil then 4669a16e110SKyle Evans -- Offset by one, drats 4679a16e110SKyle Evans if cgroups == 1 then 4689a16e110SKyle Evans c = v 4699a16e110SKyle Evans v = nil 4709a16e110SKyle Evans end 471088b4f5fSWarner Losh 472dbef5253SKyle Evans if isValidComment(c) then 473aedd6be5SKyle Evans val.process(k, v) 4749a16e110SKyle Evans goto nextline 475088b4f5fSWarner Losh end 476088b4f5fSWarner Losh 477aedd6be5SKyle Evans break 478088b4f5fSWarner Losh end 479088b4f5fSWarner Losh end 480088b4f5fSWarner Losh 481fdabb5f5SKyle Evans print(MSG_MALFORMED:format(n, line)) 482aedd6be5SKyle Evans status = false 483088b4f5fSWarner Losh end 4849a16e110SKyle Evans ::nextline:: 485aedd6be5SKyle Evans n = n + 1 486088b4f5fSWarner Losh end 487088b4f5fSWarner Losh 488aedd6be5SKyle Evans return status 489088b4f5fSWarner Loshend 490088b4f5fSWarner Losh 49118c286a0SKyle Evans-- other_kernel is optionally the name of a kernel to load, if not the default 49218c286a0SKyle Evans-- or autoloaded default from the module_path 493322a2dddSKyle Evansfunction config.loadKernel(other_kernel) 494aedd6be5SKyle Evans local flags = loader.getenv("kernel_options") or "" 495aedd6be5SKyle Evans local kernel = other_kernel or loader.getenv("kernel") 496088b4f5fSWarner Losh 497322a2dddSKyle Evans local function tryLoad(names) 498088b4f5fSWarner Losh for name in names:gmatch("([^;]+)%s*;?") do 49980eb81f6SKyle Evans local r = loader.perform("load " .. name .. 50080eb81f6SKyle Evans " " .. flags) 5019f71d421SKyle Evans if r == 0 then 502aedd6be5SKyle Evans return name 503088b4f5fSWarner Losh end 504088b4f5fSWarner Losh end 505aedd6be5SKyle Evans return nil 50618c286a0SKyle Evans end 507088b4f5fSWarner Losh 5085dd1b834SKyle Evans local function getModulePath() 5095dd1b834SKyle Evans local module_path = loader.getenv("module_path") 5105dd1b834SKyle Evans local kernel_path = loader.getenv("kernel_path") 5115dd1b834SKyle Evans 5125dd1b834SKyle Evans if kernel_path == nil then 5135dd1b834SKyle Evans return module_path 5145dd1b834SKyle Evans end 5155dd1b834SKyle Evans 5165dd1b834SKyle Evans -- Strip the loaded kernel path from module_path. This currently assumes 5175dd1b834SKyle Evans -- that the kernel path will be prepended to the module_path when it's 5185dd1b834SKyle Evans -- found. 5195dd1b834SKyle Evans kernel_path = escapeName(kernel_path .. ';') 5205dd1b834SKyle Evans return module_path:gsub(kernel_path, '') 5215dd1b834SKyle Evans end 5225dd1b834SKyle Evans 523322a2dddSKyle Evans local function loadBootfile() 524aedd6be5SKyle Evans local bootfile = loader.getenv("bootfile") 525088b4f5fSWarner Losh 526088b4f5fSWarner Losh -- append default kernel name 5279f71d421SKyle Evans if bootfile == nil then 528aedd6be5SKyle Evans bootfile = "kernel" 529088b4f5fSWarner Losh else 530aedd6be5SKyle Evans bootfile = bootfile .. ";kernel" 531088b4f5fSWarner Losh end 532088b4f5fSWarner Losh 533322a2dddSKyle Evans return tryLoad(bootfile) 53424a1bd54SKyle Evans end 535088b4f5fSWarner Losh 536088b4f5fSWarner Losh -- kernel not set, try load from default module_path 5379f71d421SKyle Evans if kernel == nil then 538322a2dddSKyle Evans local res = loadBootfile() 539088b4f5fSWarner Losh 5409f71d421SKyle Evans if res ~= nil then 541d4591301SKyle Evans -- Default kernel is loaded 542aedd6be5SKyle Evans config.kernel_loaded = nil 543aedd6be5SKyle Evans return true 544088b4f5fSWarner Losh else 545fdabb5f5SKyle Evans print(MSG_DEFAULTKERNFAIL) 546aedd6be5SKyle Evans return false 547088b4f5fSWarner Losh end 548088b4f5fSWarner Losh else 54915d8e03cSKyle Evans -- Use our cached module_path, so we don't end up with multiple 55015d8e03cSKyle Evans -- automatically added kernel paths to our final module_path 5515dd1b834SKyle Evans local module_path = getModulePath() 552e2df27e3SKyle Evans local res 553088b4f5fSWarner Losh 5549f71d421SKyle Evans if other_kernel ~= nil then 555aedd6be5SKyle Evans kernel = other_kernel 55618c286a0SKyle Evans end 557088b4f5fSWarner Losh -- first try load kernel with module_path = /boot/${kernel} 558088b4f5fSWarner Losh -- then try load with module_path=${kernel} 559aedd6be5SKyle Evans local paths = {"/boot/" .. kernel, kernel} 560088b4f5fSWarner Losh 561e2df27e3SKyle Evans for _, v in pairs(paths) do 562aedd6be5SKyle Evans loader.setenv("module_path", v) 563322a2dddSKyle Evans res = loadBootfile() 564088b4f5fSWarner Losh 56515d8e03cSKyle Evans -- succeeded, add path to module_path 5669f71d421SKyle Evans if res ~= nil then 567aedd6be5SKyle Evans config.kernel_loaded = kernel 5689f71d421SKyle Evans if module_path ~= nil then 569c990f0a9SKyle Evans loader.setenv("module_path", v .. ";" .. 570aedd6be5SKyle Evans module_path) 5715dd1b834SKyle Evans loader.setenv("kernel_path", v) 572c990f0a9SKyle Evans end 573aedd6be5SKyle Evans return true 574088b4f5fSWarner Losh end 575088b4f5fSWarner Losh end 576088b4f5fSWarner Losh 577088b4f5fSWarner Losh -- failed to load with ${kernel} as a directory 578088b4f5fSWarner Losh -- try as a file 579322a2dddSKyle Evans res = tryLoad(kernel) 5809f71d421SKyle Evans if res ~= nil then 581aedd6be5SKyle Evans config.kernel_loaded = kernel 582aedd6be5SKyle Evans return true 583088b4f5fSWarner Losh else 584fdabb5f5SKyle Evans print(MSG_KERNFAIL:format(kernel)) 585aedd6be5SKyle Evans return false 586088b4f5fSWarner Losh end 587088b4f5fSWarner Losh end 588088b4f5fSWarner Loshend 589088b4f5fSWarner Losh 590322a2dddSKyle Evansfunction config.selectKernel(kernel) 591aedd6be5SKyle Evans config.kernel_selected = kernel 592fa4a2394SKyle Evansend 593088b4f5fSWarner Losh 5947aba5b2fSKyle Evansfunction config.load(file, reloading) 5959f71d421SKyle Evans if not file then 596aedd6be5SKyle Evans file = "/boot/defaults/loader.conf" 597088b4f5fSWarner Losh end 598088b4f5fSWarner Losh 5994adde50dSKyle Evans if not config.processFile(file) then 600fdabb5f5SKyle Evans print(MSG_FAILPARSECFG:format(file)) 601088b4f5fSWarner Losh end 602088b4f5fSWarner Losh 6034072dcb3SKyle Evans local loaded_files = {file = true} 6044072dcb3SKyle Evans readConfFiles(loaded_files) 605088b4f5fSWarner Losh 606322a2dddSKyle Evans checkNextboot() 6073dcb7648SKyle Evans 6088d21763eSKyle Evans local verbose = loader.getenv("verbose_loading") or "no" 609f0b03262SKyle Evans config.verbose = verbose:lower() == "yes" 6107aba5b2fSKyle Evans if not reloading then 6117aba5b2fSKyle Evans hook.runAll("config.loaded") 6127aba5b2fSKyle Evans end 613fa4a2394SKyle Evansend 614fa4a2394SKyle Evans 615fa4a2394SKyle Evans-- Reload configuration 616fa4a2394SKyle Evansfunction config.reload(file) 617aedd6be5SKyle Evans modules = {} 61864c91742SKyle Evans restoreEnv() 6197aba5b2fSKyle Evans config.load(file, true) 620aea262bfSKyle Evans hook.runAll("config.reloaded") 621fa4a2394SKyle Evansend 622fa4a2394SKyle Evans 623fa4a2394SKyle Evansfunction config.loadelf() 6240d7bee6aSKyle Evans local xen_kernel = loader.getenv('xen_kernel') 625aedd6be5SKyle Evans local kernel = config.kernel_selected or config.kernel_loaded 626e2df27e3SKyle Evans local loaded 62715d8e03cSKyle Evans 6280d7bee6aSKyle Evans if xen_kernel ~= nil then 6290d7bee6aSKyle Evans print(MSG_XENKERNLOADING) 6300d7bee6aSKyle Evans if cli_execute_unparsed('load ' .. xen_kernel) ~= 0 then 6310d7bee6aSKyle Evans print(MSG_XENKERNFAIL:format(xen_kernel)) 632*fdc0a80dSKyle Evans return false 6330d7bee6aSKyle Evans end 6340d7bee6aSKyle Evans end 635fdabb5f5SKyle Evans print(MSG_KERNLOADING) 636322a2dddSKyle Evans loaded = config.loadKernel(kernel) 637fa4a2394SKyle Evans 6389f71d421SKyle Evans if not loaded then 639*fdc0a80dSKyle Evans return false 640fa4a2394SKyle Evans end 641088b4f5fSWarner Losh 642fdabb5f5SKyle Evans print(MSG_MODLOADING) 643*fdc0a80dSKyle Evans return loadModule(modules, not config.verbose) 644088b4f5fSWarner Loshend 645088b4f5fSWarner Losh 6467aba5b2fSKyle Evanshook.registerType("config.loaded") 647aea262bfSKyle Evanshook.registerType("config.reloaded") 648aedd6be5SKyle Evansreturn config 649