+2004-06-20 Yoshinori K. Okuji <okuji@enbug.org>
+
+ This is a big change on saving a default entry. This change
+ makes it possible to set up a quite robust system using GRUB.
+ Now we do not use the second sector of Stage 2 to store an
+ entry number but use the file /boot/grub/default. This file
+ must be generated by grub-set-default, although this file is
+ plain-text.
+
+ * util/grub-set-default.in: New file.
+
+ * util/grub-install.in (grub_set_default): New variable.
+ Use /grub instead of /boot/grub on OpenBSD as well as NetBSD.
+ Run grub-set-default to make a default file.
+
+ * util/Makefile.am (sbin_SCRIPTS): Added grub-set-default.
+
+ * stage2/stage2.c (run_menu): Change the fallback handling to
+ support multiple fallback entries.
+ (cmain): Likewise. Also, get a saved entry from a default file
+ if possible, before reading a config file.
+
+ * stage2/shared.h (DEFAULT_FILE_BUF): New macro.
+ (DEFAULT_FILE_BUFLEN): Likewise.
+ (CMDLINE_BUF): Set to DEFAULT_FILE_BUF + DEFAULT_FILE_BUFLEN.
+ (MENU_BUFLEN): Set to 0x8000 + PASSWORD_BUF - MENU_BUF.
+ (fallback_entry): Removed.
+ (fallback_entries): Declared.
+ (fallback_entryno): Likewise.
+ (MAX_FALLBACK_ENTRIES): New macro.
+
+ * stage2/cmdline.c (run_script): Use FALLBACK_ENTRYNO instead of
+ FALLBACK_ENTRY.
+
+ * stage2/builtins.c (fallback_entry): Removed.
+ (fallback_entryno): New variable.
+ (fallback_entries): Likewise.
+ (init_config): Initialize FALLBACK_ENTRYNO and FALLBACK_ENTRIES.
+ (fallback_func): Rewritten completely.
+ (savedefault_func): Likewise.
+
+ * docs/grub.texi (grub-set-default): New direntry.
+ (Installation): Describe grub-set-default for manual
+ installations.
+ (Making your system robust): New section.
+ (Booting once-only): New subsection.
+ (Booting fallback systems): Likewise.
+ (fallback): Describe multiple fallback entries.
+ (savedefault): Describe an optional argument.
+ (Invoking grub-set-default): New chapter.
+ (Future): Replaced with a description about GRUB 2.
+
+ * configure.ac (AC_CONFIG_FILES): Added util/grub-set-default.
+
2004-06-19 Yoshinori K. Okuji <okuji@enbug.org>
* stage2/ufs2.h (int8_t): Renamed to ...
NEWS - list of user-visible changes between releases of GRUB
+New:
+* The command "fallback" supports mutiple fallback entries.
+* The command "savedefault" supports an optional argument which
+ is the number of next boot entry or the special keyword `fallback'.
+* New utility "grub-set-default".
+* New section "Making your system robust" in the manual.
+
New in 0.95 - 2004-06-13:
* Add support for ReiserFS 3.
* Fix support for FreeBSD 5.
- ac_config_files="$ac_config_files Makefile stage1/Makefile stage2/Makefile docs/Makefile lib/Makefile util/Makefile grub/Makefile netboot/Makefile util/grub-image util/grub-install util/grub-md5-crypt util/grub-terminfo"
+ ac_config_files="$ac_config_files Makefile stage1/Makefile stage2/Makefile docs/Makefile lib/Makefile util/Makefile grub/Makefile netboot/Makefile util/grub-image util/grub-install util/grub-md5-crypt util/grub-terminfo util/grub-set-default"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
"util/grub-install" ) CONFIG_FILES="$CONFIG_FILES util/grub-install" ;;
"util/grub-md5-crypt" ) CONFIG_FILES="$CONFIG_FILES util/grub-md5-crypt" ;;
"util/grub-terminfo" ) CONFIG_FILES="$CONFIG_FILES util/grub-terminfo" ;;
+ "util/grub-set-default" ) CONFIG_FILES="$CONFIG_FILES util/grub-set-default" ;;
"depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
"config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
docs/Makefile lib/Makefile util/Makefile \
grub/Makefile netboot/Makefile util/grub-image \
util/grub-install util/grub-md5-crypt \
- util/grub-terminfo])
+ util/grub-terminfo util/grub-set-default])
AC_OUTPUT
* grub-terminfo: (grub)Invoking grub-terminfo. Generate a terminfo
command from a
terminfo name
+* grub-set-default: (grub)Invoking grub-set-default. Set a default boot
+ entry
* mbchk: (grub)Invoking mbchk. Check for the format of a Multiboot kernel
@end direntry
* Invoking grub-install:: How to use the GRUB installer
* Invoking grub-md5-crypt:: How to generate a cryptic password
* Invoking grub-terminfo:: How to generate a terminfo command
+* Invoking grub-set-default:: How to set a default boot entry
* Invoking mbchk:: How to use the Multiboot checker
* Obtaining and Building GRUB:: How to obtain and build GRUB
* Reporting bugs:: Where you should send a bug report
GRUB comes with boot images, which are normally put in the directory
@file{/usr/share/grub/i386-pc}. If you do not use grub-install, then
you need to copy the files @file{stage1}, @file{stage2}, and
-@file{*stage1_5} to the directory @file{/boot/grub}. Hereafter, the
-directory where GRUB images are initially placed (normally
-@file{/usr/share/grub/i386-pc}) will be called the @dfn{image
-directory}, and the directory where the boot loader needs to find them
-(usually @file{/boot/grub}) will be called the @dfn{boot directory}.
+@file{*stage1_5} to the directory @file{/boot/grub}, and run the
+@command{grub-set-default} (@pxref{Invoking grub-set-default}) if you
+intend to use @samp{default saved} (@pxref{default}) in your
+configuration file. Hereafter, the directory where GRUB images are
+initially placed (normally @file{/usr/share/grub/i386-pc}) will be
+called the @dfn{image directory}, and the directory where the boot
+loader needs to find them (usually @file{/boot/grub}) will be called
+the @dfn{boot directory}.
@menu
* Creating a GRUB boot floppy::
@menu
* General boot methods:: How to boot OSes with GRUB generally
* OS-specific notes:: Notes on some operating systems
+* Making your system robust:: How to make your system robust
@end menu
@end example
+@node Making your system robust
+@section How to make your system robust
+
+When you test a new kernel or a new OS, it is important to make sure
+that your computer can boot even if the new system is unbootable. This
+is crucial especially if you maintain servers or remote systems. To
+accomplish this goal, you need to set up two things:
+
+@enumerate
+@item
+You must maintain a system which is always bootable. For instance, if
+you test a new kernel, you need to keep a working kernel in a
+different place. And, it would sometimes be very nice to even have a
+complete copy of a working system in a different partition or disk.
+
+@item
+You must direct GRUB to boot a working system when the new system
+fails. This is possible with the @dfn{fallback} system in GRUB.
+@end enumerate
+
+The former requirement is very specific to each OS, so this
+documentation does not cover that topic. It is better to consult some
+backup tools.
+
+So let's see the GRUB part. There are two possibilities: one of them
+is quite simple but not very robust, and the other is a bit complex to
+set up but probably the best solution to make sure that your system
+can start as long as GRUB itself is bootable.
+
+@menu
+* Booting once-only::
+* Booting fallback systems::
+@end menu
+
+
+@node Booting once-only
+@subsection Booting once-only
+
+You can teach GRUB to boot an entry only at next boot time. Suppose
+that your have an old kernel @file{old_kernel} and a new kernel
+@file{new_kernel}. You know that @file{old_kernel} can boot
+your system correctly, and you want to test @file{new_kernel}.
+
+To ensure that your system will go back to the old kernel even if the
+new kernel fails (e.g. it panics), you can specify that GRUB should
+try the new kernel only once and boot the old kernel after that.
+
+First, modify your configuration file. Here is an example:
+
+@group
+@example
+default saved # This is important!!!
+timeout 10
+
+title the old kernel
+root (hd0,0)
+kernel /old_kernel
+savedefault
+
+title the new kernel
+root (hd0,0)
+kernel /new_kernel
+savedefault 0 # This is important!!!
+@end example
+@end group
+
+Note that this configuration file uses @samp{default saved}
+(@pxref{default}) at the head and @samp{savedefault 0}
+(@pxref{savedefault}) in the entry for the new kernel. This means
+that GRUB boots a saved entry by default, and booting the entry for the
+new kernel saves @samp{0} as the saved entry.
+
+With this configuration file, after all, GRUB always tries to boot the
+old kernel after it booted the new one, because @samp{0} is the entry
+of @code{the old kernel}.
+
+The next step is to tell GRUB to boot the new kernel at next boot
+time. For this, execute @command{grub-set-default} (@pxref{Invoking
+grub-set-default}):
+
+@example
+# @kbd{grub-set-default 1}
+@end example
+
+This command sets the saved entry to @samp{1}, that is, to the new
+kernel.
+
+This method is useful, but still not very robust, because GRUB stops
+booting, if there is any error in the boot entry, such that the new
+kernel has an invalid executable format. Thus, it it even better to
+use the @dfn{fallback} mechanism of GRUB. Look at next subsection for
+this feature.
+
+
+@node Booting fallback systems
+@subsection Booting fallback systems
+
+GRUB supports a fallback mechanism of booting one or more other
+entries if a default boot entry fails. You can specify multiple
+fallback entries if you wish.
+
+Suppose that you have three systems, @samp{A}, @samp{B} and
+@samp{C}. @samp{A} is a system which you want to boot by
+default. @samp{B} is a backup system which is supposed to boot
+safely. @samp{C} is another backup system which is used in case where
+@samp{B} is broken.
+
+Then you may want GRUB to boot the first system which is bootable
+among @samp{A}, @samp{B} and @samp{C}. A configuration file can be
+written in this way:
+
+@group
+@example
+default saved # This is important!!!
+timeout 10
+fallback 1 2 # This is important!!!
+
+title A
+root (hd0,0)
+kernel /kernel
+savedefault fallback # This is important!!!
+
+title B
+root (hd1,0)
+kernel /kernel
+savedefault fallback # This is important!!!
+
+title C
+root (hd2,0)
+kernel /kernel
+savedefault
+@end example
+@end group
+
+Note that @samp{default saved} (@pxref{default}), @samp{fallback 1 2}
+and @samp{savedefault fallback} are used. GRUB will boot a saved entry
+by default and save a fallback entry as next boot entry with this
+configuration.
+
+When GRUB tries to boot @samp{A}, GRUB saves @samp{1} as next boot
+entry, because the command @command{fallback} specifies that @samp{1}
+is the first fallback entry. The entry @samp{1} is @samp{B}, so GRUB
+will try to boot @samp{B} at next boot time.
+
+Likewise, when GRUB tries to boot @samp{B}, GRUB saves @samp{2} as
+next boot entry, because @command{fallback} specifies @samp{2} as next
+fallback entry. This makes sure that GRUB will boot @samp{C} after
+booting @samp{B}.
+
+It is noteworthy that GRUB uses fallback entries both when GRUB
+itself fails in booting an entry and when @samp{A} or @samp{B} fails
+in starting up your system. So this solution ensures that your system
+is started even if GRUB cannot find your kernel or if your kernel
+panics.
+
+However, you need to run @command{grub-set-default} (@pxref{Invoking
+grub-set-default}) when @samp{A} starts correctly or you fix @samp{A}
+after it crashes, since GRUB always sets next boot entry to a fallback
+entry. You should run this command in a startup script such as
+@file{rc.local} to boot @samp{A} by default:
+
+@example
+# @kbd{grub-set-default 0}
+@end example
+
+where @samp{0} is the number of the boot entry for the system
+@samp{A}.
+
+If you want to see what is current default entry, you can look at the
+file @file{/boot/grub/default} (or @file{/grub/default} in
+some systems). Because this file is plain-text, you can just
+@command{cat} this file. But it is strongly recommended @strong{not to
+modify this file directly}, because GRUB may fail in saving a default
+entry in this file, if you change this file in an unintended
+manner. Therefore, you should use @command{grub-set-default} when you
+need to change the default entry.
+
+
@node Configuration
@chapter Configuration
@node fallback
@subsection fallback
-@deffn Command fallback num
+@deffn Command fallback num...
Go into unattended boot mode: if the default boot entry has any errors,
instead of waiting for the user to do something, immediately start
over using the @var{num} entry (same numbering as the @code{default}
command (@pxref{default})). This obviously won't help if the machine was
-rebooted by a kernel that GRUB loaded.
+rebooted by a kernel that GRUB loaded. You can specify multiple
+fallback entry numbers.
@end deffn
@node savedefault
@subsection savedefault
-@deffn Command savedefault
-Save the current menu entry as a default entry. Here is an example:
+@deffn Command savedefault num
+Save the current menu entry or @var{num} if specified as a default
+entry. Here is an example:
@example
@group
@end example
With this configuration, GRUB will choose the entry booted previously as
-the default entry. See also @ref{default}.
+the default entry.
+
+You can specify @samp{fallback} instead of a number. Then, next
+fallback entry is saved. Next fallback entry is chosen from fallback
+entries. Normally, this will be the first entry in fallback ones.
+
+See also @ref{default} and @ref{Invoking grub-set-default}.
@end deffn
@end example
+@node Invoking grub-set-default
+@chapter Invoking grub-set-default
+
+The program @command{grub-set-default} sets the default boot entry for
+GRUB. This automatically creates a file named @file{default} under
+your GRUB directory (i.e. @file{/boot/grub}), if it is not
+present. This file is used to determine the default boot entry when
+GRUB boots up your system when you use @samp{default saved} in your
+configuration file (@pxref{default}), and to save next default boot
+entry when you use @samp{savedefault} in a boot entry
+(@pxref{savedefault}).
+
+@command{grub-set-default} accepts the following options:
+
+@table @option
+@item --help
+Print a summary of the command-line options and exit.
+
+@item --version
+Print the version information and exit.
+
+@item --root-directory=@var{dir}
+Use the directory @var{dir} instead of the root directory
+(i.e. @file{/}) to define the location of the default file. This
+is useful when you mount a disk which is used for another system.
+@end table
+
+You must specify a single argument to @command{grub-set-default}. This
+argument is normally the number of a default boot entry. For example,
+if you have this configuration file:
+
+@group
+@example
+default saved
+timeout 10
+
+title GNU/Hurd
+root (hd0,0)
+...
+
+title GNU/Linux
+root (hd0,1)
+...
+@end example
+@end group
+
+and if you want to set the next default boot entry to GNU/Linux, you
+may execute this command:
+
+@example
+@kbd{grub-set-default 1}
+@end example
+
+Because the entry for GNU/Linux is @samp{1}. Note that entries are
+counted from zero. So, if you want to specify GNU/Hurd here, then you
+should specify @samp{0}.
+
+This feature is very useful if you want to test a new kernel or to
+make your system quite robust. @xref{Making your system robust}, for
+more hints about how to set up a robust system.
+
+
@node Invoking mbchk
@chapter Invoking mbchk
@node Future
@chapter Where GRUB will go
-Here are some ideas of what might happen in the future:
-
-@itemize @bullet
-@item
-Support dynamic loading.
-
-@item
-Add real memory management.
-
-@item
-Add a real scripting language.
-
-@item
-Support internationalization.
-
-@item
-Support other architectures than i386-pc.
-@end itemize
-
-See the file @file{TODO} in the source distribution, for more
-information.
+We started the next generation of GRUB, GRUB 2. This will include
+internationalization, dynamic module loading, real memory management,
+multiple architecture support, a scripting language, and many other
+nice feature. If you are interested in the development of GRUB 2, take
+a look at @uref{http://www.gnu.org/software/grub/grub.html, the
+homepage}.
@c Separate the programming guide.
-@set UPDATED 11 May 2004
-@set UPDATED-MONTH May 2004
+@set UPDATED 20 June 2004
+@set UPDATED-MONTH June 2004
@set EDITION 0.95
@set VERSION 0.95
-@set UPDATED 11 May 2004
-@set UPDATED-MONTH May 2004
+@set UPDATED 20 June 2004
+@set UPDATED-MONTH June 2004
@set EDITION 0.95
@set VERSION 0.95
VARIABLE(install_partition)
.long 0xFFFFFF
+/* This variable is here only because of a historical reason. */
VARIABLE(saved_entryno)
.long 0
VARIABLE(stage2_id)
/* The default entry. */
int default_entry = 0;
/* The fallback entry. */
-int fallback_entry = -1;
+int fallback_entryno;
+int fallback_entries[MAX_FALLBACK_ENTRIES];
/* The number of current entry. */
int current_entryno;
/* The address for Multiboot command-line buffer. */
{
default_entry = 0;
password = 0;
- fallback_entry = -1;
+ fallback_entryno = -1;
+ fallback_entries[0] = -1;
grub_timeout = -1;
}
static int
fallback_func (char *arg, int flags)
{
- if (! safe_parse_maxint (&arg, &fallback_entry))
- return 1;
+ int i = 0;
+
+ while (*arg)
+ {
+ int entry;
+ int j;
+
+ if (! safe_parse_maxint (&arg, &entry))
+ return 1;
+
+ /* Remove duplications to prevent infinite looping. */
+ for (j = 0; j < i; j++)
+ if (entry == fallback_entries[j])
+ break;
+ if (j != i)
+ continue;
+
+ fallback_entries[i++] = entry;
+ if (i == MAX_FALLBACK_ENTRIES)
+ break;
+
+ arg = skip_to (0, arg);
+ }
+
+ if (i < MAX_FALLBACK_ENTRIES)
+ fallback_entries[i] = -1;
+ fallback_entryno = (i == 0) ? -1 : 0;
+
return 0;
}
fallback_func,
BUILTIN_MENU,
#if 0
- "fallback NUM",
+ "fallback NUM...",
"Go into unattended boot mode: if the default boot entry has any"
" errors, instead of waiting for the user to do anything, it"
" immediately starts over using the NUM entry (same numbering as the"
savedefault_func (char *arg, int flags)
{
#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
- char buffer[512];
- int *entryno_ptr;
+ unsigned long tmp_drive = saved_drive;
+ unsigned long tmp_partition = saved_partition;
+ char *default_file = (char *) DEFAULT_FILE_BUF;
+ char buf[10];
+ char sect[SECTOR_SIZE];
+ int entryno;
+ int sector_count = 0;
+ int saved_sectors[2];
+ int saved_offsets[2];
+ int saved_lengths[2];
+
+ /* Save sector information about at most two sectors. */
+ auto void disk_read_savesect_func (int sector, int offset, int length);
+ void disk_read_savesect_func (int sector, int offset, int length)
+ {
+ if (sector_count < 2)
+ {
+ saved_sectors[sector_count] = sector;
+ saved_offsets[sector_count] = offset;
+ saved_lengths[sector_count] = length;
+ }
+ sector_count++;
+ }
/* This command is only useful when you boot an entry from the menu
interface. */
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))
- {
- errnum = ERR_NO_DISK;
- return 1;
- }
- /* Load the second sector of this stage2. */
- if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer))
+ /* Determine a saved entry number. */
+ if (*arg)
{
- return 1;
- }
+ if (grub_memcmp (arg, "fallback", sizeof ("fallback") - 1) == 0)
+ {
+ int i;
+ int index = 0;
+
+ for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
+ {
+ if (fallback_entries[i] < 0)
+ break;
+ if (fallback_entries[i] == current_entryno)
+ {
+ index = i + 1;
+ break;
+ }
+ }
+
+ if (index >= MAX_FALLBACK_ENTRIES || fallback_entries[index] < 0)
+ {
+ /* This is the last. */
+ errnum = ERR_BAD_ARGUMENT;
+ 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 = fallback_entries[index];
+ }
+ else if (! safe_parse_maxint (&arg, &entryno))
+ return 1;
}
-
- entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO);
+ else
+ entryno = current_entryno;
- /* Check if the saved entry number differs from current entry number. */
- if (*entryno_ptr != current_entryno)
+ /* Open the default file. */
+ saved_drive = boot_drive;
+ saved_partition = install_partition;
+ if (grub_open (default_file))
{
- /* Overwrite the saved entry number. */
- *entryno_ptr = current_entryno;
+ int len;
- /* Save the image in the disk. */
- if (! rawwrite (boot_drive, install_second_sector, buffer))
- return 1;
+ disk_read_hook = disk_read_savesect_func;
+ len = grub_read (buf, sizeof (buf));
+ disk_read_hook = 0;
+ grub_close ();
+
+ if (len != sizeof (buf))
+ {
+ /* This is too small. Do not modify the file manually, please! */
+ errnum = ERR_READ;
+ goto fail;
+ }
+
+ if (sector_count > 2)
+ {
+ /* Is this possible?! Too fragmented! */
+ errnum = ERR_FSYS_CORRUPT;
+ goto fail;
+ }
+ /* Set up a string to be written. */
+ grub_memset (buf, '\n', sizeof (buf));
+ grub_sprintf (buf, "%d", entryno);
+
+ if (saved_lengths[0] < sizeof (buf))
+ {
+ /* The file is anchored to another file and the first few bytes
+ are spanned in two sectors. Uggh... */
+ if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
+ sect))
+ goto fail;
+ grub_memmove (sect + saved_offsets[0], buf, saved_lengths[0]);
+ if (! rawwrite (current_drive, saved_sectors[0], sect))
+ goto fail;
+
+ if (! rawread (current_drive, saved_sectors[1], 0, SECTOR_SIZE,
+ sect))
+ goto fail;
+ grub_memmove (sect + saved_offsets[1],
+ buf + saved_lengths[0],
+ sizeof (buf) - saved_lengths[0]);
+ if (! rawwrite (current_drive, saved_sectors[1], sect))
+ goto fail;
+ }
+ else
+ {
+ /* This is a simple case. It fits into a single sector. */
+ if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
+ sect))
+ goto fail;
+ grub_memmove (sect + saved_offsets[0], buf, sizeof (buf));
+ if (! rawwrite (current_drive, saved_sectors[0], sect))
+ goto fail;
+ }
+
/* Clear the cache. */
buf_track = -1;
}
- return 0;
+ fail:
+ saved_drive = tmp_drive;
+ saved_partition = tmp_partition;
+ return errnum;
#else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
errnum = ERR_UNRECOGNIZED;
return 1;
"savedefault",
savedefault_func,
BUILTIN_CMDLINE,
- "savedefault",
- "Save the current entry as the default boot entry."
+ "savedefault [NUM | `fallback']",
+ "Save the current entry as the default boot entry if no argument is"
+ " specified. If a number is specified, this number is saved. If"
+ " `fallback' is used, next fallback entry is saved."
};
\f
/* If a fallback entry is defined, don't prompt a user's
intervention. */
- if (fallback_entry < 0)
+ if (fallback_entryno >= 0)
{
grub_printf ("\nPress any key to continue...");
(void) getkey ();
#define PASSWORD_BUF RAW_ADDR (0x78000)
#define PASSWORD_BUFLEN 0x200
+/* THe buffer for the filename of "/boot/grub/default". */
+#define DEFAULT_FILE_BUF (PASSWORD_BUF + PASSWORD_BUFLEN)
+#define DEFAULT_FILE_BUFLEN 0x60
+
/* The buffer for the command-line. */
-#define CMDLINE_BUF (PASSWORD_BUF + PASSWORD_BUFLEN)
+#define CMDLINE_BUF (DEFAULT_FILE_BUF + DEFAULT_FILE_BUFLEN)
#define CMDLINE_BUFLEN MAX_CMDLINE
/* The kill buffer for the command-line. */
/* The buffer for the menu entries. */
#define MENU_BUF (UNIQUE_BUF + UNIQUE_BUFLEN)
-#define MENU_BUFLEN (0x8000 + PASSWORD_BUF - UNIQUE_BUF)
+#define MENU_BUFLEN (0x8000 + PASSWORD_BUF - MENU_BUF)
/* The size of the drive map. */
#define DRIVE_MAP_SIZE 8
#ifndef STAGE1_5
/* GUI interface variables. */
-extern int fallback_entry;
+# define MAX_FALLBACK_ENTRIES 8
+extern int fallback_entries[MAX_FALLBACK_ENTRIES];
+extern int fallback_entryno;
extern int default_entry;
extern int current_entryno;
gotoxy (3, 22);
printf (" ");
grub_timeout = -1;
- fallback_entry = -1;
+ fallback_entryno = -1;
if (! (current_term->flags & TERM_DUMB))
gotoxy (74, 4 + entryno);
}
if (run_script (cur_entry, heap))
{
- if (fallback_entry < 0)
- break;
- else
+ if (fallback_entryno >= 0)
{
cur_entry = NULL;
first_entry = 0;
- entryno = fallback_entry;
- fallback_entry = -1;
+ entryno = fallback_entries[fallback_entryno];
+ fallback_entryno++;
+ if (fallback_entryno >= MAX_FALLBACK_ENTRIES
+ || fallback_entries[fallback_entryno] < 0)
+ fallback_entryno = -1;
}
+ else
+ break;
}
else
break;
menu_entries = (char *) MENU_BUF;
init_config ();
}
-
+
/* Initialize the environment for restarting Stage 2. */
grub_setjmp (restart_env);
if (use_config_file)
#endif /* GRUB_UTIL */
{
+ char *default_file = (char *) DEFAULT_FILE_BUF;
+ int i;
+
+ /* Get a saved default entry if possible. */
+ saved_entryno = 0;
+ grub_strncat (default_file, config_file, DEFAULT_FILE_BUFLEN);
+ for (i = grub_strlen(default_file); i >= 0; i--)
+ if (default_file[i] == '/')
+ {
+ i++;
+ break;
+ }
+ grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i);
+ if (grub_open (default_file))
+ {
+ char buf[10]; /* This is good enough. */
+ char *p = buf;
+ int len;
+
+ len = grub_read (buf, sizeof (buf));
+ if (len > 0)
+ {
+ buf[sizeof (buf) - 1] = 0;
+ safe_parse_maxint (&p, &saved_entryno);
+ }
+
+ grub_close ();
+ }
+ errnum = ERR_NONE;
+
do
{
/* STATE 0: Before any title command.
grub_memmove (config_entries + config_len, menu_entries,
menu_len);
menu_entries = config_entries + config_len;
-
+
+ /* Make sure that all fallback entries are valid. */
+ if (fallback_entryno >= 0)
+ {
+ for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
+ {
+ if (fallback_entries[i] < 0)
+ break;
+ if (fallback_entries[i] >= num_entries)
+ {
+ grub_memmove (fallback_entries + i,
+ fallback_entries + i + 1,
+ ((MAX_FALLBACK_ENTRIES - i - 1)
+ * sizeof (int)));
+ i--;
+ }
+ }
+
+ if (fallback_entries[0] < 0)
+ fallback_entryno = -1;
+ }
/* Check if the default entry is present. Otherwise reset
it to fallback if fallback is valid, or to DEFAULT_ENTRY
if not. */
if (default_entry >= num_entries)
{
- if (fallback_entry < 0 || fallback_entry >= num_entries)
- default_entry = 0;
+ if (fallback_entryno >= 0)
+ {
+ default_entry = fallback_entries[0];
+ fallback_entryno++;
+ if (fallback_entryno >= MAX_FALLBACK_ENTRIES
+ || fallback_entries[fallback_entryno] < 0)
+ fallback_entryno = -1;
+ }
else
- default_entry = fallback_entry;
+ default_entry = 0;
}
if (is_preset)
bin_PROGRAMS = mbchk
-sbin_SCRIPTS = grub-install grub-md5-crypt grub-terminfo
+sbin_SCRIPTS = grub-install grub-md5-crypt grub-terminfo \
+ grub-set-default
noinst_SCRIPTS = grub-image mkbimage
EXTRA_DIST = mkbimage
subdir = util
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
$(srcdir)/grub-image.in $(srcdir)/grub-install.in \
- $(srcdir)/grub-md5-crypt.in $(srcdir)/grub-terminfo.in
+ $(srcdir)/grub-md5-crypt.in $(srcdir)/grub-set-default.in \
+ $(srcdir)/grub-terminfo.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.ac
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES = grub-image grub-install grub-md5-crypt \
- grub-terminfo
+ grub-terminfo grub-set-default
am__installdirs = $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir)
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
-sbin_SCRIPTS = grub-install grub-md5-crypt grub-terminfo
+sbin_SCRIPTS = grub-install grub-md5-crypt grub-terminfo \
+ grub-set-default
+
noinst_SCRIPTS = grub-image mkbimage
EXTRA_DIST = mkbimage
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
grub-terminfo: $(top_builddir)/config.status $(srcdir)/grub-terminfo.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+grub-set-default: $(top_builddir)/config.status $(srcdir)/grub-set-default.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkdir_p) $(DESTDIR)$(bindir)
pkgdatadir=${datadir}/${PACKAGE}/${host_cpu}-${host_vendor}
grub_shell=${sbindir}/grub
+grub_set_default=${sbindir}/grub-set-default
log_file=/tmp/grub-install.log.$$
img_file=/tmp/grub-install.img.$$
rootdir=
# Initialize these directories here, since ROOTDIR was initialized.
case "$host_os" in
-netbsd*)
- # Because /boot is used for the boot block in NetBSD, use /grub
+netbsd* | openbsd*)
+ # Because /boot is used for the boot block in NetBSD and OpenBSD, use /grub
# instead of /boot/grub.
grub_prefix=/grub
bootdir=${rootdir}
cp -f $file ${grubdir} || exit 1
done
+# Make a default file.
+${grub_set_default} --root-directory=${rootdir} default
+
# Make sure that GRUB reads the same images as the host OS.
test -n "$mkimg" && img_file=`$mkimg`
test -n "$mklog" && log_file=`$mklog`
--- /dev/null
+#! /bin/sh
+
+# Set a default boot entry for GRUB
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# Initialize some variables.
+PACKAGE=@PACKAGE@
+VERSION=@VERSION@
+
+rootdir=
+entry=
+
+# Usage: usage
+# Print the usage.
+usage () {
+ cat <<EOF
+Usage: grub-set-default [OPTION] entry
+Set the default boot entry for GRUB.
+
+ -h, --help print this message and exit
+ -v, --version print the version information and exit
+ --root-directory=DIR Use the directory DIR instead of the root directory
+
+ENTRY is a number or the special keyword \`default'.
+
+Report bugs to <bug-grub@gnu.org>.
+EOF
+}
+
+# Check the arguments.
+for option in "$@"; do
+ case "$option" in
+ -h | --help)
+ usage
+ exit 0 ;;
+ -v | --version)
+ echo "grub-set-default (GNU GRUB ${VERSION})"
+ exit 0 ;;
+ --root-directory=*)
+ rootdir=`echo "$option" | sed 's/--root-directory=//'` ;;
+ -*)
+ echo "Unrecognized option \`$option'" 1>&2
+ usage
+ exit 1
+ ;;
+ *)
+ if test "x$entry" != x; then
+ echo "More than one entries?" 1>&2
+ usage
+ exit 1
+ fi
+ # We don't care about what the user specified actually.
+ entry="${option}" ;;
+ esac
+done
+
+if test "x$entry" = x; then
+ echo "entry not specified." 1>&2
+ usage
+ exit 1
+fi
+
+# Determine the GRUB directory. This is different among OSes.
+grubdir=${rootdir}/boot/grub
+if test -d ${grubdir}; then
+ :
+else
+ grubdir=${rootdir}/grub
+ if test -d ${grubdir}; then
+ :
+ else
+ echo "No GRUB directory found under ${rootdir}/" 1>&2
+ exit 1
+ fi
+fi
+
+file=${grubdir}/default
+if test -f ${file}; then
+ chmod 0600 ${file}
+ rm -f ${file}
+fi
+cat <<EOF > $file
+$entry
+#
+#
+#
+#
+#
+#
+#
+#
+#
+#
+# WARNING: If you want to edit this file directly, do not remove any line
+# from this file, including this warning. Using `grub-set-default' is
+# strongly recommended.
+EOF
+
+# Bye.
+exit 0