]>
Commit | Line | Data |
---|---|---|
ce5bf700 | 1 | /* main.c - the normal mode main routine */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
6fa42fa6 | 4 | * Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc. |
ce5bf700 | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
ce5bf700 | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
ce5bf700 | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
ce5bf700 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
ce5bf700 | 18 | */ |
19 | ||
4b13b216 | 20 | #include <grub/kernel.h> |
21 | #include <grub/normal.h> | |
22 | #include <grub/dl.h> | |
4b13b216 | 23 | #include <grub/misc.h> |
24 | #include <grub/file.h> | |
25 | #include <grub/mm.h> | |
26 | #include <grub/term.h> | |
27 | #include <grub/env.h> | |
77c4a393 | 28 | #include <grub/parser.h> |
d558e6b5 | 29 | #include <grub/reader.h> |
6fa42fa6 | 30 | #include <grub/menu_viewer.h> |
e7e1f93f | 31 | #include <grub/auth.h> |
e2c37719 | 32 | #include <grub/i18n.h> |
2e713831 | 33 | #include <grub/charset.h> |
d56a6ac7 | 34 | #include <grub/script_sh.h> |
ce5bf700 | 35 | |
4b13b216 | 36 | #define GRUB_DEFAULT_HISTORY_SIZE 50 |
5aded270 | 37 | |
6066889c VS |
38 | static int nested_level = 0; |
39 | int grub_normal_exit_level = 0; | |
40 | ||
ce5bf700 | 41 | /* Read a line from the file FILE. */ |
d05f0df3 | 42 | char * |
43 | grub_file_getline (grub_file_t file) | |
ce5bf700 | 44 | { |
45 | char c; | |
46 | int pos = 0; | |
47 | int literal = 0; | |
2cff3677 | 48 | char *cmdline; |
49 | int max_len = 64; | |
50 | ||
51 | /* Initially locate some space. */ | |
52 | cmdline = grub_malloc (max_len); | |
53 | if (! cmdline) | |
54 | return 0; | |
ce5bf700 | 55 | |
56 | while (1) | |
57 | { | |
4b13b216 | 58 | if (grub_file_read (file, &c, 1) != 1) |
ce5bf700 | 59 | break; |
60 | ||
61 | /* Skip all carriage returns. */ | |
62 | if (c == '\r') | |
63 | continue; | |
64 | ||
65 | /* Replace tabs with spaces. */ | |
66 | if (c == '\t') | |
67 | c = ' '; | |
68 | ||
69 | /* The previous is a backslash, then... */ | |
70 | if (literal) | |
71 | { | |
72 | /* If it is a newline, replace it with a space and continue. */ | |
73 | if (c == '\n') | |
74 | { | |
75 | c = ' '; | |
76 | ||
77 | /* Go back to overwrite the backslash. */ | |
78 | if (pos > 0) | |
79 | pos--; | |
80 | } | |
81 | ||
82 | literal = 0; | |
83 | } | |
84 | ||
85 | if (c == '\\') | |
86 | literal = 1; | |
87 | ||
d558e6b5 | 88 | if (pos == 0) |
ce5bf700 | 89 | { |
d558e6b5 | 90 | if (! grub_isspace (c)) |
ce5bf700 | 91 | cmdline[pos++] = c; |
92 | } | |
93 | else | |
94 | { | |
2cff3677 | 95 | if (pos >= max_len) |
96 | { | |
97 | char *old_cmdline = cmdline; | |
98 | max_len = max_len * 2; | |
99 | cmdline = grub_realloc (cmdline, max_len); | |
100 | if (! cmdline) | |
101 | { | |
102 | grub_free (old_cmdline); | |
103 | return 0; | |
104 | } | |
105 | } | |
106 | ||
e93e4679 | 107 | if (c == '\n') |
108 | break; | |
109 | ||
2cff3677 | 110 | cmdline[pos++] = c; |
ce5bf700 | 111 | } |
112 | } | |
113 | ||
114 | cmdline[pos] = '\0'; | |
2cff3677 | 115 | |
116 | /* If the buffer is empty, don't return anything at all. */ | |
117 | if (pos == 0) | |
118 | { | |
119 | grub_free (cmdline); | |
120 | cmdline = 0; | |
121 | } | |
b39f9d20 | 122 | |
2cff3677 | 123 | return cmdline; |
ce5bf700 | 124 | } |
125 | ||
fc55cc4c VS |
126 | void |
127 | grub_normal_free_menu (grub_menu_t menu) | |
ce5bf700 | 128 | { |
4b13b216 | 129 | grub_menu_entry_t entry = menu->entry_list; |
a8aa5762 | 130 | |
ce5bf700 | 131 | while (entry) |
132 | { | |
4b13b216 | 133 | grub_menu_entry_t next_entry = entry->next; |
ce5bf700 | 134 | |
4b13b216 | 135 | grub_free ((void *) entry->title); |
77c4a393 | 136 | grub_free ((void *) entry->sourcecode); |
ce5bf700 | 137 | entry = next_entry; |
138 | } | |
139 | ||
4b13b216 | 140 | grub_free (menu); |
2fbcbbc3 | 141 | grub_env_unset_menu (); |
ce5bf700 | 142 | } |
143 | ||
4b13b216 | 144 | static grub_menu_t |
d558e6b5 | 145 | read_config_file (const char *config) |
ce5bf700 | 146 | { |
4b13b216 | 147 | grub_file_t file; |
b39f9d20 | 148 | |
d558e6b5 | 149 | auto grub_err_t getline (char **line, int cont); |
150 | grub_err_t getline (char **line, int cont __attribute__ ((unused))) | |
77c4a393 | 151 | { |
d558e6b5 | 152 | while (1) |
153 | { | |
154 | char *buf; | |
155 | ||
156 | *line = buf = grub_file_getline (file); | |
157 | if (! buf) | |
158 | return grub_errno; | |
159 | ||
160 | if (buf[0] == '#') | |
d56a6ac7 | 161 | grub_free (*line); |
d558e6b5 | 162 | else |
163 | break; | |
164 | } | |
77c4a393 | 165 | |
166 | return GRUB_ERR_NONE; | |
167 | } | |
168 | ||
77c4a393 | 169 | grub_menu_t newmenu; |
170 | ||
2fbcbbc3 | 171 | newmenu = grub_env_get_menu (); |
d558e6b5 | 172 | if (! newmenu) |
a8aa5762 | 173 | { |
eab58da2 | 174 | newmenu = grub_zalloc (sizeof (*newmenu)); |
a8aa5762 | 175 | if (! newmenu) |
176 | return 0; | |
d558e6b5 | 177 | |
2fbcbbc3 | 178 | grub_env_set_menu (newmenu); |
a8aa5762 | 179 | } |
77c4a393 | 180 | |
ce5bf700 | 181 | /* Try to open the config file. */ |
4b13b216 | 182 | file = grub_file_open (config); |
ce5bf700 | 183 | if (! file) |
184 | return 0; | |
185 | ||
f4c623e1 VS |
186 | while (1) |
187 | { | |
188 | char *line; | |
189 | ||
190 | /* Print an error, if any. */ | |
191 | grub_print_error (); | |
192 | grub_errno = GRUB_ERR_NONE; | |
193 | ||
194 | if ((getline (&line, 0)) || (! line)) | |
195 | break; | |
196 | ||
d56a6ac7 | 197 | grub_normal_parse_line (line, getline); |
f4c623e1 VS |
198 | grub_free (line); |
199 | } | |
200 | ||
77c4a393 | 201 | grub_file_close (file); |
6de2ee99 | 202 | |
65f201ad | 203 | return newmenu; |
ce5bf700 | 204 | } |
205 | ||
ce5bf700 | 206 | /* Initialize the screen. */ |
207 | void | |
f4c623e1 | 208 | grub_normal_init_page (struct grub_term_output *term) |
ce5bf700 | 209 | { |
b99518d1 | 210 | int msg_len; |
211 | int posx; | |
212 | const char *msg = _("GNU GRUB version %s"); | |
8b442f3f | 213 | char *msg_formatted; |
b99518d1 | 214 | grub_uint32_t *unicode_msg; |
215 | grub_uint32_t *last_position; | |
a2c1332b | 216 | |
fa533ebb | 217 | grub_term_cls (term); |
8b442f3f | 218 | |
61eb45ee | 219 | msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION); |
8b442f3f VS |
220 | if (!msg_formatted) |
221 | return; | |
222 | ||
a2c1332b | 223 | msg_len = grub_utf8_to_ucs4_alloc (msg_formatted, |
b99518d1 | 224 | &unicode_msg, &last_position); |
4a8a763c | 225 | grub_free (msg_formatted); |
a2c1332b | 226 | |
b99518d1 | 227 | if (msg_len < 0) |
228 | { | |
229 | return; | |
230 | } | |
b362c9e9 | 231 | |
2e713831 | 232 | posx = grub_getstringwidth (unicode_msg, last_position, term); |
f4c623e1 | 233 | posx = (grub_term_width (term) - posx) / 2; |
2e713831 | 234 | grub_term_gotoxy (term, posx, 1); |
b362c9e9 | 235 | |
f588f1c8 | 236 | grub_print_ucs4 (unicode_msg, last_position, 0, 0, term); |
701f1df9 VS |
237 | grub_putcode ('\n', term); |
238 | grub_putcode ('\n', term); | |
b99518d1 | 239 | grub_free (unicode_msg); |
ce5bf700 | 240 | } |
241 | ||
027de555 VS |
242 | static void |
243 | read_lists (const char *val) | |
244 | { | |
72539694 BC |
245 | if (! grub_no_autoload) |
246 | { | |
247 | read_command_list (val); | |
248 | read_fs_list (val); | |
249 | read_crypto_list (val); | |
250 | read_terminal_list (val); | |
251 | } | |
027de555 VS |
252 | } |
253 | ||
e880248e | 254 | static char * |
027de555 VS |
255 | read_lists_hook (struct grub_env_var *var __attribute__ ((unused)), |
256 | const char *val) | |
e880248e | 257 | { |
027de555 | 258 | read_lists (val); |
e880248e RM |
259 | return val ? grub_strdup (val) : NULL; |
260 | } | |
39c9d41d | 261 | |
4241d2b1 | 262 | /* Read the config file CONFIG and execute the menu interface or |
263 | the command line interface if BATCH is false. */ | |
ce5bf700 | 264 | void |
f04f02e4 | 265 | grub_normal_execute (const char *config, int nested, int batch) |
ce5bf700 | 266 | { |
4b13b216 | 267 | grub_menu_t menu = 0; |
3fa06487 | 268 | const char *prefix; |
ce5bf700 | 269 | |
3fa06487 CW |
270 | if (! nested) |
271 | { | |
272 | prefix = grub_env_get ("prefix"); | |
273 | read_lists (prefix); | |
274 | grub_register_variable_hook ("prefix", NULL, read_lists_hook); | |
275 | grub_command_execute ("parser.grub", 0, 0); | |
276 | } | |
d558e6b5 | 277 | |
ce5bf700 | 278 | if (config) |
279 | { | |
d558e6b5 | 280 | menu = read_config_file (config); |
ce5bf700 | 281 | |
282 | /* Ignore any error. */ | |
4b13b216 | 283 | grub_errno = GRUB_ERR_NONE; |
ce5bf700 | 284 | } |
285 | ||
f04f02e4 | 286 | if (! batch) |
93f3a1d8 | 287 | { |
f04f02e4 | 288 | if (menu && menu->size) |
d558e6b5 | 289 | { |
dcb883b1 | 290 | grub_show_menu (menu, nested, 0); |
d558e6b5 | 291 | if (nested) |
fc55cc4c | 292 | grub_normal_free_menu (menu); |
d558e6b5 | 293 | } |
93f3a1d8 | 294 | } |
ce5bf700 | 295 | } |
296 | ||
d558e6b5 | 297 | /* This starts the normal mode. */ |
298 | void | |
299 | grub_enter_normal_mode (const char *config) | |
b1b797cb | 300 | { |
6066889c | 301 | nested_level++; |
d558e6b5 | 302 | grub_normal_execute (config, 0, 0); |
6066889c VS |
303 | grub_cmdline_run (0); |
304 | nested_level--; | |
305 | if (grub_normal_exit_level) | |
306 | grub_normal_exit_level--; | |
b1b797cb | 307 | } |
308 | ||
ce5bf700 | 309 | /* Enter normal mode from rescue mode. */ |
b1b797cb | 310 | static grub_err_t |
2e713831 | 311 | grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), |
b1b797cb | 312 | int argc, char *argv[]) |
ce5bf700 | 313 | { |
314 | if (argc == 0) | |
315 | { | |
5f3607e0 | 316 | /* Guess the config filename. It is necessary to make CONFIG static, |
317 | so that it won't get broken by longjmp. */ | |
2e713831 | 318 | char *config; |
ce5bf700 | 319 | const char *prefix; |
b39f9d20 | 320 | |
4b13b216 | 321 | prefix = grub_env_get ("prefix"); |
ce5bf700 | 322 | if (prefix) |
323 | { | |
61eb45ee | 324 | config = grub_xasprintf ("%s/grub.cfg", prefix); |
ce5bf700 | 325 | if (! config) |
b1b797cb | 326 | goto quit; |
ce5bf700 | 327 | |
4b13b216 | 328 | grub_enter_normal_mode (config); |
329 | grub_free (config); | |
ce5bf700 | 330 | } |
331 | else | |
4b13b216 | 332 | grub_enter_normal_mode (0); |
ce5bf700 | 333 | } |
334 | else | |
4b13b216 | 335 | grub_enter_normal_mode (argv[0]); |
b1b797cb | 336 | |
337 | quit: | |
b1b797cb | 338 | return 0; |
ce5bf700 | 339 | } |
340 | ||
6066889c | 341 | /* Exit from normal mode to rescue mode. */ |
d558e6b5 | 342 | static grub_err_t |
6066889c VS |
343 | grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)), |
344 | int argc __attribute__ ((unused)), | |
345 | char *argv[] __attribute__ ((unused))) | |
d558e6b5 | 346 | { |
6066889c VS |
347 | if (nested_level <= grub_normal_exit_level) |
348 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment"); | |
349 | grub_normal_exit_level++; | |
350 | return GRUB_ERR_NONE; | |
d558e6b5 | 351 | } |
352 | ||
353 | static grub_err_t | |
6066889c | 354 | grub_normal_reader_init (int nested) |
d558e6b5 | 355 | { |
f4c623e1 | 356 | struct grub_term_output *term; |
7f39d92f | 357 | const char *msg = _("Minimal BASH-like line editing is supported. For " |
358 | "the first word, TAB lists possible command completions. Anywhere " | |
359 | "else TAB lists possible device or file completions. %s"); | |
7f39d92f | 360 | const char *msg_esc = _("ESC at any time exits."); |
8b442f3f | 361 | char *msg_formatted; |
7f39d92f | 362 | |
61eb45ee | 363 | msg_formatted = grub_xasprintf (msg, nested ? msg_esc : ""); |
8b442f3f VS |
364 | if (!msg_formatted) |
365 | return grub_errno; | |
7f39d92f | 366 | |
3be7f8de VS |
367 | FOR_ACTIVE_TERM_OUTPUTS(term) |
368 | { | |
369 | grub_normal_init_page (term); | |
370 | grub_term_setcursor (term, 1); | |
701f1df9 | 371 | |
3be7f8de | 372 | grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term); |
701f1df9 VS |
373 | grub_putcode ('\n', term); |
374 | grub_putcode ('\n', term); | |
3be7f8de | 375 | } |
7f39d92f | 376 | grub_free (msg_formatted); |
a2c1332b | 377 | |
d558e6b5 | 378 | return 0; |
379 | } | |
380 | ||
d558e6b5 | 381 | static grub_err_t |
6066889c | 382 | grub_normal_read_line_real (char **line, int cont, int nested) |
d558e6b5 | 383 | { |
d56a6ac7 | 384 | const char *prompt; |
d558e6b5 | 385 | |
6066889c | 386 | if (cont) |
d56a6ac7 | 387 | prompt = ">"; |
6066889c | 388 | else |
d56a6ac7 | 389 | prompt = "grub>"; |
d558e6b5 | 390 | |
b09a4a8d VS |
391 | if (!prompt) |
392 | return grub_errno; | |
d558e6b5 | 393 | |
394 | while (1) | |
395 | { | |
2e713831 VS |
396 | *line = grub_cmdline_get (prompt); |
397 | if (*line) | |
d558e6b5 | 398 | break; |
399 | ||
6066889c | 400 | if (cont || nested) |
d558e6b5 | 401 | { |
2e713831 | 402 | grub_free (*line); |
d558e6b5 | 403 | *line = 0; |
404 | return grub_errno; | |
405 | } | |
406 | } | |
4a8a763c | 407 | |
d558e6b5 | 408 | return 0; |
409 | } | |
410 | ||
6066889c VS |
411 | static grub_err_t |
412 | grub_normal_read_line (char **line, int cont) | |
413 | { | |
414 | return grub_normal_read_line_real (line, cont, 0); | |
415 | } | |
416 | ||
f4c623e1 VS |
417 | void |
418 | grub_cmdline_run (int nested) | |
419 | { | |
420 | grub_err_t err = GRUB_ERR_NONE; | |
421 | ||
422 | err = grub_auth_check_authentication (NULL); | |
423 | ||
424 | if (err) | |
425 | { | |
426 | grub_print_error (); | |
427 | grub_errno = GRUB_ERR_NONE; | |
428 | return; | |
429 | } | |
430 | ||
6066889c | 431 | grub_normal_reader_init (nested); |
f4c623e1 VS |
432 | |
433 | while (1) | |
434 | { | |
435 | char *line; | |
436 | ||
6066889c VS |
437 | if (grub_normal_exit_level) |
438 | break; | |
439 | ||
f4c623e1 VS |
440 | /* Print an error, if any. */ |
441 | grub_print_error (); | |
442 | grub_errno = GRUB_ERR_NONE; | |
443 | ||
6066889c | 444 | grub_normal_read_line_real (&line, 0, nested); |
f4c623e1 | 445 | if (! line) |
2e713831 | 446 | break; |
f4c623e1 | 447 | |
d56a6ac7 | 448 | grub_normal_parse_line (line, grub_normal_read_line); |
f4c623e1 VS |
449 | grub_free (line); |
450 | } | |
451 | } | |
d558e6b5 | 452 | |
453 | static char * | |
454 | grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)), | |
455 | const char *val) | |
456 | { | |
457 | grub_set_more ((*val == '1')); | |
458 | return grub_strdup (val); | |
459 | } | |
460 | ||
919e37b0 VS |
461 | /* clear */ |
462 | static grub_err_t | |
463 | grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)), | |
464 | int argc __attribute__ ((unused)), | |
465 | char *argv[] __attribute__ ((unused))) | |
466 | { | |
467 | grub_cls (); | |
468 | return 0; | |
469 | } | |
470 | ||
471 | static grub_command_t cmd_clear; | |
472 | ||
0a239a82 VS |
473 | static void (*grub_xputs_saved) (const char *str); |
474 | ||
6d099807 | 475 | GRUB_MOD_INIT(normal) |
ce5bf700 | 476 | { |
fc2ef117 VS |
477 | /* Previously many modules depended on gzio. Be nice to user and load it. */ |
478 | grub_dl_load ("gzio"); | |
479 | ||
df895792 | 480 | grub_normal_auth_init (); |
2fbcbbc3 | 481 | grub_context_init (); |
20ebf732 | 482 | grub_script_init (); |
9284756e | 483 | grub_menu_init (); |
2fbcbbc3 | 484 | |
0a239a82 VS |
485 | grub_xputs_saved = grub_xputs; |
486 | grub_xputs = grub_xputs_normal; | |
487 | ||
ce5bf700 | 488 | /* Normal mode shouldn't be unloaded. */ |
6d099807 | 489 | if (mod) |
490 | grub_dl_ref (mod); | |
ce5bf700 | 491 | |
919e37b0 VS |
492 | cmd_clear = |
493 | grub_register_command ("clear", grub_mini_cmd_clear, | |
494 | 0, N_("Clear the screen.")); | |
495 | ||
4b13b216 | 496 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 497 | |
d558e6b5 | 498 | grub_register_variable_hook ("pager", 0, grub_env_write_pager); |
537dc9be | 499 | grub_env_export ("pager"); |
d558e6b5 | 500 | |
ce5bf700 | 501 | /* Register a command "normal" for the rescue mode. */ |
2e713831 | 502 | grub_register_command ("normal", grub_cmd_normal, |
d8b5cd40 | 503 | 0, N_("Enter normal mode.")); |
6066889c | 504 | grub_register_command ("normal_exit", grub_cmd_normal_exit, |
d8b5cd40 | 505 | 0, N_("Exit from normal mode.")); |
ce5bf700 | 506 | |
0ece25b1 | 507 | /* Reload terminal colors when these variables are written to. */ |
508 | grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal); | |
509 | grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight); | |
510 | ||
511 | /* Preserve hooks after context changes. */ | |
512 | grub_env_export ("color_normal"); | |
513 | grub_env_export ("color_highlight"); | |
681440aa BC |
514 | |
515 | /* Set default color names. */ | |
516 | grub_env_set ("color_normal", "white/black"); | |
517 | grub_env_set ("color_highlight", "black/white"); | |
b38a4983 VS |
518 | |
519 | grub_env_set ("grub_feature_chainloader_bpb", "--bpb"); | |
520 | grub_env_export ("grub_feature_chainloader_bpb"); | |
ce5bf700 | 521 | } |
522 | ||
6d099807 | 523 | GRUB_MOD_FINI(normal) |
ce5bf700 | 524 | { |
2fbcbbc3 | 525 | grub_context_fini (); |
20ebf732 | 526 | grub_script_fini (); |
9284756e | 527 | grub_menu_fini (); |
df895792 | 528 | grub_normal_auth_fini (); |
2fbcbbc3 | 529 | |
0a239a82 VS |
530 | grub_xputs = grub_xputs_saved; |
531 | ||
4b13b216 | 532 | grub_set_history (0); |
d558e6b5 | 533 | grub_register_variable_hook ("pager", 0, 0); |
534 | grub_fs_autoload_hook = 0; | |
919e37b0 | 535 | grub_unregister_command (cmd_clear); |
ce5bf700 | 536 | } |