]>
Commit | Line | Data |
---|---|---|
ce5bf700 | 1 | /* main.c - the normal mode main routine */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
93f3a1d8 | 4 | * Copyright (C) 2000,2001,2002,2003,2005 Free Software Foundation, Inc. |
ce5bf700 | 5 | * |
6 | * This program 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 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program 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 this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
4b13b216 | 21 | #include <grub/kernel.h> |
22 | #include <grub/normal.h> | |
23 | #include <grub/dl.h> | |
24 | #include <grub/rescue.h> | |
25 | #include <grub/misc.h> | |
26 | #include <grub/file.h> | |
27 | #include <grub/mm.h> | |
28 | #include <grub/term.h> | |
29 | #include <grub/env.h> | |
ce5bf700 | 30 | |
4b13b216 | 31 | grub_jmp_buf grub_exit_env; |
ce5bf700 | 32 | |
39c9d41d | 33 | static grub_fs_module_list_t fs_module_list = 0; |
34 | ||
4b13b216 | 35 | #define GRUB_DEFAULT_HISTORY_SIZE 50 |
5aded270 | 36 | |
ce5bf700 | 37 | /* Read a line from the file FILE. */ |
38 | static int | |
4b13b216 | 39 | get_line (grub_file_t file, char cmdline[], int max_len) |
ce5bf700 | 40 | { |
41 | char c; | |
42 | int pos = 0; | |
43 | int literal = 0; | |
44 | int comment = 0; | |
45 | ||
46 | while (1) | |
47 | { | |
4b13b216 | 48 | if (grub_file_read (file, &c, 1) != 1) |
ce5bf700 | 49 | break; |
50 | ||
51 | /* Skip all carriage returns. */ | |
52 | if (c == '\r') | |
53 | continue; | |
54 | ||
55 | /* Replace tabs with spaces. */ | |
56 | if (c == '\t') | |
57 | c = ' '; | |
58 | ||
59 | /* The previous is a backslash, then... */ | |
60 | if (literal) | |
61 | { | |
62 | /* If it is a newline, replace it with a space and continue. */ | |
63 | if (c == '\n') | |
64 | { | |
65 | c = ' '; | |
66 | ||
67 | /* Go back to overwrite the backslash. */ | |
68 | if (pos > 0) | |
69 | pos--; | |
70 | } | |
71 | ||
72 | literal = 0; | |
73 | } | |
74 | ||
75 | if (c == '\\') | |
76 | literal = 1; | |
77 | ||
78 | if (comment) | |
79 | { | |
80 | if (c == '\n') | |
81 | comment = 0; | |
82 | } | |
83 | else if (pos == 0) | |
84 | { | |
85 | if (c == '#') | |
86 | comment = 1; | |
4b13b216 | 87 | else if (! grub_isspace (c)) |
ce5bf700 | 88 | cmdline[pos++] = c; |
89 | } | |
90 | else | |
91 | { | |
92 | if (c == '\n') | |
93 | break; | |
94 | ||
95 | if (pos < max_len) | |
96 | cmdline[pos++] = c; | |
97 | } | |
98 | } | |
99 | ||
100 | cmdline[pos] = '\0'; | |
101 | ||
102 | return pos; | |
103 | } | |
104 | ||
105 | static void | |
4b13b216 | 106 | free_menu (grub_menu_t menu) |
ce5bf700 | 107 | { |
4b13b216 | 108 | grub_menu_entry_t entry = menu->entry_list; |
ce5bf700 | 109 | |
110 | while (entry) | |
111 | { | |
4b13b216 | 112 | grub_menu_entry_t next_entry = entry->next; |
113 | grub_command_list_t cmd = entry->command_list; | |
ce5bf700 | 114 | |
115 | while (cmd) | |
116 | { | |
4b13b216 | 117 | grub_command_list_t next_cmd = cmd->next; |
ce5bf700 | 118 | |
4b13b216 | 119 | grub_free ((void *) cmd->command); |
ce5bf700 | 120 | cmd = next_cmd; |
121 | } | |
122 | ||
4b13b216 | 123 | grub_free ((void *) entry->title); |
ce5bf700 | 124 | entry = next_entry; |
125 | } | |
126 | ||
4b13b216 | 127 | grub_free (menu); |
ce5bf700 | 128 | } |
129 | ||
130 | /* Read the config file CONFIG and return a menu. If no entry is present, | |
131 | return NULL. */ | |
4b13b216 | 132 | static grub_menu_t |
ce5bf700 | 133 | read_config_file (const char *config) |
134 | { | |
4b13b216 | 135 | grub_file_t file; |
136 | static char cmdline[GRUB_MAX_CMDLINE]; | |
137 | grub_menu_t menu; | |
138 | grub_menu_entry_t *next_entry, cur_entry = 0; | |
139 | grub_command_list_t *next_cmd, cur_cmd; | |
ce5bf700 | 140 | |
141 | /* Try to open the config file. */ | |
4b13b216 | 142 | file = grub_file_open (config); |
ce5bf700 | 143 | if (! file) |
144 | return 0; | |
145 | ||
146 | /* Initialize the menu. */ | |
4b13b216 | 147 | menu = (grub_menu_t) grub_malloc (sizeof (*menu)); |
ce5bf700 | 148 | if (! menu) |
149 | { | |
4b13b216 | 150 | grub_file_close (file); |
ce5bf700 | 151 | return 0; |
152 | } | |
153 | menu->default_entry = 0; | |
154 | menu->fallback_entry = -1; | |
155 | menu->timeout = -1; | |
156 | menu->size = 0; | |
157 | menu->entry_list = 0; | |
158 | ||
93f3a1d8 | 159 | if (! grub_context_push_menu (menu)) |
160 | { | |
161 | grub_print_error (); | |
162 | grub_errno = GRUB_ERR_NONE; | |
163 | ||
164 | free_menu (menu); | |
165 | grub_file_close (file); | |
166 | ||
167 | /* Wait until the user pushes any key so that the user | |
168 | can see what happened. */ | |
169 | grub_printf ("\nPress any key to continue..."); | |
170 | (void) grub_getkey (); | |
171 | return 0; | |
172 | } | |
173 | ||
ce5bf700 | 174 | next_entry = &(menu->entry_list); |
175 | next_cmd = 0; | |
176 | ||
177 | /* Read each line. */ | |
178 | while (get_line (file, cmdline, sizeof (cmdline))) | |
179 | { | |
4b13b216 | 180 | grub_command_t cmd; |
ce5bf700 | 181 | |
4b13b216 | 182 | cmd = grub_command_find (cmdline); |
183 | grub_errno = GRUB_ERR_NONE; | |
ce5bf700 | 184 | if (! cmd) |
185 | { | |
4b13b216 | 186 | grub_printf ("Unknown command `%s' is ignored.\n", cmdline); |
ce5bf700 | 187 | continue; |
188 | } | |
189 | ||
4b13b216 | 190 | if (cmd->flags & GRUB_COMMAND_FLAG_TITLE) |
ce5bf700 | 191 | { |
192 | char *p; | |
193 | ||
4b13b216 | 194 | cur_entry = (grub_menu_entry_t) grub_malloc (sizeof (*cur_entry)); |
ce5bf700 | 195 | if (! cur_entry) |
196 | goto fail; | |
197 | ||
4b13b216 | 198 | p = grub_strchr (cmdline, ' '); |
ce5bf700 | 199 | if (p) |
4b13b216 | 200 | cur_entry->title = grub_strdup (p); |
ce5bf700 | 201 | else |
4b13b216 | 202 | cur_entry->title = grub_strdup (""); |
ce5bf700 | 203 | |
204 | if (! cur_entry->title) | |
205 | { | |
4b13b216 | 206 | grub_free (cur_entry); |
ce5bf700 | 207 | goto fail; |
208 | } | |
209 | ||
210 | cur_entry->num = 0; | |
211 | cur_entry->command_list = 0; | |
212 | cur_entry->next = 0; | |
213 | ||
214 | *next_entry = cur_entry; | |
215 | next_entry = &(cur_entry->next); | |
216 | ||
217 | next_cmd = &(cur_entry->command_list); | |
218 | ||
219 | menu->size++; | |
220 | } | |
221 | else if (! cur_entry) | |
222 | { | |
223 | /* Run the command if possible. */ | |
4b13b216 | 224 | if (cmd->flags & GRUB_COMMAND_FLAG_MENU) |
ce5bf700 | 225 | { |
4b13b216 | 226 | grub_command_execute (cmdline); |
93f3a1d8 | 227 | if (grub_errno != GRUB_ERR_NONE) |
228 | { | |
229 | grub_print_error (); | |
230 | grub_errno = GRUB_ERR_NONE; | |
231 | } | |
ce5bf700 | 232 | } |
233 | else | |
234 | { | |
4b13b216 | 235 | grub_printf ("Invalid command `%s' is ignored.\n", cmdline); |
ce5bf700 | 236 | continue; |
237 | } | |
238 | } | |
239 | else | |
240 | { | |
4b13b216 | 241 | cur_cmd = (grub_command_list_t) grub_malloc (sizeof (*cur_cmd)); |
ce5bf700 | 242 | if (! cur_cmd) |
243 | goto fail; | |
244 | ||
4b13b216 | 245 | cur_cmd->command = grub_strdup (cmdline); |
ce5bf700 | 246 | if (! cur_cmd->command) |
247 | { | |
4b13b216 | 248 | grub_free (cur_cmd); |
ce5bf700 | 249 | goto fail; |
250 | } | |
251 | ||
252 | cur_cmd->next = 0; | |
253 | ||
254 | *next_cmd = cur_cmd; | |
255 | next_cmd = &(cur_cmd->next); | |
256 | ||
257 | cur_entry->num++; | |
258 | } | |
259 | } | |
260 | ||
261 | fail: | |
262 | ||
4b13b216 | 263 | grub_file_close (file); |
ce5bf700 | 264 | |
265 | /* If no entry was found or any error occurred, return NULL. */ | |
4b13b216 | 266 | if (menu->size == 0 || grub_errno != GRUB_ERR_NONE) |
ce5bf700 | 267 | { |
93f3a1d8 | 268 | grub_context_pop_menu (); |
ce5bf700 | 269 | free_menu (menu); |
270 | return 0; | |
271 | } | |
272 | ||
273 | /* Check values of the default entry and the fallback one. */ | |
274 | if (menu->fallback_entry >= menu->size) | |
275 | menu->fallback_entry = -1; | |
276 | ||
277 | if (menu->default_entry < 0 || menu->default_entry >= menu->size) | |
278 | { | |
279 | if (menu->fallback_entry < 0) | |
280 | menu->default_entry = 0; | |
281 | else | |
282 | { | |
283 | menu->default_entry = menu->fallback_entry; | |
284 | menu->fallback_entry = -1; | |
285 | } | |
286 | } | |
287 | ||
288 | return menu; | |
289 | } | |
290 | ||
291 | /* This starts the normal mode. */ | |
292 | void | |
4b13b216 | 293 | grub_enter_normal_mode (const char *config) |
ce5bf700 | 294 | { |
4b13b216 | 295 | if (grub_setjmp (grub_exit_env) == 0) |
296 | grub_normal_execute (config, 0); | |
ce5bf700 | 297 | } |
298 | ||
299 | /* Initialize the screen. */ | |
300 | void | |
4b13b216 | 301 | grub_normal_init_page (void) |
ce5bf700 | 302 | { |
4b13b216 | 303 | grub_cls (); |
304 | grub_printf ("\n\ | |
97543f08 | 305 | GNU GRUB version %s\n\n", |
ce5bf700 | 306 | PACKAGE_VERSION); |
307 | } | |
308 | ||
5822ff87 | 309 | /* Read the file command.lst for auto-loading. */ |
310 | static void | |
311 | read_command_list (void) | |
312 | { | |
313 | const char *prefix; | |
314 | ||
315 | prefix = grub_env_get ("prefix"); | |
316 | if (prefix) | |
317 | { | |
318 | char *filename; | |
319 | ||
320 | filename = grub_malloc (grub_strlen (prefix) + sizeof ("/command.lst")); | |
321 | if (filename) | |
322 | { | |
323 | grub_file_t file; | |
324 | ||
325 | grub_sprintf (filename, "%s/command.lst", prefix); | |
326 | file = grub_file_open (filename); | |
327 | if (file) | |
328 | { | |
329 | char buf[80]; /* XXX arbitrary */ | |
330 | ||
331 | while (get_line (file, buf, sizeof (buf))) | |
332 | { | |
333 | char *p; | |
334 | grub_command_t cmd; | |
335 | ||
336 | if (! grub_isgraph (buf[0])) | |
337 | continue; | |
338 | ||
339 | p = grub_strchr (buf, ':'); | |
340 | if (! p) | |
341 | continue; | |
342 | ||
343 | *p = '\0'; | |
344 | while (*++p == ' ') | |
345 | ; | |
346 | ||
347 | if (! grub_isgraph (*p)) | |
348 | continue; | |
349 | ||
350 | cmd = grub_register_command (buf, 0, | |
351 | GRUB_COMMAND_FLAG_NOT_LOADED, | |
352 | 0, 0, 0); | |
353 | if (! cmd) | |
354 | continue; | |
355 | ||
356 | cmd->module_name = grub_strdup (p); | |
357 | if (! cmd->module_name) | |
358 | grub_unregister_command (buf); | |
359 | } | |
360 | ||
361 | grub_file_close (file); | |
362 | } | |
363 | ||
364 | grub_free (filename); | |
365 | } | |
366 | } | |
367 | ||
368 | /* Ignore errors. */ | |
369 | grub_errno = GRUB_ERR_NONE; | |
370 | } | |
371 | ||
39c9d41d | 372 | /* The auto-loading hook for filesystems. */ |
373 | static int | |
374 | autoload_fs_module (void) | |
375 | { | |
376 | grub_fs_module_list_t p; | |
377 | ||
378 | while ((p = fs_module_list) != 0) | |
379 | { | |
380 | if (! grub_dl_get (p->name) && grub_dl_load (p->name)) | |
381 | return 1; | |
382 | ||
383 | fs_module_list = p->next; | |
384 | grub_free (p->name); | |
385 | grub_free (p); | |
386 | } | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
391 | /* Read the file fs.lst for auto-loading. */ | |
392 | static void | |
393 | read_fs_list (void) | |
394 | { | |
395 | const char *prefix; | |
396 | ||
397 | prefix = grub_env_get ("prefix"); | |
398 | if (prefix) | |
399 | { | |
400 | char *filename; | |
401 | ||
402 | filename = grub_malloc (grub_strlen (prefix) + sizeof ("/fs.lst")); | |
403 | if (filename) | |
404 | { | |
405 | grub_file_t file; | |
406 | ||
407 | grub_sprintf (filename, "%s/fs.lst", prefix); | |
408 | file = grub_file_open (filename); | |
409 | if (file) | |
410 | { | |
411 | char buf[80]; /* XXX arbitrary */ | |
412 | ||
413 | while (get_line (file, buf, sizeof (buf))) | |
414 | { | |
415 | char *p = buf; | |
416 | char *q = buf + grub_strlen (buf) - 1; | |
417 | grub_fs_module_list_t fs_mod; | |
418 | ||
419 | /* Ignore space. */ | |
420 | while (grub_isspace (*p)) | |
421 | p++; | |
422 | ||
423 | while (p < q && grub_isspace (*q)) | |
424 | *q-- = '\0'; | |
425 | ||
426 | /* If the line is empty, skip it. */ | |
427 | if (p >= q) | |
428 | continue; | |
429 | ||
430 | fs_mod = grub_malloc (sizeof (*fs_mod)); | |
431 | if (! fs_mod) | |
432 | continue; | |
433 | ||
434 | fs_mod->name = grub_strdup (p); | |
435 | if (! fs_mod->name) | |
436 | { | |
437 | grub_free (fs_mod); | |
438 | continue; | |
439 | } | |
440 | ||
441 | fs_mod->next = fs_module_list; | |
442 | fs_module_list = fs_mod; | |
443 | } | |
444 | ||
445 | grub_file_close (file); | |
446 | } | |
447 | ||
448 | grub_free (filename); | |
449 | } | |
450 | } | |
451 | ||
452 | /* Ignore errors. */ | |
453 | grub_errno = GRUB_ERR_NONE; | |
454 | ||
455 | /* Set the hook. */ | |
456 | grub_fs_autoload_hook = autoload_fs_module; | |
457 | } | |
458 | ||
ce5bf700 | 459 | /* Read the config file CONFIG and execute the menu interface or |
460 | the command-line interface. */ | |
461 | void | |
4b13b216 | 462 | grub_normal_execute (const char *config, int nested) |
ce5bf700 | 463 | { |
4b13b216 | 464 | grub_menu_t menu = 0; |
ce5bf700 | 465 | |
466 | if (config) | |
467 | { | |
468 | menu = read_config_file (config); | |
469 | ||
470 | /* Ignore any error. */ | |
4b13b216 | 471 | grub_errno = GRUB_ERR_NONE; |
ce5bf700 | 472 | } |
473 | ||
5822ff87 | 474 | read_command_list (); |
39c9d41d | 475 | read_fs_list (); |
5822ff87 | 476 | |
ce5bf700 | 477 | if (menu) |
93f3a1d8 | 478 | { |
479 | grub_menu_run (menu, nested); | |
480 | grub_context_pop_menu (); | |
481 | free_menu (menu); | |
482 | } | |
ce5bf700 | 483 | else |
4b13b216 | 484 | grub_cmdline_run (nested); |
ce5bf700 | 485 | } |
486 | ||
487 | /* Enter normal mode from rescue mode. */ | |
488 | static void | |
4b13b216 | 489 | grub_rescue_cmd_normal (int argc, char *argv[]) |
ce5bf700 | 490 | { |
491 | if (argc == 0) | |
492 | { | |
5f3607e0 | 493 | /* Guess the config filename. It is necessary to make CONFIG static, |
494 | so that it won't get broken by longjmp. */ | |
495 | static char *config; | |
ce5bf700 | 496 | const char *prefix; |
497 | ||
4b13b216 | 498 | prefix = grub_env_get ("prefix"); |
ce5bf700 | 499 | if (prefix) |
500 | { | |
4b13b216 | 501 | config = grub_malloc (grub_strlen (prefix) + sizeof ("/grub.cfg")); |
ce5bf700 | 502 | if (! config) |
503 | return; | |
504 | ||
4b13b216 | 505 | grub_sprintf (config, "%s/grub.cfg", prefix); |
506 | grub_enter_normal_mode (config); | |
507 | grub_free (config); | |
ce5bf700 | 508 | } |
509 | else | |
4b13b216 | 510 | grub_enter_normal_mode (0); |
ce5bf700 | 511 | } |
512 | else | |
4b13b216 | 513 | grub_enter_normal_mode (argv[0]); |
ce5bf700 | 514 | } |
515 | ||
4b13b216 | 516 | #ifdef GRUB_UTIL |
1f7315a3 | 517 | void |
4b13b216 | 518 | grub_normal_init (void) |
1f7315a3 | 519 | { |
4b13b216 | 520 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 521 | |
1f7315a3 | 522 | /* Register a command "normal" for the rescue mode. */ |
4b13b216 | 523 | grub_rescue_register_command ("normal", grub_rescue_cmd_normal, |
1f7315a3 | 524 | "enter normal mode"); |
525 | ||
526 | /* This registers some built-in commands. */ | |
4b13b216 | 527 | grub_command_init (); |
1f7315a3 | 528 | |
529 | } | |
530 | ||
531 | void | |
4b13b216 | 532 | grub_normal_fini (void) |
1f7315a3 | 533 | { |
4b13b216 | 534 | grub_set_history (0); |
535 | grub_rescue_unregister_command ("normal"); | |
1f7315a3 | 536 | |
537 | } | |
4b13b216 | 538 | #else /* ! GRUB_UTIL */ |
539 | GRUB_MOD_INIT | |
ce5bf700 | 540 | { |
541 | /* Normal mode shouldn't be unloaded. */ | |
4b13b216 | 542 | grub_dl_ref (mod); |
ce5bf700 | 543 | |
4b13b216 | 544 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 545 | |
ce5bf700 | 546 | /* Register a command "normal" for the rescue mode. */ |
4b13b216 | 547 | grub_rescue_register_command ("normal", grub_rescue_cmd_normal, |
ce5bf700 | 548 | "enter normal mode"); |
549 | ||
550 | /* This registers some built-in commands. */ | |
4b13b216 | 551 | grub_command_init (); |
ce5bf700 | 552 | } |
553 | ||
4b13b216 | 554 | GRUB_MOD_FINI |
ce5bf700 | 555 | { |
4b13b216 | 556 | grub_set_history (0); |
557 | grub_rescue_unregister_command ("normal"); | |
ce5bf700 | 558 | } |
4b13b216 | 559 | #endif /* ! GRUB_UTIL */ |