Submitted By: Oliver Brakmann Date: 2003-08-17 Initial Package Version: 0.93 Origin: http://mail.gnu.org/archive/html/bug-grub/2002-02/msg00151.html http://mail.gnu.org/archive/html/bug-grub/2002-03/msg00011.html modifications for grub 0.93 by me Description: The following is patch to grub-0.90 which provides one-shot setting of the default boot entry. If the 'savedentry' field in the stage2 file has a ONCEONLY flag set, the entry is reset to zero at boot time. The 'savedefault' function has been rewritten for the GRUB shell, do that you can do something like: # savedefault --default=1 --once This specifies a one-shot change of the default to entry 1. A one-shot entry is latched by the 'default' command at boot time, even if the 'default' argument is not specified (ie. one-shot overrides a numeric argument). The patch is pretty clean -- it works perfectly against 0.91 as well (except the ChangeLog update). The rewritten 'savedefault' may need fixing up -- in particular, it will only patch a stage2 file which is accessible via a mounted filesystem. [...] The following is a slight modification to the patch I submitted a week or so ago. This will cause GRUB to skip displaying the boot menu if a one-shot default has been specified (just as lilo does with '-R'). Same caveats as for previous patch: * The new 'savedefault' function in the GRUB shell is incomplete. Only works if the filesystem containing stage2 file is mounted. * Patch is against grub-0.90, but patches directly to 0.91 I hope this goes into CVS in the next round of updates :-) -- Keir Fraser diff -Naur grub-0.93-orig/ChangeLog grub-0.93/ChangeLog --- grub-0.93-orig/ChangeLog 2003-08-17 11:27:16.000000000 +0000 +++ grub-0.93/ChangeLog 2003-08-17 11:27:39.000000000 +0000 @@ -672,6 +672,13 @@ * stage2/cmdline.c (run_script): Prompt a user's intervention, only when FALLBACK_ENTRY is negative. +2002-02-22 Keir Fraser + + * stage2/shared.h: New flag STAGE2_ONCEONLY_ENTRY causes + SAVED_ENTRYNO to be reset to zero on next reboot. + * stage2/builtins.c: A new version of 'savedefault' function + for the GRUB shell. Provides 'lilo -R' functionality. + 2002-02-11 Pavel Roskin * util/grub-install.in (find_device): New function - find block diff -Naur grub-0.93-orig/stage2/builtins.c grub-0.93/stage2/builtins.c --- grub-0.93-orig/stage2/builtins.c 2003-08-17 11:27:16.000000000 +0000 +++ grub-0.93/stage2/builtins.c 2003-08-17 11:27:39.000000000 +0000 @@ -760,6 +760,17 @@ default_func (char *arg, int flags) { #ifndef SUPPORT_DISKLESS +#ifndef GRUB_UTIL + /* Has a forced once-only default been specified? */ + static int savedefault_helper(int); + if ((saved_entryno & STAGE2_ONCEONLY_ENTRY) != 0) + { + grub_timeout = 0; + default_entry = saved_entryno & ~STAGE2_ONCEONLY_ENTRY; + savedefault_helper(0); + return 0; + } +#endif if (grub_strcmp (arg, "saved") == 0) { default_entry = saved_entryno; @@ -3175,21 +3186,14 @@ /* savedefault */ +#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL) +/* Write specified default entry number into stage2 file. */ static int -savedefault_func (char *arg, int flags) +savedefault_helper(int new_default) { -#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL) char buffer[512]; int *entryno_ptr; - - /* This command is only useful when you boot an entry from the menu - interface. */ - if (! (flags & BUILTIN_SCRIPT)) - { - errnum = ERR_UNRECOGNIZED; - return 1; - } - + /* Get the geometry of the boot drive (i.e. the disk which contains this stage2). */ if (get_diskinfo (boot_drive, &buf_geom)) @@ -3215,10 +3219,10 @@ entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO); /* Check if the saved entry number differs from current entry number. */ - if (*entryno_ptr != current_entryno) + if (*entryno_ptr != new_default) { /* Overwrite the saved entry number. */ - *entryno_ptr = current_entryno; + *entryno_ptr = new_default; /* Save the image in the disk. */ if (! rawwrite (boot_drive, install_second_sector, buffer)) @@ -3229,6 +3233,117 @@ } return 0; +} +#endif + +#if !defined(SUPPORT_DISKLESS) && defined(GRUB_UTIL) +/* + * Full implementation of new avedefault' for GRUB shell. + * XXX This needs fixing for stage2 files which aren't accessible + * through a mounted filesystem. + */ +static int +savedefault_shell(char *arg, int flags) +{ + char *stage2_os_file = "/boot/grub/stage2"; /* Default filename */ + FILE *fp; + char buffer[512]; + int *entryno_ptr; + int new_default = 0; + + while (1) + { + if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) + { + stage2_os_file = arg + sizeof ("--stage2=") - 1; + arg = skip_to (0, arg); + nul_terminate (stage2_os_file); + } + else if (grub_memcmp ("--default=", arg, sizeof ("--default=") - 1) == 0) + { + char *p = arg + sizeof ("--default=") - 1; + if (! safe_parse_maxint (&p, &new_default)) + return 1; + arg = skip_to (0, arg); + } + else if (grub_memcmp ("--once", arg, sizeof ("--once") - 1) == 0) + { + new_default |= STAGE2_ONCEONLY_ENTRY; + arg = skip_to (0, arg); + } + else + break; + } + + if (! (fp = fopen(stage2_os_file, "r+"))) + { + errnum = ERR_FILE_NOT_FOUND; + return 1; + } + + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_BAD_VERSION; + return 1; + } + + if (fread (buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) + { + fclose (fp); + errnum = ERR_READ; + return 1; + } + + /* Sanity check. */ + if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2 + || *((short *) (buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION) + { + errnum = ERR_BAD_VERSION; + return 1; + } + + entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO); + *entryno_ptr = new_default; + + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_BAD_VERSION; + return 1; + } + + if (fwrite (buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) + { + fclose (fp); + errnum = ERR_WRITE; + return 1; + } + + (void)fflush (fp); + fclose (fp); + return 0; +} +#endif + +/* savedefault */ +static int +savedefault_func (char *arg, int flags) +{ +#if !defined(SUPPORT_DISKLESS) +#if !defined(GRUB_UTIL) + /* This command is only useful when you boot an entry from the menu + interface. */ + if (! (flags & BUILTIN_SCRIPT)) + { + errnum = ERR_UNRECOGNIZED; + return 1; + } + + return savedefault_helper(current_entryno); +#else /* defined(GRUB_UTIL) */ + return savedefault_shell(arg, flags); +#endif #else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */ errnum = ERR_UNRECOGNIZED; return 1; @@ -3240,8 +3355,14 @@ "savedefault", savedefault_func, BUILTIN_CMDLINE, +#ifdef GRUB_UTIL + "savedefault [--stage2=STAGE2_FILE] [--default=DEFAULT] [--once]", + "Save DEFAULT as the default boot entry in STAGE2_FILE. If '--once'" + " is specified, the default is reset after the next reboot." +#else "savedefault", "Save the current entry as the default boot entry." +#endif }; @@ -4458,6 +4579,15 @@ static int timeout_func (char *arg, int flags) { + /* One-shot default shenanigans -- don't piss around with the menu! */ + if (grub_timeout != -1) + return 0; + if ((saved_entryno & STAGE2_ONCEONLY_ENTRY) != 0) + { + grub_timeout = 0; + return 0; + } + if (! safe_parse_maxint (&arg, &grub_timeout)) return 1; diff -Naur grub-0.93-orig/stage2/shared.h grub-0.93/stage2/shared.h --- grub-0.93-orig/stage2/shared.h 2003-08-17 11:27:16.000000000 +0000 +++ grub-0.93/stage2/shared.h 2003-08-17 11:27:39.000000000 +0000 @@ -195,6 +195,8 @@ #define STAGE2_FORCE_LBA 0x11 #define STAGE2_VER_STR_OFFS 0x12 +#define STAGE2_ONCEONLY_ENTRY 0x10000 + /* Stage 2 identifiers */ #define STAGE2_ID_STAGE2 0 #define STAGE2_ID_FFS_STAGE1_5 1