| /* menuentry.c - menuentry command */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2010 Free Software Foundation, Inc. |
| * |
| * GRUB 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. |
| * |
| * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <grub/types.h> |
| #include <grub/misc.h> |
| #include <grub/err.h> |
| #include <grub/dl.h> |
| #include <grub/extcmd.h> |
| #include <grub/i18n.h> |
| #include <grub/normal.h> |
| |
| static const struct grub_arg_option options[] = |
| { |
| {"class", 1, GRUB_ARG_OPTION_REPEATABLE, |
| N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING}, |
| {"users", 2, 0, |
| N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"), |
| ARG_TYPE_STRING}, |
| {"hotkey", 3, 0, |
| N_("Keyboard key to quickly boot this entry."), N_("KEYBOARD_KEY"), ARG_TYPE_STRING}, |
| {"source", 4, 0, |
| N_("Use STRING as menu entry body."), N_("STRING"), ARG_TYPE_STRING}, |
| {"id", 0, 0, N_("Menu entry identifier."), N_("STRING"), ARG_TYPE_STRING}, |
| /* TRANSLATORS: menu entry can either be bootable by anyone or only by |
| handful of users. By default when security is active only superusers can |
| boot a given menu entry. With --unrestricted (this option) |
| anyone can boot it. */ |
| {"unrestricted", 0, 0, N_("This entry can be booted by any user."), |
| 0, ARG_TYPE_NONE}, |
| {0, 0, 0, 0, 0, 0} |
| }; |
| |
| static struct |
| { |
| const char *name; |
| int key; |
| } hotkey_aliases[] = |
| { |
| {"backspace", '\b'}, |
| {"tab", '\t'}, |
| {"delete", GRUB_TERM_KEY_DC}, |
| {"insert", GRUB_TERM_KEY_INSERT}, |
| {"f1", GRUB_TERM_KEY_F1}, |
| {"f2", GRUB_TERM_KEY_F2}, |
| {"f3", GRUB_TERM_KEY_F3}, |
| {"f4", GRUB_TERM_KEY_F4}, |
| {"f5", GRUB_TERM_KEY_F5}, |
| {"f6", GRUB_TERM_KEY_F6}, |
| {"f7", GRUB_TERM_KEY_F7}, |
| {"f8", GRUB_TERM_KEY_F8}, |
| {"f9", GRUB_TERM_KEY_F9}, |
| {"f10", GRUB_TERM_KEY_F10}, |
| {"f11", GRUB_TERM_KEY_F11}, |
| {"f12", GRUB_TERM_KEY_F12}, |
| }; |
| |
| /* Add a menu entry to the current menu context (as given by the environment |
| variable data slot `menu'). As the configuration file is read, the script |
| parser calls this when a menu entry is to be created. */ |
| grub_err_t |
| grub_normal_add_menu_entry (int argc, const char **args, |
| char **classes, const char *id, |
| const char *users, const char *hotkey, |
| const char *prefix, const char *sourcecode, |
| int submenu) |
| { |
| int menu_hotkey = 0; |
| char **menu_args = NULL; |
| char *menu_users = NULL; |
| char *menu_title = NULL; |
| char *menu_sourcecode = NULL; |
| char *menu_id = NULL; |
| struct grub_menu_entry_class *menu_classes = NULL; |
| |
| grub_menu_t menu; |
| grub_menu_entry_t *last; |
| |
| menu = grub_env_get_menu (); |
| if (! menu) |
| return grub_error (GRUB_ERR_MENU, "no menu context"); |
| |
| last = &menu->entry_list; |
| |
| menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode); |
| if (! menu_sourcecode) |
| return grub_errno; |
| |
| if (classes && classes[0]) |
| { |
| int i; |
| for (i = 0; classes[i]; i++); /* count # of menuentry classes */ |
| menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class) |
| * (i + 1)); |
| if (! menu_classes) |
| goto fail; |
| |
| for (i = 0; classes[i]; i++) |
| { |
| menu_classes[i].name = grub_strdup (classes[i]); |
| if (! menu_classes[i].name) |
| goto fail; |
| menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL; |
| } |
| } |
| |
| if (users) |
| { |
| menu_users = grub_strdup (users); |
| if (! menu_users) |
| goto fail; |
| } |
| |
| if (hotkey) |
| { |
| unsigned i; |
| for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++) |
| if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0) |
| { |
| menu_hotkey = hotkey_aliases[i].key; |
| break; |
| } |
| if (i == ARRAY_SIZE (hotkey_aliases)) |
| menu_hotkey = hotkey[0]; |
| } |
| |
| if (! argc) |
| { |
| grub_error (GRUB_ERR_MENU, "menuentry is missing title"); |
| goto fail; |
| } |
| |
| menu_title = grub_strdup (args[0]); |
| if (! menu_title) |
| goto fail; |
| |
| menu_id = grub_strdup (id ? : menu_title); |
| if (! menu_id) |
| goto fail; |
| |
| /* Save argc, args to pass as parameters to block arg later. */ |
| menu_args = grub_malloc (sizeof (char*) * (argc + 1)); |
| if (! menu_args) |
| goto fail; |
| |
| { |
| int i; |
| for (i = 0; i < argc; i++) |
| { |
| menu_args[i] = grub_strdup (args[i]); |
| if (! menu_args[i]) |
| goto fail; |
| } |
| menu_args[argc] = NULL; |
| } |
| |
| /* Add the menu entry at the end of the list. */ |
| while (*last) |
| last = &(*last)->next; |
| |
| *last = grub_zalloc (sizeof (**last)); |
| if (! *last) |
| goto fail; |
| |
| (*last)->title = menu_title; |
| (*last)->id = menu_id; |
| (*last)->hotkey = menu_hotkey; |
| (*last)->classes = menu_classes; |
| if (menu_users) |
| (*last)->restricted = 1; |
| (*last)->users = menu_users; |
| (*last)->argc = argc; |
| (*last)->args = menu_args; |
| (*last)->sourcecode = menu_sourcecode; |
| (*last)->submenu = submenu; |
| |
| menu->size++; |
| return GRUB_ERR_NONE; |
| |
| fail: |
| |
| grub_free (menu_sourcecode); |
| { |
| int i; |
| for (i = 0; menu_classes && menu_classes[i].name; i++) |
| grub_free (menu_classes[i].name); |
| grub_free (menu_classes); |
| } |
| |
| { |
| int i; |
| for (i = 0; menu_args && menu_args[i]; i++) |
| grub_free (menu_args[i]); |
| grub_free (menu_args); |
| } |
| |
| grub_free (menu_users); |
| grub_free (menu_title); |
| grub_free (menu_id); |
| return grub_errno; |
| } |
| |
| static char * |
| setparams_prefix (int argc, char **args) |
| { |
| int i; |
| int j; |
| char *p; |
| char *result; |
| grub_size_t len = 10; |
| |
| /* Count resulting string length */ |
| for (i = 0; i < argc; i++) |
| { |
| len += 3; /* 3 = 1 space + 2 quotes */ |
| p = args[i]; |
| while (*p) |
| len += (*p++ == '\'' ? 3 : 1); |
| } |
| |
| result = grub_malloc (len + 2); |
| if (! result) |
| return 0; |
| |
| grub_strcpy (result, "setparams"); |
| p = result + 9; |
| |
| for (j = 0; j < argc; j++) |
| { |
| *p++ = ' '; |
| *p++ = '\''; |
| p = grub_strchrsub (p, args[j], '\'', "'\\''"); |
| *p++ = '\''; |
| } |
| *p++ = '\n'; |
| *p = '\0'; |
| return result; |
| } |
| |
| static grub_err_t |
| grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) |
| { |
| char ch; |
| char *src; |
| char *prefix; |
| unsigned len; |
| grub_err_t r; |
| const char *users; |
| |
| if (! argc) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments"); |
| |
| if (ctxt->state[3].set && ctxt->script) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions"); |
| |
| if (! ctxt->state[3].set && ! ctxt->script) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); |
| |
| if (ctxt->state[1].set) |
| users = ctxt->state[1].arg; |
| else if (ctxt->state[5].set) |
| users = NULL; |
| else |
| users = ""; |
| |
| if (! ctxt->script) |
| return grub_normal_add_menu_entry (argc, (const char **) args, |
| (ctxt->state[0].set ? ctxt->state[0].args |
| : NULL), |
| ctxt->state[4].arg, |
| users, |
| ctxt->state[2].arg, 0, |
| ctxt->state[3].arg, |
| ctxt->extcmd->cmd->name[0] == 's'); |
| |
| src = args[argc - 1]; |
| args[argc - 1] = NULL; |
| |
| len = grub_strlen(src); |
| ch = src[len - 1]; |
| src[len - 1] = '\0'; |
| |
| prefix = setparams_prefix (argc - 1, args); |
| if (! prefix) |
| return grub_errno; |
| |
| r = grub_normal_add_menu_entry (argc - 1, (const char **) args, |
| ctxt->state[0].args, ctxt->state[4].arg, |
| users, |
| ctxt->state[2].arg, prefix, src + 1, |
| ctxt->extcmd->cmd->name[0] == 's'); |
| |
| src[len - 1] = ch; |
| args[argc - 1] = src; |
| grub_free (prefix); |
| return r; |
| } |
| |
| static grub_extcmd_t cmd, cmd_sub; |
| |
| void |
| grub_menu_init (void) |
| { |
| cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry, |
| GRUB_COMMAND_FLAG_BLOCKS |
| | GRUB_COMMAND_ACCEPT_DASH |
| | GRUB_COMMAND_FLAG_EXTRACTOR, |
| N_("BLOCK"), N_("Define a menu entry."), options); |
| cmd_sub = grub_register_extcmd ("submenu", grub_cmd_menuentry, |
| GRUB_COMMAND_FLAG_BLOCKS |
| | GRUB_COMMAND_ACCEPT_DASH |
| | GRUB_COMMAND_FLAG_EXTRACTOR, |
| N_("BLOCK"), N_("Define a submenu."), |
| options); |
| } |
| |
| void |
| grub_menu_fini (void) |
| { |
| grub_unregister_extcmd (cmd); |
| grub_unregister_extcmd (cmd_sub); |
| } |