Added support for remote booting: commands "expires" and "fallback"

This commit is contained in:
vanrein 2004-01-05 18:03:11 +00:00
parent d1d9022b1f
commit 6e7a81f3aa
9 changed files with 261 additions and 19 deletions

View file

@ -48,3 +48,6 @@ Tilmann Bubeck added support for vt100-incompatible terminals.
KB Sriram added a better detection of FAT filesystem and fixed a
network device completion.
Rick van Rein added the expires command to smoothen remote kernel upgrades.

View file

@ -1,3 +1,13 @@
2004-01-05 Rick van Rein <rick@vanrein.org>
* stage2/builtins.c: New menu command "expires". The change also
influences the files stage2/shared.h, stage2/common.c, stage2/asm.S,
grub/asmstub.c and docs/grub.texi. The "fallback" command is now a
general command, in support of chained fallbacks.
* docs/grub.texi: Added missing documentation for ERR_NUMBER_OVERFLOW.
2003-12-30 Yoshinori K. Okuji <okuji@enbug.org>
* stage2/fsys_ext2fs.c (ext2_is_fast_symlink): New function.

2
NEWS
View file

@ -1,6 +1,8 @@
NEWS - list of user-visible changes between releases of GRUB
New:
* Add an expires command for temporary testing of boot entries, to solve
problems with remotely maintained systems that need a kernel changeover.
* Support building on x86-64 with gcc -m32.
* Use a BIOS call to turn on/off Gate A20. This should solve various
problems related to Gate A20 in modern BIOSes.

View file

@ -1829,7 +1829,6 @@ These commands can only be used in the menu:
@menu
* default:: Set the default entry
* fallback:: Set the fallback entry
* hiddenmenu:: Hide the menu interface
* timeout:: Set the timeout
* title:: Start a menu entry
@ -1850,18 +1849,6 @@ default entry is the entry saved with the command
@end deffn
@node fallback
@subsection fallback
@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 anything, 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.
@end deffn
@node hiddenmenu
@subsection hiddenmenu
@ -1902,6 +1889,8 @@ Commands usable both in the menu and in the command-line.
* color:: Color the menu interface
* device:: Specify a file as a drive
* dhcp:: Initialize a network device via DHCP
* expires:: Invalidate a menu entry after some time
* fallback:: Set the fallback entry
* hide:: Hide a partition
* ifconfig:: Configure a network device manually
* pager:: Change the state of the internal pager
@ -2053,6 +2042,66 @@ with the vendor tag @samp{150}.
@end deffn
@node expires
@subsection expires
@deffn Command expires YYYY-MM-DD HH:MM
Make the current boot entry, as introduced by @command{title} (@pxref{title})
expire after the given date and time. This is usually combined with
the @command{fallback} command (@pxref{fallback}),
which introduces the boot entry to be used after expiry.
When adminstering systems remotely, it is troublesome and dangerous
to switch to new kernel versions. This option enables a GRUB setup
to test new kernels, with a fallback to the old kernel after some
time. All that is needed to fall back is a reset after the expiration,
either manually imposed by a local operator or initiated by the loaded
operating system.
The date is written in numbers, with the year in four digits. The time
is written in 24-hour format, and reflects the time of the realtime clock.
If the realtime clock holds the GMT time and the computer operates in
another zone, then the argument to @command{expires} should still be
GMT-time, because GRUB accesses the realtime clock.
@example
@group
default 0
title Test kernel with IPsec in place
fallback 1
expires 2004-01-04 18:15
root (hd0,0)
kernel /boot/vmlinuz-ipsec root=/dev/hda1
initrd /boot/initrd.img
boot
title Known-to-work kernel without IPsec
root (hd0,0)
kernel /boot/vmlinuz root=/dev/hda1
initrd /boot/initrd.img
boot
@end group
@end example
@end deffn
@node fallback
@subsection fallback
@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 anything, 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, unless combined with @command{expires}
(@pxref{expires}).
The fallback command can be chained by specifying it in a boot entry
which serves as a fallback option. The only point of caution is that
it must be executed before the error triggering it occurs.
@end deffn
@node hide
@subsection hide
@ -3190,6 +3239,19 @@ This error is returned if a disk doesn't have enough spare space. This
happens when you try to embed Stage 1.5 into the unused sectors after
the MBR, but the first partition starts right after the MBR or they are
used by EZ-BIOS.
@item 35 : Overflow while parsing number
Overflow while parsing number.
@item 36 : This boot option has expired
This error is returned if an expiration date/time setup for this boot
option falls before the date/time currently in the realtime clock.
@item 37 : Datestamp not formatted as YYYY-MM-DD HH:MM
The expires command requires strict adherence to the given argument
format. This failure is the safe one, as it avoids that a badly
interpreted argument leads to never-expiring boot options.
@end table

View file

@ -486,6 +486,30 @@ getrtsecs (void)
return time (0);
}
unsigned char makebcd (int plainval)
{
return ((plainval / 10) << 4) + (plainval % 10);
}
/* obtain current timestamp and store it in bytes rtc_now [6] */
void
get_rtc_now ()
{
extern unsigned char rtc_now [6];
time_t ticks;
struct tm *now;
time (&ticks);
now = localtime (&ticks);
rtc_now [0] = makebcd ( ( now->tm_year + 1900 ) / 100 );
rtc_now [1] = makebcd ( now->tm_year % 100 );
rtc_now [2] = makebcd ( now->tm_mon + 1 );
rtc_now [3] = makebcd ( now->tm_mday );
rtc_now [4] = makebcd ( now->tm_hour );
rtc_now [5] = makebcd ( now->tm_min );
}
int
currticks (void)
{

View file

@ -2259,7 +2259,7 @@ ENTRY(console_setcursor)
* getrtsecs()
* if a seconds value can be read, read it and return it (BCD),
* otherwise return 0xFF
* BIOS call "INT 1AH Function 02H" to check whether a character is pending
* BIOS call "INT 1AH Function 02H" to obtain the current time from the RTC
* Call with %ah = 0x2
* Return:
* If RT Clock can give correct values
@ -2294,7 +2294,86 @@ gottime:
pop %ebp
ret
/*
* get_rtc_now()
* obtain bytes rtc_now[6] holding YYYYMMDDHHMM from the realtime clock under the BIOS
* be sure to correct for any glitches
* BIOS call "INT 1AH Function 02H" to obtain the current time from the RTC
* Call with %ah = 0x2
* Return:
* If RT Clock can give correct values
* %ch = hour (BCD)
* %cl = minutes (BCD)
* %dh = seconds (BCD)
* %dl = daylight savings time (00h std, 01h daylight)
* Carry flag = clear
* else
* Carry flag = set
* (this indicates that the clock is updating, or
* BIOS call "INT 1AH Function 04H" to obtain the current date from the RTC
* Call with %ah = 0x4
* Return:
* If RT Clock can give correct values
* %ch = century (BCD)
* %cl = year (BCD)
* %dh = month (BCD)
* %dl = day of month (BCD)
* Carry flag = clear
* else
* Carry flag = set
* (this indicates that the clock is updating, or
* that it isn't running)
*/
ENTRY(get_rtc_now)
pushl %ebp
call EXT_C(prot_to_real) /* enter real mode */
.code16
rtcredo:
movb $0x4, %ah
int $0x1a
DATA32 jc rtcredo
pushw %dx
pushw %cx
rtcretry:
movb $0x2, %ah
int $0x1a
DATA32 jc rtcretry
pushw %cx
/* Now ensure that the date hasn't advanced in the meantime */
movb $0x4, %ah
int $0x1a
movb %dl, %al
popw %bx
popw %cx
popw %dx
DATA32 jc rtcdone
cmpb %al, %dl
DATA32 jne rtcredo
rtcdone:
DATA32 call EXT_C(real_to_prot)
.code32
movb %ch, EXT_C(rtc_now+0)
movb %cl, EXT_C(rtc_now+1)
movb %dh, EXT_C(rtc_now+2)
movb %dl, EXT_C(rtc_now+3)
movb %bh, EXT_C(rtc_now+4)
movb %bl, EXT_C(rtc_now+5)
popl %ebp
ret
/*
* currticks()
* return the real time in ticks, of which there are about

View file

@ -1138,6 +1138,64 @@ static struct builtin builtin_embed =
" Print the number of sectors which STAGE1_5 occupies if successful."
};
/* expires */
static unsigned char parsebcdbyte (char *str)
{
if ((str [0] >= '0') && (str [0] <= '9') && (str [1] >= '0') && (str [1] <= '9'))
{
return ( str [0] << 4 ) + str [1] - ( 17 * '0' );
}
return 0xff;
};
unsigned char rtc_now [6];
unsigned char expires [6];
static int
expires_func (char *arg, int flags)
{
/* Store YYYYMMDDHHMM in expires[] */
if ( ((expires [0] = parsebcdbyte (arg+ 0)) == 0xff) ||
((expires [1] = parsebcdbyte (arg+ 2)) == 0xff) ||
(arg [ 4] != '-') ||
((expires [2] = parsebcdbyte (arg+ 5)) == 0xff) ||
(arg [ 7] != '-') ||
((expires [3] = parsebcdbyte (arg+ 8)) == 0xff) ||
(arg [10] != ' ') ||
((expires [4] = parsebcdbyte (arg+11)) == 0xff) ||
(arg [13] != ':') ||
((expires [5] = parsebcdbyte (arg+14)) == 0xff) ||
(arg [16] != 0) )
{
errnum = ERR_DATESTAMP;
return 1;
}
get_rtc_now (); /* Call BIOS to fill rtc_now[6] with YYYYMMDDHHMM */
if (grub_memcmp (rtc_now, expires, 6) >= 0)
{
errnum = ERR_EXPIRED;
return 1; /* Failure: this boot entry has expired, try fallback */
}
return 0; /* Success: this boot entry has not expired yet */
};
static struct builtin builtin_expires =
{
"expires",
expires_func,
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
"expires YYYY-MM-DD HH:MM",
"The current boot entry fails after the realtime clock reaches"
" the date and time provided. This may be useful to test new kernels"
" on distant systems -- rebooting those after a testing period can"
" fallback to the old kernel when the boot entry under test expires."
};
/* fallback */
static int
@ -1153,15 +1211,13 @@ static struct builtin builtin_fallback =
{
"fallback",
fallback_func,
BUILTIN_MENU,
#if 0
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
"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"
" `default' command). This obviously won't help if the machine"
" was rebooted by a kernel that GRUB loaded."
#endif
" was rebooted by a kernel that GRUB loaded, unless combined with expires."
};
@ -4690,6 +4746,7 @@ struct builtin *builtin_table[] =
&builtin_dump,
#endif /* GRUB_UTIL */
&builtin_embed,
&builtin_expires,
&builtin_fallback,
&builtin_find,
&builtin_fstest,

View file

@ -62,10 +62,12 @@ char *err_list[] =
[ERR_BOOT_COMMAND] = "Kernel must be loaded before booting",
[ERR_BOOT_FAILURE] = "Unknown boot failure",
[ERR_BOOT_FEATURES] = "Unsupported Multiboot features requested",
[ERR_DATESTAMP] = "Datestamp not formatted as YYYY-MM-DD HH:MM",
[ERR_DEV_FORMAT] = "Unrecognized device string",
[ERR_DEV_NEED_INIT] = "Device not initialized yet",
[ERR_DEV_VALUES] = "Invalid device requested",
[ERR_EXEC_FORMAT] = "Invalid or unsupported executable format",
[ERR_EXPIRED] = "This boot option has expired",
[ERR_FILELENGTH] =
"Filesystem compatibility error, cannot read whole file",
[ERR_FILE_NOT_FOUND] = "File not found",

View file

@ -533,6 +533,8 @@ typedef enum
ERR_DEV_NEED_INIT,
ERR_NO_DISK_SPACE,
ERR_NUMBER_OVERFLOW,
ERR_EXPIRED,
ERR_DATESTAMP,
MAX_ERR_NUM
} grub_error_t;
@ -760,6 +762,7 @@ int get_code_end (void);
/* low-level timing info */
int getrtsecs (void);
int currticks (void);
void get_rtc_now (void);
/* Clear the screen. */
void cls (void);