1*4f99b703Smartin /* $NetBSD: bootmenu.c,v 1.20 2024/11/27 17:19:37 martin Exp $ */ 244435f5aSad 344435f5aSad /*- 444435f5aSad * Copyright (c) 2008 The NetBSD Foundation, Inc. 544435f5aSad * All rights reserved. 644435f5aSad * 744435f5aSad * Redistribution and use in source and binary forms, with or without 844435f5aSad * modification, are permitted provided that the following conditions 944435f5aSad * are met: 1044435f5aSad * 1. Redistributions of source code must retain the above copyright 1144435f5aSad * notice, this list of conditions and the following disclaimer. 1244435f5aSad * 2. Redistributions in binary form must reproduce the above copyright 1344435f5aSad * notice, this list of conditions and the following disclaimer in the 1444435f5aSad * documentation and/or other materials provided with the distribution. 1544435f5aSad * 1644435f5aSad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1744435f5aSad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1844435f5aSad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1944435f5aSad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2044435f5aSad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2144435f5aSad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2244435f5aSad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2344435f5aSad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2444435f5aSad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2544435f5aSad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2644435f5aSad * POSSIBILITY OF SUCH DAMAGE. 2744435f5aSad */ 2844435f5aSad 2944435f5aSad #ifndef SMALL 3044435f5aSad 3144435f5aSad #include <sys/types.h> 3244435f5aSad #include <sys/reboot.h> 3344435f5aSad #include <sys/bootblock.h> 3444435f5aSad 3544435f5aSad #include <lib/libsa/stand.h> 36e9c95bf5Srtr #include <lib/libsa/bootcfg.h> 3744435f5aSad #include <lib/libsa/ufs.h> 3844435f5aSad #include <lib/libkern/libkern.h> 3944435f5aSad 4044435f5aSad #include <libi386.h> 4144435f5aSad #include <bootmenu.h> 4244435f5aSad 436a91dd31She static void docommandchoice(int); 446a91dd31She 4544435f5aSad extern struct x86_boot_params boot_params; 4644435f5aSad extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 4744435f5aSad 4844435f5aSad #define MENUFORMAT_AUTO 0 4944435f5aSad #define MENUFORMAT_NUMBER 1 5044435f5aSad #define MENUFORMAT_LETTER 2 5144435f5aSad 5244435f5aSad /* 53e9c95bf5Srtr * XXX 54e9c95bf5Srtr * if module_add, userconf_add are strictly mi they can be folded back 55e9c95bf5Srtr * into sys/lib/libsa/bootcfg.c:perform_bootcfg(). 5644435f5aSad */ 57e9c95bf5Srtr static void 58e9c95bf5Srtr do_bootcfg_command(const char *cmd, char *arg) 59e9c95bf5Srtr { 60e9c95bf5Srtr if (strcmp(cmd, BOOTCFG_CMD_LOAD) == 0) 61e9c95bf5Srtr module_add(arg); 626bcd9d5cSmlelstv else if (strcmp(cmd, "fs") == 0) 636bcd9d5cSmlelstv fs_add(arg); 64e9c95bf5Srtr else if (strcmp(cmd, BOOTCFG_CMD_USERCONF) == 0) 65e9c95bf5Srtr userconf_add(arg); 66e9c95bf5Srtr } 67e9c95bf5Srtr 6895e6c117Snonaka int 6944435f5aSad parsebootconf(const char *conf) 7044435f5aSad { 7195e6c117Snonaka return perform_bootcfg(conf, &do_bootcfg_command, 32768); 7244435f5aSad } 7344435f5aSad 7444435f5aSad /* 7544435f5aSad * doboottypemenu will render the menu and parse any user input 7644435f5aSad */ 7744435f5aSad static int 7844435f5aSad getchoicefrominput(char *input, int def) 7944435f5aSad { 801b3357e5Sjmcneill int choice, usedef; 811b3357e5Sjmcneill 8244435f5aSad choice = -1; 831b3357e5Sjmcneill usedef = 0; 841b3357e5Sjmcneill 851b3357e5Sjmcneill if (*input == '\0' || *input == '\r' || *input == '\n') { 8644435f5aSad choice = def; 871b3357e5Sjmcneill usedef = 1; 88e9c95bf5Srtr } else if (*input >= 'A' && *input < bootcfg_info.nummenu + 'A') 8944435f5aSad choice = (*input) - 'A'; 90e9c95bf5Srtr else if (*input >= 'a' && *input < bootcfg_info.nummenu + 'a') 9144435f5aSad choice = (*input) - 'a'; 927b2cf765Sisaki else if (isdigit(*input)) { 9344435f5aSad choice = atoi(input) - 1; 94e9c95bf5Srtr if (choice < 0 || choice >= bootcfg_info.nummenu) 9544435f5aSad choice = -1; 9644435f5aSad } 97df6bdc8bSjmcneill 98e9c95bf5Srtr if (bootcfg_info.menuformat != MENUFORMAT_LETTER && 997b2cf765Sisaki !isdigit(*input) && !usedef) 100df6bdc8bSjmcneill choice = -1; 101df6bdc8bSjmcneill 10244435f5aSad return choice; 10344435f5aSad } 10444435f5aSad 1056a91dd31She static void 1066a91dd31She docommandchoice(int choice) 1076a91dd31She { 1086a91dd31She char input[80], *ic, *oc; 1096a91dd31She 110e9c95bf5Srtr ic = bootcfg_info.command[choice]; 1116a91dd31She /* Split command string at ; into separate commands */ 1126a91dd31She do { 1136a91dd31She oc = input; 1146a91dd31She /* Look for ; separator */ 1156a91dd31She for (; *ic && *ic != COMMAND_SEPARATOR; ic++) 1166a91dd31She *oc++ = *ic; 1176a91dd31She if (*input == '\0') 1186a91dd31She continue; 1196a91dd31She /* Strip out any trailing spaces */ 1206a91dd31She oc--; 1216a91dd31She for (; *oc == ' ' && oc > input; oc--); 1226a91dd31She *++oc = '\0'; 1236a91dd31She if (*ic == COMMAND_SEPARATOR) 1246a91dd31She ic++; 1256a91dd31She /* Stop silly command strings like ;;; */ 1266a91dd31She if (*input != '\0') 1276a91dd31She docommand(input); 1286a91dd31She /* Skip leading spaces */ 1296a91dd31She for (; *ic == ' '; ic++); 1306a91dd31She } while (*ic); 1316a91dd31She } 1326a91dd31She 133ee69c9e8Sjakllsch __dead void 13444435f5aSad doboottypemenu(void) 13544435f5aSad { 13644435f5aSad int choice; 1376a91dd31She char input[80]; 13844435f5aSad 139*4f99b703Smartin /* 140*4f99b703Smartin * If we have a single menu entry with empty description and 141*4f99b703Smartin * timeout = 0 we do not display any menu. 142*4f99b703Smartin */ 143*4f99b703Smartin if ((bootcfg_info.nummenu > 0 && 144*4f99b703Smartin bootcfg_info.desc[0] != bootcfg_info.command[0] && 145*4f99b703Smartin bootcfg_info.desc[0][0] != 0) || bootcfg_info.timeout > 0) { 14644435f5aSad printf("\n"); 147*4f99b703Smartin 14844435f5aSad /* Display menu */ 149e9c95bf5Srtr if (bootcfg_info.menuformat == MENUFORMAT_LETTER) { 150*4f99b703Smartin for (choice = 0; choice < bootcfg_info.nummenu; 151*4f99b703Smartin choice++) 15244435f5aSad printf(" %c. %s\n", choice + 'A', 153e9c95bf5Srtr bootcfg_info.desc[choice]); 15444435f5aSad } else { 15544435f5aSad /* Can't use %2d format string with libsa */ 156*4f99b703Smartin for (choice = 0; choice < bootcfg_info.nummenu; 157*4f99b703Smartin choice++) 15844435f5aSad printf(" %s%d. %s\n", 15944435f5aSad (choice < 9) ? " " : "", 16044435f5aSad choice + 1, 161e9c95bf5Srtr bootcfg_info.desc[choice]); 16244435f5aSad } 163*4f99b703Smartin } 16444435f5aSad choice = -1; 16544435f5aSad for (;;) { 16644435f5aSad input[0] = '\0'; 16744435f5aSad 168e9c95bf5Srtr if (bootcfg_info.timeout < 0) { 169e9c95bf5Srtr if (bootcfg_info.menuformat == MENUFORMAT_LETTER) 17044435f5aSad printf("\nOption: [%c]:", 171e9c95bf5Srtr bootcfg_info.def + 'A'); 17244435f5aSad else 17344435f5aSad printf("\nOption: [%d]:", 174e9c95bf5Srtr bootcfg_info.def + 1); 17544435f5aSad 176979b72c6Sdholland kgets(input, sizeof(input)); 177e9c95bf5Srtr choice = getchoicefrominput(input, bootcfg_info.def); 178e9c95bf5Srtr } else if (bootcfg_info.timeout == 0) 179e9c95bf5Srtr choice = bootcfg_info.def; 18044435f5aSad else { 18144435f5aSad printf("\nChoose an option; RETURN for default; " 18244435f5aSad "SPACE to stop countdown.\n"); 183e9c95bf5Srtr if (bootcfg_info.menuformat == MENUFORMAT_LETTER) 18444435f5aSad printf("Option %c will be chosen in ", 185e9c95bf5Srtr bootcfg_info.def + 'A'); 18644435f5aSad else 18744435f5aSad printf("Option %d will be chosen in ", 188e9c95bf5Srtr bootcfg_info.def + 1); 189e9c95bf5Srtr input[0] = awaitkey(bootcfg_info.timeout, 1); 19044435f5aSad input[1] = '\0'; 191e9c95bf5Srtr choice = getchoicefrominput(input, bootcfg_info.def); 19244435f5aSad /* If invalid key pressed, drop to menu */ 19344435f5aSad if (choice == -1) 194e9c95bf5Srtr bootcfg_info.timeout = -1; 19544435f5aSad } 19644435f5aSad if (choice < 0) 19744435f5aSad continue; 198e9c95bf5Srtr if (!strcmp(bootcfg_info.command[choice], "prompt") && 19944435f5aSad ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || 20044435f5aSad check_password((char *)boot_params.bp_password))) { 20144435f5aSad printf("type \"?\" or \"help\" for help.\n"); 20244435f5aSad bootmenu(); /* does not return */ 20344435f5aSad } else { 2046a91dd31She docommandchoice(choice); 20544435f5aSad } 20644435f5aSad 20744435f5aSad } 20844435f5aSad } 20944435f5aSad 21044435f5aSad #endif /* !SMALL */ 211