1-- 2-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 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 return true; 301 else 302 print("No kernel set, failed to load from module_path"); 303 return false; 304 end 305 else 306 -- Use our cached module_path, so we don't end up with multiple 307 -- automatically added kernel paths to our final module_path 308 local module_path = config.module_path; 309 local res = nil; 310 311 if (other_kernel ~= nil) then 312 kernel = other_kernel; 313 end 314 -- first try load kernel with module_path = /boot/${kernel} 315 -- then try load with module_path=${kernel} 316 local paths = {"/boot/" .. kernel, kernel}; 317 318 for k,v in pairs(paths) do 319 loader.setenv("module_path", v); 320 res = load_bootfile(); 321 322 -- succeeded, add path to module_path 323 if (res ~= nil) then 324 if (module_path ~= nil) then 325 loader.setenv("module_path", v .. ";" .. 326 module_path); 327 end 328 return true; 329 end 330 end 331 332 -- failed to load with ${kernel} as a directory 333 -- try as a file 334 res = try_load(kernel); 335 if (res ~= nil) then 336 return true; 337 else 338 print("Failed to load kernel '" .. kernel .. "'"); 339 return false; 340 end 341 end 342end 343 344 345function config.load(file) 346 if (not file) then 347 file = "/boot/defaults/loader.conf"; 348 end 349 350 if (not config.parse(file)) then 351-- print("Failed to parse configuration: '" .. file .. "'"); 352 end 353 354 local f = loader.getenv("loader_conf_files"); 355 if (f ~= nil) then 356 for name in f:gmatch("([%w%p]+)%s*") do 357 if (not config.parse(name)) then 358-- print("Failed to parse configuration: '" .. 359-- name .. "'"); 360 end 361 end 362 end 363 364 -- Cache the provided module_path at load time for later use 365 config.module_path = loader.getenv("module_path"); 366 367 print("Loading kernel..."); 368 config.loadkernel(); 369 370 print("Loading configured modules..."); 371 if (not config.loadmod(modules)) then 372 print("Could not load one or more modules!"); 373 end 374end 375 376function config.reload(kernel) 377 local kernel_loaded = false; 378 379 -- unload all modules 380 print("Unloading modules..."); 381 loader.perform("unload"); 382 383 if (kernel ~= nil) then 384 print("Trying to load '" .. kernel .. "'") 385 kernel_loaded = config.loadkernel(kernel); 386 if (kernel_loaded) then 387 print("Kernel '" .. kernel .. "' loaded!"); 388 end 389 end 390 391 -- failed to load kernel or it is nil 392 -- then load default 393 if (not kernel_loaded) then 394 print("Loading default kernel..."); 395 config.loadkernel(); 396 end 397 398 -- load modules 399 config.loadmod(modules); 400end 401 402return config 403