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