1 /* main.c - the normal mode main routine */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
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.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/kernel.h>
21 #include <grub/normal.h>
23 #include <grub/misc.h>
24 #include <grub/file.h>
26 #include <grub/term.h>
28 #include <grub/parser.h>
29 #include <grub/reader.h>
30 #include <grub/menu_viewer.h>
31 #include <grub/auth.h>
32 #include <grub/i18n.h>
33 #include <grub/charset.h>
34 #include <grub/script_sh.h>
36 #define GRUB_DEFAULT_HISTORY_SIZE 50
38 static int nested_level
= 0;
39 int grub_normal_exit_level
= 0;
41 /* Read a line from the file FILE. */
43 grub_file_getline (grub_file_t file
)
51 /* Initially locate some space. */
52 cmdline
= grub_malloc (max_len
);
58 if (grub_file_read (file
, &c
, 1) != 1)
61 /* Skip all carriage returns. */
65 /* Replace tabs with spaces. */
69 /* The previous is a backslash, then... */
72 /* If it is a newline, replace it with a space and continue. */
77 /* Go back to overwrite the backslash. */
90 if (! grub_isspace (c
))
97 char *old_cmdline
= cmdline
;
98 max_len
= max_len
* 2;
99 cmdline
= grub_realloc (cmdline
, max_len
);
102 grub_free (old_cmdline
);
116 /* If the buffer is empty, don't return anything at all. */
127 free_menu (grub_menu_t menu
)
129 grub_menu_entry_t entry
= menu
->entry_list
;
133 grub_menu_entry_t next_entry
= entry
->next
;
135 grub_free ((void *) entry
->title
);
136 grub_free ((void *) entry
->sourcecode
);
141 grub_env_unset_menu ();
145 free_menu_entry_classes (struct grub_menu_entry_class
*head
)
147 /* Free all the classes. */
150 struct grub_menu_entry_class
*next
;
152 grub_free (head
->name
);
167 {"delete", GRUB_TERM_DC
}
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. */
174 grub_normal_add_menu_entry (int argc
, const char **args
,
175 const char *sourcecode
)
177 const char *menutitle
= 0;
178 const char *menusourcecode
;
180 grub_menu_entry_t
*last
;
183 struct grub_menu_entry_class
*classes_head
; /* Dummy head node for list. */
184 struct grub_menu_entry_class
*classes_tail
;
188 /* Allocate dummy head node for class list. */
189 classes_head
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
192 classes_tail
= classes_head
;
194 menu
= grub_env_get_menu ();
196 return grub_error (GRUB_ERR_MENU
, "no menu context");
198 last
= &menu
->entry_list
;
200 menusourcecode
= grub_strdup (sourcecode
);
201 if (! menusourcecode
)
204 /* Parse menu arguments. */
205 for (i
= 0; i
< argc
; i
++)
207 /* Capture arguments. */
208 if (grub_strncmp ("--", args
[i
], 2) == 0 && i
+ 1 < argc
)
210 const char *arg
= &args
[i
][2];
212 /* Handle menu class. */
213 if (grub_strcmp(arg
, "class") == 0)
216 struct grub_menu_entry_class
*new_class
;
219 class_name
= grub_strdup (args
[i
]);
226 /* Create a new class and add it at the tail of the list. */
227 new_class
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
230 grub_free (class_name
);
234 /* Fill in the new class node. */
235 new_class
->name
= class_name
;
236 /* Link the tail to it, and make it the new tail. */
237 classes_tail
->next
= new_class
;
238 classes_tail
= new_class
;
241 else if (grub_strcmp(arg
, "users") == 0)
244 users
= grub_strdup (args
[i
]);
253 else if (grub_strcmp(arg
, "hotkey") == 0)
264 for (j
= 0; j
< ARRAY_SIZE (hotkey_aliases
); j
++)
265 if (grub_strcmp (args
[i
], hotkey_aliases
[j
].name
) == 0)
267 hotkey
= hotkey_aliases
[j
].key
;
271 if (j
< ARRAY_SIZE (hotkey_aliases
))
275 grub_error (GRUB_ERR_MENU
,
276 "Invalid hotkey: '%s'.", args
[i
]);
281 /* Handle invalid argument. */
283 grub_error (GRUB_ERR_MENU
,
284 "invalid argument for menuentry: %s", args
[i
]);
292 menutitle
= grub_strdup (args
[i
]);
297 grub_error (GRUB_ERR_MENU
,
298 "too many titles for menuentry: %s", args
[i
]);
303 /* Validate arguments. */
304 if ((! failed
) && (! menutitle
))
306 grub_error (GRUB_ERR_MENU
, "menuentry is missing title");
310 /* If argument parsing failed, free any allocated resources. */
313 free_menu_entry_classes (classes_head
);
314 grub_free ((void *) menutitle
);
315 grub_free ((void *) menusourcecode
);
317 /* Here we assume that grub_error has been used to specify failure details. */
321 /* Add the menu entry at the end of the list. */
323 last
= &(*last
)->next
;
325 *last
= grub_zalloc (sizeof (**last
));
328 free_menu_entry_classes (classes_head
);
329 grub_free ((void *) menutitle
);
330 grub_free ((void *) menusourcecode
);
334 (*last
)->title
= menutitle
;
335 (*last
)->hotkey
= hotkey
;
336 (*last
)->classes
= classes_head
;
338 (*last
)->restricted
= 1;
339 (*last
)->users
= users
;
340 (*last
)->sourcecode
= menusourcecode
;
344 return GRUB_ERR_NONE
;
348 read_config_file (const char *config
)
352 auto grub_err_t
getline (char **line
, int cont
);
353 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
359 *line
= buf
= grub_file_getline (file
);
369 return GRUB_ERR_NONE
;
374 newmenu
= grub_env_get_menu ();
377 newmenu
= grub_zalloc (sizeof (*newmenu
));
381 grub_env_set_menu (newmenu
);
384 /* Try to open the config file. */
385 file
= grub_file_open (config
);
393 /* Print an error, if any. */
395 grub_errno
= GRUB_ERR_NONE
;
397 if ((getline (&line
, 0)) || (! line
))
400 grub_normal_parse_line (line
, getline
);
404 grub_file_close (file
);
409 /* Initialize the screen. */
411 grub_normal_init_page (struct grub_term_output
*term
)
415 const char *msg
= _("GNU GRUB version %s");
417 grub_uint32_t
*unicode_msg
;
418 grub_uint32_t
*last_position
;
420 grub_term_cls (term
);
422 msg_formatted
= grub_xasprintf (msg
, PACKAGE_VERSION
);
426 msg_len
= grub_utf8_to_ucs4_alloc (msg_formatted
,
427 &unicode_msg
, &last_position
);
428 grub_free (msg_formatted
);
435 posx
= grub_getstringwidth (unicode_msg
, last_position
, term
);
436 posx
= (grub_term_width (term
) - posx
) / 2;
437 grub_term_gotoxy (term
, posx
, 1);
439 grub_print_ucs4 (unicode_msg
, last_position
, 0, 0, term
);
440 grub_putcode ('\n', term
);
441 grub_putcode ('\n', term
);
442 grub_free (unicode_msg
);
446 read_lists (const char *val
)
448 if (! grub_no_autoload
)
450 read_command_list (val
);
452 read_crypto_list (val
);
453 read_terminal_list (val
);
458 read_lists_hook (struct grub_env_var
*var
__attribute__ ((unused
)),
462 return val
? grub_strdup (val
) : NULL
;
465 /* Read the config file CONFIG and execute the menu interface or
466 the command line interface if BATCH is false. */
468 grub_normal_execute (const char *config
, int nested
, int batch
)
470 grub_menu_t menu
= 0;
475 prefix
= grub_env_get ("prefix");
477 grub_register_variable_hook ("prefix", NULL
, read_lists_hook
);
478 grub_command_execute ("parser.grub", 0, 0);
483 menu
= read_config_file (config
);
485 /* Ignore any error. */
486 grub_errno
= GRUB_ERR_NONE
;
491 if (menu
&& menu
->size
)
493 grub_show_menu (menu
, nested
);
500 /* This starts the normal mode. */
502 grub_enter_normal_mode (const char *config
)
505 grub_normal_execute (config
, 0, 0);
506 grub_cmdline_run (0);
508 if (grub_normal_exit_level
)
509 grub_normal_exit_level
--;
512 /* Enter normal mode from rescue mode. */
514 grub_cmd_normal (struct grub_command
*cmd
__attribute__ ((unused
)),
515 int argc
, char *argv
[])
519 /* Guess the config filename. It is necessary to make CONFIG static,
520 so that it won't get broken by longjmp. */
524 prefix
= grub_env_get ("prefix");
527 config
= grub_xasprintf ("%s/grub.cfg", prefix
);
531 grub_enter_normal_mode (config
);
535 grub_enter_normal_mode (0);
538 grub_enter_normal_mode (argv
[0]);
544 /* Exit from normal mode to rescue mode. */
546 grub_cmd_normal_exit (struct grub_command
*cmd
__attribute__ ((unused
)),
547 int argc
__attribute__ ((unused
)),
548 char *argv
[] __attribute__ ((unused
)))
550 if (nested_level
<= grub_normal_exit_level
)
551 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "not in normal environment");
552 grub_normal_exit_level
++;
553 return GRUB_ERR_NONE
;
557 grub_normal_reader_init (int nested
)
559 struct grub_term_output
*term
;
560 const char *msg
= _("Minimal BASH-like line editing is supported. For "
561 "the first word, TAB lists possible command completions. Anywhere "
562 "else TAB lists possible device or file completions. %s");
563 const char *msg_esc
= _("ESC at any time exits.");
566 msg_formatted
= grub_xasprintf (msg
, nested
? msg_esc
: "");
570 FOR_ACTIVE_TERM_OUTPUTS(term
)
572 grub_normal_init_page (term
);
573 grub_term_setcursor (term
, 1);
575 grub_print_message_indented (msg_formatted
, 3, STANDARD_MARGIN
, term
);
576 grub_putcode ('\n', term
);
577 grub_putcode ('\n', term
);
579 grub_free (msg_formatted
);
585 grub_normal_read_line_real (char **line
, int cont
, int nested
)
599 *line
= grub_cmdline_get (prompt
);
615 grub_normal_read_line (char **line
, int cont
)
617 return grub_normal_read_line_real (line
, cont
, 0);
621 grub_cmdline_run (int nested
)
623 grub_err_t err
= GRUB_ERR_NONE
;
625 err
= grub_auth_check_authentication (NULL
);
630 grub_errno
= GRUB_ERR_NONE
;
634 grub_normal_reader_init (nested
);
640 if (grub_normal_exit_level
)
643 /* Print an error, if any. */
645 grub_errno
= GRUB_ERR_NONE
;
647 grub_normal_read_line_real (&line
, 0, nested
);
651 grub_normal_parse_line (line
, grub_normal_read_line
);
657 grub_env_write_pager (struct grub_env_var
*var
__attribute__ ((unused
)),
660 grub_set_more ((*val
== '1'));
661 return grub_strdup (val
);
666 grub_mini_cmd_clear (struct grub_command
*cmd
__attribute__ ((unused
)),
667 int argc
__attribute__ ((unused
)),
668 char *argv
[] __attribute__ ((unused
)))
674 static grub_command_t cmd_clear
;
676 static void (*grub_xputs_saved
) (const char *str
);
678 GRUB_MOD_INIT(normal
)
680 grub_context_init ();
683 grub_xputs_saved
= grub_xputs
;
684 grub_xputs
= grub_xputs_normal
;
686 /* Normal mode shouldn't be unloaded. */
691 grub_register_command ("clear", grub_mini_cmd_clear
,
692 0, N_("Clear the screen."));
694 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE
);
696 grub_register_variable_hook ("pager", 0, grub_env_write_pager
);
698 /* Register a command "normal" for the rescue mode. */
699 grub_register_command ("normal", grub_cmd_normal
,
700 0, N_("Enter normal mode."));
701 grub_register_command ("normal_exit", grub_cmd_normal_exit
,
702 0, N_("Exit from normal mode."));
704 /* Reload terminal colors when these variables are written to. */
705 grub_register_variable_hook ("color_normal", NULL
, grub_env_write_color_normal
);
706 grub_register_variable_hook ("color_highlight", NULL
, grub_env_write_color_highlight
);
708 /* Preserve hooks after context changes. */
709 grub_env_export ("color_normal");
710 grub_env_export ("color_highlight");
712 /* Set default color names. */
713 grub_env_set ("color_normal", "white/black");
714 grub_env_set ("color_highlight", "black/white");
717 GRUB_MOD_FINI(normal
)
719 grub_context_fini ();
722 grub_xputs
= grub_xputs_saved
;
724 grub_set_history (0);
725 grub_register_variable_hook ("pager", 0, 0);
726 grub_fs_autoload_hook
= 0;
727 grub_unregister_command (cmd_clear
);