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