xref: /freebsd-12.1/stand/lua/config.lua (revision 74709ed0)
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