]> git.proxmox.com Git - grub2.git/blame - normal/main.c
* loader/i386/multiboot_mbi.c (retrieve_video_parameters): Set red
[grub2.git] / 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
6fa42fa6 144static void
145free_menu_entry_classes (struct grub_menu_entry_class *head)
146{
147 /* Free all the classes. */
148 while (head)
149 {
150 struct grub_menu_entry_class *next;
151
152 grub_free (head->name);
153 next = head->next;
154 grub_free (head);
155 head = next;
156 }
157}
158
16c8e9fd
VS
159static struct
160{
161 char *name;
162 int key;
163} hotkey_aliases[] =
164 {
165 {"backspace", '\b'},
166 {"tab", '\t'},
167 {"delete", GRUB_TERM_DC}
168 };
169
230c0ad6 170/* Add a menu entry to the current menu context (as given by the environment
171 variable data slot `menu'). As the configuration file is read, the script
172 parser calls this when a menu entry is to be created. */
77c4a393 173grub_err_t
230c0ad6 174grub_normal_add_menu_entry (int argc, const char **args,
175 const char *sourcecode)
77c4a393 176{
6fa42fa6 177 const char *menutitle = 0;
a8aa5762 178 const char *menusourcecode;
179 grub_menu_t menu;
180 grub_menu_entry_t *last;
6fa42fa6 181 int failed = 0;
182 int i;
183 struct grub_menu_entry_class *classes_head; /* Dummy head node for list. */
184 struct grub_menu_entry_class *classes_tail;
e7e1f93f 185 char *users = NULL;
16c8e9fd 186 int hotkey = 0;
6fa42fa6 187
188 /* Allocate dummy head node for class list. */
eab58da2 189 classes_head = grub_zalloc (sizeof (struct grub_menu_entry_class));
6fa42fa6 190 if (! classes_head)
191 return grub_errno;
6fa42fa6 192 classes_tail = classes_head;
a8aa5762 193
2fbcbbc3 194 menu = grub_env_get_menu ();
a8aa5762 195 if (! menu)
196 return grub_error (GRUB_ERR_MENU, "no menu context");
197
198 last = &menu->entry_list;
199
200 menusourcecode = grub_strdup (sourcecode);
201 if (! menusourcecode)
202 return grub_errno;
77c4a393 203
6fa42fa6 204 /* Parse menu arguments. */
205 for (i = 0; i < argc; i++)
206 {
207 /* Capture arguments. */
c2ffc8e9 208 if (grub_strncmp ("--", args[i], 2) == 0 && i + 1 < argc)
6fa42fa6 209 {
210 const char *arg = &args[i][2];
211
212 /* Handle menu class. */
213 if (grub_strcmp(arg, "class") == 0)
214 {
215 char *class_name;
216 struct grub_menu_entry_class *new_class;
217
218 i++;
219 class_name = grub_strdup (args[i]);
220 if (! class_name)
221 {
222 failed = 1;
223 break;
224 }
225
226 /* Create a new class and add it at the tail of the list. */
eab58da2 227 new_class = grub_zalloc (sizeof (struct grub_menu_entry_class));
6fa42fa6 228 if (! new_class)
229 {
230 grub_free (class_name);
231 failed = 1;
232 break;
233 }
234 /* Fill in the new class node. */
235 new_class->name = class_name;
6fa42fa6 236 /* Link the tail to it, and make it the new tail. */
237 classes_tail->next = new_class;
238 classes_tail = new_class;
239 continue;
240 }
e7e1f93f 241 else if (grub_strcmp(arg, "users") == 0)
242 {
243 i++;
244 users = grub_strdup (args[i]);
245 if (! users)
246 {
247 failed = 1;
248 break;
249 }
250
251 continue;
252 }
16c8e9fd
VS
253 else if (grub_strcmp(arg, "hotkey") == 0)
254 {
255 unsigned j;
256
257 i++;
258 if (args[i][1] == 0)
259 {
260 hotkey = args[i][0];
261 continue;
262 }
263
264 for (j = 0; j < ARRAY_SIZE (hotkey_aliases); j++)
265 if (grub_strcmp (args[i], hotkey_aliases[j].name) == 0)
266 {
267 hotkey = hotkey_aliases[j].key;
268 break;
269 }
270
271 if (j < ARRAY_SIZE (hotkey_aliases))
272 continue;
273
274 failed = 1;
275 grub_error (GRUB_ERR_MENU,
276 "Invalid hotkey: '%s'.", args[i]);
277 break;
278 }
6fa42fa6 279 else
280 {
281 /* Handle invalid argument. */
282 failed = 1;
f04f02e4 283 grub_error (GRUB_ERR_MENU,
d558e6b5 284 "invalid argument for menuentry: %s", args[i]);
6fa42fa6 285 break;
286 }
287 }
288
289 /* Capture title. */
290 if (! menutitle)
291 {
292 menutitle = grub_strdup (args[i]);
293 }
294 else
295 {
296 failed = 1;
f04f02e4 297 grub_error (GRUB_ERR_MENU,
d558e6b5 298 "too many titles for menuentry: %s", args[i]);
6fa42fa6 299 break;
300 }
301 }
302
303 /* Validate arguments. */
304 if ((! failed) && (! menutitle))
a8aa5762 305 {
6fa42fa6 306 grub_error (GRUB_ERR_MENU, "menuentry is missing title");
307 failed = 1;
308 }
309
310 /* If argument parsing failed, free any allocated resources. */
311 if (failed)
312 {
313 free_menu_entry_classes (classes_head);
314 grub_free ((void *) menutitle);
a8aa5762 315 grub_free ((void *) menusourcecode);
6fa42fa6 316
317 /* Here we assume that grub_error has been used to specify failure details. */
a8aa5762 318 return grub_errno;
319 }
77c4a393 320
321 /* Add the menu entry at the end of the list. */
322 while (*last)
323 last = &(*last)->next;
324
eab58da2 325 *last = grub_zalloc (sizeof (**last));
77c4a393 326 if (! *last)
327 {
6fa42fa6 328 free_menu_entry_classes (classes_head);
77c4a393 329 grub_free ((void *) menutitle);
a8aa5762 330 grub_free ((void *) menusourcecode);
77c4a393 331 return grub_errno;
332 }
333
77c4a393 334 (*last)->title = menutitle;
16c8e9fd 335 (*last)->hotkey = hotkey;
6fa42fa6 336 (*last)->classes = classes_head;
e7e1f93f 337 if (users)
338 (*last)->restricted = 1;
339 (*last)->users = users;
a8aa5762 340 (*last)->sourcecode = menusourcecode;
77c4a393 341
a8aa5762 342 menu->size++;
65f201ad 343
77c4a393 344 return GRUB_ERR_NONE;
345}
346
4b13b216 347static grub_menu_t
d558e6b5 348read_config_file (const char *config)
ce5bf700 349{
4b13b216 350 grub_file_t file;
b39f9d20 351
d558e6b5 352 auto grub_err_t getline (char **line, int cont);
353 grub_err_t getline (char **line, int cont __attribute__ ((unused)))
77c4a393 354 {
d558e6b5 355 while (1)
356 {
357 char *buf;
358
359 *line = buf = grub_file_getline (file);
360 if (! buf)
361 return grub_errno;
362
363 if (buf[0] == '#')
d56a6ac7 364 grub_free (*line);
d558e6b5 365 else
366 break;
367 }
77c4a393 368
369 return GRUB_ERR_NONE;
370 }
371
77c4a393 372 grub_menu_t newmenu;
373
2fbcbbc3 374 newmenu = grub_env_get_menu ();
d558e6b5 375 if (! newmenu)
a8aa5762 376 {
eab58da2 377 newmenu = grub_zalloc (sizeof (*newmenu));
a8aa5762 378 if (! newmenu)
379 return 0;
d558e6b5 380
2fbcbbc3 381 grub_env_set_menu (newmenu);
a8aa5762 382 }
77c4a393 383
ce5bf700 384 /* Try to open the config file. */
4b13b216 385 file = grub_file_open (config);
ce5bf700 386 if (! file)
387 return 0;
388
f4c623e1
VS
389 while (1)
390 {
391 char *line;
392
393 /* Print an error, if any. */
394 grub_print_error ();
395 grub_errno = GRUB_ERR_NONE;
396
397 if ((getline (&line, 0)) || (! line))
398 break;
399
d56a6ac7 400 grub_normal_parse_line (line, getline);
f4c623e1
VS
401 grub_free (line);
402 }
403
77c4a393 404 grub_file_close (file);
6de2ee99 405
65f201ad 406 return newmenu;
ce5bf700 407}
408
ce5bf700 409/* Initialize the screen. */
410void
f4c623e1 411grub_normal_init_page (struct grub_term_output *term)
ce5bf700 412{
b99518d1 413 int msg_len;
414 int posx;
415 const char *msg = _("GNU GRUB version %s");
8b442f3f 416 char *msg_formatted;
b99518d1 417 grub_uint32_t *unicode_msg;
418 grub_uint32_t *last_position;
a2c1332b 419
fa533ebb 420 grub_term_cls (term);
8b442f3f 421
61eb45ee 422 msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION);
8b442f3f
VS
423 if (!msg_formatted)
424 return;
425
a2c1332b 426 msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
b99518d1 427 &unicode_msg, &last_position);
4a8a763c 428 grub_free (msg_formatted);
a2c1332b 429
b99518d1 430 if (msg_len < 0)
431 {
432 return;
433 }
b362c9e9 434
2e713831 435 posx = grub_getstringwidth (unicode_msg, last_position, term);
f4c623e1 436 posx = (grub_term_width (term) - posx) / 2;
2e713831 437 grub_term_gotoxy (term, posx, 1);
b362c9e9 438
2e713831 439 grub_print_ucs4 (unicode_msg, last_position, term);
b99518d1 440 grub_printf("\n\n");
441 grub_free (unicode_msg);
ce5bf700 442}
443
027de555
VS
444static void
445read_lists (const char *val)
446{
447 read_command_list (val);
448 read_fs_list (val);
449 read_crypto_list (val);
450 read_terminal_list (val);
451}
452
e880248e 453static char *
027de555
VS
454read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
455 const char *val)
e880248e 456{
027de555 457 read_lists (val);
e880248e
RM
458 return val ? grub_strdup (val) : NULL;
459}
39c9d41d 460
4241d2b1 461/* Read the config file CONFIG and execute the menu interface or
462 the command line interface if BATCH is false. */
ce5bf700 463void
f04f02e4 464grub_normal_execute (const char *config, int nested, int batch)
ce5bf700 465{
4b13b216 466 grub_menu_t menu = 0;
027de555 467 const char *prefix = grub_env_get ("prefix");
ce5bf700 468
027de555 469 read_lists (prefix);
027de555 470 grub_register_variable_hook ("prefix", NULL, read_lists_hook);
13b33fba 471 grub_command_execute ("parser.grub", 0, 0);
d558e6b5 472
ce5bf700 473 if (config)
474 {
d558e6b5 475 menu = read_config_file (config);
ce5bf700 476
477 /* Ignore any error. */
4b13b216 478 grub_errno = GRUB_ERR_NONE;
ce5bf700 479 }
480
f04f02e4 481 if (! batch)
93f3a1d8 482 {
f04f02e4 483 if (menu && menu->size)
d558e6b5 484 {
2e713831 485 grub_show_menu (menu, nested);
d558e6b5 486 if (nested)
487 free_menu (menu);
488 }
93f3a1d8 489 }
ce5bf700 490}
491
d558e6b5 492/* This starts the normal mode. */
493void
494grub_enter_normal_mode (const char *config)
b1b797cb 495{
6066889c 496 nested_level++;
d558e6b5 497 grub_normal_execute (config, 0, 0);
6066889c
VS
498 grub_cmdline_run (0);
499 nested_level--;
500 if (grub_normal_exit_level)
501 grub_normal_exit_level--;
b1b797cb 502}
503
ce5bf700 504/* Enter normal mode from rescue mode. */
b1b797cb 505static grub_err_t
2e713831 506grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
b1b797cb 507 int argc, char *argv[])
ce5bf700 508{
509 if (argc == 0)
510 {
5f3607e0 511 /* Guess the config filename. It is necessary to make CONFIG static,
512 so that it won't get broken by longjmp. */
2e713831 513 char *config;
ce5bf700 514 const char *prefix;
b39f9d20 515
4b13b216 516 prefix = grub_env_get ("prefix");
ce5bf700 517 if (prefix)
518 {
61eb45ee 519 config = grub_xasprintf ("%s/grub.cfg", prefix);
ce5bf700 520 if (! config)
b1b797cb 521 goto quit;
ce5bf700 522
4b13b216 523 grub_enter_normal_mode (config);
524 grub_free (config);
ce5bf700 525 }
526 else
4b13b216 527 grub_enter_normal_mode (0);
ce5bf700 528 }
529 else
4b13b216 530 grub_enter_normal_mode (argv[0]);
b1b797cb 531
532quit:
b1b797cb 533 return 0;
ce5bf700 534}
535
6066889c 536/* Exit from normal mode to rescue mode. */
d558e6b5 537static grub_err_t
6066889c
VS
538grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
539 int argc __attribute__ ((unused)),
540 char *argv[] __attribute__ ((unused)))
d558e6b5 541{
6066889c
VS
542 if (nested_level <= grub_normal_exit_level)
543 return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
544 grub_normal_exit_level++;
545 return GRUB_ERR_NONE;
d558e6b5 546}
547
548static grub_err_t
6066889c 549grub_normal_reader_init (int nested)
d558e6b5 550{
f4c623e1 551 struct grub_term_output *term;
7f39d92f 552 const char *msg = _("Minimal BASH-like line editing is supported. For "
553 "the first word, TAB lists possible command completions. Anywhere "
554 "else TAB lists possible device or file completions. %s");
7f39d92f 555 const char *msg_esc = _("ESC at any time exits.");
8b442f3f 556 char *msg_formatted;
7f39d92f 557
61eb45ee 558 msg_formatted = grub_xasprintf (msg, nested ? msg_esc : "");
8b442f3f
VS
559 if (!msg_formatted)
560 return grub_errno;
7f39d92f 561
3be7f8de
VS
562 FOR_ACTIVE_TERM_OUTPUTS(term)
563 {
564 grub_normal_init_page (term);
565 grub_term_setcursor (term, 1);
566
567 grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
568 grub_puts ("\n");
569 }
7f39d92f 570 grub_free (msg_formatted);
a2c1332b 571
d558e6b5 572 return 0;
573}
574
d558e6b5 575static grub_err_t
6066889c 576grub_normal_read_line_real (char **line, int cont, int nested)
d558e6b5 577{
d56a6ac7 578 const char *prompt;
d558e6b5 579
6066889c 580 if (cont)
d56a6ac7 581 prompt = ">";
6066889c 582 else
d56a6ac7 583 prompt = "grub>";
d558e6b5 584
b09a4a8d
VS
585 if (!prompt)
586 return grub_errno;
d558e6b5 587
588 while (1)
589 {
2e713831
VS
590 *line = grub_cmdline_get (prompt);
591 if (*line)
d558e6b5 592 break;
593
6066889c 594 if (cont || nested)
d558e6b5 595 {
2e713831 596 grub_free (*line);
d558e6b5 597 *line = 0;
598 return grub_errno;
599 }
600 }
4a8a763c 601
d558e6b5 602 return 0;
603}
604
6066889c
VS
605static grub_err_t
606grub_normal_read_line (char **line, int cont)
607{
608 return grub_normal_read_line_real (line, cont, 0);
609}
610
f4c623e1
VS
611void
612grub_cmdline_run (int nested)
613{
614 grub_err_t err = GRUB_ERR_NONE;
615
616 err = grub_auth_check_authentication (NULL);
617
618 if (err)
619 {
620 grub_print_error ();
621 grub_errno = GRUB_ERR_NONE;
622 return;
623 }
624
6066889c 625 grub_normal_reader_init (nested);
f4c623e1
VS
626
627 while (1)
628 {
629 char *line;
630
6066889c
VS
631 if (grub_normal_exit_level)
632 break;
633
f4c623e1
VS
634 /* Print an error, if any. */
635 grub_print_error ();
636 grub_errno = GRUB_ERR_NONE;
637
6066889c 638 grub_normal_read_line_real (&line, 0, nested);
f4c623e1 639 if (! line)
2e713831 640 break;
f4c623e1 641
d56a6ac7 642 grub_normal_parse_line (line, grub_normal_read_line);
f4c623e1
VS
643 grub_free (line);
644 }
645}
d558e6b5 646
647static char *
648grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
649 const char *val)
650{
651 grub_set_more ((*val == '1'));
652 return grub_strdup (val);
653}
654
6d099807 655GRUB_MOD_INIT(normal)
ce5bf700 656{
2fbcbbc3
VS
657 grub_context_init ();
658
ce5bf700 659 /* Normal mode shouldn't be unloaded. */
6d099807 660 if (mod)
661 grub_dl_ref (mod);
ce5bf700 662
4b13b216 663 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
5aded270 664
c6f2fe52 665 grub_install_newline_hook ();
d558e6b5 666 grub_register_variable_hook ("pager", 0, grub_env_write_pager);
667
ce5bf700 668 /* Register a command "normal" for the rescue mode. */
2e713831 669 grub_register_command ("normal", grub_cmd_normal,
d8b5cd40 670 0, N_("Enter normal mode."));
6066889c 671 grub_register_command ("normal_exit", grub_cmd_normal_exit,
d8b5cd40 672 0, N_("Exit from normal mode."));
ce5bf700 673
0ece25b1 674 /* Reload terminal colors when these variables are written to. */
675 grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
676 grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
677
678 /* Preserve hooks after context changes. */
679 grub_env_export ("color_normal");
680 grub_env_export ("color_highlight");
ce5bf700 681}
682
6d099807 683GRUB_MOD_FINI(normal)
ce5bf700 684{
2fbcbbc3
VS
685 grub_context_fini ();
686
4b13b216 687 grub_set_history (0);
d558e6b5 688 grub_register_variable_hook ("pager", 0, 0);
689 grub_fs_autoload_hook = 0;
ce5bf700 690}