diff -Naur grub/docs/grub.texi grub.patched.orig/docs/grub.texi --- grub/docs/grub.texi 2003-04-02 11:17:51.000000000 +0200 +++ grub.patched.orig/docs/grub.texi 2003-04-02 11:17:43.000000000 +0200 @@ -2367,7 +2367,8 @@ * read:: Read data from memory * root:: Set GRUB's root device * rootnoverify:: Set GRUB's root device without mounting -* savedefault:: Save current entry as the default entry +* savedefault:: Save entry as the default entry +* showdefault:: Show the entry currently saved as the default * setup:: Set up GRUB's installation automatically * testload:: Load a file for testing a filesystem * testvbe:: Test VESA BIOS EXTENSION @@ -2828,8 +2829,19 @@ @node savedefault @subsection savedefault address@hidden Command savedefault -Save the current menu entry as a default entry. Here is an example: address@hidden Command savedefault address@hidden [default] +Invoked from the boot menu or from the configuration file, @command{savedefault} +saves the current menu entry or the specified @var{default} one as the +default entry. In this context, @var{stage2} cannot be specified. +Invoked from the command line when your system is up and running, address@hidden must be specified. In this case, grub will try to +guess which stage2 will be modified by looking for it @file{/stage2} address@hidden/boot/grub/stage2} or @file{/grub/stage2}, unless @var{stage2} +is specified as a parameter to the command. @var{stage2} can be +either an absolute path starting with "/" or a relative pathname. +In either case, grub will try to look for the stage2 image sequentially +in @file{/}, @file{/boot/grub} and @file{/grub}. +Here is a simple usage example: @example @group @@ -2851,6 +2863,62 @@ With this configuration, GRUB will choose the entry booted previously as the default entry. See also @ref{default}. + +This is a more complex example: + address@hidden address@hidden +default saved +timeout 10 + +title GNU/Linux +root (hd0,0) +kernel /boot/vmlinuz-2.4.20 root=/dev/sda1 vga=ext panic=10 +initrd /boot/initrd-2.4.20 +savedefault 1 + +title GNU/Linux +root (hd0,0) +kernel /boot/vmlinuz-2.4.19 root=/dev/sda1 vga=ext +initrd /boot/initrd-2.4.19 address@hidden group address@hidden example + +Let's suppose we have just compiled a new 2.4.20 and +we have installed it on a machine we don't have physical +access to and we know the good old 2.4.19 works well. +Now, with the above configuration and running the +following commands from the grub shell: + address@hidden +grub> @kbd{savedefault 0} address@hidden example + +after the first reboot, grub will try to run the new +2.4.20 kernel. In case the kernel panics, the "panic=10" +parameter given to the kernel will make it reboot in +10 seconds. This time, grub will load the kernel +2.4.19 since the last @command{savedefault} encountered told +grub next time to load the entry number 1. + +See also @ref{showdefault}. + address@hidden deffn + + address@hidden showdefault address@hidden showdefault + address@hidden Command showdefault address@hidden +Invoked from the boot menu or from the configuration file, @command{showdefault} +will show the current entry saved as the default entry. +In this context, @var{stage2} cannot be specified. +Invoked from the command line when your system is up and running, +grub will try to guess wich stage2 should be looked for unless address@hidden is specified. + +See also @ref{savedefault}. + @end deffn diff -Naur grub/stage2/builtins.c grub.patched.orig/stage2/builtins.c --- grub/stage2/builtins.c 2003-04-02 11:17:51.000000000 +0200 +++ grub.patched.orig/stage2/builtins.c 2003-04-02 11:20:00.000000000 +0200 @@ -3172,21 +3172,39 @@ /* savedefault */ +#ifndef STAGE2_COMPAT_VERSION_OFFS +# define STAGE2_COMPAT_VERSION_OFFS STAGE2_VER_MAJ_OFFS +# define version_cast short * +#else +# define version_cast unsigned char * +#endif + +#ifndef ERR_CANT_SEEK +# define ERR_CANT_SEEK ERR_WRITE +#endif static int savedefault_func (char *arg, int flags) { -#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL) +#if !defined(SUPPORT_DISKLESS) +#if !defined(GRUB_UTIL) char buffer[512]; + int set_entryno; int *entryno_ptr; - /* This command is only useful when you boot an entry from the menu - interface. */ - if (! (flags & BUILTIN_SCRIPT)) + if(*arg) { - errnum = ERR_UNRECOGNIZED; - return 1; + if (! safe_parse_maxint (&arg, &set_entryno)) + return 1; } - + else + set_entryno = current_entryno; + + /* Here, the default is saved based on the following assumptions: + * grub has booted, thus + * we know on which disk we booted on + * we know which stage2 we want to use and where it is saved + */ + /* Get the geometry of the boot drive (i.e. the disk which contains this stage2). */ if (get_diskinfo (boot_drive, &buf_geom)) @@ -3203,7 +3221,7 @@ /* Sanity check. */ if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2 - || *((short *) (buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION) + || *((version_cast) (buffer + STAGE2_COMPAT_VERSION_OFFS)) != COMPAT_VERSION) { errnum = ERR_BAD_VERSION; return 1; @@ -3212,10 +3230,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 != set_entryno) { /* Overwrite the saved entry number. */ - *entryno_ptr = current_entryno; + *entryno_ptr = set_entryno; /* Save the image in the disk. */ if (! rawwrite (boot_drive, install_second_sector, buffer)) @@ -3226,22 +3244,307 @@ } return 0; -#else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */ +#else /* GRUB_UTIL */ + /* Here, we can't make the same assumptions... + * grub has not booted, thus + * we don't know on which disk grub has been installed + * we don't know which stage2 we want to use and where it is saved + * + * so, we either need: + * to know where grub was installed + * which stage2 was used (or we want to use) + * actually, I like the second option. + */ + int set_entryno; + int *entryno_ptr; + char * stage2_supplied_file = "stage2"; + char stage2_file[64]; + char * stage2_buffer = (char *) RAW_ADDR (0x100000); + char * prefix[] = { "", "/boot/grub", "/grub", NULL }; + FILE * fp = NULL; + int i; + + /* Check if a stage2 image was provided */ + if(grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) + { + stage2_supplied_file = arg + sizeof ("--stage2=") - 1; + arg = skip_to (0, arg); + nul_terminate (stage2_supplied_file); + } + + /* Verify that we were given a default to save */ + if(! *arg) + { + grub_printf("You must specify a default to set.\n"); + return 1; + } + + if(! safe_parse_maxint (&arg, &set_entryno)) + return 1; + + /* Try to guess filename and open file */ + for (i=0; prefix[i] != NULL; i++) + { + grub_sprintf(stage2_file, "%s/%s", prefix[i], + stage2_supplied_file[0] == '/' ? stage2_supplied_file+1 : stage2_supplied_file); + grub_printf(" Trying to use \"%s\"... ", stage2_file); + fp=fopen(stage2_file, "r+"); + if (fp) + { + grub_printf("succeeded\n"); + break; + } + grub_printf("failed\n"); + } + + if (!fp) + { + grub_printf("Couldn't open any of the indicated files in read/write mode.\n"); + errnum = ERR_FILE_NOT_FOUND; + return 1; + } + + /* Skip first sector */ + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_CANT_SEEK; + return 1; + } + + /* Read second sector in memory */ + if (fread(stage2_buffer, SECTOR_SIZE, 1, fp) != 1) + { + fclose (fp); + errnum = ERR_READ; + return 1; + } + + /* Check stage version signature */ + if (*((version_cast) (stage2_buffer + STAGE2_COMPAT_VERSION_OFFS)) + != COMPAT_VERSION) + { + fclose(fp); + errnum = ERR_BAD_VERSION; + return 1; + } + + /* Check if it really is a stage2 */ + if (stage2_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2) + { + fclose(fp); + errnum = ERR_BAD_VERSION; + grub_printf("\"%s\" is not a stage2 image\n", stage2_file); + return 1; + } + + /* Finally, save default */ + entryno_ptr = (int *)(stage2_buffer + STAGE2_SAVED_ENTRYNO); + if(*entryno_ptr != set_entryno) + { + *entryno_ptr = set_entryno; + + /* Seek to the correct location */ + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_CANT_SEEK; + return 1; + } + + if (fwrite (stage2_buffer, SECTOR_SIZE, 1, fp) != 1) + { + fclose (fp); + errnum = ERR_WRITE; + return 1; + } + } + fclose(fp); + + return 0; +#endif /* GRUB_UTIL */ +#else /* ! SUPPORT_DISKLESS */ errnum = ERR_UNRECOGNIZED; return 1; -#endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */ +#endif /* ! SUPPORT_DISKLESS */ } static struct builtin builtin_savedefault = { "savedefault", savedefault_func, + +#ifdef SUPPORT_DISKLESS BUILTIN_CMDLINE, - "savedefault", - "Save the current entry as the default boot entry." +#else + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, +#endif + +#ifdef GRUB_UTIL + "savedefault [--stage2=stage2] number", + "Save the entry specified as the default boot entry" + " in the stage2 image specified with `--stage2'. If no image is specified," + " grub will try to guess which one to use." +#else + "savedefault [number]", + "Save the current entry or the specified one as the default boot entry to use." +#endif + }; +/* showdefault */ +static int +showdefault_func (char *arg, int flags) +{ +#if !defined(SUPPORT_DISKLESS) +#if !defined(GRUB_UTIL) + char buffer[512]; + int *entryno_ptr; + + /* Get the geometry of the boot drive (i.e. the disk which contains + this stage2). */ + if (get_diskinfo (boot_drive, &buf_geom)) + { + errnum = ERR_NO_DISK; + return 1; + } + + /* Load the second sector of this stage2. */ + if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer)) + { + return 1; + } + + /* Sanity check. */ + if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2 + || *((version_cast) (buffer + STAGE2_COMPAT_VERSION_OFFS)) != COMPAT_VERSION) + { + errnum = ERR_BAD_VERSION; + return 1; + } + + entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO); + grub_printf("Saved default entry: %d\n", *entryno_ptr); + + return 0; +#else /* GRUB_UTIL */ + int *entryno_ptr; + char * stage2_supplied_file = "stage2"; + char stage2_file[64]; + char * stage2_buffer = (char *) RAW_ADDR (0x100000); + char * prefix[] = { "", "/boot/grub", "/grub", NULL }; + FILE * fp = NULL; + int i; + + /* Check if a stage2 image was provided */ + if(grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) + { + stage2_supplied_file = arg + sizeof ("--stage2=") - 1; + arg = skip_to (0, arg); + nul_terminate (stage2_supplied_file); + } + + /* Try to guess filename and open file */ + for (i=0; prefix[i] != NULL; i++) + { + grub_sprintf(stage2_file, "%s/%s", prefix[i], + stage2_supplied_file[0] == '/' ? stage2_supplied_file+1 : stage2_supplied_file); + grub_printf(" Trying to use \"%s\"... ", stage2_file); + fp=fopen(stage2_file, "r"); + if (fp) + { + grub_printf("succeeded\n"); + break; + } + grub_printf("failed\n"); + } + + if (!fp) + { + errnum = ERR_FILE_NOT_FOUND; + return 1; + } + + /* Skip first sector */ + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_CANT_SEEK; + return 1; + } + + /* Read second sector in memory */ + if (fread(stage2_buffer, SECTOR_SIZE, 1, fp) != 1) + { + fclose (fp); + errnum = ERR_READ; + return 1; + } + + /* Check stage version signature */ + if (*((version_cast) (stage2_buffer + STAGE2_COMPAT_VERSION_OFFS)) + != COMPAT_VERSION) + { + fclose(fp); + errnum = ERR_BAD_VERSION; + return 1; + } + + /* Check if it really is a stage2 */ + if (stage2_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2) + { + fclose(fp); + errnum = ERR_BAD_VERSION; + grub_printf("\"%s\" is not a stage2 image\n", stage2_file); + return 1; + } + + /* Finally, show default */ + entryno_ptr = (int *)(stage2_buffer + STAGE2_SAVED_ENTRYNO); + grub_printf("\nSaved default entry: %d\n", *entryno_ptr); + fclose(fp); + + return 0; +#endif /* GRUB_UTIL */ +#else /* ! SUPPORT_DISKLESS */ + errnum = ERR_UNRECOGNIZED; + return 1; +#endif /* ! SUPPORT_DISKLESS */ +} + +static struct builtin builtin_showdefault = +{ + "showdefault", + showdefault_func, + +#ifdef SUPPORT_DISKLESS + BUILTIN_CMDLINE, +#else /* ! SUPPORT_DISKLESS */ + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, +#endif + +#ifdef GRUB_UTIL + "showdefault [--stage2=stage2]", + "Show the entry previously saved as default entry" + " in the specified stage2 image." + " If you use the `savedefault' directive, this is usually" + " the last entry you booted on from the menu or the last entry" + " you set with `savedefault'." + " If `--stage2' is not specified, grub will try to guess the" + " stage2 image to use by itself." +#else + "showdefault", + "Show the entry previously saved as default entry." + " If you use the `savedefault' directive, this is usually" + " the last entry you booted on from the menu or the last entry" + " you set with `savedefault'." +#endif + +}; + + + #ifdef SUPPORT_SERIAL /* serial */ static int @@ -4630,6 +4933,7 @@ #endif /* SUPPORT_SERIAL */ &builtin_setkey, &builtin_setup, + &builtin_showdefault, #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) &builtin_terminal, #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */