xref: /freebsd/stand/lua/config.lua (revision eb69d1f144a6fcc765d1b9d44a5ae8082353e70b)
1--
2-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
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			return true;
301		else
302			print("No kernel set, failed to load from module_path");
303			return false;
304		end
305	else
306		-- Use our cached module_path, so we don't end up with multiple
307		-- automatically added kernel paths to our final module_path
308		local module_path = config.module_path;
309		local res = nil;
310
311		if (other_kernel ~= nil) then
312			kernel = other_kernel;
313		end
314		-- first try load kernel with module_path = /boot/${kernel}
315		-- then try load with module_path=${kernel}
316		local paths = {"/boot/" .. kernel, kernel};
317
318		for k,v in pairs(paths) do
319			loader.setenv("module_path", v);
320			res = load_bootfile();
321
322			-- succeeded, add path to module_path
323			if (res ~= nil) then
324				if (module_path ~= nil) then
325					loader.setenv("module_path", v .. ";" ..
326					    module_path);
327				end
328				return true;
329			end
330		end
331
332		-- failed to load with ${kernel} as a directory
333		-- try as a file
334		res = try_load(kernel);
335		if (res ~= nil) then
336			return true;
337		else
338			print("Failed to load kernel '" .. kernel .. "'");
339			return false;
340		end
341	end
342end
343
344
345function config.load(file)
346	if (not file) then
347		file = "/boot/defaults/loader.conf";
348	end
349
350	if (not config.parse(file)) then
351--		print("Failed to parse configuration: '" .. file .. "'");
352	end
353
354	local f = loader.getenv("loader_conf_files");
355	if (f ~= nil) then
356		for name in f:gmatch("([%w%p]+)%s*") do
357			if (not config.parse(name)) then
358--				print("Failed to parse configuration: '" ..
359--				    name .. "'");
360			end
361		end
362	end
363
364	-- Cache the provided module_path at load time for later use
365	config.module_path = loader.getenv("module_path");
366
367	print("Loading kernel...");
368	config.loadkernel();
369
370	print("Loading configured modules...");
371	if (not config.loadmod(modules)) then
372		print("Could not load one or more modules!");
373	end
374end
375
376function config.reload(kernel)
377	local kernel_loaded = false;
378
379	-- unload all modules
380	print("Unloading modules...");
381	loader.perform("unload");
382
383	if (kernel ~= nil) then
384		print("Trying to load '" .. kernel .. "'")
385		kernel_loaded = config.loadkernel(kernel);
386		if (kernel_loaded) then
387			print("Kernel '" .. kernel .. "' loaded!");
388		end
389	end
390
391	-- failed to load kernel or it is nil
392	-- then load default
393	if (not kernel_loaded) then
394		print("Loading default kernel...");
395		config.loadkernel();
396	end
397
398	-- load modules
399	config.loadmod(modules);
400end
401
402return config
403