diff --git a/ChangeLog b/ChangeLog index 9fbad468c..7932efe8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,29 @@ -2012-03-03 Vladimir Serbinenko +2012-03-04 Vladimir Serbinenko + + Fix menu title instability bug. + + * grub-core/commands/menuentry.c (options): New option --id. + (grub_normal_add_menu_entry): New argument id. All users updated. + (grub_cmd_menuentry): Handle --id. + (grub_menu_init): Accept unknown arguments. + * grub-core/normal/main.c (features): Add feature_menuentry_id and + feature_menuentry_options. + * grub-core/normal/menu.c (grub_menu_execute_entry): Use id for + saved_entry. + (get_entry_number): Match with id as well. + * include/grub/menu.h (grub_menu_entry): New member id. + * util/grub-mkconfig_lib.in (grub_get_device_id): New function. + * util/grub.d/00_header.in: Define menuentry_id_option. + * util/grub.d/10_hurd.in: Define id. + * util/grub.d/10_illumos.in: Likewise. + * util/grub.d/10_kfreebsd.in: Likewise. + * util/grub.d/10_linux.in: Likewise. + * util/grub.d/10_netbsd.in: Likewise. + * util/grub.d/10_windows.in: Likewise. + * util/grub.d/20_linux_xen.in: Likewise. + * util/grub.d/30_os-prober.in: Likewise. + +2012-03-04 Vladimir Serbinenko * grub-core/script/execute.c (grub_script_return): Replace ambiguous "scope" with "body". diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index e8a58f75c..aab0e6323 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -123,7 +123,7 @@ legacy_file (const char *filename) return grub_errno; } args[0] = oldname; - grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, + grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, NULL, entrysrc, 0); grub_free (args); entrysrc[0] = 0; @@ -174,7 +174,8 @@ legacy_file (const char *filename) return grub_errno; } args[0] = entryname; - grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, entrysrc, 0); + grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, + NULL, NULL, entrysrc, 0); grub_free (args); } diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index a607c101e..a4e498a95 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -36,6 +36,8 @@ static const struct grub_arg_option options[] = 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", 1, GRUB_ARG_OPTION_REPEATABLE, + N_("Menu entry identifier."), N_("STRING"), ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; @@ -67,7 +69,8 @@ static struct 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, +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) @@ -77,6 +80,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, 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; @@ -139,6 +143,10 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, 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) @@ -164,6 +172,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, goto fail; (*last)->title = menu_title; + (*last)->id = menu_id; (*last)->hotkey = menu_hotkey; (*last)->classes = menu_classes; if (menu_users) @@ -196,6 +205,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, grub_free (menu_users); grub_free (menu_title); + grub_free (menu_id); return grub_errno; } @@ -257,7 +267,9 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) if (! ctxt->script) return grub_normal_add_menu_entry (argc, (const char **) args, (ctxt->state[0].set ? ctxt->state[0].args - : NULL), ctxt->state[1].arg, + : NULL), + ctxt->state[4].arg, + ctxt->state[1].arg, ctxt->state[2].arg, 0, ctxt->state[3].arg, ctxt->extcmd->cmd->name[0] == 's'); @@ -274,7 +286,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) return grub_errno; r = grub_normal_add_menu_entry (argc - 1, (const char **) args, - ctxt->state[0].args, ctxt->state[1].arg, + ctxt->state[0].args, ctxt->state[4].arg, + ctxt->state[1].arg, ctxt->state[2].arg, prefix, src + 1, ctxt->extcmd->cmd->name[0] == 's'); @@ -291,10 +304,12 @@ 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); diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 50c314dfa..be5d6b5bf 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -475,7 +475,8 @@ static grub_command_t cmd_clear; static void (*grub_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_default_font_path", "feature_all_video_module", + "feature_menuentry_id", "feature_menuentry_options" }; GRUB_MOD_INIT(normal) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index b511d4c48..a0ab2297d 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -188,7 +188,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) grub_env_set ("timeout", "0"); } - for (ptr = entry->title; *ptr; ptr++) + for (ptr = entry->id; *ptr; ptr++) sz += (*ptr == '>') ? 2 : 1; if (chosen) { @@ -217,7 +217,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) optr = grub_stpcpy (optr, chosen); *optr++ = '>'; } - for (ptr = entry->title; *ptr; ptr++) + for (ptr = entry->id; *ptr; ptr++) { if (*ptr == '>') *optr++ = '>'; @@ -411,10 +411,10 @@ grub_menu_register_viewer (struct grub_menu_viewer *viewer) } static int -menuentry_eq (const char *title, const char *spec) +menuentry_eq (const char *id, const char *spec) { const char *ptr1, *ptr2; - ptr1 = title; + ptr1 = id; ptr2 = spec; while (1) { @@ -459,7 +459,8 @@ get_entry_number (grub_menu_t menu, const char *name) for (i = 0; e; i++) { - if (menuentry_eq (e->title, val)) + if (menuentry_eq (e->title, val) + || menuentry_eq (e->id, val)) { entry = i; break; diff --git a/include/grub/menu.h b/include/grub/menu.h index 1f23a2363..ee2b5e910 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -32,6 +32,9 @@ struct grub_menu_entry /* The title name. */ const char *title; + /* The identifier. */ + const char *id; + /* If set means not everybody is allowed to boot this entry. */ int restricted; diff --git a/include/grub/normal.h b/include/grub/normal.h index b6b7d288d..00f8f1b7d 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -120,6 +120,7 @@ extern int grub_extractor_level; 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); diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index e776ef07b..ab3b934e0 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -162,6 +162,16 @@ prepare_grub_to_access_device () fi } +grub_get_device_id () +{ + device="$1" + if fs_uuid="`"${grub_probe}" --device "${device}" --target=fs_uuid 2> /dev/null`" ; then + echo "$fs_uuid"; + else + echo "$device" + fi +} + grub_file_is_not_garbage () { if test -f "$1" ; then diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 217067c17..765bfdcd3 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -63,6 +63,15 @@ set default="${GRUB_DEFAULT}" EOF fi cat <&2 cat << EOF -menuentry "$OS" { +menuentry "$OS" \$menuentry_id_option '$osid-$(grub_get_device_id "${dev}")' { EOF save_default_entry | sed -e 's,^,\t,' diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 09ba2f0f1..1fc34377c 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -87,7 +87,10 @@ linux_entry () else title="$(gettext_quoted "%s, with Xen %s and Linux %s")" fi - printf "menuentry '${title}' ${CLASS} {\n" "${os}" "${xen_version}" "${version}" + if [ -z "$boot_device_id" ]; then + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" + fi + printf "menuentry '${title}' ${CLASS} \$menuentry_id_option 'xen-gnulinux-$version-$recovery-$boot_device_id' {\n" "${os}" "${xen_version}" "${version}" if ! ${recovery} ; then save_default_entry | sed -e "s/^/\t/" fi @@ -139,6 +142,7 @@ xen_list=`for i in /boot/xen*; do if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi done` prepare_boot_cache= +boot_device_id= while [ "x${xen_list}" != "x" ] ; do list="${linux_list}" @@ -147,7 +151,10 @@ while [ "x${xen_list}" != "x" ] ; do xen_dirname=`dirname ${current_xen}` rel_xen_dirname=`make_system_path_relative_to_its_root $xen_dirname` xen_version=`echo $xen_basename | sed -e "s,.gz$,,g;s,^xen-,,g"` - echo "submenu \"Xen ${xen_version}\" {" + if [ -z "$boot_device_id" ]; then + boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" + fi + echo "submenu \"Xen ${xen_version}\" \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {" while [ "x$list" != "x" ] ; do linux=`version_find_latest $list` gettext_printf "Found linux image: %s\n" "$linux" >&2 diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 4be888866..6ef4027e5 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -52,7 +52,7 @@ osx_entry() { # TRANSLATORS: it refers on the OS residing on device %s onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF -menuentry "${LONGNAME} $bitstr $onstr" --class osx --class darwin --class os { +menuentry "${LONGNAME} $bitstr $onstr" --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" @@ -104,6 +104,8 @@ EOF EOF } +used_osprober_linux_ids= + for OS in ${OSPROBED} ; do DEVICE="`echo ${OS} | cut -d ':' -f 1`" LONGNAME="`echo ${OS} | cut -d ':' -f 2 | tr '^' ' '`" @@ -121,7 +123,7 @@ for OS in ${OSPROBED} ; do onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF -menuentry "${LONGNAME} $onstr" --class windows --class os { +menuentry "${LONGNAME} $onstr" --class windows --class os \$menuentry_id_option 'osprober-chain-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" @@ -144,6 +146,7 @@ EOF linux) LINUXPROBED="`linux-boot-prober ${DEVICE} 2> /dev/null | tr ' ' '^' | paste -s -d ' '`" prepare_boot_cache= + boot_device_id= for LINUX in ${LINUXPROBED} ; do LROOT="`echo ${LINUX} | cut -d ':' -f 1`" @@ -163,8 +166,17 @@ EOF fi onstr="$(gettext_printf "(on %s)" "${DEVICE}")" + recovery_params="$(echo "${LPARAMS}" | grep single)" + counter=1 + while echo "$used_osprober_linux_ids" | grep 'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id' > /dev/null; do + counter=$((counter+1)); + done + if [ -z "$boot_device_id" ]; then + boot_device_id="$(grub_get_device_id "${DEVICE}")" + fi + used_osprober_linux_ids="$used_osprober_linux_ids 'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id'" cat << EOF -menuentry "${LLABEL} $onstr" --class gnu-linux --class gnu --class os { +menuentry "${LLABEL} $onstr" --class gnu-linux --class gnu --class os \$menuentry_id_option 'osprober-gnulinux-$LKERNEL-${recovery_params}-$boot_device_id' { EOF save_default_entry | sed -e "s/^/\t/" if [ -z "${prepare_boot_cache}" ]; then @@ -192,7 +204,7 @@ EOF hurd) onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF -menuentry "${LONGNAME} $onstr" --class hurd --class gnu --class os { +menuentry "${LONGNAME} $onstr" --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/"