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("== Dumping modules"); 42 for k, v in pairs(modules) do 43 print(k, v.load); 44 end 45 print("== Dump 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.."' with value: "..v..""); 120 end 121 end 122 }, 123 -- env_var=num 124 [11] = { 125 str = "^%s*([%w%p]+)%s*=%s*(%d+)%s*(.*)", 126 process = function(k, v) 127 if loader.setenv(k, v) ~= 0 then 128 print("Failed to set '"..k.."' with value: "..v..""); 129 end 130 end 131 } 132}; 133 134function config.isValidComment(c) 135 if c ~= nil then 136 local s = c:match("^%s*#.*"); 137 if s == nil then 138 s = c:match("^%s*$"); 139 end 140 if s == nil then 141 return false; 142 end 143 end 144 return true; 145end 146 147function config.loadmod(mod, silent) 148 local status = true; 149 for k, v in pairs(mod) do 150 if v.load == "YES" then 151 local str = "load "; 152 if v.flags ~= nil then 153 str = str .. v.flags .. " "; 154 end 155 if v.type ~= nil then 156 str = str .. "-t " .. v.type .. " "; 157 end 158 if v.name ~= nil then 159 str = str .. v.name; 160 else 161 str = str .. k; 162 end 163 164 if v.before ~= nil then 165 if loader.perform(v.before) ~= 0 then 166 if not silent then 167 print("Failed to execute '"..v.before.."' before loading '"..k.."'"); 168 end 169 status = false; 170 end 171 end 172 173 if loader.perform(str) ~= 0 then 174 if not silent then 175 print("Failed to execute '" .. str .. "'"); 176 end 177 if v.error ~= nil then 178 loader.perform(v.error); 179 end 180 status = false; 181 end 182 183 if v.after ~= nil then 184 if loader.perform(v.after) ~= 0 then 185 if not silent then 186 print("Failed to execute '"..v.after.."' after loading '"..k.."'"); 187 end 188 status = false; 189 end 190 end 191 192 else 193 --if not silent then print("Skiping module '".. k .. "'"); end 194 end 195 end 196 197 return status; 198end 199 200function config.parse(name, silent) 201 local f = io.open(name); 202 if f == nil then 203 if not silent then 204 print("Failed to open config: '" .. name.."'"); 205 end 206 return false; 207 end 208 209 local text; 210 local r; 211 212 text, r = io.read(f); 213 214 if text == nil then 215 if not silent then 216 print("Failed to read config: '" .. name.."'"); 217 end 218 return false; 219 end 220 221 local n = 1; 222 local status = true; 223 224 for line in text:gmatch("([^\n]+)") do 225 226 if line:match("^%s*$") == nil then 227 local found = false; 228 229 for i, val in ipairs(pattern_table) do 230 local k, v, c = line:match(val.str); 231 if k ~= nil then 232 found = true; 233 234 if config.isValidComment(c) then 235 val.process(k, v); 236 else 237 print("Malformed line ("..n.."):\n\t'"..line.."'"); 238 status = false; 239 end 240 241 break; 242 end 243 end 244 245 if found == false then 246 print("Malformed line ("..n.."):\n\t'"..line.."'"); 247 status = false; 248 end 249 end 250 n = n + 1; 251 end 252 253 return status; 254end 255 256-- other_kernel is optionally the name of a kernel to load, if not the default 257-- or autoloaded default from the module_path 258function config.loadkernel(other_kernel) 259 local flags = loader.getenv("kernel_options") or ""; 260 local kernel = other_kernel or loader.getenv("kernel"); 261 262 local try_load = function (names) 263 for name in names:gmatch("([^;]+)%s*;?") do 264 r = loader.perform("load "..flags.." "..name); 265 if r == 0 then 266 return name; 267 end 268 end 269 return nil; 270 end 271 272 local load_bootfile = function() 273 local bootfile = loader.getenv("bootfile"); 274 275 -- append default kernel name 276 if not bootfile then 277 bootfile = "kernel"; 278 else 279 bootfile = bootfile..";kernel"; 280 end 281 282 return try_load(bootfile); 283 end 284 285 -- kernel not set, try load from default module_path 286 if kernel == nil then 287 local res = load_bootfile(); 288 289 if res ~= nil then 290 return true; 291 else 292 print("No kernel set, failed to load from module_path"); 293 return false; 294 end 295 else 296 local module_path = loader.getenv("module_path"); 297 local res = nil; 298 299 if other_kern ~= nil then 300 kernel = other_kern; 301 end 302 -- first try load kernel with module_path = /boot/${kernel} 303 -- then try load with module_path=${kernel} 304 local paths = {"/boot/"..kernel, kernel}; 305 306 for k,v in pairs(paths) do 307 308 loader.setenv("module_path", v); 309 res = load_bootfile(); 310 311 -- succeeded add path to module_path 312 if res ~= nil then 313 if module_path ~= nil then 314 loader.setenv("module_path", v..";".. 315 module_path); 316 end 317 return true; 318 end 319 end 320 321 -- failed to load with ${kernel} as a directory 322 -- try as a file 323 res = try_load(kernel); 324 if res ~= nil then 325 return true; 326 else 327 print("Failed to load kernel '"..kernel.."'"); 328 return false; 329 end 330 end 331end 332 333 334function config.load(file) 335 336 if not file then 337 file = "/boot/defaults/loader.conf"; 338 end 339 340 if not config.parse(file) then 341-- print("Failed to parse configuration: '"..file.."'"); 342 end 343 344 local f = loader.getenv("loader_conf_files"); 345 if f ~= nil then 346 for name in f:gmatch("([%w%p]+)%s*") do 347 if not config.parse(name) then 348-- print("Failed to parse configuration: '"..name.."'"); 349 end 350 end 351 end 352 353 print("Loading kernel..."); 354 config.loadkernel(); 355 356 print("Loading configured modules..."); 357 if not config.loadmod(modules) then 358 print("Could not load one or more modules!"); 359 end 360end 361 362function config.reload(kernel) 363 local kernel_loaded = false; 364 365 -- unload all modules 366 print("Unloading modules..."); 367 loader.perform("unload"); 368 369 if (kernel ~= nil) then 370 print("Trying to load '" .. kernel .. "'") 371 kernel_loaded = config.loadkernel(kernel); 372 if (kernel_loaded) then 373 print("Kernel '"..kernel.."' loaded!"); 374 end 375 end 376 377 -- failed to load kernel or it is nil 378 -- then load default 379 if (not kernel_loaded) then 380 print("Loading default kernel..."); 381 config.loadkernel(); 382 end 383 384 -- load modules 385 config.loadmod(modules); 386end 387 388return config 389