Add support for /boot.cfg configuration file in x86 boot loader.

This allows easy configuration of banner text, console device and timeout
as well as allowing menus of commands to be displayed. If /boot.cfg
is not present, then the existing behaviour does not change.

The sections in the boot loader source are surrounded by #ifdef SMALL
allowing this functionality to be removed if space is at a premium.
This commit is contained in:
sborrill 2007-11-20 15:37:36 +00:00
parent e0122367ec
commit d849e6b356
5 changed files with 438 additions and 11 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1038 2007/11/17 17:00:15 tsutsui Exp $
# $NetBSD: mi,v 1.1039 2007/11/20 15:37:36 sborrill Exp $
./etc/mtree/set.man man-sys-root
./usr/share/info/am-utils.info man-amd-info info
./usr/share/info/as.info man-computil-info bfd,info
@ -1615,6 +1615,7 @@
./usr/share/man/cat5/audit-packages.conf.0 man-pkgutil-catman .cat
./usr/share/man/cat5/bluetooth.conf.0 man-obsolete obsolete
./usr/share/man/cat5/body_checks.0 man-postfix-catman postfix,.cat
./usr/share/man/cat5/boot.cfg.0 man-sys-catman .cat
./usr/share/man/cat5/bootparams.0 man-bootserver-catman .cat
./usr/share/man/cat5/bootptab.0 man-bootserver-catman .cat
./usr/share/man/cat5/bounce.0 man-postfix-catman postfix,.cat
@ -4127,6 +4128,7 @@
./usr/share/man/man5/audit-packages.conf.5 man-pkgutil-man .man
./usr/share/man/man5/bluetooth.conf.5 man-obsolete obsolete
./usr/share/man/man5/body_checks.5 man-postfix-man postfix,.man
./usr/share/man/man5/boot.cfg.5 man-sys-man .man
./usr/share/man/man5/bootparams.5 man-bootserver-man .man
./usr/share/man/man5/bootptab.5 man-bootserver-man .man
./usr/share/man/man5/bounce.5 man-postfix-man postfix,.man

View File

@ -1,9 +1,10 @@
# $NetBSD: Makefile,v 1.58 2007/10/06 11:39:36 reed Exp $
# $NetBSD: Makefile,v 1.59 2007/11/20 15:37:36 sborrill Exp $
# @(#)Makefile 8.1 (Berkeley) 6/5/93
# missing: dump.5 plot.5
MAN= a.out.5 acct.5 ar.5 core.5 daily.conf.5 dir.5 disktab.5 elf.5 \
MAN= a.out.5 acct.5 ar.5 boot.cfg.5 \
core.5 daily.conf.5 dir.5 disktab.5 elf.5 \
ethers.5 forward.5 \
fs.5 fstab.5 genassym.cf.5 group.5 hesiod.conf.5 \
hosts.5 hosts.equiv.5 ifaliases.5 ifconfig.if.5 intro.5 \

161
share/man/man5/boot.cfg.5 Normal file
View File

@ -0,0 +1,161 @@
.\" $NetBSD: boot.cfg.5,v 1.1 2007/11/20 15:37:36 sborrill Exp $
.\"
.\" Copyright (c) 2007 Stephen Borrill
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd November 19, 2007
.Dt BOOT.CFG 5 i386
.Os
.Sh NAME
.Nm boot.cfg
.Nd configuration file for /boot
.Sh DESCRIPTION
The file /boot.cfg
is used to alter the behaviour of the standard boot loader described in
.Xr boot 8 .
Configuration changes include setting the timeout, choosing a console device,
altering the banner text and displaying a menu allowing boot commands to be
easily chosen.
If a
.Nm
file is not present, the system will boot as normal.
.Ss FILE FORMAT
The format of the file is a series of lines containing keyword/value pairs
separated by an equals sign
.Pq Sq = .
There should be no whitespace surrounding the equals sign.
Lines beginning with a hash
.Pq Sq #
are comments and will be ignored.
.Pp
Some keywords can be present multiple times in the file to define additional
items.
Such keywords are noted below.
.Pp
.Bl -tag -width timeout
.It Sy banner
(may be present multiple times)
The text from banner lines is displayed instead of the standard welcome text
by the boot loader.
Up to 10 lines can be defined.
No special character sequences are recognised, so to specify a blank line, a
banner line with no value should be given.
.It Sy menu
(may be present multiple times)
Used to define a menu item to be displayed to the end-user at boot time
which allows a series of boot commands to be run without further typing.
The value consists of the required menu text, followed by a colon
.Po So : Sc Pc
and then the desired command.
For example:
.Bd -literal
menu=Boot normally:boot
menu=Boot single-user:boot -s
.Ed
.Pp
Each menu item will be prefixed by an ascending number when displayed,
i.e. the order in the
.Nm
file is important.
.Pp
The command is executed just as though the user had typed it in
and so can be any valid command that would be accepted at the
normal boot prompt.
In addition,
.So prompt Sc
can be used to drop to the normal boot prompt.
.It Sy timeout
If the value is greater than zero, this specifies the time in seconds
that the boot loader will wait for the end-user to choose a menu item.
During the countdown period, they may press Return to choose the default
option or press a number key corresponding to a menu option.
If any other key is pressed, the countdown will stop and the user will be
prompted to choose a menu option with no further time limit.
If the timeout value is set to zero, the default option will be booted
immediately.
If the timeout value is negative or is not a number, there will be no
time limit for the user to choose an option.
.It Sy default
Used to specify the default menu item which will be chosen in the case of
Return being pressed or the timeout timer reaching zero.
The value is the number of the menu item as displayed.
As described above, the menu items are counted from 1 in the order listed in
.Nm .
If not specified, the default value will be option 1, i.e. the first item.
.It Sy consdev
Changes the console device to that specified in the value.
Valid values are any of those that could be specified at the normal boot
prompt with the consdev command.
.El
.Sh EXAMPLES
Here is an example
.Nm
file:
.Bd -literal -offset indent
banner=Welcome to NetBSD
banner==================
banner=
banner=Please choose an option from the following menu:
menu=Boot normally:boot
menu=Boot single-user:boot -s
menu=Boot from second disk:boot hd1a:
menu=Go to command line (advanced users only):prompt
timeout=-1
default=1
.Ed
.Pp
This will display:
.Bd -literal -offset indent
Welcome to NetBSD
=================
Please choose an option from the following menu:
1. Boot normally
2. Boot single-user
3. Boot from second disk
4. Go to command line (advanced users only)
Option [1]:
.Ed
.Pp
It will then wait for the user to type 1, 2, 3 or 4 followed by Return.
Pressing Return by itself will run option 1.
There will be no timeout.
.Sh SEE ALSO
.Xr boot 8
.Sh HISTORY
The
.Nm
utility appeared in
.Nx 5.0 .
.Sh AUTHORS
The
.Nm
extensions to
.Xr boot 8
were written by
.An Stephen Borrill
.Aq sborrill@NetBSD.org .

View File

@ -1,4 +1,4 @@
.\" $NetBSD: boot.8,v 1.38 2006/06/29 20:24:13 christos Exp $
.\" $NetBSD: boot.8,v 1.39 2007/11/20 15:37:36 sborrill Exp $
.\"
.\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -135,6 +135,12 @@ enter interactive mode.
When using a short or 0 timeout, it is often useful to interrupt the boot
by holding down a shift key, as some BIOSes and BIOS extensions will drain the
keystroke buffer at various points during POST.
.Pp
If present, the file /boot.cfg will be used to configure the behaviour of the
boot loader including setting the timeout, choosing a console device, altering
the banner text and displaying a menu allowing boot commands to be easily
chosen. See
.Xr boot.cfg 5 .
.Ss Diagnostic Output
If the first stage boot fails to load the boot, it will print a terse
message indicating the reason for the failure.
@ -264,6 +270,8 @@ can be used to boot from floppy.
.Bl -tag -width /usr/mdec/bootxx_fstype -compact
.It Pa /boot
boot program code loaded by the primary bootstrap
.It Pa /boot.cfg
optional configuration file
.It Pa /netbsd
system code
.It Pa /netbsd.gz
@ -278,6 +286,7 @@ the netbsd partition by
.Sh SEE ALSO
.Xr ddb 4 ,
.Xr userconf 4 ,
.Xr boot.cfg 5 ,
.Xr boot_console 8 ,
.Xr dosboot 8 ,
.Xr halt 8 ,

View File

@ -1,4 +1,4 @@
/* $NetBSD: boot2.c,v 1.14 2007/10/17 19:54:59 garbled Exp $ */
/* $NetBSD: boot2.c,v 1.15 2007/11/20 15:37:37 sborrill Exp $ */
/*
* Copyright (c) 2003
@ -77,6 +77,12 @@ static const char * const names[][2] = {
#define MAXDEVNAME 16
#ifndef SMALL
#define BOOTCONF "boot.cfg"
#define MAXMENU 10
#define MAXBANNER 10
#endif /* !SMALL */
static char *default_devname;
static int default_unit, default_partition;
static const char *default_filename;
@ -86,6 +92,12 @@ void bootit(const char *, int, int);
void print_banner(void);
void boot2(int, u_int);
#ifndef SMALL
void parsebootconf(const char *);
void doboottypemenu(void);
int atoi(const char *);
#endif /* !SMALL */
void command_help(char *);
void command_ls(char *);
void command_quit(char *);
@ -104,6 +116,18 @@ const struct bootblk_command commands[] = {
{ NULL, NULL },
};
#ifndef SMALL
struct bootconf_def {
char *banner[MAXBANNER]; /* Banner text */
char *command[MAXMENU]; /* Menu commands per entry*/
char *consdev; /* Console device */
int def; /* Default menu option */
char *desc[MAXMENU]; /* Menu text per entry */
int nummenu; /* Number of menu items */
int timeout; /* Timeout in seconds */
} bootconf;
#endif /* !SMALL */
int
parsebootfile(const char *fname, char **fsname, char **devname,
int *unit, int *partition, const char **file)
@ -207,13 +231,220 @@ bootit(const char *filename, int howto, int tell)
void
print_banner(void)
{
#ifndef SMALL
int n;
if (bootconf.banner[0]) {
for (n = 0; bootconf.banner[n]; n++)
printf("%s\n", bootconf.banner[n]);
printf("\n");
} else {
#endif /* !SMALL */
printf("\n");
printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
printf("\n");
printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
#ifndef SMALL
}
#endif /* !SMALL */
}
#ifndef SMALL
int
atoi(const char *in)
{
char *c;
int ret;
ret = 0;
c = (char *)in;
if (*c == '-')
c++;
for (; isnum(*c); c++)
ret = (ret * 10) + (*c - '0');
return (*in == '-') ? -ret : ret;
}
/*
* This function parses a boot.cnf file in the root of the filesystem
* (if present) and populates the global boot configuration.
*
* The file consists of a number of lines each terminated by \n
* The lines are in the format keyword=value. There should be spaces
* around the = sign.
*
* The recognised keywords are:
* banner: text displayed instead of the normal welcome text
* menu: Descriptive text:command to use
* timeout: Timeout in seconds (overrides that set by installboot)
* default: the default menu option to use if Return is pressed
* consdev: the console device to use
*
* Example boot.cnf file:
* banner=Welcome to NetBSD
* banner=Please choose the boot type from the following menu
* menu=Boot NetBSD:boot netbsd
* menu=Boot into single user mode:boot netbsd -s
* menu=Goto boot comand line:prompt
* timeout=10
* consdev=com0
* default=1
*/
void
parsebootconf(const char *conf)
{
char *bc, *c;
int cmenu, cbanner, len;
int fd, err, off;
struct stat st;
char *value, *key;
/* Clear bootconf structure */
bzero((void *)&bootconf, sizeof(bootconf));
/* Set timeout to configured */
bootconf.timeout = boot_params.bp_timeout;
err = stat(BOOTCONF, &st);
if (err == -1)
return;
fd = open(BOOTCONF, 0);
if (fd < 0)
return;
bc = alloc(st.st_size + 1);
if (bc == NULL) {
printf("Could not allocate memory for boot configuration\n");
return;
}
off = 0;
do {
len = read(fd, bc + off, 1024);
if (len <= 0)
break;
off += len;
} while (len > 0);
bc[off] = '\0';
close(fd);
/* bc now contains the whole boot.cnf file */
cmenu = 0;
cbanner = 0;
for(c = bc; *c; c++) {
key = c;
/* Look for = separator between key and value */
for (; *c && *c != '='; c++)
continue;
if (*c == '\0')
break; /* break if at end of data */
/* zero terminate key which points to keyword */
*c++ = 0;
value = c;
/* Look for end of line (or file) and zero terminate value */
for (; *c && *c != '\n'; c++)
continue;
*c = 0;
if (!strncmp(key, "menu", 4)) {
if (cmenu >= MAXMENU)
continue;
bootconf.desc[cmenu] = value;
/* Look for : between description and command */
for (; *value && *value != ':'; value++)
continue;
if(*value) {
*value++ = 0;
bootconf.command[cmenu] = value;
cmenu++;
} else {
/* No delimiter means invalid line */
bootconf.desc[cmenu] = NULL;
}
} else if (!strncmp(key, "banner", 6)) {
if (cbanner < MAXBANNER)
bootconf.banner[cbanner++] = value;
} else if (!strncmp(key, "timeout", 7)) {
if (!isnum(*value))
bootconf.timeout = -1;
else
bootconf.timeout = atoi(value);
} else if (!strncmp(key, "default", 7)) {
bootconf.def = atoi(value) - 1;
} else if (!strncmp(key, "consdev", 7)) {
bootconf.consdev = value;
}
}
bootconf.nummenu = cmenu;
if (bootconf.def < 0)
bootconf.def = 0;
if (bootconf.def >= cmenu)
bootconf.def = cmenu - 1;
}
/*
* doboottypemenu will render the menu and parse any user input
*/
void
doboottypemenu(void)
{
int choice;
char input[80], c;
/* Display menu */
for (choice = 0; bootconf.desc[choice]; choice++)
printf(" %d. %s\n", choice+1, bootconf.desc[choice]);
choice = -1;
for(;;) {
input[0] = '\0';
if (bootconf.timeout < 0) {
printf("\nOption: [%d]:", bootconf.def + 1);
gets(input);
if (input[0] == '\0') choice = bootconf.def;
if (input[0] >= '1' &&
input[0] <= bootconf.nummenu + '0')
choice = input[0] - '1';
} else if (bootconf.timeout == 0)
choice = bootconf.def;
else {
printf("\nPress the key for your chosen option or ");
printf("Return to choose the default (%d)\n",
bootconf.def + 1);
printf("Option %d will be chosen in ",
bootconf.def + 1);
c = awaitkey(bootconf.timeout, 1);
if (c >= '1' && c <= bootconf.nummenu + '0')
choice = c - '1';
else if (c == '\r' || c == '\n' || c == '\0')
/* default if timed out or Return pressed */
choice = bootconf.def;
else {
/* If any other key pressed, drop to menu */
bootconf.timeout = -1;
choice = -1;
}
}
if (choice < 0)
continue;
if (!strcmp(bootconf.command[choice], "prompt") &&
((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
check_password(boot_params.bp_password))) {
printf("type \"?\" or \"help\" for help.\n");
bootmenu(); /* does not return */
} else
docommand(bootconf.command[choice]);
}
}
#endif /* !SMALL */
/*
* Called from the initial entry point boot_start in biosboot.S
*
@ -236,8 +467,6 @@ boot2(int biosdev, u_int biossector)
if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO)
biosvideomode();
print_banner();
/* need to remember these */
boot_biosdev = biosdev;
boot_biossector = biossector;
@ -249,12 +478,37 @@ boot2(int biosdev, u_int biossector)
/* if the user types "boot" without filename */
default_filename = DEFFILENAME;
#ifndef SMALL
parsebootconf(BOOTCONF);
/*
* If console set in boot.cnf, switch to it.
* This will print the banner, so we don't need to explicitly do it
*/
if (bootconf.consdev)
command_consdev(bootconf.consdev);
else
print_banner();
/* Display the menu, if applicable */
if (bootconf.nummenu > 0) {
/* Does not return */
doboottypemenu();
}
#else
print_banner();
#endif
printf("Press return to boot now, any other key for boot menu\n");
for (currname = 0; currname < NUMNAMES; currname++) {
printf("booting %s - starting in ",
sprint_bootsel(names[currname][0]));
#ifdef SMALL
c = awaitkey(boot_params.bp_timeout, 1);
#else
c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1);
#endif
if ((c != '\r') && (c != '\n') && (c != '\0') &&
((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0
|| check_password(boot_params.bp_password))) {