]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
2004-06-20 Yoshinori K. Okuji <okuji@enbug.org>
authorokuji <okuji@localhost>
Sun, 20 Jun 2004 13:48:47 +0000 (13:48 +0000)
committerokuji <okuji@localhost>
Sun, 20 Jun 2004 13:48:47 +0000 (13:48 +0000)
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.

16 files changed:
ChangeLog
NEWS
configure
configure.ac
docs/grub.texi
docs/stamp-vti
docs/version.texi
stage2/asm.S
stage2/builtins.c
stage2/cmdline.c
stage2/shared.h
stage2/stage2.c
util/Makefile.am
util/Makefile.in
util/grub-install.in
util/grub-set-default.in [new file with mode: 0644]

index a7cc92b67583228abbe87657a50063099435d4cc..01b44d25a6e5abc8273058f22f00a0d936d09dfc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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 ...
diff --git a/NEWS b/NEWS
index e9ca852c5fc8e28cb4d540dbc48c708255ed805d..d0660c9e5caf843c615befd6e19ca9b9db016c94 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -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.
index 701c82205063468e72cc231763c321ef2830fb56..c69ebf946e9118fe5570d80abe0b19b4d72b74cc 100644 (file)
--- a/configure
+++ b/configure
@@ -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
index 2b378cea0313c143f518006efad10b715fda99cc..7e337e02a63dc40bed45d540068dc711823d88ee 100644 (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
index d9702fc7a3f729bd6e4862c58ea47df0a8dc4e1e..8a23e15b6fea7ba429a309627e7330f2f1854c1b 100644 (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.
index 7f71ab849cfb974c415869eb6bfa84d3b59336d4..43a51afc856d77e3d0c5556f7ed509fb9a7e410d 100644 (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
index 7f71ab849cfb974c415869eb6bfa84d3b59336d4..43a51afc856d77e3d0c5556f7ed509fb9a7e410d 100644 (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
index b4db6d58cc688c7fac4447e4c548b78f1d1d1e87..34b6e7d058ff905c3917b26f48aecbf52664e3f0 100644 (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)
index 596506d343f43e571cc2fe613e0ca4ed30bc8ece..535c713d28a4b77a954f52194a2942831bd86d26 100644 (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,9 +1145,35 @@ 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.  */
@@ -3195,46 +3244,110 @@ savedefault_func (char *arg, int flags)
       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;
@@ -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."
 };
 
 \f
index d6ea1281cc6d940cff1999127ff57501d790b053..b3b8c616195ffa0900a0c7b14ccbc875cecf39fc 100644 (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 ();
index 1cc8e063e0194d6e6ea7403b710f1d3e79a94615..77eef11fc8b3dbd3e65579d9653524c9f46d3eaf 100644 (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;
 
index cd497aa649cce4f939a62e42382a607118cd49db..f5982b6ff84d1cc9db4e737233351ee3b8a71647 100644 (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;
@@ -844,7 +847,7 @@ cmain (void)
       menu_entries = (char *) MENU_BUF;
       init_config ();
     }
-      
+  
   /* Initialize the environment for restarting Stage 2.  */
   grub_setjmp (restart_env);
   
@@ -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.
@@ -969,16 +1002,42 @@ cmain (void)
              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)
index 48604d6db1e3854aed65a7bbba1e539a639972c8..2e047113572b810b28e2b196f576b305e6bd76c5 100644 (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
index a1bebc06525b07cdb2c2bc91d6e784a2fbf8eb03..f643f7fd22325e22d6963c787da0067ec7053ffb 100644 (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)
index 4a547f947c28ec0ce198b8f70d0fb341ef4aaaf0..33101f4cdc6620d08ae70ca8480b6b92e14fe554 100644 (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`
diff --git a/util/grub-set-default.in b/util/grub-set-default.in
new file mode 100644 (file)
index 0000000..1c7fdc3
--- /dev/null
@@ -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