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