Index: boot2.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/stand/boot/boot2.c,v
retrieving revision 1.14
diff -u -u -r1.14 boot2.c
--- boot2.c	17 Oct 2007 19:54:59 -0000	1.14
+++ boot2.c	19 Nov 2007 17:39:33 -0000
@@ -77,6 +77,12 @@
 
 #define MAXDEVNAME 16
 
+#ifndef SMALL
+#define BOOTCONF "boot.cfg"
+#define MAXMENU 10
+#define MAXBANNER 10
+#endif /* ifndef SMALL */
+
 static char *default_devname;
 static int default_unit, default_partition;
 static const char *default_filename;
@@ -86,6 +92,12 @@
 void print_banner(void);
 void boot2(int, u_int);
 
+#ifndef SMALL
+void parsebootconf(const char *);
+void doboottypemenu(void);
+int atoi(const char *);
+#endif /* ifndef SMALL */
+
 void	command_help(char *);
 void	command_ls(char *);
 void	command_quit(char *);
@@ -104,6 +116,18 @@
 	{ 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 /* ifndef SMALL */
+
 int
 parsebootfile(const char *fname, char **fsname, char **devname,
 	      int *unit, int *partition, const char **file)
@@ -207,14 +231,221 @@
 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 /* ifndef 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());
+
+#ifndef SMALL
+	}
+#endif /* ifndef 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;
 
-	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());
+	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 /* ifndef SMALL */
+
+/*
  * Called from the initial entry point boot_start in biosboot.S
  *
  * biosdev: BIOS drive number the system booted from
@@ -236,8 +467,6 @@
 	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 @@
 	/* 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 /* ifndef SMALL */
+
 	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 /* ifdef SMALL */
 		if ((c != '\r') && (c != '\n') && (c != '\0') &&
 		    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0
 		     || check_password(boot_params.bp_password))) {