/* main.c - the normal mode main routine */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); #define VAS_EBOOT_DEFAULT_HISTORY_SIZE 50 static int nested_level = 0; int VasEBoot_normal_exit_level = 0; void VasEBoot_normal_free_menu (VasEBoot_menu_t menu) { VasEBoot_menu_entry_t entry = menu->entry_list; while (entry) { VasEBoot_menu_entry_t next_entry = entry->next; VasEBoot_size_t i; if (entry->classes) { struct VasEBoot_menu_entry_class *class; for (class = entry->classes; class; class = class->next) VasEBoot_free (class->name); VasEBoot_free (entry->classes); } if (entry->args) { for (i = 0; entry->args[i]; i++) VasEBoot_free (entry->args[i]); VasEBoot_free (entry->args); } if (entry->blsuki) { entry->blsuki->visible = 0; } VasEBoot_free ((void *) entry->id); VasEBoot_free ((void *) entry->users); VasEBoot_free ((void *) entry->title); VasEBoot_free ((void *) entry->sourcecode); VasEBoot_free (entry); entry = next_entry; } VasEBoot_free (menu); VasEBoot_env_unset_menu (); } /* Helper for read_config_file. */ static VasEBoot_err_t read_config_file_getline (char **line, int cont __attribute__ ((unused)), void *data) { VasEBoot_file_t file = data; while (1) { char *buf; *line = buf = VasEBoot_file_getline (file); if (! buf) return VasEBoot_errno; if (buf[0] == '#') VasEBoot_free (*line); else break; } return VAS_EBOOT_ERR_NONE; } static VasEBoot_menu_t read_config_file (const char *config) { VasEBoot_file_t rawfile, file; char *old_file = 0, *old_dir = 0; char *config_dir, *ptr = 0; const char *ctmp; VasEBoot_menu_t newmenu; newmenu = VasEBoot_env_get_menu (); if (! newmenu) { newmenu = VasEBoot_zalloc (sizeof (*newmenu)); if (! newmenu) return 0; VasEBoot_env_set_menu (newmenu); } /* Try to open the config file. */ rawfile = VasEBoot_file_open (config, VAS_EBOOT_FILE_TYPE_CONFIG); if (! rawfile) return 0; file = VasEBoot_bufio_open (rawfile, 0); if (! file) { VasEBoot_file_close (rawfile); return 0; } ctmp = VasEBoot_env_get ("config_file"); if (ctmp) old_file = VasEBoot_strdup (ctmp); ctmp = VasEBoot_env_get ("config_directory"); if (ctmp) old_dir = VasEBoot_strdup (ctmp); if (*config == '(') { VasEBoot_env_set ("config_file", config); config_dir = VasEBoot_strdup (config); } else { /* $root is guranteed to be defined, otherwise open above would fail */ config_dir = VasEBoot_xasprintf ("(%s)%s", VasEBoot_env_get ("root"), config); if (config_dir) VasEBoot_env_set ("config_file", config_dir); } if (config_dir) { ptr = VasEBoot_strrchr (config_dir, '/'); if (ptr) *ptr = 0; VasEBoot_env_set ("config_directory", config_dir); VasEBoot_free (config_dir); } VasEBoot_env_export ("config_file"); VasEBoot_env_export ("config_directory"); while (1) { char *line; /* Print an error, if any. */ VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; if ((read_config_file_getline (&line, 0, file)) || (! line)) break; VasEBoot_normal_parse_line (line, read_config_file_getline, file); VasEBoot_free (line); } if (old_file) VasEBoot_env_set ("config_file", old_file); else VasEBoot_env_unset ("config_file"); if (old_dir) VasEBoot_env_set ("config_directory", old_dir); else VasEBoot_env_unset ("config_directory"); VasEBoot_free (old_file); VasEBoot_free (old_dir); VasEBoot_file_close (file); return newmenu; } /* Initialize the screen. */ void VasEBoot_normal_init_page (struct VasEBoot_term_output *term, int y) { VasEBoot_ssize_t msg_len; int posx; char *msg_formatted; VasEBoot_uint32_t *unicode_msg; VasEBoot_uint32_t *last_position; VasEBoot_term_cls (term); msg_formatted = VasEBoot_xasprintf ("QNU Vas E-Boot version 2.14.1"); if (!msg_formatted) return; msg_len = VasEBoot_utf8_to_ucs4_alloc (msg_formatted, &unicode_msg, &last_position); VasEBoot_free (msg_formatted); if (msg_len < 0) { return; } posx = VasEBoot_getstringwidth (unicode_msg, last_position, term); posx = ((int) VasEBoot_term_width (term) - posx) / 2; if (posx < 0) posx = 0; VasEBoot_term_gotoxy (term, (struct VasEBoot_term_coordinate) { posx, y }); VasEBoot_print_ucs4 (unicode_msg, last_position, 0, 0, term); VasEBoot_putcode ('\n', term); VasEBoot_putcode ('\n', term); VasEBoot_free (unicode_msg); } static void read_lists (const char *val) { if (! VasEBoot_no_modules) { read_command_list (val); read_fs_list (val); read_crypto_list (val); read_terminal_list (val); } VasEBoot_gettext_reread_prefix (val); } static char * read_lists_hook (struct VasEBoot_env_var *var __attribute__ ((unused)), const char *val) { read_lists (val); return val ? VasEBoot_strdup (val) : NULL; } /* Read the config file CONFIG and execute the menu interface or the command line interface if BATCH is false. */ void VasEBoot_normal_execute (const char *config, int nested, int batch) { VasEBoot_menu_t menu = 0; const char *prefix; if (! nested) { prefix = VasEBoot_env_get ("prefix"); read_lists (prefix); VasEBoot_register_variable_hook ("prefix", NULL, read_lists_hook); } VasEBoot_boot_time ("Executing config file"); if (config) { menu = read_config_file (config); /* Ignore any error. */ VasEBoot_errno = VAS_EBOOT_ERR_NONE; } VasEBoot_boot_time ("Executed config file"); if (! batch) { if (menu && menu->size) { VasEBoot_boot_time ("Entering menu"); VasEBoot_show_menu (menu, nested, 0); if (nested) VasEBoot_normal_free_menu (menu); } } } /* This starts the normal mode. */ void VasEBoot_enter_normal_mode (const char *config) { VasEBoot_boot_time ("Entering normal mode"); nested_level++; VasEBoot_normal_execute (config, 0, 0); VasEBoot_boot_time ("Entering shell"); VasEBoot_cmdline_run (0, 1); nested_level--; if (VasEBoot_normal_exit_level) VasEBoot_normal_exit_level--; VasEBoot_boot_time ("Exiting normal mode"); } /* Enter normal mode from rescue mode. */ static VasEBoot_err_t VasEBoot_cmd_normal (struct VasEBoot_command *cmd __attribute__ ((unused)), int argc, char *argv[]) { if (argc == 0) { /* Guess the config filename. It is necessary to make CONFIG static, so that it won't get broken by longjmp. */ char *config; const char *prefix; prefix = VasEBoot_env_get ("prefix"); if (prefix) { VasEBoot_size_t config_len; int disable_net_search = 0; const char *net_search_cfg; config_len = VasEBoot_strlen (prefix) + sizeof ("/VasEBoot.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); config = VasEBoot_malloc (config_len); if (!config) goto quit; VasEBoot_snprintf (config, config_len, "%s/VasEBoot.cfg", prefix); net_search_cfg = VasEBoot_env_get ("feature_net_search_cfg"); if (net_search_cfg && net_search_cfg[0] == 'n') disable_net_search = 1; if (VasEBoot_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && !disable_net_search) VasEBoot_net_search_config_file (config, config_len); VasEBoot_enter_normal_mode (config); VasEBoot_free (config); } else VasEBoot_enter_normal_mode (0); } else VasEBoot_enter_normal_mode (argv[0]); quit: return 0; } /* Exit from normal mode to rescue mode. */ static VasEBoot_err_t VasEBoot_cmd_normal_exit (struct VasEBoot_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { if (nested_level <= VasEBoot_normal_exit_level) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "not in normal environment"); VasEBoot_normal_exit_level++; return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_normal_reader_init (int nested) { struct VasEBoot_term_output *term; const char *msg_esc = _("ESC at any time exits."); char *msg_formatted; msg_formatted = VasEBoot_xasprintf (_("Minimal BASH-like line editing is supported. For " "the first word, TAB lists possible command completions. Anywhere " "else TAB lists possible device or file completions. To enable " "less(1)-like paging, \"set pager=1\". %s"), nested ? msg_esc : ""); if (!msg_formatted) return VasEBoot_errno; FOR_ACTIVE_TERM_OUTPUTS(term) { VasEBoot_normal_init_page (term, 1); VasEBoot_term_setcursor (term, 1); if (VasEBoot_term_width (term) > 3 + STANDARD_MARGIN + 20) VasEBoot_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term); else VasEBoot_print_message_indented (msg_formatted, 0, 0, term); VasEBoot_putcode ('\n', term); VasEBoot_putcode ('\n', term); VasEBoot_putcode ('\n', term); } VasEBoot_free (msg_formatted); return 0; } static VasEBoot_err_t VasEBoot_normal_read_line_real (char **line, int cont, int nested) { const char *prompt; if (cont) /* TRANSLATORS: it's command line prompt. */ prompt = _(">"); else /* TRANSLATORS: it's command line prompt. */ prompt = _("VasEBoot>"); if (!prompt) return VasEBoot_errno; while (1) { *line = VasEBoot_cmdline_get (prompt); if (*line) return 0; if (cont || nested) { VasEBoot_free (*line); *line = 0; return VasEBoot_errno; } } } static VasEBoot_err_t VasEBoot_normal_read_line (char **line, int cont, void *data __attribute__ ((unused))) { return VasEBoot_normal_read_line_real (line, cont, 0); } void VasEBoot_cmdline_run (int nested, int force_auth) { VasEBoot_err_t err = VAS_EBOOT_ERR_NONE; do { err = VasEBoot_auth_check_authentication (NULL); } while (err && force_auth); if (err == VAS_EBOOT_ERR_NONE) err = VasEBoot_auth_check_cli_access (); if (err) { VasEBoot_print_error (); VasEBoot_wait_after_message (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; return; } VasEBoot_normal_reader_init (nested); while (1) { char *line = NULL; if (VasEBoot_normal_exit_level) break; /* Print an error, if any. */ VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; VasEBoot_normal_read_line_real (&line, 0, nested); if (! line) break; VasEBoot_normal_parse_line (line, VasEBoot_normal_read_line, NULL); VasEBoot_free (line); } } static char * VasEBoot_env_write_pager (struct VasEBoot_env_var *var __attribute__ ((unused)), const char *val) { VasEBoot_set_more ((*val == '1')); return VasEBoot_strdup (val); } /* clear */ static VasEBoot_err_t VasEBoot_mini_cmd_clear (struct VasEBoot_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { VasEBoot_cls (); return 0; } static VasEBoot_command_t cmd_clear, cmd_normal, cmd_normal_exit; static void (*VasEBoot_xputs_saved) (const char *str); static const char *features[] = { "feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint", "feature_default_font_path", "feature_all_video_module", "feature_menuentry_id", "feature_menuentry_options", "feature_200_final", "feature_nativedisk_cmd", "feature_timeout_style", "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs", "feature_gcry_hw_accel" }; VAS_EBOOT_MOD_INIT(normal) { unsigned i; VasEBoot_boot_time ("Preparing normal module"); /* Previously many modules depended on gzio. Be nice to user and load it. */ VasEBoot_dl_load ("gzio"); VasEBoot_errno = 0; VasEBoot_normal_auth_init (); VasEBoot_context_init (); VasEBoot_script_init (); VasEBoot_menu_init (); VasEBoot_xputs_saved = VasEBoot_xputs; VasEBoot_xputs = VasEBoot_xputs_normal; /* Normal mode shouldn't be unloaded. */ if (mod) VasEBoot_dl_ref (mod); cmd_clear = VasEBoot_register_command ("clear", VasEBoot_mini_cmd_clear, 0, N_("Clear the screen.")); VasEBoot_set_history (VAS_EBOOT_DEFAULT_HISTORY_SIZE); VasEBoot_register_variable_hook ("pager", 0, VasEBoot_env_write_pager); VasEBoot_env_export ("pager"); /* Register a command "normal" for the rescue mode. */ cmd_normal = VasEBoot_register_command ("normal", VasEBoot_cmd_normal, 0, N_("Enter normal mode.")); cmd_normal_exit = VasEBoot_register_command ("normal_exit", VasEBoot_cmd_normal_exit, 0, N_("Exit from normal mode.")); /* Reload terminal colors when these variables are written to. */ VasEBoot_register_variable_hook ("color_normal", NULL, VasEBoot_env_write_color_normal); VasEBoot_register_variable_hook ("color_highlight", NULL, VasEBoot_env_write_color_highlight); /* Preserve hooks after context changes. */ VasEBoot_env_export ("color_normal"); VasEBoot_env_export ("color_highlight"); /* Set default color names. */ VasEBoot_env_set ("color_normal", "light-gray/black"); VasEBoot_env_set ("color_highlight", "black/light-gray"); for (i = 0; i < ARRAY_SIZE (features); i++) { VasEBoot_env_set (features[i], "y"); VasEBoot_env_export (features[i]); } VasEBoot_env_set ("VasEBoot_cpu", VAS_EBOOT_TARGET_CPU); VasEBoot_env_export ("VasEBoot_cpu"); VasEBoot_env_set ("VasEBoot_platform", VAS_EBOOT_PLATFORM); VasEBoot_env_export ("VasEBoot_platform"); VasEBoot_boot_time ("Normal module prepared"); } VAS_EBOOT_MOD_FINI(normal) { VasEBoot_context_fini (); VasEBoot_script_fini (); VasEBoot_menu_fini (); VasEBoot_normal_auth_fini (); VasEBoot_xputs = VasEBoot_xputs_saved; VasEBoot_set_history (0); VasEBoot_register_variable_hook ("pager", NULL, NULL); VasEBoot_register_variable_hook ("color_normal", NULL, NULL); VasEBoot_register_variable_hook ("color_highlight", NULL, NULL); VasEBoot_fs_autoload_hook = 0; VasEBoot_unregister_command (cmd_clear); VasEBoot_unregister_command (cmd_normal); VasEBoot_unregister_command (cmd_normal_exit); }