]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/config.lua
MFV r329803:
[FreeBSD/FreeBSD.git] / stand / lua / config.lua
1 --
2 -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3 -- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
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
30 local config = {}
31
32 local modules = {}
33
34 local pattern_table
35 local carousel_choices = {}
36
37 pattern_table = {
38         [1] = {
39                 str = "^%s*(#.*)",
40                 process = function(k, v)  end
41         },
42         --  module_load="value"
43         [2] = {
44                 str = "^%s*([%w_]+)_load%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
45                 process = function(k, v)
46                         if modules[k] == nil then
47                                 modules[k] = {}
48                         end
49                         modules[k].load = v:upper()
50                 end
51         },
52         --  module_name="value"
53         [3] = {
54                 str = "^%s*([%w_]+)_name%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
55                 process = function(k, v)
56                         config.setKey(k, "name", v)
57                 end
58         },
59         --  module_type="value"
60         [4] = {
61                 str = "^%s*([%w_]+)_type%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
62                 process = function(k, v)
63                         config.setKey(k, "type", v)
64                 end
65         },
66         --  module_flags="value"
67         [5] = {
68                 str = "^%s*([%w_]+)_flags%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
69                 process = function(k, v)
70                         config.setKey(k, "flags", v)
71                 end
72         },
73         --  module_before="value"
74         [6] = {
75                 str = "^%s*([%w_]+)_before%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
76                 process = function(k, v)
77                         config.setKey(k, "before", v)
78                 end
79         },
80         --  module_after="value"
81         [7] = {
82                 str = "^%s*([%w_]+)_after%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
83                 process = function(k, v)
84                         config.setKey(k, "after", v)
85                 end
86         },
87         --  module_error="value"
88         [8] = {
89                 str = "^%s*([%w_]+)_error%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
90                 process = function(k, v)
91                         config.setKey(k, "error", v)
92                 end
93         },
94         --  exec="command"
95         [9] = {
96                 str = "^%s*exec%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
97                 process = function(k, v)
98                         if loader.perform(k) ~= 0 then
99                                 print("Failed to exec '" .. k .. "'")
100                         end
101                 end
102         },
103         --  env_var="value"
104         [10] = {
105                 str = "^%s*([%w%p]+)%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
106                 process = function(k, v)
107                         if config.setenv(k, v) ~= 0 then
108                                 print("Failed to set '" .. k ..
109                                     "' with value: " .. v .. "")
110                         end
111                 end
112         },
113         --  env_var=num
114         [11] = {
115                 str = "^%s*([%w%p]+)%s*=%s*(%d+)%s*(.*)",
116                 process = function(k, v)
117                         if config.setenv(k, v) ~= 0 then
118                                 print("Failed to set '" .. k ..
119                                     "' with value: " .. v .. "")
120                         end
121                 end
122         }
123 }
124
125 -- Module exports
126 -- Which variables we changed
127 config.env_changed = {}
128 -- Values to restore env to (nil to unset)
129 config.env_restore = {}
130
131 -- The first item in every carousel is always the default item.
132 function config.getCarouselIndex(id)
133         local val = carousel_choices[id]
134         if val == nil then
135                 return 1
136         end
137         return val
138 end
139
140 function config.setCarouselIndex(id, idx)
141         carousel_choices[id] = idx
142 end
143
144 function config.restoreEnv()
145         -- Examine changed environment variables
146         for k, v in pairs(config.env_changed) do
147                 local restore_value = config.env_restore[k]
148                 if restore_value == nil then
149                         -- This one doesn't need restored for some reason
150                         goto continue
151                 end
152                 local current_value = loader.getenv(k)
153                 if current_value ~= v then
154                         -- This was overwritten by some action taken on the menu
155                         -- most likely; we'll leave it be.
156                         goto continue
157                 end
158                 restore_value = restore_value.value
159                 if restore_value ~= nil then
160                         loader.setenv(k, restore_value)
161                 else
162                         loader.unsetenv(k)
163                 end
164                 ::continue::
165         end
166
167         config.env_changed = {}
168         config.env_restore = {}
169 end
170
171 function config.setenv(k, v)
172         -- Track the original value for this if we haven't already
173         if config.env_restore[k] == nil then
174                 config.env_restore[k] = {value = loader.getenv(k)}
175         end
176
177         config.env_changed[k] = v
178
179         return loader.setenv(k, v)
180 end
181
182 function config.setKey(k, n, v)
183         if modules[k] == nil then
184                 modules[k] = {}
185         end
186         modules[k][n] = v
187 end
188
189 function config.lsModules()
190         print("== Listing modules")
191         for k, v in pairs(modules) do
192                 print(k, v.load)
193         end
194         print("== List of modules ended")
195 end
196
197
198 function config.isValidComment(c)
199         if c ~= nil then
200                 local s = c:match("^%s*#.*")
201                 if s == nil then
202                         s = c:match("^%s*$")
203                 end
204                 if s == nil then
205                         return false
206                 end
207         end
208         return true
209 end
210
211 function config.loadmod(mod, silent)
212         local status = true
213         for k, v in pairs(mod) do
214                 if v.load == "YES" then
215                         local str = "load "
216                         if v.flags ~= nil then
217                                 str = str .. v.flags .. " "
218                         end
219                         if v.type ~= nil then
220                                 str = str .. "-t " .. v.type .. " "
221                         end
222                         if v.name ~= nil then
223                                 str = str .. v.name
224                         else
225                                 str = str .. k
226                         end
227
228                         if v.before ~= nil then
229                                 if loader.perform(v.before) ~= 0 then
230                                         if not silent then
231                                                 print("Failed to execute '" ..
232                                                     v.before ..
233                                                     "' before loading '" .. k ..
234                                                     "'")
235                                         end
236                                         status = false
237                                 end
238                         end
239
240                         if loader.perform(str) ~= 0 then
241                                 if not silent then
242                                         print("Failed to execute '" .. str ..
243                                             "'")
244                                 end
245                                 if v.error ~= nil then
246                                         loader.perform(v.error)
247                                 end
248                                 status = false
249                         end
250
251                         if v.after ~= nil then
252                                 if loader.perform(v.after) ~= 0 then
253                                         if not silent then
254                                                 print("Failed to execute '" ..
255                                                     v.after ..
256                                                     "' after loading '" .. k ..
257                                                     "'")
258                                         end
259                                         status = false
260                                 end
261                         end
262
263                 else
264                         -- if not silent then
265                                 -- print("Skipping module '". . k .. "'")
266                         -- end
267                 end
268         end
269
270         return status
271 end
272
273 -- silent runs will not return false if we fail to open the file
274 function config.parse(name, silent)
275         if silent == nil then
276                 silent = false
277         end
278         local f = io.open(name)
279         if f == nil then
280                 if not silent then
281                         print("Failed to open config: '" .. name .. "'")
282                 end
283                 return silent
284         end
285
286         local text
287         local r
288
289         text, r = io.read(f)
290
291         if text == nil then
292                 if not silent then
293                         print("Failed to read config: '" .. name .. "'")
294                 end
295                 return silent
296         end
297
298         local n = 1
299         local status = true
300
301         for line in text:gmatch("([^\n]+)") do
302                 if line:match("^%s*$") == nil then
303                         local found = false
304
305                         for i, val in ipairs(pattern_table) do
306                                 local k, v, c = line:match(val.str)
307                                 if k ~= nil then
308                                         found = true
309
310                                         if config.isValidComment(c) then
311                                                 val.process(k, v)
312                                         else
313                                                 print("Malformed line (" .. n ..
314                                                     "):\n\t'" .. line .. "'")
315                                                 status = false
316                                         end
317
318                                         break
319                                 end
320                         end
321
322                         if not found then
323                                 print("Malformed line (" .. n .. "):\n\t'" ..
324                                     line .. "'")
325                                 status = false
326                         end
327                 end
328                 n = n + 1
329         end
330
331         return status
332 end
333
334 -- other_kernel is optionally the name of a kernel to load, if not the default
335 -- or autoloaded default from the module_path
336 function config.loadkernel(other_kernel)
337         local flags = loader.getenv("kernel_options") or ""
338         local kernel = other_kernel or loader.getenv("kernel")
339
340         local try_load = function (names)
341                 for name in names:gmatch("([^;]+)%s*;?") do
342                         r = loader.perform("load " .. flags .. " " .. name)
343                         if r == 0 then
344                                 return name
345                         end
346                 end
347                 return nil
348         end
349
350         local load_bootfile = function()
351                 local bootfile = loader.getenv("bootfile")
352
353                 -- append default kernel name
354                 if bootfile == nil then
355                         bootfile = "kernel"
356                 else
357                         bootfile = bootfile .. ";kernel"
358                 end
359
360                 return try_load(bootfile)
361         end
362
363         -- kernel not set, try load from default module_path
364         if kernel == nil then
365                 local res = load_bootfile()
366
367                 if res ~= nil then
368                         -- Default kernel is loaded
369                         config.kernel_loaded = nil
370                         return true
371                 else
372                         print("No kernel set, failed to load from module_path")
373                         return false
374                 end
375         else
376                 -- Use our cached module_path, so we don't end up with multiple
377                 -- automatically added kernel paths to our final module_path
378                 local module_path = config.module_path
379                 local res = nil
380
381                 if other_kernel ~= nil then
382                         kernel = other_kernel
383                 end
384                 -- first try load kernel with module_path = /boot/${kernel}
385                 -- then try load with module_path=${kernel}
386                 local paths = {"/boot/" .. kernel, kernel}
387
388                 for k,v in pairs(paths) do
389                         loader.setenv("module_path", v)
390                         res = load_bootfile()
391
392                         -- succeeded, add path to module_path
393                         if res ~= nil then
394                                 config.kernel_loaded = kernel
395                                 if module_path ~= nil then
396                                         loader.setenv("module_path", v .. ";" ..
397                                             module_path)
398                                 end
399                                 return true
400                         end
401                 end
402
403                 -- failed to load with ${kernel} as a directory
404                 -- try as a file
405                 res = try_load(kernel)
406                 if res ~= nil then
407                         config.kernel_loaded = kernel
408                         return true
409                 else
410                         print("Failed to load kernel '" .. kernel .. "'")
411                         return false
412                 end
413         end
414 end
415
416 function config.selectkernel(kernel)
417         config.kernel_selected = kernel
418 end
419
420 function config.load(file)
421         if not file then
422                 file = "/boot/defaults/loader.conf"
423         end
424
425         if not config.parse(file) then
426                 print("Failed to parse configuration: '" .. file .. "'")
427         end
428
429         local f = loader.getenv("loader_conf_files")
430         if f ~= nil then
431                 for name in f:gmatch("([%w%p]+)%s*") do
432                         -- These may or may not exist, and that's ok. Do a
433                         -- silent parse so that we complain on parse errors but
434                         -- not for them simply not existing.
435                         if not config.parse(name, true) then
436                                 print("Failed to parse configuration: '" ..
437                                     name .. "'")
438                         end
439                 end
440         end
441
442         -- Cache the provided module_path at load time for later use
443         config.module_path = loader.getenv("module_path")
444 end
445
446 -- Reload configuration
447 function config.reload(file)
448         modules = {}
449         config.restoreEnv()
450         config.load(file)
451 end
452
453 function config.loadelf()
454         local kernel = config.kernel_selected or config.kernel_loaded
455         local loaded = false
456
457         print("Loading kernel...")
458         loaded = config.loadkernel(kernel)
459
460         if not loaded then
461                 print("Failed to load any kernel")
462                 return
463         end
464
465         print("Loading configured modules...")
466         if not config.loadmod(modules) then
467                 print("Could not load one or more modules!")
468         end
469 end
470
471 return config