Problems with grub-reboot/savedefault/default=saved
There are multiple problems with grub-reboot/savedefault/default=saved.
1: grub-reboot doesn't restore the default after rebooting, making it
effectively equivalent to grub-set-default. This is because the
savedefault functionality currently saves the entry you boot from as the
new default even when grub-reboot is used. Because of problem #2 there is
no way ( outside manually editing the grub.cfg ) to use grub-reboot without
savedefault functionality also enabled ( and thus no configuration where grub-reboot isn't broken ). I've added a patch for this to the bug report @ http://savannah.gnu.org/bugs/?28161 and as the first patch
attached to this email.
2: Setting GRUB_DEFAULT=saved in /etc/default/grub also enables savedefault functionality. There are many people who would want to use grub-reboot and grub-set-default without savedefault. The second patch adds a separate variable, GRUB_SAVEDEFAULT, for enabling savedefault.
3: Savedefault functionality currently adds two lines ( one setting a variable, the other saving it ) to every menu entry and with my patch for fixing grub-reboot, an if statement as well. This clutters the menu entries and makes it hard to write and maintain custom menu entries with savedefault. The third patch moves this to a function "savedefault" defined in 00_header so entries just have to have "savedefault", like in grub legacy. And if the implementation of savedefault needs to change, custom menu entries will still work unmodified.
4: Even with the first grub-reboot fix the default is still not restored when grubenv is not writable ( /boot on lvm for example ). Since grub-reboot can't work without a writable grubenv it's at least safer to boot into the "real" default instead of the "temporary" default. The fourth patch sets default=$prev_saved_default if save_env fails. Grub-reboot ( the utility ) should also complain when grubenv isn't writable by grub. What is the best way to determine that grubenv likely is or isn't writable by grub?
5: Since grub2 uses menu entry titles for savedefault instead of numbers, the default kernel booted won't be changed when the user installs a new kernel. Using titles instead of numbers is good, but an unfortunate consequence that users may not realize ( and was not true with savedefault in grub-legacy ) is that if users set GRUB_SAVEDEFAULT=true they will never actually boot any updated kernels unless they select them manually at the grub menu. This could lead to not getting security updates for the kernel, and with major upgrades could cause serious problems when the old kernel doesn't work with newer userland components like Xorg. I can't think of a good way to solve this problem.
Signature · 1 line
Jordan Uggla ( Jordan_U on irc.freenode.net )
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +4 -2
@@ -97,8 +97,10 @@ save_default_entry () { if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then - echo 'saved_entry=${chosen}' - echo 'save_env saved_entry' + echo 'if [ ${boot_once} != true ]; then' + echo ' saved_entry=${chosen}' + echo ' save_env saved_entry' + echo 'fi' fi }
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +1 -0
@@ -46,6 +46,7 @@ save_env saved_entry prev_saved_entry= save_env prev_saved_entry + boot_once=true fi EOF
=== modified file 'util/grub-mkconfig.in'
util/grub-mkconfig.in +2 -1
@@ -220,7 +220,8 @@ GRUB_DISABLE_LINUX_UUID \ GRUB_DISABLE_LINUX_RECOVERY \ GRUB_GFXMODE \ - GRUB_DISABLE_OS_PROBER + GRUB_DISABLE_OS_PROBER \ + GRUB_SAVEDEFAULT if test "x${grub_cfg}" != "x"; then rm -f ${grub_cfg}.new
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +1 -1
@@ -96,7 +96,7 @@ save_default_entry () { - if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then + if [ "x${GRUB_SAVEDEFAULT}" = "xtrue" ] ; then echo 'if [ ${boot_once} != true ]; then' echo ' saved_entry=${chosen}' echo ' save_env saved_entry'
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +1 -4
@@ -97,10 +97,7 @@ save_default_entry () { if [ "x${GRUB_SAVEDEFAULT}" = "xtrue" ] ; then - echo 'if [ ${boot_once} != true ]; then' - echo ' saved_entry=${chosen}' - echo ' save_env saved_entry' - echo 'fi' + echo 'savedefault' fi }
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +8 -0
@@ -48,6 +48,14 @@ save_env prev_saved_entry boot_once=true fi + +function savedefault { + if [ \${boot_once} != true ]; then + saved_entry=\${chosen} + save_env saved_entry + fi +} + EOF case ${GRUB_TERMINAL_INPUT}:${GRUB_TERMINAL_OUTPUT} in
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +7 -4
@@ -43,10 +43,13 @@ set default=${GRUB_DEFAULT} if [ \${prev_saved_entry} ]; then saved_entry=\${prev_saved_entry} - save_env saved_entry - prev_saved_entry= - save_env prev_saved_entry - boot_once=true + if save_env saved_entry; then + prev_saved_entry= + save_env prev_saved_entry + boot_once=true + else + default=\${prev_saved_entry} + fi fi function savedefault {
I found another bug. For some reason grub-reboot ( the utility ) checks if prev_saved_entry ( which has just been set equal to saved_entry ) is empty ( zero length or unset ), and if it is unsets it. This makes grub-reboot again equivalent to grub-set-default the first time you use it. Instead I changed it so that if prev_saved_entry is empty it gets set to "0" because default=0 is the same as default="" but the grub.cfg detects that you've used grub-reboot by checking if prev_saved_entry is not empty.
I've attached a fix for this and also updated my previous patches so they apply cleanly and are consistent with the new changes from the savedefault branch that were merged into experimental ( using set var=value instead of just var=value ).
Signature · 1 line
Jordan Uggla ( Jordan_U on irc.freenode.net )
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +4 -2
@@ -97,8 +97,10 @@ save_default_entry () { if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then - echo 'set saved_entry=${chosen}' - echo 'save_env saved_entry' + echo 'if [ ${boot_once} != true ]; then' + echo ' set saved_entry=${chosen}' + echo ' save_env saved_entry' + echo 'fi' fi }
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +1 -0
@@ -48,6 +48,7 @@ save_env saved_entry set prev_saved_entry= save_env prev_saved_entry + set boot_once=true fi EOF
=== modified file 'util/grub-mkconfig.in'
util/grub-mkconfig.in +2 -1
@@ -220,7 +220,8 @@ GRUB_DISABLE_LINUX_UUID \ GRUB_DISABLE_LINUX_RECOVERY \ GRUB_GFXMODE \ - GRUB_DISABLE_OS_PROBER + GRUB_DISABLE_OS_PROBER \ + GRUB_SAVEDEFAULT if test "x${grub_cfg}" != "x"; then rm -f ${grub_cfg}.new
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +1 -1
@@ -96,7 +96,7 @@ save_default_entry () { - if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then + if [ "x${GRUB_SAVEDEFAULT}" = "xtrue" ] ; then echo 'if [ ${boot_once} != true ]; then' echo ' set saved_entry=${chosen}' echo ' save_env saved_entry'
=== modified file 'util/grub-mkconfig_lib.in'
util/grub-mkconfig_lib.in +1 -4
@@ -97,10 +97,7 @@ save_default_entry () { if [ "x${GRUB_SAVEDEFAULT}" = "xtrue" ] ; then - echo 'if [ ${boot_once} != true ]; then' - echo ' set saved_entry=${chosen}' - echo ' save_env saved_entry' - echo 'fi' + echo 'savedefault' fi }
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +8 -0
@@ -50,6 +50,14 @@ save_env prev_saved_entry set boot_once=true fi + +function savedefault { + if [ \${boot_once} != true ]; then + set saved_entry=\${chosen} + save_env saved_entry + fi +} + EOF case ${GRUB_TERMINAL_INPUT}:${GRUB_TERMINAL_OUTPUT} in
=== modified file 'util/grub.d/00_header.in'
util/grub.d/00_header.in +7 -4
@@ -45,10 +45,13 @@ set default="${GRUB_DEFAULT}" if [ \${prev_saved_entry} ]; then set saved_entry=\${prev_saved_entry} - save_env saved_entry - set prev_saved_entry= - save_env prev_saved_entry - set boot_once=true + if save_env saved_entry; then + set prev_saved_entry= + save_env prev_saved_entry + set boot_once=true + else + set default=\${prev_saved_entry} + fi fi function savedefault {
=== modified file 'util/grub-reboot.in'
util/grub-reboot.in +4 -1
@@ -96,7 +96,10 @@ if [ "$prev_saved_entry" ]; then $grub_editenv ${grubdir}/grubenv set prev_saved_entry="$prev_saved_entry" else - $grub_editenv ${grubdir}/grubenv unset prev_saved_entry + #Set prev_saved_entry to zero. Setting the default to "0" is the same as + #setting it to "" but because of the way grub-reboot is currently + #implemented prev_saved_entry can't be zero length. + $grub_editenv ${grubdir}/grubenv set prev_saved_entry=0 fi $grub_editenv ${grubdir}/grubenv set saved_entry="$entry"
Colin Watson
AppliedQuoted 11 lines · Jordan Uggla
I found another bug. For some reason grub-reboot ( the utility ) checks if
prev_saved_entry( which has just been set equal tosaved_entry) is empty ( zero length or unset ), and if it is unsets it. This makes grub-reboot again equivalent to grub-set-default the first time you use it. Instead I changed it so that ifprev_saved_entryis empty it gets set to "0" because default=0 is the same as default="" but the grub.cfg detects that you've used grub-reboot by checking ifprev_saved_entryis not empty.I've attached a fix for this
Thanks, applied, though I reworded the comment a bit. In future, ChangeLog (or rather ChangeLog.savedefault) entries are appreciated.
Would the maintainers be OK with me merging savedefault into trunk soon? I think it's getting pretty good now and quite a few people are interested in it. The full current patch follows.
(What is the convention for merging Changelog.<branch> files into ChangeLog? Do we keep everything including the dates intact, or collapse the entries into one, or what?)
=== added file 'ChangeLog.savedefault'
ChangeLog.savedefault +80 -0
@@ -0,0 +1,80 @@ +2010-10-05 Jordan Uggla <[email protected]> +2010-10-05 Colin Watson <[email protected]> + + * util/grub-reboot.in: Make sure prev_saved_entry always gets a + non-empty value. + +2010-10-05 Jordan Uggla <[email protected]> +2010-10-05 Colin Watson <[email protected]> + + * util/grub.d/00_header.in: Define a "savedefault" function for use + in menu entries. + * util/grub-mkconfig_lib.in (save_default_entry): Use it. + +2010-10-05 Jordan Uggla <[email protected]> +2010-10-05 Colin Watson <[email protected]> + + * util/grub-mkconfig_lib.in (save_default_entry): Only set + saved_entry if boot_once is unset. + * util/grub.d/00_header.in: Set boot_once to "true" if there was a + previous saved entry (i.e. grub-reboot). + +2009-12-08 Colin Watson <[email protected]> + + * util/grub.d/30_os-prober.in: Call save_default_entry for hurd. + +2009-12-08 Colin Watson <[email protected]> + + * util/grub.d/00_header.in: Use `set var=val' rather than plain + `var=val'. + * util/grub-mkconfig_lib.in (save_default_entry): Likewise. + +2009-12-08 Colin Watson <[email protected]> + + * util/grub-reboot.in: Fix --version output. + * util/grub-set-default.in: Likewise. + +2009-12-08 Colin Watson <[email protected]> + + * util/grub.d/00_header.in: Silently ignore zero-sized environment + blocks. + +2009-12-08 Colin Watson <[email protected]> + + * util/grub.d/00_header.in: Quote the value assigned to `default', + in case it contains spaces. + +2009-12-08 Colin Watson <[email protected]> + + * util/grub.d/30_os-prober.in: Fix merge error that moved a + `save_default_entry' call from the macosx case to the linux case. + +2009-10-25 Vladimir Serbinenko <[email protected]> +2009-10-25 Colin Watson <[email protected]> + + * normal/menu.c (grub_menu_execute_entry): Save selected entry title + in `chosen' environment variable. + * normal/menu_text.c (get_entry_number): Check if the variable + matches the title of a menu entry. + (run_menu): Pass menu to get_entry_number. + + * util/grub-reboot.in: New file. + * util/grub-set-default.in: New file. + * conf/common.rmk (grub-reboot): New utility. + (grub-set-default): New utility. + + * util/grub-mkconfig_lib.in (save_default_entry): New function. + * util/grub.d/00_header.in: If GRUB_DEFAULT is `saved', set + default to `${saved_entry}'. If `${prev_saved_entry}' is non-empty, + move it to `saved_entry' for the next boot. Load environment on + initialisation. + * util/grub.d/10_kfreebsd.in: Call save_default_entry. + * util/grub.d/10_hurd.in: Likewise. + * util/grub.d/10_linux.in (linux_entry): Likewise. + * util/grub.d/10_windows.in: Likewise. + * util/grub.d/30_os-prober.in: Likewise. + + * util/grub-install.in: Create environment block. + * util/i386/efi/grub-install.in: Likewise. + * util/ieee1275/grub-install.in: Likewise. + * util/sparc64/ieee1275/grub-install.in: Likewise.
=== modified file 'conf/common.rmk'
common.rmk +14 -0
@@ -185,6 +185,20 @@ CLEANFILES += $(grub-mkconfig_SCRIPTS) grub-mkconfig_DATA += util/grub.d/README +# For grub-set-default. +grub-set-default: util/grub-set-default.in config.status + ./config.status --file=$@:$< + chmod +x $@ +sbin_SCRIPTS += grub-set-default +CLEANFILES += grub-set-default + +# For grub-reboot. +grub-reboot: util/grub-reboot.in config.status + ./config.status --file=$@:$< + chmod +x $@ +sbin_SCRIPTS += grub-reboot +CLEANFILES += grub-reboot + # Filing systems. pkglib_MODULES += fshelp.mod fat.mod ufs1.mod ufs2.mod ext2.mod ntfs.mod \ ntfscomp.mod minix.mod hfs.mod jfs.mod iso9660.mod xfs.mod \
=== modified file 'normal/menu.c'
menu.c +2 -0
@@ -137,6 +137,8 @@ grub_menu_execute_entry(grub_menu_entry_return ; } +grub_env_set ("chosen" , entry->title); +grub_parser_execute ((char *) entry->sourcecode);if (grub_errno ==GRUB_ERR_NONE &&grub_loader_is_loaded ())
=== modified file 'normal/menu_text.c'
menu_text.c +24 -2
@@ -365,7 +365,7 @@ grub_menu_init_page (int nested, int edi/* Get the entry number from the variable NAME. */ static int -get_entry_number (const char *name) +get_entry_number (grub_menu_t menu,const char *name) {char *val;int entry;
@@ -378,6 +378,28 @@ get_entry_number (const char *name) entry = (int )grub_strtoul (val,0 ,0 ); +if (grub_errno ==GRUB_ERR_BAD_NUMBER ) + { +/* See if the variable matches the title of a menu entry. */ +grub_menu_entry_t e = menu->entry_list; +int i; + + grub_errno =GRUB_ERR_NONE ; + +for (i =0 ; e; i++) + { +if (grub_strcmp (e->title, val) ==0 ) + { + entry = i; +break ; + } + e = e->next; + } + +if (! e) + entry =-1 ; + } +if (grub_errno !=GRUB_ERR_NONE ) { grub_errno =GRUB_ERR_NONE ;
@@ -427,7 +449,7 @@ run_menu (grub_menu_t menu, int nested, first =0 ; - default_entry =get_entry_number ("default" ); + default_entry =get_entry_number (menu,"default" );/* If DEFAULT_ENTRY is not within the menu entries, fall back to the first entry. */
=== modified file 'util/grub-install.in'
grub-install.in +5 -0
@@ -40,6 +40,7 @@ else fi grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` grub_probe=${sbindir}/`echo grub-probe | sed ${transform}` +grub_editenv=${bindir}/`echo grub-editenv | sed ${transform}` rootdir= grub_prefix=`echo /boot/grub | sed ${transform}` modules=
@@ -261,6 +262,10 @@ done # Write device to a variable so we don't have to traverse /dev every time. grub_device=`$grub_probe --target=device ${grubdir}` +if ! test -f ${grubdir}/grubenv; then + $grub_editenv ${grubdir}/grubenv create +fi + # Create the core image. First, auto-detect the filesystem module. fs_module=`$grub_probe --target=fs --device ${grub_device}` if test "x$fs_module" = x -a "x$modules" = x; then
=== modified file 'util/grub-mkconfig_lib.in'
grub-mkconfig_lib.in +9 -0
@@ -94,6 +94,15 @@ convert_system_path_to_grub_path () echo ${drive}${relative_path} } +save_default_entry () +{ + if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then + cat << EOF +savedefault +EOF + fi +} + prepare_grub_to_access_device () { device=$1
=== added file 'util/grub-reboot.in'
grub-reboot.in +108 -0
108 lines not on this page · raw message
=== added file 'util/grub-set-default.in'
grub-set-default.in +99 -0
99 lines not on this page · raw message
=== modified file 'util/grub.d/00_header.in'
grub.d/00_header.in +19 -1
@@ -34,11 +34,29 @@ for i in ${GRUB_PRELOAD_MODULES} ; do done if [ "x${GRUB_DEFAULT}" = "x" ] ; then GRUB_DEFAULT=0 ; fi +if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then GRUB_DEFAULT='${saved_entry}' ; fi if [ "x${GRUB_TIMEOUT}" = "x" ] ; then GRUB_TIMEOUT=5 ; fi if [ "x${GRUB_GFXMODE}" = "x" ] ; then GRUB_GFXMODE=640x480 ; fi cat << EOF -set default=${GRUB_DEFAULT} +if [ -s \$prefix/grubenv ]; then + load_env +fi +set default="${GRUB_DEFAULT}" +if [ \${prev_saved_entry} ]; then + set saved_entry=\${prev_saved_entry} + save_env saved_entry + set prev_saved_entry= + save_env prev_saved_entry + set boot_once=true +fi + +function savedefault { + if [ -z \${boot_once} ]; then + saved_entry=\${chosen} + save_env saved_entry + fi +} EOF case ${GRUB_TERMINAL_INPUT}:${GRUB_TERMINAL_OUTPUT} in
=== modified file 'util/grub.d/10_hurd.in'
grub.d/10_hurd.in +1 -0
@@ -75,6 +75,7 @@ prepare_grub_to_access_device ${GRUB_DEV cat << EOF multiboot ${kernel} root=device:${GRUB_DEVICE#/dev/} EOF +save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e "s/^/\t/" cat << EOF module /hurd/${hurd_fs}.static ${hurd_fs} --readonly \\
=== modified file 'util/grub.d/10_kfreebsd.in'
grub.d/10_kfreebsd.in +1 -0
@@ -39,6 +39,7 @@ kfreebsd_entry () args="$4" # not used yet title="$(gettext "%s, with kFreeBSD %s")" printf "menuentry \"${title}\" {\n" "${os}" "${version}" + save_default_entry | sed -e "s/^/\t/" if [ -z "${prepare_boot_cache}" ]; then prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" fi
=== modified file 'util/grub.d/10_linux.in'
grub.d/10_linux.in +1 -0
@@ -59,6 +59,7 @@ linux_entry () title="$(gettext "%s, with Linux %s")" fi printf "menuentry \"${title}\" {\n" "${os}" "${version}" + save_default_entry | sed -e "s/^/\t/" if [ -z "${prepare_boot_cache}" ]; then prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" fi
=== modified file 'util/grub.d/10_windows.in'
grub.d/10_windows.in +1 -0
@@ -73,6 +73,7 @@ for dir in $dirlist ; do menuentry "$OS" { EOF + save_default_entry | sed -e 's,^,\t,' prepare_grub_to_access_device "$dev" | sed 's,^,\t,' cat << EOF
=== modified file 'util/grub.d/30_os-prober.in'
grub.d/30_os-prober.in +4 -0
@@ -41,6 +41,7 @@ osx_entry() { cat << EOF menuentry "${LONGNAME} (${2}-bit) (on ${DEVICE})" { EOF + save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" cat << EOF insmod ${GRUB_VIDEO_BACKEND}
@@ -105,6 +106,7 @@ for OS in ${OSPROBED} ; do cat << EOF menuentry "${LONGNAME} (on ${DEVICE})" { EOF + save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" case ${LONGNAME} in
@@ -146,6 +148,7 @@ EOF cat << EOF menuentry "${LLABEL} (on ${DEVICE})" { EOF + save_default_entry | sed -e "s/^/\t/" if [ -z "${prepare_boot_cache}" ]; then prepare_boot_cache="$(prepare_grub_to_access_device ${LBOOT} | sed -e "s/^/\t/")" fi
@@ -172,6 +175,7 @@ EOF cat << EOF menuentry "${LONGNAME} (on ${DEVICE})" { EOF + save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" grub_device="`${grub_probe} --device ${DEVICE} --target=drive`" mach_device="`echo "${grub_device}" | tr -d '()' | tr , s`"
=== modified file 'util/i386/efi/grub-install.in'
i386/efi/grub-install.in +5 -0
@@ -35,6 +35,7 @@ pkglibdir=${libdir}/`echo ${PACKAGE_TARN grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` grub_probe=${sbindir}/`echo grub-probe | sed ${transform}` +grub_editenv=${bindir}/`echo grub-editenv | sed ${transform}` rootdir= grub_prefix=`echo /boot/grub | sed ${transform}` modules=
@@ -179,6 +180,10 @@ for file in ${pkglibdir}/*.mod ${pkglibd cp -f $file ${grubdir} || exit 1 done +if ! test -f ${grubdir}/grubenv; then + $grub_editenv ${grubdir}/grubenv create +fi + # Create the core image. First, auto-detect the filesystem module. fs_module=`$grub_probe --target=fs --device-map=${device_map} ${grubdir}` if test "x$fs_module" = xfat; then :; else
=== modified file 'util/ieee1275/grub-install.in'
ieee1275/grub-install.in +5 -0
@@ -37,6 +37,7 @@ pkglibdir=${libdir}/`echo ${PACKAGE_TARN grub_mkimage=${bindir}/`echo grub-mkelfimage | sed ${transform}` grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` grub_probe=${sbindir}/`echo grub-probe | sed ${transform}` +grub_editenv=${bindir}/`echo grub-editenv | sed ${transform}` rootdir= grub_prefix=`echo /boot/grub | sed ${transform}` modules=
@@ -163,6 +164,10 @@ for file in ${pkglibdir}/*.mod ${pkglibd cp -f $file ${grubdir} || exit 1 done +if ! test -f ${grubdir}/grubenv; then + $grub_editenv ${grubdir}/grubenv create +fi + # Create the core image. First, auto-detect the filesystem module. fs_module=`$grub_probe --target=fs --device-map=${device_map} ${grubdir}` if test "x$fs_module" = x -a "x$modules" = x; then
Signature · 1 line
Colin Watson [[email protected]]
Embedding area too small... (GRUB2 on software RAID1)
Hello,
I successfully set up an Intel Matrix Raid device with a RAID1 and a RAID0 volume, each having a couple of partitions, but then I could not install GRUB2 on the RAID1 volume, which I wanted to use to boot from and mount as root. It turned out that the "IMSM" metadata is not supported in GRUB2 (v1.97.1) just yet, so I had to turn away from my original plan.
To "imitate" the setup I originally wanded, I turned both of my drives into AHCI controlled devices in the BIOS (instead of RAID), and I partitioned them to obtain /dev/sda[12] and /dev/sdb[12].
Then I used /dev/sd[ab]1 to build a RAID1 set, and /dev/sd[ab]2 to create a RAID0 set using mdadm v 3.0.3:
Quoted 2 lines
mdadm -C
/dev/md0-v -e 0 -l 1 -n 2/dev/sda1/dev/sdb1mdadm -C/dev/md1-v -e 0 -l 0 -n 2/dev/sda2/dev/sdb2
I set the metadata type to 0.90 because I would like to boot from it and allow the kernel to auto-detect the RAID devices while it's booting, in order to can get away from using an intitrd (I am building my own distribution based on CLFS x86_64 multilib).
I used cfdisk to partition both of the /dev/md[01] devices, and I obtained /dev/md0p[123] and /dev/md1p[12]. The plan is to use /dev/md0p1 as a RAID1 root partition, and have the system boot from /dev/md0. I formatted /dev/md0p1 as
Quoted 1 line
mk2efs -t ext4 -L OS
/dev/md0p1
To this point, things went smoothly. mdadm -D... and mdadm -E... did report back working devices as intended. Then mounted /dev/md0p1 on a directory called /root/os, and I did
Quoted 1 line
grub-install --root-directory=/root/os
/dev/md0
or
Quoted 1 line
grub-install --root-directory=/root/os "md0"
and I got a warning and an error message: "Embedding area is too small for core.img." and "Embedding is not possible, but this is required when the root device is on a RAID array or LVM volume."
What did I do wrong, and how do I fix it? Thanks ahead,
Tibor
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel
Quoted 1 line · Jordan Uggla
There are multiple problems with grub-reboot/savedefault/default=saved.
Hi,
I do intend to follow up on this; however, due to Christmas holidays and the like it won't be until the new year. I'm just sending this message to let you know that I'm not ignoring this thread. I have a slightly modified version of this patch queued up for testing.
Thanks,
Signature · 1 line
Colin Watson [[email protected]]
Quoted 8 lines · Jordan Uggla
1: grub-reboot doesn't restore the default after rebooting, making it
effectively equivalent to grub-set-default. This is because the
savedefault functionality currently saves the entry you boot from as the
new default even when grub-reboot is used. Because of problem #2 there is
no way ( outside manually editing the grub.cfg ) to use grub-reboot without
savedefault functionality also enabled ( and thus no configuration where grub-reboot isn't broken ). I've added a patch for this to the bug report @ http://savannah.gnu.org/bugs/?28161 and as the first patch
attached to this email.
Applied with a slight tweak, thanks.
Quoted 4 lines
2: Setting GRUB_DEFAULT=saved in
/etc/default/grubalso enables savedefault functionality. There are many people who would want to use grub-reboot and grub-set-default without savedefault. The second patch adds a separate variable,GRUB_SAVEDEFAULT, for enabling savedefault.
How would this interact with setting GRUB_DEFAULT to something other than "saved", and doesn't 00_header.in also need to be changed to actually use the saved entry (it won't be used unless you have GRUB_DEFAULT=saved)?
In my mind, GRUB_DEFAULT was overloaded for this for a good reason; but perhaps I'm simply not understanding what you mean, so would you mind explaining further how you'd like to see the whole thing laid out?
Quoted 8 lines
3: Savedefault functionality currently adds two lines ( one setting a variable, the other saving it ) to every menu entry and with my patch for fixing grub-reboot, an if statement as well. This clutters the menu entries and makes it hard to write and maintain custom menu entries with savedefault. The third patch moves this to a function "savedefault" defined in 00_header so entries just have to have "savedefault", like in grub legacy. And if the implementation of savedefault needs to change, custom menu entries will still work unmodified.
Makes sense. Applied.
Quoted 8 lines
4: Even with the first grub-reboot fix the default is still not restored when grubenv is not writable ( /boot on lvm for example ). Since grub-reboot can't work without a writable grubenv it's at least safer to boot into the "real" default instead of the "temporary" default. The fourth patch sets default=$prev_saved_default if
save_envfails. Grub-reboot ( the utility ) should also complain when grubenv isn't writable by grub. What is the best way to determine that grubenv likely is or isn't writable by grub?
I don't understand why /boot on LVM should mean that grubenv is not writable. The point of grubenv is to be a short chunk of contiguous reserved disk space which can be written by GRUB without needing any special filesystem intelligence. Surely LVM doesn't split up those 1024 bytes into different physical extents?
Quoted 10 lines
5: Since grub2 uses menu entry titles for savedefault instead of numbers, the default kernel booted won't be changed when the user installs a new kernel. Using titles instead of numbers is good, but an unfortunate consequence that users may not realize ( and was not true with savedefault in grub-legacy ) is that if users set GRUB_SAVEDEFAULT=true they will never actually boot any updated kernels unless they select them manually at the grub menu. This could lead to not getting security updates for the kernel, and with major upgrades could cause serious problems when the old kernel doesn't work with newer userland components like Xorg. I can't think of a good way to solve this problem.
IIRC GRUB Legacy (or at least Debian's update-grub, once upon a time) handled this by having a special "default" entry at the top of the Linux kernel list. Would it be worth introducing this to grub-mkconfig?
Signature · 1 line
Colin Watson [[email protected]]
Quoted 10 lines · Colin Watson
Quoted 9 lines
4: Even with the first grub-reboot fix the default is still not restored when grubenv is not writable ( /boot on lvm for example ). Since grub-reboot can't work without a writable grubenv it's at least safer to boot into the "real" default instead of the "temporary" default. The fourth patch sets default=$prev_saved_default if
save_envfails. Grub-reboot ( the utility ) should also complain when grubenv isn't writable by grub. What is the best way to determine that grubenv likely is or isn't writable by grub?
Quoted 7 lines
I don't understand why /boot on LVM should mean that grubenv is not writable. The point of grubenv is to be a short chunk of contiguous reserved disk space which can be written by GRUB without needing any special filesystem intelligence. Surely LVM doesn't split up those 1024 bytes into different physical extents?
Consider a RAID-1 (mirror). Currently GRUB2 uses only one disk of the mirror. It may even be possible that second disk is inaccessible through currently loaded drivers. But writing would need to update all copies. And what to do if second disk isn't accessible? Same problem is present in other RAID levels too.
ZFS and btrfs have checksums. If you update a block you have to update its checksum too, and checksum of block containg checksum and so on. So at the end of the day the whole is more complicated that what we wanted. And a mistake in writing code may lead to corruption of unrelated data. IMO for long-term we need a better place for grubenv.
Signature · 2 lines
Regards Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 7 lines · Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 5 lines · Colin Watson
I don't understand why /boot on LVM should mean that grubenv is not writable. The point of grubenv is to be a short chunk of contiguous reserved disk space which can be written by GRUB without needing any special filesystem intelligence. Surely LVM doesn't split up those 1024 bytes into different physical extents?
Quoted 9 lines
Consider a RAID-1 (mirror). Currently GRUB2 uses only one disk of the mirror. It may even be possible that second disk is inaccessible through currently loaded drivers. But writing would need to update all copies. And what to do if second disk isn't accessible? Same problem is present in other RAID levels too.
ZFS and btrfs have checksums. If you update a block you have to update its checksum too, and checksum of block containg checksum and so on. So at the end of the day the whole is more complicated that what we wanted. And a mistake in writing code may lead to corruption of unrelated data.
I see. Perhaps in the short term we could have an extension to the test command to allow grub.cfg to determine whether grubenv is sanely writable?
Quoted 1 line
IMO for long-term we need a better place for grubenv.
I agree, although the question is where. If there were enough space in the embedding area then we could reserve a chunk of that, but aren't we generally pretty close to the limit there?
Signature · 1 line
Colin Watson [[email protected]]
Quoted 9 lines · Colin Watson
Quoted 4 lines
2: Setting GRUB_DEFAULT=saved in
/etc/default/grubalso enables savedefault functionality. There are many people who would want to use grub-reboot and grub-set-default without savedefault. The second patch adds a separate variable,GRUB_SAVEDEFAULT, for enabling savedefault.How would this interact with setting
GRUB_DEFAULTto something other than "saved", and doesn't 00_header.in also need to be changed to actually use the saved entry (it won't be used unless you have GRUB_DEFAULT=saved)?
That's correct. Perhaps GRUB_SAVEDEFAULT=true should imply GRUB_DEFAULT=saved?
( if [ "x${GRUB_DEFAULT}" = "xsaved" -o x${GRUB_SAVEDEFAULT} = "xtrue" ] ; then GRUB_DEFAULT='${saved_entry}' ; )
Quoted 3 lines
In my mind,
GRUB_DEFAULTwas overloaded for this for a good reason; but perhaps I'm simply not understanding what you mean, so would you mind explaining further how you'd like to see the whole thing laid out?
I am not set on a single layout ( I'd like input from others on exactly how this should work ). But I do think that it should be possible to use utilities like grub-set-default and grub-reboot without also enabling savedefault. As an example use case:
Jane has a server with two boot options, one is a well tested, known working, configuration and the other is a system for experimenting. When she has physical access to the server she can select the experimental option and if anything goes wrong she can reboot into the stable configuration if needed. When she is administering the server remotely though, she needs to be sure that she won't completely lose access to the server if the experimental system fails to boot ( or bring up the network connection / ssh properly ).
Because of this problem, Jane always wants the safe configuration to be the default so that if anything goes wrong she can simply power cycle the machine and fix things from the working system ( I'm not sure why it's so common that people have the ability to remotely power cycle but no remote access the console to be able to use the grub menu, but every person I have talked to that uses grub-reboot is in such a situation ). If she is in the safe configuration and wants to remotely switch to the experimental one she uses grub-reboot to temporarily change the default. Note that she never wants to change the default permanently, but that's what savedefault will do if she selects the experimental boot option from the grub menu ( rather than grub-reboot ) and safedefault is being used.
I would guess, though I have no data to back it up, that in fact most grub-reboot users do not want to use savedefault.
Quoted 11 lines
Quoted 10 lines
5: Since grub2 uses menu entry titles for savedefault instead of numbers, the default kernel booted won't be changed when the user installs a new kernel. Using titles instead of numbers is good, but an unfortunate consequence that users may not realize ( and was not true with savedefault in grub-legacy ) is that if users set GRUB_SAVEDEFAULT=true they will never actually boot any updated kernels unless they select them manually at the grub menu. This could lead to not getting security updates for the kernel, and with major upgrades could cause serious problems when the old kernel doesn't work with newer userland components like Xorg. I can't think of a good way to solve this problem.
Quoted 3 lines
IIRC GRUB Legacy (or at least Debian's update-grub, once upon a time) handled this by having a special "default" entry at the top of the Linux kernel list. Would it be worth introducing this to grub-mkconfig?
Recently Vladimir has talked about having submenus ( one debian menu with separate kernel versions in a submenu for instance ) which will probably help solve this problem.
Signature · 1 line
Jordan Uggla ( Jordan_U on irc.freenode.net )
Quoted 11 lines · Jordan Uggla
Quoted 9 lines · Colin Watson
Quoted 4 lines
2: Setting GRUB_DEFAULT=saved in
/etc/default/grubalso enables savedefault functionality. There are many people who would want to use grub-reboot and grub-set-default without savedefault. The second patch adds a separate variable,GRUB_SAVEDEFAULT, for enabling savedefault.How would this interact with setting
GRUB_DEFAULTto something other than "saved", and doesn't 00_header.in also need to be changed to actually use the saved entry (it won't be used unless you have GRUB_DEFAULT=saved)?
Quoted 4 lines
That's correct. Perhaps GRUB_SAVEDEFAULT=true should imply GRUB_DEFAULT=saved?
( if [ "x${GRUB_DEFAULT}" = "xsaved" -o x${GRUB_SAVEDEFAULT} = "xtrue" ] ; then GRUB_DEFAULT='${saved_entry}' ; )
I've left them independent for now, since I'm not sure I've thought this through properly. (For instance you might want to change the saved entry but not use it right away for some reason?)
Quoted 8 lines
Quoted 3 lines
In my mind,
GRUB_DEFAULTwas overloaded for this for a good reason; but perhaps I'm simply not understanding what you mean, so would you mind explaining further how you'd like to see the whole thing laid out?I am not set on a single layout ( I'd like input from others on exactly how this should work ). But I do think that it should be possible to use utilities like grub-set-default and grub-reboot without also enabling savedefault. As an example use case:
[...]
Thanks for the explanation. I agree now, and have committed your patch.
Signature · 1 line
Colin Watson [[email protected]]