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>
34 #define GRUB_DEFAULT_HISTORY_SIZE 50
36 /* Read a line from the file FILE. */
38 grub_file_getline (grub_file_t file
)
46 /* Initially locate some space. */
47 cmdline
= grub_malloc (max_len
);
53 if (grub_file_read (file
, &c
, 1) != 1)
56 /* Skip all carriage returns. */
60 /* Replace tabs with spaces. */
64 /* The previous is a backslash, then... */
67 /* If it is a newline, replace it with a space and continue. */
72 /* Go back to overwrite the backslash. */
85 if (! grub_isspace (c
))
92 char *old_cmdline
= cmdline
;
93 max_len
= max_len
* 2;
94 cmdline
= grub_realloc (cmdline
, max_len
);
97 grub_free (old_cmdline
);
111 /* If the buffer is empty, don't return anything at all. */
122 free_menu (grub_menu_t menu
)
124 grub_menu_entry_t entry
= menu
->entry_list
;
128 grub_menu_entry_t next_entry
= entry
->next
;
130 grub_free ((void *) entry
->title
);
131 grub_free ((void *) entry
->sourcecode
);
136 grub_env_unset_data_slot ("menu");
140 free_menu_entry_classes (struct grub_menu_entry_class
*head
)
142 /* Free all the classes. */
145 struct grub_menu_entry_class
*next
;
147 grub_free (head
->name
);
154 /* Add a menu entry to the current menu context (as given by the environment
155 variable data slot `menu'). As the configuration file is read, the script
156 parser calls this when a menu entry is to be created. */
158 grub_normal_add_menu_entry (int argc
, const char **args
,
159 const char *sourcecode
)
161 const char *menutitle
= 0;
162 const char *menusourcecode
;
164 grub_menu_entry_t
*last
;
167 struct grub_menu_entry_class
*classes_head
; /* Dummy head node for list. */
168 struct grub_menu_entry_class
*classes_tail
;
171 /* Allocate dummy head node for class list. */
172 classes_head
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
175 classes_tail
= classes_head
;
177 menu
= grub_env_get_data_slot ("menu");
179 return grub_error (GRUB_ERR_MENU
, "no menu context");
181 last
= &menu
->entry_list
;
183 menusourcecode
= grub_strdup (sourcecode
);
184 if (! menusourcecode
)
187 /* Parse menu arguments. */
188 for (i
= 0; i
< argc
; i
++)
190 /* Capture arguments. */
191 if (grub_strncmp ("--", args
[i
], 2) == 0)
193 const char *arg
= &args
[i
][2];
195 /* Handle menu class. */
196 if (grub_strcmp(arg
, "class") == 0)
199 struct grub_menu_entry_class
*new_class
;
202 class_name
= grub_strdup (args
[i
]);
209 /* Create a new class and add it at the tail of the list. */
210 new_class
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
213 grub_free (class_name
);
217 /* Fill in the new class node. */
218 new_class
->name
= class_name
;
219 /* Link the tail to it, and make it the new tail. */
220 classes_tail
->next
= new_class
;
221 classes_tail
= new_class
;
224 else if (grub_strcmp(arg
, "users") == 0)
227 users
= grub_strdup (args
[i
]);
238 /* Handle invalid argument. */
240 grub_error (GRUB_ERR_MENU
,
241 "invalid argument for menuentry: %s", args
[i
]);
249 menutitle
= grub_strdup (args
[i
]);
254 grub_error (GRUB_ERR_MENU
,
255 "too many titles for menuentry: %s", args
[i
]);
260 /* Validate arguments. */
261 if ((! failed
) && (! menutitle
))
263 grub_error (GRUB_ERR_MENU
, "menuentry is missing title");
267 /* If argument parsing failed, free any allocated resources. */
270 free_menu_entry_classes (classes_head
);
271 grub_free ((void *) menutitle
);
272 grub_free ((void *) menusourcecode
);
274 /* Here we assume that grub_error has been used to specify failure details. */
278 /* Add the menu entry at the end of the list. */
280 last
= &(*last
)->next
;
282 *last
= grub_zalloc (sizeof (**last
));
285 free_menu_entry_classes (classes_head
);
286 grub_free ((void *) menutitle
);
287 grub_free ((void *) menusourcecode
);
291 (*last
)->title
= menutitle
;
292 (*last
)->classes
= classes_head
;
294 (*last
)->restricted
= 1;
295 (*last
)->users
= users
;
296 (*last
)->sourcecode
= menusourcecode
;
300 return GRUB_ERR_NONE
;
304 read_config_file (const char *config
)
307 grub_parser_t old_parser
= 0;
309 auto grub_err_t
getline (char **line
, int cont
);
310 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
316 *line
= buf
= grub_file_getline (file
);
324 grub_parser_t parser
;
325 grub_named_list_t list
;
328 while (grub_isspace (*buf
))
332 old_parser
= grub_parser_get_current ();
334 list
= GRUB_AS_NAMED_LIST (grub_parser_class
.handler_list
);
335 parser
= grub_named_list_find (list
, buf
);
337 grub_parser_set_current (parser
);
340 char cmd_name
[8 + grub_strlen (buf
)];
342 /* Perhaps it's not loaded yet, try the autoload
344 grub_strcpy (cmd_name
, "parser.");
345 grub_strcat (cmd_name
, buf
);
346 grub_command_execute (cmd_name
, 0, 0);
355 return GRUB_ERR_NONE
;
360 newmenu
= grub_env_get_data_slot ("menu");
363 newmenu
= grub_zalloc (sizeof (*newmenu
));
367 grub_env_set_data_slot ("menu", newmenu
);
370 /* Try to open the config file. */
371 file
= grub_file_open (config
);
375 grub_reader_loop (getline
);
376 grub_file_close (file
);
379 grub_parser_set_current (old_parser
);
384 /* Initialize the screen. */
386 grub_normal_init_page (void)
388 grub_uint8_t width
, margin
;
390 #define TITLE ("GNU GRUB version " PACKAGE_VERSION)
392 width
= grub_getwh () >> 8;
393 margin
= (width
- (sizeof(TITLE
) + 7)) / 2;
401 grub_printf ("%s\n\n", TITLE
);
406 static int reader_nested
;
408 /* Read the config file CONFIG and execute the menu interface or
409 the command line interface if BATCH is false. */
411 grub_normal_execute (const char *config
, int nested
, int batch
)
413 grub_menu_t menu
= 0;
415 read_command_list ();
417 read_handler_list ();
418 grub_command_execute ("parser.grub", 0, 0);
420 reader_nested
= nested
;
424 menu
= read_config_file (config
);
426 /* Ignore any error. */
427 grub_errno
= GRUB_ERR_NONE
;
432 if (menu
&& menu
->size
)
434 grub_menu_viewer_show_menu (menu
, nested
);
441 /* This starts the normal mode. */
443 grub_enter_normal_mode (const char *config
)
445 grub_normal_execute (config
, 0, 0);
448 /* Enter normal mode from rescue mode. */
450 grub_cmd_normal (struct grub_command
*cmd
,
451 int argc
, char *argv
[])
453 grub_unregister_command (cmd
);
457 /* Guess the config filename. It is necessary to make CONFIG static,
458 so that it won't get broken by longjmp. */
462 prefix
= grub_env_get ("prefix");
465 config
= grub_malloc (grub_strlen (prefix
) + sizeof ("/grub.cfg"));
469 grub_sprintf (config
, "%s/grub.cfg", prefix
);
470 grub_enter_normal_mode (config
);
474 grub_enter_normal_mode (0);
477 grub_enter_normal_mode (argv
[0]);
484 grub_cmdline_run (int nested
)
486 grub_reader_t reader
;
487 grub_err_t err
= GRUB_ERR_NONE
;
489 err
= grub_auth_check_authentication (NULL
);
494 grub_errno
= GRUB_ERR_NONE
;
498 reader
= grub_reader_get_current ();
500 reader_nested
= nested
;
503 grub_reader_loop (0);
507 grub_normal_reader_init (void)
509 grub_normal_init_page ();
513 [ Minimal BASH-like line editing is supported. For the first word, TAB\n\
514 lists possible command completions. Anywhere else TAB lists possible\n\
515 device/file completions.%s ]\n\n"),
516 reader_nested
? " ESC at any time exits." : "");
521 static char cmdline
[GRUB_MAX_CMDLINE
];
524 grub_normal_read_line (char **line
, int cont
)
526 grub_parser_t parser
= grub_parser_get_current ();
527 char prompt
[sizeof("> ") + grub_strlen (parser
->name
)];
529 grub_sprintf (prompt
, "%s> ", parser
->name
);
534 if (grub_cmdline_get (prompt
, cmdline
, sizeof (cmdline
), 0, 1, 1))
537 if ((reader_nested
) || (cont
))
544 *line
= grub_strdup (cmdline
);
548 static struct grub_reader grub_normal_reader
=
551 .init
= grub_normal_reader_init
,
552 .read_line
= grub_normal_read_line
556 grub_env_write_pager (struct grub_env_var
*var
__attribute__ ((unused
)),
559 grub_set_more ((*val
== '1'));
560 return grub_strdup (val
);
563 GRUB_MOD_INIT(normal
)
565 /* Normal mode shouldn't be unloaded. */
569 grub_menu_viewer_register (&grub_normal_text_menu_viewer
);
571 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE
);
573 grub_reader_register ("normal", &grub_normal_reader
);
574 grub_reader_set_current (&grub_normal_reader
);
575 grub_register_variable_hook ("pager", 0, grub_env_write_pager
);
577 /* Register a command "normal" for the rescue mode. */
578 grub_register_command_prio ("normal", grub_cmd_normal
,
579 0, "Enter normal mode", 0);
581 /* Reload terminal colors when these variables are written to. */
582 grub_register_variable_hook ("color_normal", NULL
, grub_env_write_color_normal
);
583 grub_register_variable_hook ("color_highlight", NULL
, grub_env_write_color_highlight
);
585 /* Preserve hooks after context changes. */
586 grub_env_export ("color_normal");
587 grub_env_export ("color_highlight");
590 GRUB_MOD_FINI(normal
)
592 grub_set_history (0);
593 grub_reader_unregister (&grub_normal_reader
);
594 grub_register_variable_hook ("pager", 0, 0);
595 grub_fs_autoload_hook
= 0;
596 free_handler_list ();