xref: /freebsd/stand/lua/config.lua (revision a07d59d1daafdaae0d1b1ad1f977f9eda92dc83b)
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
40local pattern_table = {
41	[1] = {
42		str = "^%s*(#.*)",
43		process = function(k, v)  end
44	},
45	--  module_load="value"
46	[2] = {
47		str = "^%s*([%w_]+)_load%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
48		process = function(k, v)
49			if modules[k] == nil then
50				modules[k] = {};
51			end
52			modules[k].load = string.upper(v);
53		end
54	},
55	--  module_name="value"
56	[3] = {
57		str = "^%s*([%w_]+)_name%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
58		process = function(k, v)
59			config.setKey(k, "name", v);
60		end
61	},
62	--  module_type="value"
63	[4] = {
64		str = "^%s*([%w_]+)_type%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
65		process = function(k, v)
66			config.setKey(k, "type", v);
67		end
68	},
69	--  module_flags="value"
70	[5] = {
71		str = "^%s*([%w_]+)_flags%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
72		process = function(k, v)
73			config.setKey(k, "flags", v);
74		end
75	},
76	--  module_before="value"
77	[6] = {
78		str = "^%s*([%w_]+)_before%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
79		process = function(k, v)
80			config.setKey(k, "before", v);
81		end
82	},
83	--  module_after="value"
84	[7] = {
85		str = "^%s*([%w_]+)_after%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
86		process = function(k, v)
87			config.setKey(k, "after", v);
88		end
89	},
90	--  module_error="value"
91	[8] = {
92		str = "^%s*([%w_]+)_error%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
93		process = function(k, v)
94			config.setKey(k, "error", v);
95		end
96	},
97	--  exec="command"
98	[9] = {
99		str = "^%s*exec%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
100		process = function(k, v)
101			if loader.perform(k) ~= 0 then
102				print("Failed to exec '"..k.."'");
103			end
104		end
105	},
106	--  env_var="value"
107	[10] = {
108		str = "^%s*([%w%p]+)%s*=%s*\"([%w%s%p]-)\"%s*(.*)",
109		process = function(k, v)
110			if loader.setenv(k, v) ~= 0 then
111				print("Failed to set '"..k.."' with value: "..v.."");
112			end
113		end
114	},
115	--  env_var=num
116	[11] = {
117		str = "^%s*([%w%p]+)%s*=%s*(%d+)%s*(.*)",
118		process = function(k, v)
119			if loader.setenv(k, v) ~= 0 then
120				print("Failed to set '"..k.."' with value: "..v.."");
121			end
122		end
123	}
124};
125
126function config.isValidComment(c)
127	if c ~= nil then
128		local s = string.match(c, "^%s*#.*");
129		if s == nil then
130			s = string.match(c, "^%s*$");
131		end
132		if s == nil then
133			return false;
134		end
135	end
136	return true;
137end
138
139function config.loadmod(mod, silent)
140	local status = true;
141	for k, v in pairs(mod) do
142		if v.load == "YES" then
143			local str = "load ";
144			if v.flags ~= nil then
145				str = str .. v.flags .. " ";
146			end
147			if v.type ~= nil then
148				str = str .. "-t " .. v.type .. " ";
149			end
150			if v.name ~= nil then
151				str = str .. v.name;
152			else
153				str = str .. k;
154			end
155
156			if v.before ~= nil then
157				if loader.perform(v.before) ~= 0 then
158					if not silent then
159						print("Failed to execute '"..v.before.."' before loading '"..k.."'");
160					end
161					status = false;
162				end
163			end
164
165			if loader.perform(str) ~= 0 then
166				if not silent then
167					print("Failed to execute '" .. str .. "'");
168				end
169				if v.error ~= nil then
170					loader.perform(v.error);
171				end
172				status = false;
173			end
174
175			if v.after ~= nil then
176				if loader.perform(v.after) ~= 0 then
177					if not silent then
178						print("Failed to execute '"..v.after.."' after loading '"..k.."'");
179					end
180					status = false;
181				end
182			end
183
184		else
185			--if not silent then print("Skiping module '".. k .. "'"); end
186		end
187	end
188
189	return status;
190end
191
192function config.parse(name, silent)
193	local f = io.open(name);
194	if f == nil then
195		if not silent then
196			print("Failed to open config: '" .. name.."'");
197		end
198		return false;
199	end
200
201	local text;
202	local r;
203
204	text, r = io.read(f);
205
206	if text == nil then
207		if not silent then
208			print("Failed to read config: '" .. name.."'");
209		end
210		return false;
211	end
212
213	local n = 1;
214	local status = true;
215
216	for line in string.gmatch(text, "([^\n]+)") do
217
218		if string.match(line, "^%s*$") == nil then
219			local found = false;
220
221			for i, val in ipairs(pattern_table) do
222				local k, v, c = string.match(line, val.str);
223				if k ~= nil then
224					found = true;
225
226					if config.isValidComment(c) then
227						val.process(k, v);
228					else
229						print("Malformed line ("..n.."):\n\t'"..line.."'");
230						status = false;
231					end
232
233					break;
234				end
235			end
236
237			if found == false then
238				print("Malformed line ("..n.."):\n\t'"..line.."'");
239				status = false;
240			end
241		end
242		n = n + 1;
243	end
244
245	return status;
246end
247
248function config.loadkernel()
249	local flags = loader.getenv("kernel_options") or "";
250	local kernel = loader.getenv("kernel");
251
252	local try_load = function (names)
253		for name in names:gmatch("([^;]+)%s*;?") do
254			r = loader.perform("load "..flags.." "..name);
255			if r == 0 then
256				return name;
257			end
258		end
259		return nil;
260	end;
261
262	local load_bootfile = function()
263		local bootfile = loader.getenv("bootfile");
264
265		-- append default kernel name
266		if not bootfile then
267			bootfile = "kernel";
268		else
269			bootfile = bootfile..";kernel";
270		end
271
272		return try_load(bootfile);
273	end;
274
275	-- kernel not set, try load from default module_path
276	if kernel == nil then
277		local res = load_bootfile();
278
279		if res ~= nil then
280			return true;
281		else
282			print("Failed to load kernel '"..res.."'");
283			return false;
284		end
285	else
286		local module_path = loader.getenv("module_path");
287		local res = nil;
288
289		-- first try load kernel with module_path = /boot/${kernel}
290		-- then try load with module_path=${kernel}
291		local paths = {"/boot/"..kernel, kernel};
292
293		for k,v in pairs(paths) do
294
295			loader.setenv("module_path", v);
296			res = load_bootfile();
297
298			-- succeeded add path to module_path
299			if res ~= nil then
300				loader.setenv("module_path", v..";"..module_path);
301				return true;
302			end
303		end
304
305		-- failed to load with ${kernel} as a directory
306		-- try as a file
307		res = try_load(kernel);
308		if res ~= nil then
309			return true;
310		else
311			print("Failed to load kernel '"..res.."'");
312			return false;
313		end
314	end
315end
316
317
318function config.load(file)
319
320	if not file then
321		file = "/boot/defaults/loader.conf";
322	end
323
324	if not config.parse(file) then
325--		print("Failed to parse configuration: '"..file.."'");
326	end
327
328	local f = loader.getenv("loader_conf_files");
329	if f ~= nil then
330		for name in string.gmatch(f, "([%w%p]+)%s*") do
331			if not config.parse(name) then
332--				print("Failed to parse configuration: '"..name.."'");
333			end
334		end
335	end
336
337	print("Loading kernel...");
338	config.loadkernel();
339
340	print("Loading configurations...");
341	if not config.loadmod(modules) then
342		print("Could not load configurations!");
343	end
344end
345
346function config.reload(kernel)
347	local res = 1;
348
349	-- unload all modules
350	print("Unloading modules...");
351	loader.perform("unload");
352
353	if kernel ~= nil then
354		res = loader.perform("load "..kernel);
355		if res == 0 then
356			print("Kernel '"..kernel.."' loaded!");
357		end
358	end
359
360	-- failed to load kernel or it is nil
361	-- then load default
362	if res == 1 then
363		print("Loading default kernel...");
364		config.loadkernel();
365	end
366
367	-- load modules
368	config.loadmod(modules);
369end
370
371return config
372