xref: /freebsd-14.2/stand/lua/config.lua (revision c798d98e)
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