xref: /netbsd-src/sys/arch/i386/stand/lib/bootmenu.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: bootmenu.c,v 1.13 2014/06/28 09:16:18 rtr Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef SMALL
30 
31 #include <sys/types.h>
32 #include <sys/reboot.h>
33 #include <sys/bootblock.h>
34 
35 #include <lib/libsa/stand.h>
36 #include <lib/libsa/bootcfg.h>
37 #include <lib/libsa/ufs.h>
38 #include <lib/libkern/libkern.h>
39 
40 #include <libi386.h>
41 #include <bootmenu.h>
42 
43 #define isnum(c) ((c) >= '0' && (c) <= '9')
44 
45 static void docommandchoice(int);
46 
47 extern struct x86_boot_params boot_params;
48 extern	const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
49 
50 #define MENUFORMAT_AUTO	  0
51 #define MENUFORMAT_NUMBER 1
52 #define MENUFORMAT_LETTER 2
53 
54 int
55 atoi(const char *in)
56 {
57 	char *c;
58 	int ret;
59 
60 	ret = 0;
61 	c = (char *)in;
62 	if (*c == '-')
63 		c++;
64 	for (; isnum(*c); c++)
65 		ret = (ret * 10) + (*c - '0');
66 
67 	return (*in == '-') ? -ret : ret;
68 }
69 
70 /*
71  * XXX
72  * if module_add, userconf_add are strictly mi they can be folded back
73  * into sys/lib/libsa/bootcfg.c:perform_bootcfg().
74  */
75 static void
76 do_bootcfg_command(const char *cmd, char *arg)
77 {
78 	if (strcmp(cmd, BOOTCFG_CMD_LOAD) == 0)
79 		module_add(arg);
80 	else if (strcmp(cmd, BOOTCFG_CMD_USERCONF) == 0)
81 		userconf_add(arg);
82 }
83 
84 void
85 parsebootconf(const char *conf)
86 {
87 	perform_bootcfg(conf, &do_bootcfg_command, 32768);
88 }
89 
90 /*
91  * doboottypemenu will render the menu and parse any user input
92  */
93 static int
94 getchoicefrominput(char *input, int def)
95 {
96 	int choice, usedef;
97 
98 	choice = -1;
99 	usedef = 0;
100 
101 	if (*input == '\0' || *input == '\r' || *input == '\n') {
102 		choice = def;
103 		usedef = 1;
104 	} else if (*input >= 'A' && *input < bootcfg_info.nummenu + 'A')
105 		choice = (*input) - 'A';
106 	else if (*input >= 'a' && *input < bootcfg_info.nummenu + 'a')
107 		choice = (*input) - 'a';
108 	else if (isnum(*input)) {
109 		choice = atoi(input) - 1;
110 		if (choice < 0 || choice >= bootcfg_info.nummenu)
111 			choice = -1;
112 	}
113 
114 	if (bootcfg_info.menuformat != MENUFORMAT_LETTER &&
115 	    !isnum(*input) && !usedef)
116 		choice = -1;
117 
118 	return choice;
119 }
120 
121 static void
122 docommandchoice(int choice)
123 {
124 	char input[80], *ic, *oc;
125 
126 	ic = bootcfg_info.command[choice];
127 	/* Split command string at ; into separate commands */
128 	do {
129 		oc = input;
130 		/* Look for ; separator */
131 		for (; *ic && *ic != COMMAND_SEPARATOR; ic++)
132 			*oc++ = *ic;
133 		if (*input == '\0')
134 			continue;
135 		/* Strip out any trailing spaces */
136 		oc--;
137 		for (; *oc == ' ' && oc > input; oc--);
138 		*++oc = '\0';
139 		if (*ic == COMMAND_SEPARATOR)
140 			ic++;
141 		/* Stop silly command strings like ;;; */
142 		if (*input != '\0')
143 			docommand(input);
144 		/* Skip leading spaces */
145 		for (; *ic == ' '; ic++);
146 	} while (*ic);
147 }
148 
149 void
150 bootdefault(void)
151 {
152 	int choice;
153 	static int entered;
154 
155 	if (bootcfg_info.nummenu > 0) {
156 		if (entered) {
157 			printf("default boot twice, skipping...\n");
158 			return;
159 		}
160 		entered = 1;
161 		choice = bootcfg_info.def;
162 		printf("command(s): %s\n", bootcfg_info.command[choice]);
163 		docommandchoice(choice);
164 	}
165 }
166 
167 __dead void
168 doboottypemenu(void)
169 {
170 	int choice;
171 	char input[80];
172 
173 	printf("\n");
174 	/* Display menu */
175 	if (bootcfg_info.menuformat == MENUFORMAT_LETTER) {
176 		for (choice = 0; choice < bootcfg_info.nummenu; choice++)
177 			printf("    %c. %s\n", choice + 'A',
178 			    bootcfg_info.desc[choice]);
179 	} else {
180 		/* Can't use %2d format string with libsa */
181 		for (choice = 0; choice < bootcfg_info.nummenu; choice++)
182 			printf("    %s%d. %s\n",
183 			    (choice < 9) ?  " " : "",
184 			    choice + 1,
185 			    bootcfg_info.desc[choice]);
186 	}
187 	choice = -1;
188 	for (;;) {
189 		input[0] = '\0';
190 
191 		if (bootcfg_info.timeout < 0) {
192 			if (bootcfg_info.menuformat == MENUFORMAT_LETTER)
193 				printf("\nOption: [%c]:",
194 				    bootcfg_info.def + 'A');
195 			else
196 				printf("\nOption: [%d]:",
197 				    bootcfg_info.def + 1);
198 
199 			gets(input);
200 			choice = getchoicefrominput(input, bootcfg_info.def);
201 		} else if (bootcfg_info.timeout == 0)
202 			choice = bootcfg_info.def;
203 		else  {
204 			printf("\nChoose an option; RETURN for default; "
205 			       "SPACE to stop countdown.\n");
206 			if (bootcfg_info.menuformat == MENUFORMAT_LETTER)
207 				printf("Option %c will be chosen in ",
208 				    bootcfg_info.def + 'A');
209 			else
210 				printf("Option %d will be chosen in ",
211 				    bootcfg_info.def + 1);
212 			input[0] = awaitkey(bootcfg_info.timeout, 1);
213 			input[1] = '\0';
214 			choice = getchoicefrominput(input, bootcfg_info.def);
215 			/* If invalid key pressed, drop to menu */
216 			if (choice == -1)
217 				bootcfg_info.timeout = -1;
218 		}
219 		if (choice < 0)
220 			continue;
221 		if (!strcmp(bootcfg_info.command[choice], "prompt") &&
222 		    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
223 		    check_password((char *)boot_params.bp_password))) {
224 			printf("type \"?\" or \"help\" for help.\n");
225 			bootmenu(); /* does not return */
226 		} else {
227 			docommandchoice(choice);
228 		}
229 
230 	}
231 }
232 
233 #endif	/* !SMALL */
234