]> git.proxmox.com Git - grub2.git/blame - normal/main.c
2006-05-07 Yoshinori K. Okuji <okuji@enbug.org>
[grub2.git] / normal / main.c
CommitLineData
ce5bf700 1/* main.c - the normal mode main routine */
2/*
4b13b216 3 * GRUB -- GRand Unified Bootloader
4e93851c 4 * Copyright (C) 2000,2001,2002,2003,2005,2006 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>
77c4a393 30#include <grub/parser.h>
31#include <grub/script.h>
ce5bf700 32
4b13b216 33grub_jmp_buf grub_exit_env;
ce5bf700 34
39c9d41d 35static grub_fs_module_list_t fs_module_list = 0;
36
77c4a393 37/* The menu to which the new entries are added by the parser. */
38static grub_menu_t current_menu = 0;
39
4b13b216 40#define GRUB_DEFAULT_HISTORY_SIZE 50
5aded270 41
ce5bf700 42/* Read a line from the file FILE. */
43static int
4b13b216 44get_line (grub_file_t file, char cmdline[], int max_len)
ce5bf700 45{
46 char c;
47 int pos = 0;
48 int literal = 0;
49 int comment = 0;
50
51 while (1)
52 {
4b13b216 53 if (grub_file_read (file, &c, 1) != 1)
ce5bf700 54 break;
55
56 /* Skip all carriage returns. */
57 if (c == '\r')
58 continue;
59
60 /* Replace tabs with spaces. */
61 if (c == '\t')
62 c = ' ';
63
64 /* The previous is a backslash, then... */
65 if (literal)
66 {
67 /* If it is a newline, replace it with a space and continue. */
68 if (c == '\n')
69 {
70 c = ' ';
71
72 /* Go back to overwrite the backslash. */
73 if (pos > 0)
74 pos--;
75 }
76
77 literal = 0;
78 }
79
80 if (c == '\\')
81 literal = 1;
82
83 if (comment)
84 {
85 if (c == '\n')
86 comment = 0;
87 }
88 else if (pos == 0)
89 {
90 if (c == '#')
91 comment = 1;
4b13b216 92 else if (! grub_isspace (c))
ce5bf700 93 cmdline[pos++] = c;
94 }
95 else
96 {
97 if (c == '\n')
98 break;
99
100 if (pos < max_len)
101 cmdline[pos++] = c;
102 }
103 }
104
105 cmdline[pos] = '\0';
106
107 return pos;
108}
109
110static void
4b13b216 111free_menu (grub_menu_t menu)
ce5bf700 112{
4b13b216 113 grub_menu_entry_t entry = menu->entry_list;
ce5bf700 114
115 while (entry)
116 {
4b13b216 117 grub_menu_entry_t next_entry = entry->next;
ce5bf700 118
77c4a393 119 grub_script_free (entry->commands);
4b13b216 120 grub_free ((void *) entry->title);
77c4a393 121 grub_free ((void *) entry->sourcecode);
ce5bf700 122 entry = next_entry;
123 }
124
4b13b216 125 grub_free (menu);
ce5bf700 126}
127
77c4a393 128grub_err_t
129grub_normal_menu_addentry (const char *title, struct grub_script *script,
130 const char *sourcecode)
131{
132 const char *menutitle;
133 grub_menu_entry_t *last = &current_menu->entry_list;
134
135 menutitle = grub_strdup (title);
136 if (! menutitle)
137 return grub_errno;
138
139 /* Add the menu entry at the end of the list. */
140 while (*last)
141 last = &(*last)->next;
142
143 *last = grub_malloc (sizeof (**last));
144 if (! *last)
145 {
146 grub_free ((void *) menutitle);
147 grub_free ((void *) sourcecode);
148 return grub_errno;
149 }
150
151 (*last)->commands = script;
152 (*last)->title = menutitle;
153 (*last)->next = 0;
154 (*last)->sourcecode = sourcecode;
155
65f201ad 156 current_menu->size++;
157
77c4a393 158 return GRUB_ERR_NONE;
159}
160
4b13b216 161static grub_menu_t
ce5bf700 162read_config_file (const char *config)
163{
4b13b216 164 grub_file_t file;
77c4a393 165 auto grub_err_t getline (char **line);
166 int currline = 0;
6de2ee99 167 int errors = 0;
ce5bf700 168
77c4a393 169 grub_err_t getline (char **line)
170 {
171 char cmdline[100];
172 currline++;
173
174 if (! get_line (file, cmdline, sizeof (cmdline)))
175 return 0;
176
177 *line = grub_strdup (cmdline);
178 if (! *line)
179 return grub_errno;
180
181 return GRUB_ERR_NONE;
182 }
183
184 char cmdline[100];
185 grub_menu_t newmenu;
186
187 newmenu = grub_malloc (sizeof (*newmenu));
188 if (! newmenu)
189 return 0;
77c4a393 190 newmenu->size = 0;
191 newmenu->entry_list = 0;
192 current_menu = newmenu;
193
ce5bf700 194 /* Try to open the config file. */
4b13b216 195 file = grub_file_open (config);
ce5bf700 196 if (! file)
197 return 0;
198
ce5bf700 199 while (get_line (file, cmdline, sizeof (cmdline)))
200 {
77c4a393 201 struct grub_script *parsed_script;
6de2ee99 202 int startline;
0a74e62f 203
6de2ee99 204 startline = ++currline;
ce5bf700 205
77c4a393 206 /* Execute the script, line for line. */
207 parsed_script = grub_script_parse (cmdline, getline);
ce5bf700 208
77c4a393 209 if (! parsed_script)
ce5bf700 210 {
6de2ee99 211 grub_printf ("(line %d-%d)\n", startline, currline);
212 errors++;
213 continue;
77c4a393 214 }
ce5bf700 215
77c4a393 216 /* Execute the command(s). */
217 grub_script_execute (parsed_script);
ce5bf700 218
77c4a393 219 /* The parsed script was executed, throw it away. */
220 grub_script_free (parsed_script);
ce5bf700 221 }
222
77c4a393 223 grub_file_close (file);
6de2ee99 224
225 if (errors > 0)
226 {
227 /* Wait until the user pushes any key so that the user can
228 see what happened. */
229 grub_printf ("\nPress any key to continue...");
230 (void) grub_getkey ();
231 }
232
233 /* If the menu is empty, just drop it. */
234 if (current_menu->size == 0)
235 {
236 grub_free (current_menu);
237 return 0;
238 }
239
65f201ad 240 return newmenu;
ce5bf700 241}
242
243/* This starts the normal mode. */
244void
4b13b216 245grub_enter_normal_mode (const char *config)
ce5bf700 246{
4b13b216 247 if (grub_setjmp (grub_exit_env) == 0)
248 grub_normal_execute (config, 0);
ce5bf700 249}
250
251/* Initialize the screen. */
252void
4b13b216 253grub_normal_init_page (void)
ce5bf700 254{
4b13b216 255 grub_cls ();
256 grub_printf ("\n\
97543f08 257 GNU GRUB version %s\n\n",
ce5bf700 258 PACKAGE_VERSION);
259}
260
5822ff87 261/* Read the file command.lst for auto-loading. */
262static void
263read_command_list (void)
264{
265 const char *prefix;
266
267 prefix = grub_env_get ("prefix");
268 if (prefix)
269 {
270 char *filename;
271
272 filename = grub_malloc (grub_strlen (prefix) + sizeof ("/command.lst"));
273 if (filename)
274 {
275 grub_file_t file;
276
277 grub_sprintf (filename, "%s/command.lst", prefix);
278 file = grub_file_open (filename);
279 if (file)
280 {
281 char buf[80]; /* XXX arbitrary */
282
283 while (get_line (file, buf, sizeof (buf)))
284 {
285 char *p;
286 grub_command_t cmd;
287
288 if (! grub_isgraph (buf[0]))
289 continue;
290
291 p = grub_strchr (buf, ':');
292 if (! p)
293 continue;
294
295 *p = '\0';
296 while (*++p == ' ')
297 ;
298
299 if (! grub_isgraph (*p))
300 continue;
301
302 cmd = grub_register_command (buf, 0,
303 GRUB_COMMAND_FLAG_NOT_LOADED,
304 0, 0, 0);
305 if (! cmd)
306 continue;
307
308 cmd->module_name = grub_strdup (p);
309 if (! cmd->module_name)
310 grub_unregister_command (buf);
311 }
312
313 grub_file_close (file);
314 }
315
316 grub_free (filename);
317 }
318 }
319
320 /* Ignore errors. */
321 grub_errno = GRUB_ERR_NONE;
322}
323
39c9d41d 324/* The auto-loading hook for filesystems. */
325static int
326autoload_fs_module (void)
327{
328 grub_fs_module_list_t p;
329
330 while ((p = fs_module_list) != 0)
331 {
332 if (! grub_dl_get (p->name) && grub_dl_load (p->name))
333 return 1;
334
335 fs_module_list = p->next;
336 grub_free (p->name);
337 grub_free (p);
338 }
339
340 return 0;
341}
342
343/* Read the file fs.lst for auto-loading. */
344static void
345read_fs_list (void)
346{
347 const char *prefix;
348
349 prefix = grub_env_get ("prefix");
350 if (prefix)
351 {
352 char *filename;
353
354 filename = grub_malloc (grub_strlen (prefix) + sizeof ("/fs.lst"));
355 if (filename)
356 {
357 grub_file_t file;
358
359 grub_sprintf (filename, "%s/fs.lst", prefix);
360 file = grub_file_open (filename);
361 if (file)
362 {
363 char buf[80]; /* XXX arbitrary */
364
365 while (get_line (file, buf, sizeof (buf)))
366 {
367 char *p = buf;
368 char *q = buf + grub_strlen (buf) - 1;
369 grub_fs_module_list_t fs_mod;
370
371 /* Ignore space. */
372 while (grub_isspace (*p))
373 p++;
374
375 while (p < q && grub_isspace (*q))
376 *q-- = '\0';
377
378 /* If the line is empty, skip it. */
379 if (p >= q)
380 continue;
381
382 fs_mod = grub_malloc (sizeof (*fs_mod));
383 if (! fs_mod)
384 continue;
385
386 fs_mod->name = grub_strdup (p);
387 if (! fs_mod->name)
388 {
389 grub_free (fs_mod);
390 continue;
391 }
392
393 fs_mod->next = fs_module_list;
394 fs_module_list = fs_mod;
395 }
396
397 grub_file_close (file);
398 }
399
400 grub_free (filename);
401 }
402 }
403
404 /* Ignore errors. */
405 grub_errno = GRUB_ERR_NONE;
406
407 /* Set the hook. */
408 grub_fs_autoload_hook = autoload_fs_module;
409}
410
ce5bf700 411/* Read the config file CONFIG and execute the menu interface or
412 the command-line interface. */
413void
4b13b216 414grub_normal_execute (const char *config, int nested)
ce5bf700 415{
4b13b216 416 grub_menu_t menu = 0;
ce5bf700 417
0a74e62f 418 read_command_list ();
419 read_fs_list ();
420
ce5bf700 421 if (config)
422 {
423 menu = read_config_file (config);
424
425 /* Ignore any error. */
4b13b216 426 grub_errno = GRUB_ERR_NONE;
ce5bf700 427 }
428
429 if (menu)
93f3a1d8 430 {
4e93851c 431 grub_env_set_data_slot ("menu", menu);
93f3a1d8 432 grub_menu_run (menu, nested);
4e93851c 433 grub_env_unset_data_slot ("menu");
93f3a1d8 434 free_menu (menu);
435 }
ce5bf700 436 else
4b13b216 437 grub_cmdline_run (nested);
ce5bf700 438}
439
440/* Enter normal mode from rescue mode. */
441static void
4b13b216 442grub_rescue_cmd_normal (int argc, char *argv[])
ce5bf700 443{
444 if (argc == 0)
445 {
5f3607e0 446 /* Guess the config filename. It is necessary to make CONFIG static,
447 so that it won't get broken by longjmp. */
448 static char *config;
ce5bf700 449 const char *prefix;
450
4b13b216 451 prefix = grub_env_get ("prefix");
ce5bf700 452 if (prefix)
453 {
4b13b216 454 config = grub_malloc (grub_strlen (prefix) + sizeof ("/grub.cfg"));
ce5bf700 455 if (! config)
456 return;
457
4b13b216 458 grub_sprintf (config, "%s/grub.cfg", prefix);
459 grub_enter_normal_mode (config);
460 grub_free (config);
ce5bf700 461 }
462 else
4b13b216 463 grub_enter_normal_mode (0);
ce5bf700 464 }
465 else
4b13b216 466 grub_enter_normal_mode (argv[0]);
ce5bf700 467}
468
6d099807 469GRUB_MOD_INIT(normal)
ce5bf700 470{
471 /* Normal mode shouldn't be unloaded. */
6d099807 472 if (mod)
473 grub_dl_ref (mod);
ce5bf700 474
4b13b216 475 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
5aded270 476
ce5bf700 477 /* Register a command "normal" for the rescue mode. */
4b13b216 478 grub_rescue_register_command ("normal", grub_rescue_cmd_normal,
ce5bf700 479 "enter normal mode");
480
481 /* This registers some built-in commands. */
4b13b216 482 grub_command_init ();
ce5bf700 483}
484
6d099807 485GRUB_MOD_FINI(normal)
ce5bf700 486{
4b13b216 487 grub_set_history (0);
488 grub_rescue_unregister_command ("normal");
ce5bf700 489}
6d099807 490