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>
33 #define GRUB_DEFAULT_HISTORY_SIZE 50
35 /* Read a line from the file FILE. */
37 grub_file_getline (grub_file_t file
)
45 /* Initially locate some space. */
46 cmdline
= grub_malloc (max_len
);
52 if (grub_file_read (file
, &c
, 1) != 1)
55 /* Skip all carriage returns. */
59 /* Replace tabs with spaces. */
63 /* The previous is a backslash, then... */
66 /* If it is a newline, replace it with a space and continue. */
71 /* Go back to overwrite the backslash. */
84 if (! grub_isspace (c
))
91 char *old_cmdline
= cmdline
;
92 max_len
= max_len
* 2;
93 cmdline
= grub_realloc (cmdline
, max_len
);
96 grub_free (old_cmdline
);
110 /* If the buffer is empty, don't return anything at all. */
121 free_menu (grub_menu_t menu
)
123 grub_menu_entry_t entry
= menu
->entry_list
;
127 grub_menu_entry_t next_entry
= entry
->next
;
129 grub_free ((void *) entry
->title
);
130 grub_free ((void *) entry
->sourcecode
);
135 grub_env_unset_data_slot ("menu");
139 free_menu_entry_classes (struct grub_menu_entry_class
*head
)
141 /* Free all the classes. */
144 struct grub_menu_entry_class
*next
;
146 grub_free (head
->name
);
153 /* Add a menu entry to the current menu context (as given by the environment
154 variable data slot `menu'). As the configuration file is read, the script
155 parser calls this when a menu entry is to be created. */
157 grub_normal_add_menu_entry (int argc
, const char **args
,
158 const char *sourcecode
)
160 const char *menutitle
= 0;
161 const char *menusourcecode
;
163 grub_menu_entry_t
*last
;
166 struct grub_menu_entry_class
*classes_head
; /* Dummy head node for list. */
167 struct grub_menu_entry_class
*classes_tail
;
170 /* Allocate dummy head node for class list. */
171 classes_head
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
174 classes_tail
= classes_head
;
176 menu
= grub_env_get_data_slot ("menu");
178 return grub_error (GRUB_ERR_MENU
, "no menu context");
180 last
= &menu
->entry_list
;
182 menusourcecode
= grub_strdup (sourcecode
);
183 if (! menusourcecode
)
186 /* Parse menu arguments. */
187 for (i
= 0; i
< argc
; i
++)
189 /* Capture arguments. */
190 if (grub_strncmp ("--", args
[i
], 2) == 0)
192 const char *arg
= &args
[i
][2];
194 /* Handle menu class. */
195 if (grub_strcmp(arg
, "class") == 0)
198 struct grub_menu_entry_class
*new_class
;
201 class_name
= grub_strdup (args
[i
]);
208 /* Create a new class and add it at the tail of the list. */
209 new_class
= grub_zalloc (sizeof (struct grub_menu_entry_class
));
212 grub_free (class_name
);
216 /* Fill in the new class node. */
217 new_class
->name
= class_name
;
218 /* Link the tail to it, and make it the new tail. */
219 classes_tail
->next
= new_class
;
220 classes_tail
= new_class
;
223 else if (grub_strcmp(arg
, "users") == 0)
226 users
= grub_strdup (args
[i
]);
237 /* Handle invalid argument. */
239 grub_error (GRUB_ERR_MENU
,
240 "invalid argument for menuentry: %s", args
[i
]);
248 menutitle
= grub_strdup (args
[i
]);
253 grub_error (GRUB_ERR_MENU
,
254 "too many titles for menuentry: %s", args
[i
]);
259 /* Validate arguments. */
260 if ((! failed
) && (! menutitle
))
262 grub_error (GRUB_ERR_MENU
, "menuentry is missing title");
266 /* If argument parsing failed, free any allocated resources. */
269 free_menu_entry_classes (classes_head
);
270 grub_free ((void *) menutitle
);
271 grub_free ((void *) menusourcecode
);
273 /* Here we assume that grub_error has been used to specify failure details. */
277 /* Add the menu entry at the end of the list. */
279 last
= &(*last
)->next
;
281 *last
= grub_zalloc (sizeof (**last
));
284 free_menu_entry_classes (classes_head
);
285 grub_free ((void *) menutitle
);
286 grub_free ((void *) menusourcecode
);
290 (*last
)->title
= menutitle
;
291 (*last
)->classes
= classes_head
;
293 (*last
)->restricted
= 1;
294 (*last
)->users
= users
;
295 (*last
)->sourcecode
= menusourcecode
;
299 return GRUB_ERR_NONE
;
303 read_config_file (const char *config
)
306 grub_parser_t old_parser
= 0;
308 auto grub_err_t
getline (char **line
, int cont
);
309 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
315 *line
= buf
= grub_file_getline (file
);
323 grub_parser_t parser
;
324 grub_named_list_t list
;
327 while (grub_isspace (*buf
))
331 old_parser
= grub_parser_get_current ();
333 list
= GRUB_AS_NAMED_LIST (grub_parser_class
.handler_list
);
334 parser
= grub_named_list_find (list
, buf
);
336 grub_parser_set_current (parser
);
339 char cmd_name
[8 + grub_strlen (buf
)];
341 /* Perhaps it's not loaded yet, try the autoload
343 grub_strcpy (cmd_name
, "parser.");
344 grub_strcat (cmd_name
, buf
);
345 grub_command_execute (cmd_name
, 0, 0);
354 return GRUB_ERR_NONE
;
359 newmenu
= grub_env_get_data_slot ("menu");
362 newmenu
= grub_zalloc (sizeof (*newmenu
));
366 grub_env_set_data_slot ("menu", newmenu
);
369 /* Try to open the config file. */
370 file
= grub_file_open (config
);
374 grub_reader_loop (getline
);
375 grub_file_close (file
);
378 grub_parser_set_current (old_parser
);
383 /* Initialize the screen. */
385 grub_normal_init_page (void)
387 grub_uint8_t width
, margin
;
389 #define TITLE ("GNU GRUB version " PACKAGE_VERSION)
391 width
= grub_getwh () >> 8;
392 margin
= (width
- (sizeof(TITLE
) + 7)) / 2;
400 grub_printf ("%s\n\n", TITLE
);
405 static int reader_nested
;
407 /* Read the config file CONFIG and execute the menu interface or
408 the command line interface if BATCH is false. */
410 grub_normal_execute (const char *config
, int nested
, int batch
)
412 grub_menu_t menu
= 0;
414 read_command_list ();
416 read_handler_list ();
417 grub_command_execute ("parser.sh", 0, 0);
419 reader_nested
= nested
;
423 menu
= read_config_file (config
);
425 /* Ignore any error. */
426 grub_errno
= GRUB_ERR_NONE
;
431 if (menu
&& menu
->size
)
433 grub_menu_viewer_show_menu (menu
, nested
);
440 /* This starts the normal mode. */
442 grub_enter_normal_mode (const char *config
)
444 grub_normal_execute (config
, 0, 0);
447 /* Enter normal mode from rescue mode. */
449 grub_cmd_normal (struct grub_command
*cmd
,
450 int argc
, char *argv
[])
452 grub_unregister_command (cmd
);
456 /* Guess the config filename. It is necessary to make CONFIG static,
457 so that it won't get broken by longjmp. */
461 prefix
= grub_env_get ("prefix");
464 config
= grub_malloc (grub_strlen (prefix
) + sizeof ("/grub.cfg"));
468 grub_sprintf (config
, "%s/grub.cfg", prefix
);
469 grub_enter_normal_mode (config
);
473 grub_enter_normal_mode (0);
476 grub_enter_normal_mode (argv
[0]);
483 grub_cmdline_run (int nested
)
485 grub_reader_t reader
;
486 grub_err_t err
= GRUB_ERR_NONE
;
488 err
= grub_auth_check_authentication (NULL
);
493 grub_errno
= GRUB_ERR_NONE
;
497 reader
= grub_reader_get_current ();
499 reader_nested
= nested
;
502 grub_reader_loop (0);
506 grub_normal_reader_init (void)
508 grub_normal_init_page ();
512 [ Minimal BASH-like line editing is supported. For the first word, TAB\n\
513 lists possible command completions. Anywhere else TAB lists possible\n\
514 device/file completions.%s ]\n\n",
515 reader_nested
? " ESC at any time exits." : "");
520 static char cmdline
[GRUB_MAX_CMDLINE
];
523 grub_normal_read_line (char **line
, int cont
)
525 grub_parser_t parser
= grub_parser_get_current ();
526 char prompt
[8 + grub_strlen (parser
->name
)];
528 grub_sprintf (prompt
, "%s:%s> ", parser
->name
, (cont
) ? "" : "grub");
533 if (grub_cmdline_get (prompt
, cmdline
, sizeof (cmdline
), 0, 1, 1))
536 if ((reader_nested
) || (cont
))
543 *line
= grub_strdup (cmdline
);
547 static struct grub_reader grub_normal_reader
=
550 .init
= grub_normal_reader_init
,
551 .read_line
= grub_normal_read_line
555 grub_env_write_pager (struct grub_env_var
*var
__attribute__ ((unused
)),
558 grub_set_more ((*val
== '1'));
559 return grub_strdup (val
);
562 GRUB_MOD_INIT(normal
)
564 /* Normal mode shouldn't be unloaded. */
568 grub_menu_viewer_register (&grub_normal_text_menu_viewer
);
570 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE
);
572 grub_reader_register ("normal", &grub_normal_reader
);
573 grub_reader_set_current (&grub_normal_reader
);
574 grub_register_variable_hook ("pager", 0, grub_env_write_pager
);
576 /* Register a command "normal" for the rescue mode. */
577 grub_register_command_prio ("normal", grub_cmd_normal
,
578 0, "Enter normal mode", 0);
580 /* Reload terminal colors when these variables are written to. */
581 grub_register_variable_hook ("color_normal", NULL
, grub_env_write_color_normal
);
582 grub_register_variable_hook ("color_highlight", NULL
, grub_env_write_color_highlight
);
584 /* Preserve hooks after context changes. */
585 grub_env_export ("color_normal");
586 grub_env_export ("color_highlight");
589 GRUB_MOD_FINI(normal
)
591 grub_set_history (0);
592 grub_reader_unregister (&grub_normal_reader
);
593 grub_register_variable_hook ("pager", 0, 0);
594 grub_fs_autoload_hook
= 0;
595 free_handler_list ();