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