]>
Commit | Line | Data |
---|---|---|
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> |
ce5bf700 | 34 | |
4b13b216 | 35 | #define GRUB_DEFAULT_HISTORY_SIZE 50 |
5aded270 | 36 | |
6066889c VS |
37 | static int nested_level = 0; |
38 | int grub_normal_exit_level = 0; | |
39 | ||
ce5bf700 | 40 | /* Read a line from the file FILE. */ |
d05f0df3 | 41 | char * |
42 | grub_file_getline (grub_file_t file) | |
ce5bf700 | 43 | { |
44 | char c; | |
45 | int pos = 0; | |
46 | int literal = 0; | |
2cff3677 | 47 | char *cmdline; |
48 | int max_len = 64; | |
49 | ||
50 | /* Initially locate some space. */ | |
51 | cmdline = grub_malloc (max_len); | |
52 | if (! cmdline) | |
53 | return 0; | |
ce5bf700 | 54 | |
55 | while (1) | |
56 | { | |
4b13b216 | 57 | if (grub_file_read (file, &c, 1) != 1) |
ce5bf700 | 58 | break; |
59 | ||
60 | /* Skip all carriage returns. */ | |
61 | if (c == '\r') | |
62 | continue; | |
63 | ||
64 | /* Replace tabs with spaces. */ | |
65 | if (c == '\t') | |
66 | c = ' '; | |
67 | ||
68 | /* The previous is a backslash, then... */ | |
69 | if (literal) | |
70 | { | |
71 | /* If it is a newline, replace it with a space and continue. */ | |
72 | if (c == '\n') | |
73 | { | |
74 | c = ' '; | |
75 | ||
76 | /* Go back to overwrite the backslash. */ | |
77 | if (pos > 0) | |
78 | pos--; | |
79 | } | |
80 | ||
81 | literal = 0; | |
82 | } | |
83 | ||
84 | if (c == '\\') | |
85 | literal = 1; | |
86 | ||
d558e6b5 | 87 | if (pos == 0) |
ce5bf700 | 88 | { |
d558e6b5 | 89 | if (! grub_isspace (c)) |
ce5bf700 | 90 | cmdline[pos++] = c; |
91 | } | |
92 | else | |
93 | { | |
2cff3677 | 94 | if (pos >= max_len) |
95 | { | |
96 | char *old_cmdline = cmdline; | |
97 | max_len = max_len * 2; | |
98 | cmdline = grub_realloc (cmdline, max_len); | |
99 | if (! cmdline) | |
100 | { | |
101 | grub_free (old_cmdline); | |
102 | return 0; | |
103 | } | |
104 | } | |
105 | ||
e93e4679 | 106 | if (c == '\n') |
107 | break; | |
108 | ||
2cff3677 | 109 | cmdline[pos++] = c; |
ce5bf700 | 110 | } |
111 | } | |
112 | ||
113 | cmdline[pos] = '\0'; | |
2cff3677 | 114 | |
115 | /* If the buffer is empty, don't return anything at all. */ | |
116 | if (pos == 0) | |
117 | { | |
118 | grub_free (cmdline); | |
119 | cmdline = 0; | |
120 | } | |
b39f9d20 | 121 | |
2cff3677 | 122 | return cmdline; |
ce5bf700 | 123 | } |
124 | ||
125 | static void | |
4b13b216 | 126 | free_menu (grub_menu_t menu) |
ce5bf700 | 127 | { |
4b13b216 | 128 | grub_menu_entry_t entry = menu->entry_list; |
a8aa5762 | 129 | |
ce5bf700 | 130 | while (entry) |
131 | { | |
4b13b216 | 132 | grub_menu_entry_t next_entry = entry->next; |
ce5bf700 | 133 | |
4b13b216 | 134 | grub_free ((void *) entry->title); |
77c4a393 | 135 | grub_free ((void *) entry->sourcecode); |
ce5bf700 | 136 | entry = next_entry; |
137 | } | |
138 | ||
4b13b216 | 139 | grub_free (menu); |
2fbcbbc3 | 140 | grub_env_unset_menu (); |
ce5bf700 | 141 | } |
142 | ||
6fa42fa6 | 143 | static void |
144 | free_menu_entry_classes (struct grub_menu_entry_class *head) | |
145 | { | |
146 | /* Free all the classes. */ | |
147 | while (head) | |
148 | { | |
149 | struct grub_menu_entry_class *next; | |
150 | ||
151 | grub_free (head->name); | |
152 | next = head->next; | |
153 | grub_free (head); | |
154 | head = next; | |
155 | } | |
156 | } | |
157 | ||
16c8e9fd VS |
158 | static struct |
159 | { | |
160 | char *name; | |
161 | int key; | |
162 | } hotkey_aliases[] = | |
163 | { | |
164 | {"backspace", '\b'}, | |
165 | {"tab", '\t'}, | |
166 | {"delete", GRUB_TERM_DC} | |
167 | }; | |
168 | ||
230c0ad6 | 169 | /* Add a menu entry to the current menu context (as given by the environment |
170 | variable data slot `menu'). As the configuration file is read, the script | |
171 | parser calls this when a menu entry is to be created. */ | |
77c4a393 | 172 | grub_err_t |
230c0ad6 | 173 | grub_normal_add_menu_entry (int argc, const char **args, |
174 | const char *sourcecode) | |
77c4a393 | 175 | { |
6fa42fa6 | 176 | const char *menutitle = 0; |
a8aa5762 | 177 | const char *menusourcecode; |
178 | grub_menu_t menu; | |
179 | grub_menu_entry_t *last; | |
6fa42fa6 | 180 | int failed = 0; |
181 | int i; | |
182 | struct grub_menu_entry_class *classes_head; /* Dummy head node for list. */ | |
183 | struct grub_menu_entry_class *classes_tail; | |
e7e1f93f | 184 | char *users = NULL; |
16c8e9fd | 185 | int hotkey = 0; |
6fa42fa6 | 186 | |
187 | /* Allocate dummy head node for class list. */ | |
eab58da2 | 188 | classes_head = grub_zalloc (sizeof (struct grub_menu_entry_class)); |
6fa42fa6 | 189 | if (! classes_head) |
190 | return grub_errno; | |
6fa42fa6 | 191 | classes_tail = classes_head; |
a8aa5762 | 192 | |
2fbcbbc3 | 193 | menu = grub_env_get_menu (); |
a8aa5762 | 194 | if (! menu) |
195 | return grub_error (GRUB_ERR_MENU, "no menu context"); | |
196 | ||
197 | last = &menu->entry_list; | |
198 | ||
199 | menusourcecode = grub_strdup (sourcecode); | |
200 | if (! menusourcecode) | |
201 | return grub_errno; | |
77c4a393 | 202 | |
6fa42fa6 | 203 | /* Parse menu arguments. */ |
204 | for (i = 0; i < argc; i++) | |
205 | { | |
206 | /* Capture arguments. */ | |
207 | if (grub_strncmp ("--", args[i], 2) == 0) | |
208 | { | |
209 | const char *arg = &args[i][2]; | |
210 | ||
211 | /* Handle menu class. */ | |
212 | if (grub_strcmp(arg, "class") == 0) | |
213 | { | |
214 | char *class_name; | |
215 | struct grub_menu_entry_class *new_class; | |
216 | ||
217 | i++; | |
218 | class_name = grub_strdup (args[i]); | |
219 | if (! class_name) | |
220 | { | |
221 | failed = 1; | |
222 | break; | |
223 | } | |
224 | ||
225 | /* Create a new class and add it at the tail of the list. */ | |
eab58da2 | 226 | new_class = grub_zalloc (sizeof (struct grub_menu_entry_class)); |
6fa42fa6 | 227 | if (! new_class) |
228 | { | |
229 | grub_free (class_name); | |
230 | failed = 1; | |
231 | break; | |
232 | } | |
233 | /* Fill in the new class node. */ | |
234 | new_class->name = class_name; | |
6fa42fa6 | 235 | /* Link the tail to it, and make it the new tail. */ |
236 | classes_tail->next = new_class; | |
237 | classes_tail = new_class; | |
238 | continue; | |
239 | } | |
e7e1f93f | 240 | else if (grub_strcmp(arg, "users") == 0) |
241 | { | |
242 | i++; | |
243 | users = grub_strdup (args[i]); | |
244 | if (! users) | |
245 | { | |
246 | failed = 1; | |
247 | break; | |
248 | } | |
249 | ||
250 | continue; | |
251 | } | |
16c8e9fd VS |
252 | else if (grub_strcmp(arg, "hotkey") == 0) |
253 | { | |
254 | unsigned j; | |
255 | ||
256 | i++; | |
257 | if (args[i][1] == 0) | |
258 | { | |
259 | hotkey = args[i][0]; | |
260 | continue; | |
261 | } | |
262 | ||
263 | for (j = 0; j < ARRAY_SIZE (hotkey_aliases); j++) | |
264 | if (grub_strcmp (args[i], hotkey_aliases[j].name) == 0) | |
265 | { | |
266 | hotkey = hotkey_aliases[j].key; | |
267 | break; | |
268 | } | |
269 | ||
270 | if (j < ARRAY_SIZE (hotkey_aliases)) | |
271 | continue; | |
272 | ||
273 | failed = 1; | |
274 | grub_error (GRUB_ERR_MENU, | |
275 | "Invalid hotkey: '%s'.", args[i]); | |
276 | break; | |
277 | } | |
6fa42fa6 | 278 | else |
279 | { | |
280 | /* Handle invalid argument. */ | |
281 | failed = 1; | |
f04f02e4 | 282 | grub_error (GRUB_ERR_MENU, |
d558e6b5 | 283 | "invalid argument for menuentry: %s", args[i]); |
6fa42fa6 | 284 | break; |
285 | } | |
286 | } | |
287 | ||
288 | /* Capture title. */ | |
289 | if (! menutitle) | |
290 | { | |
291 | menutitle = grub_strdup (args[i]); | |
292 | } | |
293 | else | |
294 | { | |
295 | failed = 1; | |
f04f02e4 | 296 | grub_error (GRUB_ERR_MENU, |
d558e6b5 | 297 | "too many titles for menuentry: %s", args[i]); |
6fa42fa6 | 298 | break; |
299 | } | |
300 | } | |
301 | ||
302 | /* Validate arguments. */ | |
303 | if ((! failed) && (! menutitle)) | |
a8aa5762 | 304 | { |
6fa42fa6 | 305 | grub_error (GRUB_ERR_MENU, "menuentry is missing title"); |
306 | failed = 1; | |
307 | } | |
308 | ||
309 | /* If argument parsing failed, free any allocated resources. */ | |
310 | if (failed) | |
311 | { | |
312 | free_menu_entry_classes (classes_head); | |
313 | grub_free ((void *) menutitle); | |
a8aa5762 | 314 | grub_free ((void *) menusourcecode); |
6fa42fa6 | 315 | |
316 | /* Here we assume that grub_error has been used to specify failure details. */ | |
a8aa5762 | 317 | return grub_errno; |
318 | } | |
77c4a393 | 319 | |
320 | /* Add the menu entry at the end of the list. */ | |
321 | while (*last) | |
322 | last = &(*last)->next; | |
323 | ||
eab58da2 | 324 | *last = grub_zalloc (sizeof (**last)); |
77c4a393 | 325 | if (! *last) |
326 | { | |
6fa42fa6 | 327 | free_menu_entry_classes (classes_head); |
77c4a393 | 328 | grub_free ((void *) menutitle); |
a8aa5762 | 329 | grub_free ((void *) menusourcecode); |
77c4a393 | 330 | return grub_errno; |
331 | } | |
332 | ||
77c4a393 | 333 | (*last)->title = menutitle; |
16c8e9fd | 334 | (*last)->hotkey = hotkey; |
6fa42fa6 | 335 | (*last)->classes = classes_head; |
e7e1f93f | 336 | if (users) |
337 | (*last)->restricted = 1; | |
338 | (*last)->users = users; | |
a8aa5762 | 339 | (*last)->sourcecode = menusourcecode; |
77c4a393 | 340 | |
a8aa5762 | 341 | menu->size++; |
65f201ad | 342 | |
77c4a393 | 343 | return GRUB_ERR_NONE; |
344 | } | |
345 | ||
4b13b216 | 346 | static grub_menu_t |
d558e6b5 | 347 | read_config_file (const char *config) |
ce5bf700 | 348 | { |
4b13b216 | 349 | grub_file_t file; |
d558e6b5 | 350 | grub_parser_t old_parser = 0; |
b39f9d20 | 351 | |
d558e6b5 | 352 | auto grub_err_t getline (char **line, int cont); |
353 | grub_err_t getline (char **line, int cont __attribute__ ((unused))) | |
77c4a393 | 354 | { |
d558e6b5 | 355 | while (1) |
356 | { | |
357 | char *buf; | |
358 | ||
359 | *line = buf = grub_file_getline (file); | |
360 | if (! buf) | |
361 | return grub_errno; | |
362 | ||
363 | if (buf[0] == '#') | |
364 | { | |
365 | if (buf[1] == '!') | |
366 | { | |
367 | grub_parser_t parser; | |
368 | grub_named_list_t list; | |
369 | ||
370 | buf += 2; | |
371 | while (grub_isspace (*buf)) | |
372 | buf++; | |
77c4a393 | 373 | |
d558e6b5 | 374 | if (! old_parser) |
375 | old_parser = grub_parser_get_current (); | |
376 | ||
377 | list = GRUB_AS_NAMED_LIST (grub_parser_class.handler_list); | |
378 | parser = grub_named_list_find (list, buf); | |
379 | if (parser) | |
380 | grub_parser_set_current (parser); | |
381 | else | |
382 | { | |
383 | char cmd_name[8 + grub_strlen (buf)]; | |
384 | ||
385 | /* Perhaps it's not loaded yet, try the autoload | |
386 | command. */ | |
387 | grub_strcpy (cmd_name, "parser."); | |
388 | grub_strcat (cmd_name, buf); | |
389 | grub_command_execute (cmd_name, 0, 0); | |
390 | } | |
391 | } | |
392 | grub_free (*line); | |
393 | } | |
394 | else | |
395 | break; | |
396 | } | |
77c4a393 | 397 | |
398 | return GRUB_ERR_NONE; | |
399 | } | |
400 | ||
77c4a393 | 401 | grub_menu_t newmenu; |
402 | ||
2fbcbbc3 | 403 | newmenu = grub_env_get_menu (); |
d558e6b5 | 404 | if (! newmenu) |
a8aa5762 | 405 | { |
eab58da2 | 406 | newmenu = grub_zalloc (sizeof (*newmenu)); |
a8aa5762 | 407 | if (! newmenu) |
408 | return 0; | |
d558e6b5 | 409 | |
2fbcbbc3 | 410 | grub_env_set_menu (newmenu); |
a8aa5762 | 411 | } |
77c4a393 | 412 | |
ce5bf700 | 413 | /* Try to open the config file. */ |
4b13b216 | 414 | file = grub_file_open (config); |
ce5bf700 | 415 | if (! file) |
416 | return 0; | |
417 | ||
f4c623e1 VS |
418 | while (1) |
419 | { | |
420 | char *line; | |
421 | ||
422 | /* Print an error, if any. */ | |
423 | grub_print_error (); | |
424 | grub_errno = GRUB_ERR_NONE; | |
425 | ||
426 | if ((getline (&line, 0)) || (! line)) | |
427 | break; | |
428 | ||
429 | grub_parser_get_current ()->parse_line (line, getline); | |
430 | grub_free (line); | |
431 | } | |
432 | ||
77c4a393 | 433 | grub_file_close (file); |
6de2ee99 | 434 | |
d558e6b5 | 435 | if (old_parser) |
436 | grub_parser_set_current (old_parser); | |
6de2ee99 | 437 | |
65f201ad | 438 | return newmenu; |
ce5bf700 | 439 | } |
440 | ||
ce5bf700 | 441 | /* Initialize the screen. */ |
442 | void | |
f4c623e1 | 443 | grub_normal_init_page (struct grub_term_output *term) |
ce5bf700 | 444 | { |
b99518d1 | 445 | int msg_len; |
446 | int posx; | |
447 | const char *msg = _("GNU GRUB version %s"); | |
8b442f3f | 448 | char *msg_formatted; |
b99518d1 | 449 | grub_uint32_t *unicode_msg; |
450 | grub_uint32_t *last_position; | |
a2c1332b | 451 | |
fa533ebb | 452 | grub_term_cls (term); |
8b442f3f | 453 | |
61eb45ee | 454 | msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION); |
8b442f3f VS |
455 | if (!msg_formatted) |
456 | return; | |
457 | ||
a2c1332b | 458 | msg_len = grub_utf8_to_ucs4_alloc (msg_formatted, |
b99518d1 | 459 | &unicode_msg, &last_position); |
4a8a763c | 460 | grub_free (msg_formatted); |
a2c1332b | 461 | |
b99518d1 | 462 | if (msg_len < 0) |
463 | { | |
464 | return; | |
465 | } | |
b362c9e9 | 466 | |
2e713831 | 467 | posx = grub_getstringwidth (unicode_msg, last_position, term); |
f4c623e1 | 468 | posx = (grub_term_width (term) - posx) / 2; |
2e713831 | 469 | grub_term_gotoxy (term, posx, 1); |
b362c9e9 | 470 | |
f588f1c8 | 471 | grub_print_ucs4 (unicode_msg, last_position, 0, 0, term); |
701f1df9 VS |
472 | grub_putcode ('\n', term); |
473 | grub_putcode ('\n', term); | |
b99518d1 | 474 | grub_free (unicode_msg); |
ce5bf700 | 475 | } |
476 | ||
027de555 VS |
477 | static void |
478 | read_lists (const char *val) | |
479 | { | |
480 | read_command_list (val); | |
481 | read_fs_list (val); | |
482 | read_crypto_list (val); | |
483 | read_terminal_list (val); | |
484 | } | |
485 | ||
e880248e | 486 | static char * |
027de555 VS |
487 | read_lists_hook (struct grub_env_var *var __attribute__ ((unused)), |
488 | const char *val) | |
e880248e | 489 | { |
027de555 | 490 | read_lists (val); |
e880248e RM |
491 | return val ? grub_strdup (val) : NULL; |
492 | } | |
39c9d41d | 493 | |
4241d2b1 | 494 | /* Read the config file CONFIG and execute the menu interface or |
495 | the command line interface if BATCH is false. */ | |
ce5bf700 | 496 | void |
f04f02e4 | 497 | grub_normal_execute (const char *config, int nested, int batch) |
ce5bf700 | 498 | { |
4b13b216 | 499 | grub_menu_t menu = 0; |
027de555 | 500 | const char *prefix = grub_env_get ("prefix"); |
ce5bf700 | 501 | |
027de555 | 502 | read_lists (prefix); |
d05f0df3 | 503 | read_handler_list (); |
027de555 | 504 | grub_register_variable_hook ("prefix", NULL, read_lists_hook); |
13b33fba | 505 | grub_command_execute ("parser.grub", 0, 0); |
d558e6b5 | 506 | |
ce5bf700 | 507 | if (config) |
508 | { | |
d558e6b5 | 509 | menu = read_config_file (config); |
ce5bf700 | 510 | |
511 | /* Ignore any error. */ | |
4b13b216 | 512 | grub_errno = GRUB_ERR_NONE; |
ce5bf700 | 513 | } |
514 | ||
f04f02e4 | 515 | if (! batch) |
93f3a1d8 | 516 | { |
f04f02e4 | 517 | if (menu && menu->size) |
d558e6b5 | 518 | { |
2e713831 | 519 | grub_show_menu (menu, nested); |
d558e6b5 | 520 | if (nested) |
521 | free_menu (menu); | |
522 | } | |
93f3a1d8 | 523 | } |
ce5bf700 | 524 | } |
525 | ||
d558e6b5 | 526 | /* This starts the normal mode. */ |
527 | void | |
528 | grub_enter_normal_mode (const char *config) | |
b1b797cb | 529 | { |
6066889c | 530 | nested_level++; |
d558e6b5 | 531 | grub_normal_execute (config, 0, 0); |
6066889c VS |
532 | grub_cmdline_run (0); |
533 | nested_level--; | |
534 | if (grub_normal_exit_level) | |
535 | grub_normal_exit_level--; | |
b1b797cb | 536 | } |
537 | ||
ce5bf700 | 538 | /* Enter normal mode from rescue mode. */ |
b1b797cb | 539 | static grub_err_t |
2e713831 | 540 | grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), |
b1b797cb | 541 | int argc, char *argv[]) |
ce5bf700 | 542 | { |
543 | if (argc == 0) | |
544 | { | |
5f3607e0 | 545 | /* Guess the config filename. It is necessary to make CONFIG static, |
546 | so that it won't get broken by longjmp. */ | |
2e713831 | 547 | char *config; |
ce5bf700 | 548 | const char *prefix; |
b39f9d20 | 549 | |
4b13b216 | 550 | prefix = grub_env_get ("prefix"); |
ce5bf700 | 551 | if (prefix) |
552 | { | |
61eb45ee | 553 | config = grub_xasprintf ("%s/grub.cfg", prefix); |
ce5bf700 | 554 | if (! config) |
b1b797cb | 555 | goto quit; |
ce5bf700 | 556 | |
4b13b216 | 557 | grub_enter_normal_mode (config); |
558 | grub_free (config); | |
ce5bf700 | 559 | } |
560 | else | |
4b13b216 | 561 | grub_enter_normal_mode (0); |
ce5bf700 | 562 | } |
563 | else | |
4b13b216 | 564 | grub_enter_normal_mode (argv[0]); |
b1b797cb | 565 | |
566 | quit: | |
b1b797cb | 567 | return 0; |
ce5bf700 | 568 | } |
569 | ||
6066889c | 570 | /* Exit from normal mode to rescue mode. */ |
d558e6b5 | 571 | static grub_err_t |
6066889c VS |
572 | grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)), |
573 | int argc __attribute__ ((unused)), | |
574 | char *argv[] __attribute__ ((unused))) | |
d558e6b5 | 575 | { |
6066889c VS |
576 | if (nested_level <= grub_normal_exit_level) |
577 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment"); | |
578 | grub_normal_exit_level++; | |
579 | return GRUB_ERR_NONE; | |
d558e6b5 | 580 | } |
581 | ||
582 | static grub_err_t | |
6066889c | 583 | grub_normal_reader_init (int nested) |
d558e6b5 | 584 | { |
f4c623e1 | 585 | struct grub_term_output *term; |
7f39d92f | 586 | const char *msg = _("Minimal BASH-like line editing is supported. For " |
587 | "the first word, TAB lists possible command completions. Anywhere " | |
588 | "else TAB lists possible device or file completions. %s"); | |
7f39d92f | 589 | const char *msg_esc = _("ESC at any time exits."); |
8b442f3f | 590 | char *msg_formatted; |
7f39d92f | 591 | |
61eb45ee | 592 | msg_formatted = grub_xasprintf (msg, nested ? msg_esc : ""); |
8b442f3f VS |
593 | if (!msg_formatted) |
594 | return grub_errno; | |
7f39d92f | 595 | |
3be7f8de VS |
596 | FOR_ACTIVE_TERM_OUTPUTS(term) |
597 | { | |
598 | grub_normal_init_page (term); | |
599 | grub_term_setcursor (term, 1); | |
701f1df9 | 600 | |
3be7f8de | 601 | grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term); |
701f1df9 VS |
602 | grub_putcode ('\n', term); |
603 | grub_putcode ('\n', term); | |
3be7f8de | 604 | } |
7f39d92f | 605 | grub_free (msg_formatted); |
a2c1332b | 606 | |
d558e6b5 | 607 | return 0; |
608 | } | |
609 | ||
d558e6b5 | 610 | |
611 | static grub_err_t | |
6066889c | 612 | grub_normal_read_line_real (char **line, int cont, int nested) |
d558e6b5 | 613 | { |
614 | grub_parser_t parser = grub_parser_get_current (); | |
8b442f3f | 615 | char *prompt; |
d558e6b5 | 616 | |
6066889c | 617 | if (cont) |
61eb45ee | 618 | prompt = grub_xasprintf (">"); |
6066889c | 619 | else |
61eb45ee | 620 | prompt = grub_xasprintf ("%s>", parser->name); |
d558e6b5 | 621 | |
b09a4a8d VS |
622 | if (!prompt) |
623 | return grub_errno; | |
d558e6b5 | 624 | |
625 | while (1) | |
626 | { | |
2e713831 VS |
627 | *line = grub_cmdline_get (prompt); |
628 | if (*line) | |
d558e6b5 | 629 | break; |
630 | ||
6066889c | 631 | if (cont || nested) |
d558e6b5 | 632 | { |
2e713831 | 633 | grub_free (*line); |
4a8a763c | 634 | grub_free (prompt); |
d558e6b5 | 635 | *line = 0; |
636 | return grub_errno; | |
637 | } | |
638 | } | |
4a8a763c VS |
639 | |
640 | grub_free (prompt); | |
d558e6b5 | 641 | |
d558e6b5 | 642 | return 0; |
643 | } | |
644 | ||
6066889c VS |
645 | static grub_err_t |
646 | grub_normal_read_line (char **line, int cont) | |
647 | { | |
648 | return grub_normal_read_line_real (line, cont, 0); | |
649 | } | |
650 | ||
f4c623e1 VS |
651 | void |
652 | grub_cmdline_run (int nested) | |
653 | { | |
654 | grub_err_t err = GRUB_ERR_NONE; | |
655 | ||
656 | err = grub_auth_check_authentication (NULL); | |
657 | ||
658 | if (err) | |
659 | { | |
660 | grub_print_error (); | |
661 | grub_errno = GRUB_ERR_NONE; | |
662 | return; | |
663 | } | |
664 | ||
6066889c | 665 | grub_normal_reader_init (nested); |
f4c623e1 VS |
666 | |
667 | while (1) | |
668 | { | |
669 | char *line; | |
670 | ||
6066889c VS |
671 | if (grub_normal_exit_level) |
672 | break; | |
673 | ||
f4c623e1 VS |
674 | /* Print an error, if any. */ |
675 | grub_print_error (); | |
676 | grub_errno = GRUB_ERR_NONE; | |
677 | ||
6066889c | 678 | grub_normal_read_line_real (&line, 0, nested); |
f4c623e1 | 679 | if (! line) |
2e713831 | 680 | break; |
f4c623e1 VS |
681 | |
682 | grub_parser_get_current ()->parse_line (line, grub_normal_read_line); | |
683 | grub_free (line); | |
684 | } | |
685 | } | |
d558e6b5 | 686 | |
687 | static char * | |
688 | grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)), | |
689 | const char *val) | |
690 | { | |
691 | grub_set_more ((*val == '1')); | |
692 | return grub_strdup (val); | |
693 | } | |
694 | ||
0a239a82 VS |
695 | static void (*grub_xputs_saved) (const char *str); |
696 | ||
6d099807 | 697 | GRUB_MOD_INIT(normal) |
ce5bf700 | 698 | { |
2fbcbbc3 VS |
699 | grub_context_init (); |
700 | ||
0a239a82 VS |
701 | grub_xputs_saved = grub_xputs; |
702 | grub_xputs = grub_xputs_normal; | |
703 | ||
ce5bf700 | 704 | /* Normal mode shouldn't be unloaded. */ |
6d099807 | 705 | if (mod) |
706 | grub_dl_ref (mod); | |
ce5bf700 | 707 | |
4b13b216 | 708 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 709 | |
d558e6b5 | 710 | grub_register_variable_hook ("pager", 0, grub_env_write_pager); |
711 | ||
ce5bf700 | 712 | /* Register a command "normal" for the rescue mode. */ |
2e713831 | 713 | grub_register_command ("normal", grub_cmd_normal, |
d8b5cd40 | 714 | 0, N_("Enter normal mode.")); |
6066889c | 715 | grub_register_command ("normal_exit", grub_cmd_normal_exit, |
d8b5cd40 | 716 | 0, N_("Exit from normal mode.")); |
ce5bf700 | 717 | |
0ece25b1 | 718 | /* Reload terminal colors when these variables are written to. */ |
719 | grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal); | |
720 | grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight); | |
721 | ||
722 | /* Preserve hooks after context changes. */ | |
723 | grub_env_export ("color_normal"); | |
724 | grub_env_export ("color_highlight"); | |
ce5bf700 | 725 | } |
726 | ||
6d099807 | 727 | GRUB_MOD_FINI(normal) |
ce5bf700 | 728 | { |
2fbcbbc3 VS |
729 | grub_context_fini (); |
730 | ||
0a239a82 VS |
731 | grub_xputs = grub_xputs_saved; |
732 | ||
4b13b216 | 733 | grub_set_history (0); |
d558e6b5 | 734 | grub_register_variable_hook ("pager", 0, 0); |
735 | grub_fs_autoload_hook = 0; | |
d05f0df3 | 736 | free_handler_list (); |
ce5bf700 | 737 | } |