xref: /minix3/sys/lib/libsa/bootcfg.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: bootcfg.c,v 1.2 2014/08/10 07:40:49 isaki Exp $	*/
2*0a6a1f1dSLionel Sambuc 
3*0a6a1f1dSLionel Sambuc /*-
4*0a6a1f1dSLionel Sambuc  * Copyright (c) 2008 The NetBSD Foundation, Inc.
5*0a6a1f1dSLionel Sambuc  * All rights reserved.
6*0a6a1f1dSLionel Sambuc  *
7*0a6a1f1dSLionel Sambuc  * Redistribution and use in source and binary forms, with or without
8*0a6a1f1dSLionel Sambuc  * modification, are permitted provided that the following conditions
9*0a6a1f1dSLionel Sambuc  * are met:
10*0a6a1f1dSLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
11*0a6a1f1dSLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
12*0a6a1f1dSLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
13*0a6a1f1dSLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
14*0a6a1f1dSLionel Sambuc  *    documentation and/or other materials provided with the distribution.
15*0a6a1f1dSLionel Sambuc  *
16*0a6a1f1dSLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17*0a6a1f1dSLionel Sambuc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18*0a6a1f1dSLionel Sambuc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19*0a6a1f1dSLionel Sambuc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20*0a6a1f1dSLionel Sambuc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*0a6a1f1dSLionel Sambuc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*0a6a1f1dSLionel Sambuc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*0a6a1f1dSLionel Sambuc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*0a6a1f1dSLionel Sambuc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*0a6a1f1dSLionel Sambuc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*0a6a1f1dSLionel Sambuc  * POSSIBILITY OF SUCH DAMAGE.
27*0a6a1f1dSLionel Sambuc  */
28*0a6a1f1dSLionel Sambuc 
29*0a6a1f1dSLionel Sambuc #include <sys/types.h>
30*0a6a1f1dSLionel Sambuc #include <sys/reboot.h>
31*0a6a1f1dSLionel Sambuc 
32*0a6a1f1dSLionel Sambuc #include <lib/libsa/stand.h>
33*0a6a1f1dSLionel Sambuc #include <lib/libsa/bootcfg.h>
34*0a6a1f1dSLionel Sambuc #include <lib/libkern/libkern.h>
35*0a6a1f1dSLionel Sambuc 
36*0a6a1f1dSLionel Sambuc #define MENUFORMAT_AUTO   0
37*0a6a1f1dSLionel Sambuc #define MENUFORMAT_NUMBER 1
38*0a6a1f1dSLionel Sambuc #define MENUFORMAT_LETTER 2
39*0a6a1f1dSLionel Sambuc 
40*0a6a1f1dSLionel Sambuc #define DEFAULT_FORMAT  MENUFORMAT_AUTO
41*0a6a1f1dSLionel Sambuc #define DEFAULT_TIMEOUT 10
42*0a6a1f1dSLionel Sambuc 
43*0a6a1f1dSLionel Sambuc struct bootcfg_def bootcfg_info;
44*0a6a1f1dSLionel Sambuc 
45*0a6a1f1dSLionel Sambuc void
bootcfg_do_noop(const char * cmd,char * arg)46*0a6a1f1dSLionel Sambuc bootcfg_do_noop(const char *cmd, char *arg)
47*0a6a1f1dSLionel Sambuc {
48*0a6a1f1dSLionel Sambuc 	/* noop, do nothing */
49*0a6a1f1dSLionel Sambuc }
50*0a6a1f1dSLionel Sambuc 
51*0a6a1f1dSLionel Sambuc /*
52*0a6a1f1dSLionel Sambuc  * This function parses a boot.cfg file in the root of the filesystem
53*0a6a1f1dSLionel Sambuc  * (if present) and populates the global boot configuration.
54*0a6a1f1dSLionel Sambuc  *
55*0a6a1f1dSLionel Sambuc  * The file consists of a number of lines each terminated by \n
56*0a6a1f1dSLionel Sambuc  * The lines are in the format keyword=value. There should not be spaces
57*0a6a1f1dSLionel Sambuc  * around the = sign.
58*0a6a1f1dSLionel Sambuc  *
59*0a6a1f1dSLionel Sambuc  * perform_bootcfg(conf, command, maxsz)
60*0a6a1f1dSLionel Sambuc  *
61*0a6a1f1dSLionel Sambuc  * conf		Path to boot.cfg to be passed verbatim to open()
62*0a6a1f1dSLionel Sambuc  *
63*0a6a1f1dSLionel Sambuc  * command	Pointer to a function that will be called when
64*0a6a1f1dSLionel Sambuc  * 		perform_bootcfg() encounters a key (command) it does not
65*0a6a1f1dSLionel Sambuc  *		recognize.
66*0a6a1f1dSLionel Sambuc  *		The command function is provided both the keyword and
67*0a6a1f1dSLionel Sambuc  *		value parsed as arguments to the function.
68*0a6a1f1dSLionel Sambuc  *
69*0a6a1f1dSLionel Sambuc  * maxsz	Limit the size of the boot.cfg perform_bootcfg() will parse.
70*0a6a1f1dSLionel Sambuc  * 		- If maxsz is < 0 boot.cfg will not be processed.
71*0a6a1f1dSLionel Sambuc  * 		- If maxsz is = 0 no limit will be imposed but parsing may
72*0a6a1f1dSLionel Sambuc  *		  fail due to platform or other constraints e.g. maximum
73*0a6a1f1dSLionel Sambuc  *		  segment size.
74*0a6a1f1dSLionel Sambuc  *		- If 0 < maxsz and boot.cfg exceeds maxsz it will not be
75*0a6a1f1dSLionel Sambuc  *		  parsed, otherwise it will be parsed.
76*0a6a1f1dSLionel Sambuc  *
77*0a6a1f1dSLionel Sambuc  * The recognised keywords are:
78*0a6a1f1dSLionel Sambuc  * banner: text displayed instead of the normal welcome text
79*0a6a1f1dSLionel Sambuc  * menu: Descriptive text:command to use
80*0a6a1f1dSLionel Sambuc  * timeout: Timeout in seconds (overrides that set by installboot)
81*0a6a1f1dSLionel Sambuc  * default: the default menu option to use if Return is pressed
82*0a6a1f1dSLionel Sambuc  * consdev: the console device to use
83*0a6a1f1dSLionel Sambuc  * format: how menu choices are displayed: (a)utomatic, (n)umbers or (l)etters
84*0a6a1f1dSLionel Sambuc  * clear: whether to clear the screen or not
85*0a6a1f1dSLionel Sambuc  *
86*0a6a1f1dSLionel Sambuc  * Example boot.cfg file:
87*0a6a1f1dSLionel Sambuc  * banner=Welcome to NetBSD
88*0a6a1f1dSLionel Sambuc  * banner=Please choose the boot type from the following menu
89*0a6a1f1dSLionel Sambuc  * menu=Boot NetBSD:boot netbsd
90*0a6a1f1dSLionel Sambuc  * menu=Boot into single user mode:boot netbsd -s
91*0a6a1f1dSLionel Sambuc  * menu=:boot hd1a:netbsd -cs
92*0a6a1f1dSLionel Sambuc  * menu=Goto boot comand line:prompt
93*0a6a1f1dSLionel Sambuc  * timeout=10
94*0a6a1f1dSLionel Sambuc  * consdev=com0
95*0a6a1f1dSLionel Sambuc  * default=1
96*0a6a1f1dSLionel Sambuc */
97*0a6a1f1dSLionel Sambuc void
perform_bootcfg(const char * conf,bootcfg_command command,const off_t maxsz)98*0a6a1f1dSLionel Sambuc perform_bootcfg(const char *conf, bootcfg_command command, const off_t maxsz)
99*0a6a1f1dSLionel Sambuc {
100*0a6a1f1dSLionel Sambuc 	char *bc, *c;
101*0a6a1f1dSLionel Sambuc 	int cmenu, cbanner, len;
102*0a6a1f1dSLionel Sambuc 	int fd, err, off;
103*0a6a1f1dSLionel Sambuc 	struct stat st;
104*0a6a1f1dSLionel Sambuc 	char *next, *key, *value, *v2;
105*0a6a1f1dSLionel Sambuc 
106*0a6a1f1dSLionel Sambuc 	/* clear bootcfg structure */
107*0a6a1f1dSLionel Sambuc 	memset(&bootcfg_info, 0, sizeof(bootcfg_info));
108*0a6a1f1dSLionel Sambuc 
109*0a6a1f1dSLionel Sambuc 	/* set default timeout */
110*0a6a1f1dSLionel Sambuc 	bootcfg_info.timeout = DEFAULT_TIMEOUT;
111*0a6a1f1dSLionel Sambuc 
112*0a6a1f1dSLionel Sambuc 	/* automatically switch between letter and numbers on menu */
113*0a6a1f1dSLionel Sambuc 	bootcfg_info.menuformat = DEFAULT_FORMAT;
114*0a6a1f1dSLionel Sambuc 
115*0a6a1f1dSLionel Sambuc 	fd = open(conf, 0);
116*0a6a1f1dSLionel Sambuc 	if (fd < 0)
117*0a6a1f1dSLionel Sambuc 		return;
118*0a6a1f1dSLionel Sambuc 
119*0a6a1f1dSLionel Sambuc 	err = fstat(fd, &st);
120*0a6a1f1dSLionel Sambuc 	if (err == -1) {
121*0a6a1f1dSLionel Sambuc 		close(fd);
122*0a6a1f1dSLionel Sambuc 		return;
123*0a6a1f1dSLionel Sambuc 	}
124*0a6a1f1dSLionel Sambuc 
125*0a6a1f1dSLionel Sambuc 	/* if a maximum size is being requested for the boot.cfg enforce it. */
126*0a6a1f1dSLionel Sambuc 	if (0 < maxsz && st.st_size > maxsz) {
127*0a6a1f1dSLionel Sambuc 		close(fd);
128*0a6a1f1dSLionel Sambuc 		return;
129*0a6a1f1dSLionel Sambuc 	}
130*0a6a1f1dSLionel Sambuc 
131*0a6a1f1dSLionel Sambuc 	bc = alloc(st.st_size + 1);
132*0a6a1f1dSLionel Sambuc 	if (bc == NULL) {
133*0a6a1f1dSLionel Sambuc 		printf("Could not allocate memory for boot configuration\n");
134*0a6a1f1dSLionel Sambuc 		close(fd);
135*0a6a1f1dSLionel Sambuc 		return;
136*0a6a1f1dSLionel Sambuc 	}
137*0a6a1f1dSLionel Sambuc 
138*0a6a1f1dSLionel Sambuc 	/*
139*0a6a1f1dSLionel Sambuc 	 * XXX original code, assumes error or eof return from read()
140*0a6a1f1dSLionel Sambuc 	 *     results in the entire boot.cfg being buffered.
141*0a6a1f1dSLionel Sambuc 	 *     - should bail out on read() failing.
142*0a6a1f1dSLionel Sambuc 	 *     - assumption is made that the file size doesn't change between
143*0a6a1f1dSLionel Sambuc 	 *       fstat() and read()ing.  probably safe in this context
144*0a6a1f1dSLionel Sambuc 	 *       arguably should check that reading the file won't overflow
145*0a6a1f1dSLionel Sambuc 	 *       the storage anyway.
146*0a6a1f1dSLionel Sambuc 	 */
147*0a6a1f1dSLionel Sambuc 	off = 0;
148*0a6a1f1dSLionel Sambuc 	do {
149*0a6a1f1dSLionel Sambuc 		len = read(fd, bc + off, 1024);
150*0a6a1f1dSLionel Sambuc 		if (len <= 0)
151*0a6a1f1dSLionel Sambuc 			break;
152*0a6a1f1dSLionel Sambuc 		off += len;
153*0a6a1f1dSLionel Sambuc 	} while (len > 0);
154*0a6a1f1dSLionel Sambuc 	bc[off] = '\0';
155*0a6a1f1dSLionel Sambuc 
156*0a6a1f1dSLionel Sambuc 	close(fd);
157*0a6a1f1dSLionel Sambuc 
158*0a6a1f1dSLionel Sambuc 	/* bc is now assumed to contain the whole boot.cfg file (see above) */
159*0a6a1f1dSLionel Sambuc 
160*0a6a1f1dSLionel Sambuc 	cmenu = 0;
161*0a6a1f1dSLionel Sambuc 	cbanner = 0;
162*0a6a1f1dSLionel Sambuc 	for (c = bc; *c; c = next) {
163*0a6a1f1dSLionel Sambuc 		key = c;
164*0a6a1f1dSLionel Sambuc 		/* find end of line */
165*0a6a1f1dSLionel Sambuc 		for (; *c && *c != '\n'; c++)
166*0a6a1f1dSLionel Sambuc 			/* zero terminate line on start of comment */
167*0a6a1f1dSLionel Sambuc 			if (*c == '#')
168*0a6a1f1dSLionel Sambuc 				*c = 0;
169*0a6a1f1dSLionel Sambuc 		/* zero terminate line */
170*0a6a1f1dSLionel Sambuc 		if (*(next = c))
171*0a6a1f1dSLionel Sambuc 			*next++ = 0;
172*0a6a1f1dSLionel Sambuc 		/* Look for = separator between key and value */
173*0a6a1f1dSLionel Sambuc 		for (c = key; *c && *c != '='; c++)
174*0a6a1f1dSLionel Sambuc 			continue;
175*0a6a1f1dSLionel Sambuc 		/* Ignore lines with no key=value pair */
176*0a6a1f1dSLionel Sambuc 		if (*c == '\0')
177*0a6a1f1dSLionel Sambuc 			continue;
178*0a6a1f1dSLionel Sambuc 
179*0a6a1f1dSLionel Sambuc 		/* zero terminate key which points to keyword */
180*0a6a1f1dSLionel Sambuc 		*c++ = 0;
181*0a6a1f1dSLionel Sambuc 		value = c;
182*0a6a1f1dSLionel Sambuc 		/* Look for end of line (or file) and zero terminate value */
183*0a6a1f1dSLionel Sambuc 		for (; *c && *c != '\n'; c++)
184*0a6a1f1dSLionel Sambuc 			continue;
185*0a6a1f1dSLionel Sambuc 		*c = 0;
186*0a6a1f1dSLionel Sambuc 
187*0a6a1f1dSLionel Sambuc 		if (!strncmp(key, "menu", 4)) {
188*0a6a1f1dSLionel Sambuc 			/*
189*0a6a1f1dSLionel Sambuc 			 * Parse "menu=<description>:<command>".  If the
190*0a6a1f1dSLionel Sambuc 			 * description is empty ("menu=:<command>)",
191*0a6a1f1dSLionel Sambuc 			 * then re-use the command as the description.
192*0a6a1f1dSLionel Sambuc 			 * Note that the command may contain embedded
193*0a6a1f1dSLionel Sambuc 			 * colons.
194*0a6a1f1dSLionel Sambuc 			 */
195*0a6a1f1dSLionel Sambuc 			if (cmenu >= BOOTCFG_MAXMENU)
196*0a6a1f1dSLionel Sambuc 				continue;
197*0a6a1f1dSLionel Sambuc 			bootcfg_info.desc[cmenu] = value;
198*0a6a1f1dSLionel Sambuc 			for (v2 = value; *v2 && *v2 != ':'; v2++)
199*0a6a1f1dSLionel Sambuc 				continue;
200*0a6a1f1dSLionel Sambuc 			if (*v2) {
201*0a6a1f1dSLionel Sambuc 				*v2++ = 0;
202*0a6a1f1dSLionel Sambuc 				bootcfg_info.command[cmenu] = v2;
203*0a6a1f1dSLionel Sambuc 				if (! *value)
204*0a6a1f1dSLionel Sambuc 					bootcfg_info.desc[cmenu] = v2;
205*0a6a1f1dSLionel Sambuc 				cmenu++;
206*0a6a1f1dSLionel Sambuc 			} else {
207*0a6a1f1dSLionel Sambuc 				/* No delimiter means invalid line */
208*0a6a1f1dSLionel Sambuc 				bootcfg_info.desc[cmenu] = NULL;
209*0a6a1f1dSLionel Sambuc 			}
210*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "banner", 6)) {
211*0a6a1f1dSLionel Sambuc 			if (cbanner < BOOTCFG_MAXBANNER)
212*0a6a1f1dSLionel Sambuc 				bootcfg_info.banner[cbanner++] = value;
213*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "timeout", 7)) {
214*0a6a1f1dSLionel Sambuc 			if (!isdigit(*value))
215*0a6a1f1dSLionel Sambuc 				bootcfg_info.timeout = -1;
216*0a6a1f1dSLionel Sambuc 			else
217*0a6a1f1dSLionel Sambuc 				bootcfg_info.timeout = atoi(value);
218*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "default", 7)) {
219*0a6a1f1dSLionel Sambuc 			bootcfg_info.def = atoi(value) - 1;
220*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "consdev", 7)) {
221*0a6a1f1dSLionel Sambuc 			bootcfg_info.consdev = value;
222*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, BOOTCFG_CMD_LOAD, 4)) {
223*0a6a1f1dSLionel Sambuc 			command(BOOTCFG_CMD_LOAD, value);
224*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "format", 6)) {
225*0a6a1f1dSLionel Sambuc 			printf("value:%c\n", *value);
226*0a6a1f1dSLionel Sambuc 			switch (*value) {
227*0a6a1f1dSLionel Sambuc 			case 'a':
228*0a6a1f1dSLionel Sambuc 			case 'A':
229*0a6a1f1dSLionel Sambuc 				bootcfg_info.menuformat = MENUFORMAT_AUTO;
230*0a6a1f1dSLionel Sambuc 				break;
231*0a6a1f1dSLionel Sambuc 
232*0a6a1f1dSLionel Sambuc 			case 'n':
233*0a6a1f1dSLionel Sambuc 			case 'N':
234*0a6a1f1dSLionel Sambuc 			case 'd':
235*0a6a1f1dSLionel Sambuc 			case 'D':
236*0a6a1f1dSLionel Sambuc 				bootcfg_info.menuformat = MENUFORMAT_NUMBER;
237*0a6a1f1dSLionel Sambuc 				break;
238*0a6a1f1dSLionel Sambuc 
239*0a6a1f1dSLionel Sambuc 			case 'l':
240*0a6a1f1dSLionel Sambuc 			case 'L':
241*0a6a1f1dSLionel Sambuc 				bootcfg_info.menuformat = MENUFORMAT_LETTER;
242*0a6a1f1dSLionel Sambuc 				break;
243*0a6a1f1dSLionel Sambuc 			}
244*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, "clear", 5)) {
245*0a6a1f1dSLionel Sambuc 			bootcfg_info.clear = !!atoi(value);
246*0a6a1f1dSLionel Sambuc 		} else if (!strncmp(key, BOOTCFG_CMD_USERCONF, 8)) {
247*0a6a1f1dSLionel Sambuc 			command(BOOTCFG_CMD_USERCONF, value);
248*0a6a1f1dSLionel Sambuc 		} else {
249*0a6a1f1dSLionel Sambuc 			command(key, value);
250*0a6a1f1dSLionel Sambuc 		}
251*0a6a1f1dSLionel Sambuc 	}
252*0a6a1f1dSLionel Sambuc 
253*0a6a1f1dSLionel Sambuc 	switch (bootcfg_info.menuformat) {
254*0a6a1f1dSLionel Sambuc 	case MENUFORMAT_AUTO:
255*0a6a1f1dSLionel Sambuc 		if (cmenu > 9 && bootcfg_info.timeout > 0)
256*0a6a1f1dSLionel Sambuc 			bootcfg_info.menuformat = MENUFORMAT_LETTER;
257*0a6a1f1dSLionel Sambuc 		else
258*0a6a1f1dSLionel Sambuc 			bootcfg_info.menuformat = MENUFORMAT_NUMBER;
259*0a6a1f1dSLionel Sambuc 		break;
260*0a6a1f1dSLionel Sambuc 
261*0a6a1f1dSLionel Sambuc 	case MENUFORMAT_NUMBER:
262*0a6a1f1dSLionel Sambuc 		if (cmenu > 9 && bootcfg_info.timeout > 0)
263*0a6a1f1dSLionel Sambuc 			cmenu = 9;
264*0a6a1f1dSLionel Sambuc 		break;
265*0a6a1f1dSLionel Sambuc 	}
266*0a6a1f1dSLionel Sambuc 
267*0a6a1f1dSLionel Sambuc 	bootcfg_info.nummenu = cmenu;
268*0a6a1f1dSLionel Sambuc 	if (bootcfg_info.def < 0)
269*0a6a1f1dSLionel Sambuc 		bootcfg_info.def = 0;
270*0a6a1f1dSLionel Sambuc 	if (bootcfg_info.def >= cmenu)
271*0a6a1f1dSLionel Sambuc 		bootcfg_info.def = cmenu - 1;
272*0a6a1f1dSLionel Sambuc }
273