]> git.proxmox.com Git - grub2.git/blame - grub-core/normal/main.c
* grub-core/normal/cmdline.c (grub_cmdline_get): Fix off-by-one error
[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
e745cf0c
VS
36GRUB_MOD_LICENSE ("GPLv3+");
37
4b13b216 38#define GRUB_DEFAULT_HISTORY_SIZE 50
5aded270 39
6066889c
VS
40static int nested_level = 0;
41int grub_normal_exit_level = 0;
42
ce5bf700 43/* Read a line from the file FILE. */
d05f0df3 44char *
45grub_file_getline (grub_file_t file)
ce5bf700 46{
47 char c;
9fdb2d7b 48 grub_size_t pos = 0;
2cff3677 49 char *cmdline;
9fdb2d7b
VS
50 int have_newline = 0;
51 grub_size_t max_len = 64;
2cff3677 52
53 /* Initially locate some space. */
54 cmdline = grub_malloc (max_len);
55 if (! cmdline)
56 return 0;
ce5bf700 57
58 while (1)
59 {
4b13b216 60 if (grub_file_read (file, &c, 1) != 1)
ce5bf700 61 break;
62
63 /* Skip all carriage returns. */
64 if (c == '\r')
65 continue;
66
ce5bf700 67
684aef11 68 if (pos + 1 >= max_len)
ce5bf700 69 {
9fdb2d7b
VS
70 char *old_cmdline = cmdline;
71 max_len = max_len * 2;
72 cmdline = grub_realloc (cmdline, max_len);
73 if (! cmdline)
ce5bf700 74 {
9fdb2d7b
VS
75 grub_free (old_cmdline);
76 return 0;
ce5bf700 77 }
ce5bf700 78 }
79
9fdb2d7b 80 if (c == '\n')
ce5bf700 81 {
9fdb2d7b
VS
82 have_newline = 1;
83 break;
ce5bf700 84 }
2cff3677 85
9fdb2d7b 86 cmdline[pos++] = c;
ce5bf700 87 }
88
89 cmdline[pos] = '\0';
2cff3677 90
91 /* If the buffer is empty, don't return anything at all. */
9fdb2d7b 92 if (pos == 0 && !have_newline)
2cff3677 93 {
94 grub_free (cmdline);
95 cmdline = 0;
96 }
b39f9d20 97
2cff3677 98 return cmdline;
ce5bf700 99}
100
fc55cc4c
VS
101void
102grub_normal_free_menu (grub_menu_t menu)
ce5bf700 103{
4b13b216 104 grub_menu_entry_t entry = menu->entry_list;
a8aa5762 105
ce5bf700 106 while (entry)
107 {
4b13b216 108 grub_menu_entry_t next_entry = entry->next;
5ad1be82 109 grub_size_t i;
ce5bf700 110
5ad1be82
AV
111 if (entry->classes)
112 {
113 struct grub_menu_entry_class *class;
114 for (class = entry->classes; class; class = class->next)
115 grub_free (class->name);
116 grub_free (entry->classes);
117 }
118
119 if (entry->args)
120 {
121 for (i = 0; entry->args[i]; i++)
122 grub_free (entry->args[i]);
123 grub_free (entry->args);
124 }
125
126 grub_free ((void *) entry->id);
127 grub_free ((void *) entry->users);
4b13b216 128 grub_free ((void *) entry->title);
77c4a393 129 grub_free ((void *) entry->sourcecode);
03f7c8c3 130 grub_free (entry);
ce5bf700 131 entry = next_entry;
132 }
133
4b13b216 134 grub_free (menu);
2fbcbbc3 135 grub_env_unset_menu ();
ce5bf700 136}
137
09fd6d82
CW
138/* Helper for read_config_file. */
139static grub_err_t
140read_config_file_getline (char **line, int cont __attribute__ ((unused)),
141 void *data)
ce5bf700 142{
09fd6d82 143 grub_file_t file = data;
b39f9d20 144
09fd6d82 145 while (1)
77c4a393 146 {
09fd6d82 147 char *buf;
d558e6b5 148
09fd6d82
CW
149 *line = buf = grub_file_getline (file);
150 if (! buf)
151 return grub_errno;
d558e6b5 152
09fd6d82
CW
153 if (buf[0] == '#')
154 grub_free (*line);
155 else
156 break;
77c4a393 157 }
158
09fd6d82
CW
159 return GRUB_ERR_NONE;
160}
161
162static grub_menu_t
163read_config_file (const char *config)
164{
165 grub_file_t file;
14af86e4 166 char *old_file = 0, *old_dir = 0;
09fd6d82 167 char *config_dir, *ptr = 0;
14af86e4 168 const char *ctmp;
09fd6d82 169
77c4a393 170 grub_menu_t newmenu;
171
2fbcbbc3 172 newmenu = grub_env_get_menu ();
d558e6b5 173 if (! newmenu)
a8aa5762 174 {
eab58da2 175 newmenu = grub_zalloc (sizeof (*newmenu));
a8aa5762 176 if (! newmenu)
177 return 0;
d558e6b5 178
2fbcbbc3 179 grub_env_set_menu (newmenu);
a8aa5762 180 }
77c4a393 181
ce5bf700 182 /* Try to open the config file. */
4b13b216 183 file = grub_file_open (config);
ce5bf700 184 if (! file)
185 return 0;
186
14af86e4
VS
187 ctmp = grub_env_get ("config_file");
188 if (ctmp)
189 old_file = grub_strdup (ctmp);
190 ctmp = grub_env_get ("config_directory");
191 if (ctmp)
192 old_dir = grub_strdup (ctmp);
40e80b94
VS
193 grub_env_set ("config_file", config);
194 config_dir = grub_strdup (config);
195 if (config_dir)
196 ptr = grub_strrchr (config_dir, '/');
197 if (ptr)
198 *ptr = 0;
199 grub_env_set ("config_directory", config_dir);
03f7c8c3 200 grub_free (config_dir);
40e80b94
VS
201
202 grub_env_export ("config_file");
203 grub_env_export ("config_directory");
204
f4c623e1
VS
205 while (1)
206 {
207 char *line;
208
209 /* Print an error, if any. */
210 grub_print_error ();
211 grub_errno = GRUB_ERR_NONE;
212
09fd6d82 213 if ((read_config_file_getline (&line, 0, file)) || (! line))
f4c623e1
VS
214 break;
215
09fd6d82 216 grub_normal_parse_line (line, read_config_file_getline, file);
f4c623e1
VS
217 grub_free (line);
218 }
219
40e80b94
VS
220 if (old_file)
221 grub_env_set ("config_file", old_file);
222 else
223 grub_env_unset ("config_file");
224 if (old_dir)
225 grub_env_set ("config_directory", old_dir);
226 else
227 grub_env_unset ("config_directory");
14af86e4
VS
228 grub_free (old_file);
229 grub_free (old_dir);
40e80b94 230
77c4a393 231 grub_file_close (file);
6de2ee99 232
65f201ad 233 return newmenu;
ce5bf700 234}
235
ce5bf700 236/* Initialize the screen. */
237void
f4c623e1 238grub_normal_init_page (struct grub_term_output *term)
ce5bf700 239{
8b282ad2 240 grub_ssize_t msg_len;
b99518d1 241 int posx;
242 const char *msg = _("GNU GRUB version %s");
8b442f3f 243 char *msg_formatted;
b99518d1 244 grub_uint32_t *unicode_msg;
245 grub_uint32_t *last_position;
a2c1332b 246
fa533ebb 247 grub_term_cls (term);
8b442f3f 248
61eb45ee 249 msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION);
8b442f3f
VS
250 if (!msg_formatted)
251 return;
252
a2c1332b 253 msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
b99518d1 254 &unicode_msg, &last_position);
4a8a763c 255 grub_free (msg_formatted);
a2c1332b 256
b99518d1 257 if (msg_len < 0)
258 {
259 return;
260 }
b362c9e9 261
2e713831 262 posx = grub_getstringwidth (unicode_msg, last_position, term);
f4c623e1 263 posx = (grub_term_width (term) - posx) / 2;
2e713831 264 grub_term_gotoxy (term, posx, 1);
b362c9e9 265
f588f1c8 266 grub_print_ucs4 (unicode_msg, last_position, 0, 0, term);
701f1df9
VS
267 grub_putcode ('\n', term);
268 grub_putcode ('\n', term);
b99518d1 269 grub_free (unicode_msg);
ce5bf700 270}
271
027de555
VS
272static void
273read_lists (const char *val)
274{
a6393224 275 if (! grub_no_modules)
72539694
BC
276 {
277 read_command_list (val);
278 read_fs_list (val);
279 read_crypto_list (val);
280 read_terminal_list (val);
281 }
17f38c0f 282 grub_gettext_reread_prefix (val);
027de555
VS
283}
284
e880248e 285static char *
027de555
VS
286read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
287 const char *val)
e880248e 288{
027de555 289 read_lists (val);
e880248e
RM
290 return val ? grub_strdup (val) : NULL;
291}
39c9d41d 292
4241d2b1 293/* Read the config file CONFIG and execute the menu interface or
294 the command line interface if BATCH is false. */
ce5bf700 295void
f04f02e4 296grub_normal_execute (const char *config, int nested, int batch)
ce5bf700 297{
4b13b216 298 grub_menu_t menu = 0;
3fa06487 299 const char *prefix;
ce5bf700 300
3fa06487
CW
301 if (! nested)
302 {
303 prefix = grub_env_get ("prefix");
304 read_lists (prefix);
305 grub_register_variable_hook ("prefix", NULL, read_lists_hook);
3fa06487 306 }
d558e6b5 307
e744219b
VS
308 grub_boot_time ("Executing config file");
309
ce5bf700 310 if (config)
311 {
d558e6b5 312 menu = read_config_file (config);
ce5bf700 313
314 /* Ignore any error. */
4b13b216 315 grub_errno = GRUB_ERR_NONE;
ce5bf700 316 }
317
e744219b
VS
318 grub_boot_time ("Executed config file");
319
f04f02e4 320 if (! batch)
93f3a1d8 321 {
f04f02e4 322 if (menu && menu->size)
d558e6b5 323 {
e744219b
VS
324
325 grub_boot_time ("Entering menu");
dcb883b1 326 grub_show_menu (menu, nested, 0);
d558e6b5 327 if (nested)
fc55cc4c 328 grub_normal_free_menu (menu);
d558e6b5 329 }
93f3a1d8 330 }
ce5bf700 331}
332
d558e6b5 333/* This starts the normal mode. */
334void
335grub_enter_normal_mode (const char *config)
b1b797cb 336{
e744219b 337 grub_boot_time ("Entering normal mode");
6066889c 338 nested_level++;
d558e6b5 339 grub_normal_execute (config, 0, 0);
e744219b 340 grub_boot_time ("Entering shell");
6066889c
VS
341 grub_cmdline_run (0);
342 nested_level--;
343 if (grub_normal_exit_level)
344 grub_normal_exit_level--;
e744219b 345 grub_boot_time ("Exiting normal mode");
b1b797cb 346}
347
ce5bf700 348/* Enter normal mode from rescue mode. */
b1b797cb 349static grub_err_t
2e713831 350grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
b1b797cb 351 int argc, char *argv[])
ce5bf700 352{
353 if (argc == 0)
354 {
5f3607e0 355 /* Guess the config filename. It is necessary to make CONFIG static,
356 so that it won't get broken by longjmp. */
2e713831 357 char *config;
ce5bf700 358 const char *prefix;
b39f9d20 359
4b13b216 360 prefix = grub_env_get ("prefix");
ce5bf700 361 if (prefix)
362 {
61eb45ee 363 config = grub_xasprintf ("%s/grub.cfg", prefix);
ce5bf700 364 if (! config)
b1b797cb 365 goto quit;
ce5bf700 366
4b13b216 367 grub_enter_normal_mode (config);
368 grub_free (config);
ce5bf700 369 }
370 else
4b13b216 371 grub_enter_normal_mode (0);
ce5bf700 372 }
373 else
4b13b216 374 grub_enter_normal_mode (argv[0]);
b1b797cb 375
376quit:
b1b797cb 377 return 0;
ce5bf700 378}
379
6066889c 380/* Exit from normal mode to rescue mode. */
d558e6b5 381static grub_err_t
6066889c
VS
382grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
383 int argc __attribute__ ((unused)),
384 char *argv[] __attribute__ ((unused)))
d558e6b5 385{
6066889c
VS
386 if (nested_level <= grub_normal_exit_level)
387 return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
388 grub_normal_exit_level++;
389 return GRUB_ERR_NONE;
d558e6b5 390}
391
392static grub_err_t
6066889c 393grub_normal_reader_init (int nested)
d558e6b5 394{
f4c623e1 395 struct grub_term_output *term;
7f39d92f 396 const char *msg = _("Minimal BASH-like line editing is supported. For "
397 "the first word, TAB lists possible command completions. Anywhere "
398 "else TAB lists possible device or file completions. %s");
7f39d92f 399 const char *msg_esc = _("ESC at any time exits.");
8b442f3f 400 char *msg_formatted;
7f39d92f 401
61eb45ee 402 msg_formatted = grub_xasprintf (msg, nested ? msg_esc : "");
8b442f3f
VS
403 if (!msg_formatted)
404 return grub_errno;
7f39d92f 405
3be7f8de
VS
406 FOR_ACTIVE_TERM_OUTPUTS(term)
407 {
408 grub_normal_init_page (term);
409 grub_term_setcursor (term, 1);
701f1df9 410
3be7f8de 411 grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
701f1df9
VS
412 grub_putcode ('\n', term);
413 grub_putcode ('\n', term);
3be7f8de 414 }
7f39d92f 415 grub_free (msg_formatted);
a2c1332b 416
d558e6b5 417 return 0;
418}
419
d558e6b5 420static grub_err_t
6066889c 421grub_normal_read_line_real (char **line, int cont, int nested)
d558e6b5 422{
d56a6ac7 423 const char *prompt;
d558e6b5 424
6066889c 425 if (cont)
a9e9dc7c
VS
426 /* TRANSLATORS: it's command line prompt. */
427 prompt = _(">");
6066889c 428 else
a9e9dc7c
VS
429 /* TRANSLATORS: it's command line prompt. */
430 prompt = _("grub>");
d558e6b5 431
b09a4a8d
VS
432 if (!prompt)
433 return grub_errno;
d558e6b5 434
435 while (1)
436 {
2e713831
VS
437 *line = grub_cmdline_get (prompt);
438 if (*line)
a9e9dc7c 439 return 0;
d558e6b5 440
6066889c 441 if (cont || nested)
d558e6b5 442 {
2e713831 443 grub_free (*line);
d558e6b5 444 *line = 0;
445 return grub_errno;
446 }
447 }
a9e9dc7c 448
d558e6b5 449}
450
6066889c 451static grub_err_t
09fd6d82
CW
452grub_normal_read_line (char **line, int cont,
453 void *data __attribute__ ((unused)))
6066889c
VS
454{
455 return grub_normal_read_line_real (line, cont, 0);
456}
457
f4c623e1
VS
458void
459grub_cmdline_run (int nested)
460{
461 grub_err_t err = GRUB_ERR_NONE;
462
463 err = grub_auth_check_authentication (NULL);
464
465 if (err)
466 {
467 grub_print_error ();
468 grub_errno = GRUB_ERR_NONE;
469 return;
470 }
471
6066889c 472 grub_normal_reader_init (nested);
f4c623e1
VS
473
474 while (1)
475 {
476 char *line;
477
6066889c
VS
478 if (grub_normal_exit_level)
479 break;
480
f4c623e1
VS
481 /* Print an error, if any. */
482 grub_print_error ();
483 grub_errno = GRUB_ERR_NONE;
484
6066889c 485 grub_normal_read_line_real (&line, 0, nested);
f4c623e1 486 if (! line)
2e713831 487 break;
f4c623e1 488
09fd6d82 489 grub_normal_parse_line (line, grub_normal_read_line, NULL);
f4c623e1
VS
490 grub_free (line);
491 }
492}
d558e6b5 493
494static char *
495grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
496 const char *val)
497{
498 grub_set_more ((*val == '1'));
499 return grub_strdup (val);
500}
501
919e37b0
VS
502/* clear */
503static grub_err_t
504grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
505 int argc __attribute__ ((unused)),
506 char *argv[] __attribute__ ((unused)))
507{
508 grub_cls ();
509 return 0;
510}
511
512static grub_command_t cmd_clear;
513
0a239a82 514static void (*grub_xputs_saved) (const char *str);
5bfb6e71 515static const char *features[] = {
274416e8 516 "feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint",
d9bef9bc 517 "feature_default_font_path", "feature_all_video_module",
7cd0df84
VS
518 "feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
519 "feature_nativedisk_cmd"
5bfb6e71 520};
0a239a82 521
6d099807 522GRUB_MOD_INIT(normal)
ce5bf700 523{
5bfb6e71
VS
524 unsigned i;
525
e744219b
VS
526 grub_boot_time ("Preparing normal module");
527
fc2ef117
VS
528 /* Previously many modules depended on gzio. Be nice to user and load it. */
529 grub_dl_load ("gzio");
377c98cb 530 grub_errno = 0;
fc2ef117 531
df895792 532 grub_normal_auth_init ();
2fbcbbc3 533 grub_context_init ();
20ebf732 534 grub_script_init ();
9284756e 535 grub_menu_init ();
2fbcbbc3 536
0a239a82
VS
537 grub_xputs_saved = grub_xputs;
538 grub_xputs = grub_xputs_normal;
539
ce5bf700 540 /* Normal mode shouldn't be unloaded. */
6d099807 541 if (mod)
542 grub_dl_ref (mod);
ce5bf700 543
919e37b0
VS
544 cmd_clear =
545 grub_register_command ("clear", grub_mini_cmd_clear,
546 0, N_("Clear the screen."));
547
4b13b216 548 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
5aded270 549
d558e6b5 550 grub_register_variable_hook ("pager", 0, grub_env_write_pager);
537dc9be 551 grub_env_export ("pager");
d558e6b5 552
ce5bf700 553 /* Register a command "normal" for the rescue mode. */
2e713831 554 grub_register_command ("normal", grub_cmd_normal,
d8b5cd40 555 0, N_("Enter normal mode."));
6066889c 556 grub_register_command ("normal_exit", grub_cmd_normal_exit,
d8b5cd40 557 0, N_("Exit from normal mode."));
ce5bf700 558
0ece25b1 559 /* Reload terminal colors when these variables are written to. */
560 grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
561 grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
562
563 /* Preserve hooks after context changes. */
564 grub_env_export ("color_normal");
565 grub_env_export ("color_highlight");
681440aa
BC
566
567 /* Set default color names. */
568 grub_env_set ("color_normal", "white/black");
569 grub_env_set ("color_highlight", "black/white");
b38a4983 570
5bfb6e71
VS
571 for (i = 0; i < ARRAY_SIZE (features); i++)
572 {
573 grub_env_set (features[i], "y");
574 grub_env_export (features[i]);
575 }
92cd0f6e
VS
576 grub_env_set ("grub_cpu", GRUB_TARGET_CPU);
577 grub_env_export ("grub_cpu");
578 grub_env_set ("grub_platform", GRUB_PLATFORM);
579 grub_env_export ("grub_platform");
e744219b
VS
580
581 grub_boot_time ("Normal module prepared");
ce5bf700 582}
583
6d099807 584GRUB_MOD_FINI(normal)
ce5bf700 585{
2fbcbbc3 586 grub_context_fini ();
20ebf732 587 grub_script_fini ();
9284756e 588 grub_menu_fini ();
df895792 589 grub_normal_auth_fini ();
2fbcbbc3 590
0a239a82
VS
591 grub_xputs = grub_xputs_saved;
592
4b13b216 593 grub_set_history (0);
d558e6b5 594 grub_register_variable_hook ("pager", 0, 0);
595 grub_fs_autoload_hook = 0;
919e37b0 596 grub_unregister_command (cmd_clear);
ce5bf700 597}