]> git.proxmox.com Git - grub2.git/blame - grub-core/normal/main.c
fix hotkey handling
[grub2.git] / grub-core / normal / main.c
CommitLineData
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
38static int nested_level = 0;
39int grub_normal_exit_level = 0;
40
ce5bf700 41/* Read a line from the file FILE. */
d05f0df3 42char *
43grub_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
126static void
4b13b216 127free_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 144static grub_menu_t
d558e6b5 145read_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. */
207void
f4c623e1 208grub_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
242static void
243read_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 254static char *
027de555
VS
255read_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 264void
f04f02e4 265grub_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 {
2e713831 290 grub_show_menu (menu, nested);
d558e6b5 291 if (nested)
292 free_menu (menu);
293 }
93f3a1d8 294 }
ce5bf700 295}
296
d558e6b5 297/* This starts the normal mode. */
298void
299grub_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 310static grub_err_t
2e713831 311grub_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
337quit:
b1b797cb 338 return 0;
ce5bf700 339}
340
6066889c 341/* Exit from normal mode to rescue mode. */
d558e6b5 342static grub_err_t
6066889c
VS
343grub_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
353static grub_err_t
6066889c 354grub_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 381static grub_err_t
6066889c 382grub_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
411static grub_err_t
412grub_normal_read_line (char **line, int cont)
413{
414 return grub_normal_read_line_real (line, cont, 0);
415}
416
f4c623e1
VS
417void
418grub_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
453static char *
454grub_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 */
462static grub_err_t
463grub_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
471static grub_command_t cmd_clear;
472
0a239a82
VS
473static void (*grub_xputs_saved) (const char *str);
474
6d099807 475GRUB_MOD_INIT(normal)
ce5bf700 476{
2fbcbbc3 477 grub_context_init ();
20ebf732 478 grub_script_init ();
2fbcbbc3 479
0a239a82
VS
480 grub_xputs_saved = grub_xputs;
481 grub_xputs = grub_xputs_normal;
482
ce5bf700 483 /* Normal mode shouldn't be unloaded. */
6d099807 484 if (mod)
485 grub_dl_ref (mod);
ce5bf700 486
919e37b0
VS
487 cmd_clear =
488 grub_register_command ("clear", grub_mini_cmd_clear,
489 0, N_("Clear the screen."));
490
4b13b216 491 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
5aded270 492
d558e6b5 493 grub_register_variable_hook ("pager", 0, grub_env_write_pager);
494
ce5bf700 495 /* Register a command "normal" for the rescue mode. */
2e713831 496 grub_register_command ("normal", grub_cmd_normal,
d8b5cd40 497 0, N_("Enter normal mode."));
6066889c 498 grub_register_command ("normal_exit", grub_cmd_normal_exit,
d8b5cd40 499 0, N_("Exit from normal mode."));
ce5bf700 500
0ece25b1 501 /* Reload terminal colors when these variables are written to. */
502 grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
503 grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
504
505 /* Preserve hooks after context changes. */
506 grub_env_export ("color_normal");
507 grub_env_export ("color_highlight");
681440aa
BC
508
509 /* Set default color names. */
510 grub_env_set ("color_normal", "white/black");
511 grub_env_set ("color_highlight", "black/white");
ce5bf700 512}
513
6d099807 514GRUB_MOD_FINI(normal)
ce5bf700 515{
2fbcbbc3 516 grub_context_fini ();
20ebf732 517 grub_script_fini ();
2fbcbbc3 518
0a239a82
VS
519 grub_xputs = grub_xputs_saved;
520
4b13b216 521 grub_set_history (0);
d558e6b5 522 grub_register_variable_hook ("pager", 0, 0);
523 grub_fs_autoload_hook = 0;
919e37b0 524 grub_unregister_command (cmd_clear);
ce5bf700 525}