main.c 14 KB

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