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.
This commit is contained in:
okuji 2004-06-20 13:48:47 +00:00
parent e15e075464
commit 5b5f6dc772
16 changed files with 705 additions and 98 deletions

View file

@ -1,3 +1,57 @@
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 ...

7
NEWS
View file

@ -1,5 +1,12 @@
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.

3
configure vendored
View file

@ -6109,7 +6109,7 @@ CCASFLAGS='$(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)'
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
@ -6728,6 +6728,7 @@ do
"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

View file

@ -666,5 +666,5 @@ 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-terminfo util/grub-set-default])
AC_OUTPUT

View file

@ -27,6 +27,8 @@
* 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
@ -113,6 +115,7 @@ This edition documents version @value{VERSION}.
* 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
@ -475,11 +478,14 @@ if, by any chance, your hard drive becomes unusable (unbootable).
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::
@ -734,6 +740,7 @@ magic.
@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
@ -1068,6 +1075,184 @@ grub> @kbd{boot}
@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
@ -1952,12 +2137,13 @@ default entry is the entry saved with the command
@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
@ -2962,8 +3148,9 @@ derived from attempting the mount will @emph{not} work correctly.
@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
@ -2984,7 +3171,13 @@ savedefault
@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
@ -3559,6 +3752,68 @@ You must specify one argument to this command. For example:
@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
@ -3695,27 +3950,12 @@ Once we get your report, we will try to fix the bugs.
@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.

View file

@ -1,4 +1,4 @@
@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

View file

@ -1,4 +1,4 @@
@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

View file

@ -87,6 +87,7 @@ ENTRY(main)
VARIABLE(install_partition)
.long 0xFFFFFF
/* This variable is here only because of a historical reason. */
VARIABLE(saved_entryno)
.long 0
VARIABLE(stage2_id)

View file

@ -59,7 +59,8 @@ int debug = 0;
/* 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. */
@ -97,7 +98,8 @@ init_config (void)
{
default_entry = 0;
password = 0;
fallback_entry = -1;
fallback_entryno = -1;
fallback_entries[0] = -1;
grub_timeout = -1;
}
@ -1143,8 +1145,34 @@ static struct builtin builtin_embed =
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;
}
@ -1155,7 +1183,7 @@ static struct builtin builtin_fallback =
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"
@ -3185,8 +3213,29 @@ static int
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. */
@ -3196,45 +3245,109 @@ savedefault_func (char *arg, int flags)
return 1;
}
/* Get the geometry of the boot drive (i.e. the disk which contains
this stage2). */
if (get_diskinfo (boot_drive, &buf_geom))
/* Determine a saved entry number. */
if (*arg)
{
errnum = ERR_NO_DISK;
return 1;
}
if (grub_memcmp (arg, "fallback", sizeof ("fallback") - 1) == 0)
{
int i;
int index = 0;
/* Load the second sector of this stage2. */
if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer))
{
return 1;
}
for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
{
if (fallback_entries[i] < 0)
break;
if (fallback_entries[i] == current_entryno)
{
index = i + 1;
break;
}
}
/* Sanity check. */
if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2
|| *((short *) (buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION)
{
errnum = ERR_BAD_VERSION;
return 1;
}
if (index >= MAX_FALLBACK_ENTRIES || fallback_entries[index] < 0)
{
/* This is the last. */
errnum = ERR_BAD_ARGUMENT;
return 1;
}
entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO);
/* Check if the saved entry number differs from current entry number. */
if (*entryno_ptr != current_entryno)
{
/* Overwrite the saved entry number. */
*entryno_ptr = current_entryno;
/* Save the image in the disk. */
if (! rawwrite (boot_drive, install_second_sector, buffer))
entryno = fallback_entries[index];
}
else if (! safe_parse_maxint (&arg, &entryno))
return 1;
}
else
entryno = current_entryno;
/* Open the default file. */
saved_drive = boot_drive;
saved_partition = install_partition;
if (grub_open (default_file))
{
int len;
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;
@ -3246,8 +3359,10 @@ static struct builtin builtin_savedefault =
"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."
};

View file

@ -201,7 +201,7 @@ run_script (char *script, char *heap)
/* 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 ();

View file

@ -97,8 +97,12 @@ extern char *grub_scratch_mem;
#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. */
@ -120,7 +124,7 @@ extern char *grub_scratch_mem;
/* 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
@ -585,7 +589,9 @@ extern void assign_device_name (int drive, const char *device);
#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;

View file

@ -390,7 +390,7 @@ restart:
gotoxy (3, 22);
printf (" ");
grub_timeout = -1;
fallback_entry = -1;
fallback_entryno = -1;
if (! (current_term->flags & TERM_DUMB))
gotoxy (74, 4 + entryno);
}
@ -731,15 +731,18 @@ restart:
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;
@ -864,6 +867,36 @@ cmain (void)
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.
@ -970,15 +1003,41 @@ cmain (void)
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)

View file

@ -1,5 +1,6 @@
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

View file

@ -42,7 +42,8 @@ bin_PROGRAMS = mbchk$(EXEEXT)
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
@ -51,7 +52,7 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
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)
@ -180,7 +181,9 @@ sbindir = @sbindir@
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
@ -229,6 +232,8 @@ grub-md5-crypt: $(top_builddir)/config.status $(srcdir)/grub-md5-crypt.in
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)

View file

@ -30,6 +30,7 @@ host_vendor=@host_vendor@
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=
@ -273,8 +274,8 @@ fi
# 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}
@ -410,6 +411,9 @@ for file in \
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`

114
util/grub-set-default.in Normal file
View file

@ -0,0 +1,114 @@
#! /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