Hi!
In the attachment, you'll find a small and dumb patch for
grub that:
- allows usage of savedefault both from the
os command line and the boot time one
- allows to specify which default to save
- adds the command showdefault
The purpose of the changes is to allow smoother
home-compiled kernel updates on remotely located
servers.
For example, using the provided patch and
``default saved'' in menu.lst, after installing
a new kernel, I could:
1) add a new entry 0 in menu.lst without removing the
old ones, inserting a ``savedefault 1'' and
giving ``panic=10'' as a parameter to
the linux kernel
3) run grub from the command line (either at
boot time or from the grub shell) and give the
command ``savedefault 0''
4) reboot the system
During the first boot, the default entry 0 is chosen
since it was saved as the default one.
Once the entry is loaded, the default is set to 1 thanks
to ``savedefault 1''.
If the kernel panics, the parameter panic=10 will make
the system reboot in 10 seconds. During the following
boot up, grub will run entry 1 as default instead of
entry 0.
When the system boots up, an init script could
easily detect if the new kernel or the old one had
been loaded and do something smart about that. In
case of success, it could, for example, run
``savedefault 0'' to set again the new kernel as
default and remove ``savedefault 1'' from the menu.lst
and finally remove the old kernel.
Ok, the patch was made against grub 0.93 + cvs
20030224 as provided in the corresponding debian
package + the patch for the el torito hd boot
images. I wrote it since I needed to update the
kernel on some machines without being there.
It's just a one morning effort, so don't blame
me too much. I just thought it could be useful to
others. It should apply cleanly on the vanilla
version of grub, beside maybe a reject in
stage2/builtins.c due to patch not being able
to figure out where to add &builtin_showdefault
in builtin_table[].
A couple defines avoid conflicts with the el
torito patch due to missing ERR_CANT_SEEK and
COMPAT_VERSION being a char instead of a short.
They really should be removed, and a good sed
should make it.
Please consider that I'm not subscribed
to the mailing list and that I don't follow
closely the development of grub, so please
carbon copy any comment to my private email
address and forgive me if the issue had already
been discussed in the mailing list..
Bye,
Carlo
--
GPG Fingerprint: 2383 7B14 4D08 53A4 2C1A CA29 9E98 5431 1A68 6975
-------------
Perl itself is usually pretty good about telling you what you shouldn't
do. :-)
-- Larry Wall in <[EMAIL PROTECTED]>
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
[EMAIL PROTECTED] Command savedefault
-Save the current menu entry as a default entry. Here is an example:
[EMAIL PROTECTED] Command savedefault [EMAIL PROTECTED] [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,
[EMAIL PROTECTED] must be specified. In this case, grub will try to
+guess which stage2 will be modified by looking for it @file{/stage2}
[EMAIL PROTECTED]/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:
+
[EMAIL PROTECTED]
[EMAIL PROTECTED]
+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
[EMAIL PROTECTED] group
[EMAIL PROTECTED] 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:
+
[EMAIL PROTECTED]
+grub> @kbd{savedefault 0}
[EMAIL PROTECTED] 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}.
+
[EMAIL PROTECTED] deffn
+
+
[EMAIL PROTECTED] showdefault
[EMAIL PROTECTED] showdefault
+
[EMAIL PROTECTED] Command showdefault [EMAIL PROTECTED]
+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
[EMAIL PROTECTED] 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 */
_______________________________________________
Bug-grub mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/bug-grub