1-- 2-- Copyright (c) 2015 Pedro Souza <[email protected]> 3-- Copyright (C) 2018 Kyle Evans <[email protected]> 4-- All rights reserved. 5-- 6-- Redistribution and use in source and binary forms, with or without 7-- modification, are permitted provided that the following conditions 8-- are met: 9-- 1. Redistributions of source code must retain the above copyright 10-- notice, this list of conditions and the following disclaimer. 11-- 2. Redistributions in binary form must reproduce the above copyright 12-- notice, this list of conditions and the following disclaimer in the 13-- documentation and/or other materials provided with the distribution. 14-- 15-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25-- SUCH DAMAGE. 26-- 27-- $FreeBSD$ 28-- 29 30local config = {}; 31-- Which variables we changed 32config.env_changed = {}; 33-- Values to restore env to (nil to unset) 34config.env_restore = {}; 35 36local modules = {}; 37 38function config.restoreEnv() 39 for k, v in pairs(config.env_changed) do 40 local restore_value = config.env_restore[k]; 41 if (restore_value ~= nil) then 42 loader.setenv(k, restore_value); 43 else 44 loader.unsetenv(k); 45 end 46 end 47 48 config.env_changed = {}; 49 config.env_restore = {}; 50end 51 52function config.setenv(k, v) 53 -- Do we need to track this change? 54 if (config.env_changed[k] == nil) then 55 config.env_changed[k] = true; 56 config.env_restore[k] = loader.getenv(k); 57 end 58 59 return loader.setenv(k, v); 60end 61 62function config.setKey(k, n, v) 63 if (modules[k] == nil) then 64 modules[k] = {}; 65 end 66 modules[k][n] = v; 67end 68 69function config.lsModules() 70 print("== Listing modules"); 71 for k, v in pairs(modules) do 72 print(k, v.load); 73 end 74 print("== List of modules ended"); 75end 76 77local pattern_table = { 78 [1] = { 79 str = "^%s*(#.*)", 80 process = function(k, v) end 81 }, 82 -- module_load="value" 83 [2] = { 84 str = "^%s*([%w_]+)_load%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 85 process = function(k, v) 86 if (modules[k] == nil) then 87 modules[k] = {}; 88 end 89 modules[k].load = v:upper(); 90 end 91 }, 92 -- module_name="value" 93 [3] = { 94 str = "^%s*([%w_]+)_name%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 95 process = function(k, v) 96 config.setKey(k, "name", v); 97 end 98 }, 99 -- module_type="value" 100 [4] = { 101 str = "^%s*([%w_]+)_type%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 102 process = function(k, v) 103 config.setKey(k, "type", v); 104 end 105 }, 106 -- module_flags="value" 107 [5] = { 108 str = "^%s*([%w_]+)_flags%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 109 process = function(k, v) 110 config.setKey(k, "flags", v); 111 end 112 }, 113 -- module_before="value" 114 [6] = { 115 str = "^%s*([%w_]+)_before%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 116 process = function(k, v) 117 config.setKey(k, "before", v); 118 end 119 }, 120 -- module_after="value" 121 [7] = { 122 str = "^%s*([%w_]+)_after%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 123 process = function(k, v) 124 config.setKey(k, "after", v); 125 end 126 }, 127 -- module_error="value" 128 [8] = { 129 str = "^%s*([%w_]+)_error%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 130 process = function(k, v) 131 config.setKey(k, "error", v); 132 end 133 }, 134 -- exec="command" 135 [9] = { 136 str = "^%s*exec%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 137 process = function(k, v) 138 if (loader.perform(k) ~= 0) then 139 print("Failed to exec '" .. k .. "'"); 140 end 141 end 142 }, 143 -- env_var="value" 144 [10] = { 145 str = "^%s*([%w%p]+)%s*=%s*\"([%w%s%p]-)\"%s*(.*)", 146 process = function(k, v) 147 if (config.setenv(k, v) ~= 0) then 148 print("Failed to set '" .. k .. 149 "' with value: " .. v .. ""); 150 end 151 end 152 }, 153 -- env_var=num 154 [11] = { 155 str = "^%s*([%w%p]+)%s*=%s*(%d+)%s*(.*)", 156 process = function(k, v) 157 if (config.setenv(k, v) ~= 0) then 158 print("Failed to set '" .. k .. 159 "' with value: " .. v .. ""); 160 end 161 end 162 } 163}; 164 165function config.isValidComment(c) 166 if (c ~= nil) then 167 local s = c:match("^%s*#.*"); 168 if (s == nil) then 169 s = c:match("^%s*$"); 170 end 171 if (s == nil) then 172 return false; 173 end 174 end 175 return true; 176end 177 178function config.loadmod(mod, silent) 179 local status = true; 180 for k, v in pairs(mod) do 181 if (v.load == "YES") then 182 local str = "load "; 183 if (v.flags ~= nil) then 184 str = str .. v.flags .. " "; 185 end 186 if (v.type ~= nil) then 187 str = str .. "-t " .. v.type .. " "; 188 end 189 if (v.name ~= nil) then 190 str = str .. v.name; 191 else 192 str = str .. k; 193 end 194 195 if (v.before ~= nil) then 196 if (loader.perform(v.before) ~= 0) then 197 if (not silent) then 198 print("Failed to execute '" .. 199 v.before .. 200 "' before loading '" .. k .. 201 "'"); 202 end 203 status = false; 204 end 205 end 206 207 if (loader.perform(str) ~= 0) then 208 if (not silent) then 209 print("Failed to execute '" .. str .. 210 "'"); 211 end 212 if (v.error ~= nil) then 213 loader.perform(v.error); 214 end 215 status = false; 216 end 217 218 if (v.after ~= nil) then 219 if (loader.perform(v.after) ~= 0) then 220 if (not silent) then 221 print("Failed to execute '" .. 222 v.after .. 223 "' after loading '" .. k .. 224 "'"); 225 end 226 status = false; 227 end 228 end 229 230 else 231 -- if not silent then 232 -- print("Skiping module '". . k .. "'"); 233 -- end 234 end 235 end 236 237 return status; 238end 239 240function config.parse(name, silent) 241 local f = io.open(name); 242 if (f == nil) then 243 if (not silent) then 244 print("Failed to open config: '" .. name .. "'"); 245 end 246 return false; 247 end 248 249 local text; 250 local r; 251 252 text, r = io.read(f); 253 254 if (text == nil) then 255 if (not silent) then 256 print("Failed to read config: '" .. name .. "'"); 257 end 258 return false; 259 end 260 261 local n = 1; 262 local status = true; 263 264 for line in text:gmatch("([^\n]+)") do 265 if (line:match("^%s*$") == nil) then 266 local found = false; 267 268 for i, val in ipairs(pattern_table) do 269 local k, v, c = line:match(val.str); 270 if (k ~= nil) then 271 found = true; 272 273 if (config.isValidComment(c)) then 274 val.process(k, v); 275 else 276 print("Malformed line (" .. n .. 277 "):\n\t'" .. line .. "'"); 278 status = false; 279 end 280 281 break; 282 end 283 end 284 285 if (found == false) then 286 print("Malformed line (" .. n .. "):\n\t'" .. 287 line .. "'"); 288 status = false; 289 end 290 end 291 n = n + 1; 292 end 293 294 return status; 295end 296 297-- other_kernel is optionally the name of a kernel to load, if not the default 298-- or autoloaded default from the module_path 299function config.loadkernel(other_kernel) 300 local flags = loader.getenv("kernel_options") or ""; 301 local kernel = other_kernel or loader.getenv("kernel"); 302 303 local try_load = function (names) 304 for name in names:gmatch("([^;]+)%s*;?") do 305 r = loader.perform("load " .. flags .. " " .. name); 306 if (r == 0) then 307 return name; 308 end 309 end 310 return nil; 311 end 312 313 local load_bootfile = function() 314 local bootfile = loader.getenv("bootfile"); 315 316 -- append default kernel name 317 if (bootfile == nil) then 318 bootfile = "kernel"; 319 else 320 bootfile = bootfile .. ";kernel"; 321 end 322 323 return try_load(bootfile); 324 end 325 326 -- kernel not set, try load from default module_path 327 if (kernel == nil) then 328 local res = load_bootfile(); 329 330 if (res ~= nil) then 331 -- Default kernel is loaded 332 config.kernel_loaded = nil; 333 return true; 334 else 335 print("No kernel set, failed to load from module_path"); 336 return false; 337 end 338 else 339 -- Use our cached module_path, so we don't end up with multiple 340 -- automatically added kernel paths to our final module_path 341 local module_path = config.module_path; 342 local res = nil; 343 344 if (other_kernel ~= nil) then 345 kernel = other_kernel; 346 end 347 -- first try load kernel with module_path = /boot/${kernel} 348 -- then try load with module_path=${kernel} 349 local paths = {"/boot/" .. kernel, kernel}; 350 351 for k,v in pairs(paths) do 352 loader.setenv("module_path", v); 353 res = load_bootfile(); 354 355 -- succeeded, add path to module_path 356 if (res ~= nil) then 357 config.kernel_loaded = kernel; 358 if (module_path ~= nil) then 359 loader.setenv("module_path", v .. ";" .. 360 module_path); 361 end 362 return true; 363 end 364 end 365 366 -- failed to load with ${kernel} as a directory 367 -- try as a file 368 res = try_load(kernel); 369 if (res ~= nil) then 370 config.kernel_loaded = kernel; 371 return true; 372 else 373 print("Failed to load kernel '" .. kernel .. "'"); 374 return false; 375 end 376 end 377end 378 379function config.selectkernel(kernel) 380 config.kernel_selected = kernel; 381end 382 383function config.load(file) 384 if (not file) then 385 file = "/boot/defaults/loader.conf"; 386 end 387 388 if (not config.parse(file)) then 389-- print("Failed to parse configuration: '" .. file .. "'"); 390 end 391 392 local f = loader.getenv("loader_conf_files"); 393 if (f ~= nil) then 394 for name in f:gmatch("([%w%p]+)%s*") do 395 if (not config.parse(name)) then 396-- print("Failed to parse configuration: '" .. 397-- name .. "'"); 398 end 399 end 400 end 401 402 -- Cache the provided module_path at load time for later use 403 config.module_path = loader.getenv("module_path"); 404end 405 406-- Reload configuration 407function config.reload(file) 408 modules = {}; 409 config.restoreEnv(); 410 config.load(file); 411end 412 413function config.loadelf() 414 local kernel = config.kernel_loaded or config.kernel_selected; 415 local loaded = false; 416 417 print("Loading kernel..."); 418 loaded = config.loadkernel(kernel); 419 420 if (not loaded) then 421 loaded = config.loadkernel(); 422 end 423 424 if (not loaded) then 425 -- Ultimately failed to load kernel 426 print("Failed to load any kernel"); 427 return; 428 end 429 430 print("Loading configured modules..."); 431 if (not config.loadmod(modules)) then 432 print("Could not load one or more modules!"); 433 end 434end 435 436 437return config; 438