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))) {