1 /* $NetBSD: bootcfg.c,v 1.9 2022/01/05 16:01:54 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/reboot.h> 31 32 #include <lib/libsa/stand.h> 33 #include <lib/libsa/bootcfg.h> 34 #include <lib/libkern/libkern.h> 35 36 #define MENUFORMAT_AUTO 0 37 #define MENUFORMAT_NUMBER 1 38 #define MENUFORMAT_LETTER 2 39 40 #define DEFAULT_FORMAT MENUFORMAT_AUTO 41 #ifndef DEFAULT_TIMEOUT 42 #define DEFAULT_TIMEOUT 10 43 #endif 44 45 struct bootcfg_def bootcfg_info; 46 47 void 48 bootcfg_do_noop(const char *cmd, char *arg) 49 { 50 /* noop, do nothing */ 51 } 52 53 /* 54 * This function parses a boot.cfg file in the root of the filesystem 55 * (if present) and populates the global boot configuration. 56 * 57 * The file consists of a number of lines each terminated by \n 58 * The lines are in the format keyword=value. There should not be spaces 59 * around the = sign. 60 * 61 * perform_bootcfg(conf, command, maxsz) 62 * 63 * conf Path to boot.cfg to be passed verbatim to open() 64 * 65 * command Pointer to a function that will be called when 66 * perform_bootcfg() encounters a key (command) it does not 67 * recognize. 68 * The command function is provided both the keyword and 69 * value parsed as arguments to the function. 70 * 71 * maxsz Limit the size of the boot.cfg perform_bootcfg() will parse. 72 * - If maxsz is < 0 boot.cfg will not be processed. 73 * - If maxsz is = 0 no limit will be imposed but parsing may 74 * fail due to platform or other constraints e.g. maximum 75 * segment size. 76 * - If 0 < maxsz and boot.cfg exceeds maxsz it will not be 77 * parsed, otherwise it will be parsed. 78 * 79 * The recognised keywords are: 80 * banner: text displayed instead of the normal welcome text 81 * menu: Descriptive text:command to use 82 * timeout: Timeout in seconds (overrides that set by installboot) 83 * default: the default menu option to use if Return is pressed 84 * consdev: the console device to use 85 * root: the root device to use 86 * format: how menu choices are displayed: (a)utomatic, (n)umbers or (l)etters 87 * clear: whether to clear the screen or not 88 * 89 * Example boot.cfg file: 90 * banner=Welcome to NetBSD 91 * banner=Please choose the boot type from the following menu 92 * menu=Boot NetBSD:boot netbsd 93 * menu=Boot into single user mode:boot netbsd -s 94 * menu=:boot hd1a:netbsd -cs 95 * menu=Goto boot command line:prompt 96 * timeout=10 97 * consdev=com0 98 * default=1 99 */ 100 int 101 perform_bootcfg(const char *conf, bootcfg_command command, const off_t maxsz) 102 { 103 char *bc, *c; 104 int cmenu, cbanner; 105 ssize_t len, off, resid; 106 int fd, err; 107 struct stat st; 108 char *next, *key, *value, *v2; 109 110 /* clear bootcfg structure */ 111 memset(&bootcfg_info, 0, sizeof(bootcfg_info)); 112 113 /* set default timeout */ 114 bootcfg_info.timeout = DEFAULT_TIMEOUT; 115 116 /* automatically switch between letter and numbers on menu */ 117 bootcfg_info.menuformat = DEFAULT_FORMAT; 118 119 fd = open(conf, 0); 120 if (fd < 0) 121 return ENOENT; 122 123 err = fstat(fd, &st); 124 if (err == -1) { 125 /* file descriptor may not be backed by a libsa file-system */ 126 st.st_size = maxsz; 127 } 128 129 /* if a maximum size is being requested for the boot.cfg enforce it. */ 130 if (0 < maxsz && st.st_size > maxsz) { 131 close(fd); 132 return EFBIG; 133 } 134 135 bc = alloc((size_t)st.st_size + 1); 136 if (bc == NULL) { 137 printf("Could not allocate memory for boot configuration\n"); 138 close(fd); 139 return ENOMEM; 140 } 141 142 /* 143 * XXX original code, assumes error or eof return from read() 144 * results in the entire boot.cfg being buffered. 145 * - should bail out on read() failing. 146 * - assumption is made that the file size doesn't change between 147 * fstat() and read()ing. probably safe in this context 148 * arguably should check that reading the file won't overflow 149 * the storage anyway. 150 */ 151 off = 0; 152 resid = st.st_size; 153 do { 154 len = read(fd, bc + off, uimin(1024, resid)); 155 if (len <= 0) 156 break; 157 off += len; 158 resid -= len; 159 } while (len > 0 && resid > 0); 160 bc[off] = '\0'; 161 162 close(fd); 163 164 /* bc is now assumed to contain the whole boot.cfg file (see above) */ 165 166 cmenu = 0; 167 cbanner = 0; 168 for (c = bc; *c; c = next) { 169 key = c; 170 /* find end of line */ 171 for (; *c && *c != '\n'; c++) 172 /* zero terminate line on start of comment */ 173 if (*c == '#') 174 *c = 0; 175 /* zero terminate line */ 176 if (*(next = c)) 177 *next++ = 0; 178 /* Look for = separator between key and value */ 179 for (c = key; *c && *c != '='; c++) 180 continue; 181 /* Ignore lines with no key=value pair */ 182 if (*c == '\0') 183 continue; 184 185 /* zero terminate key which points to keyword */ 186 *c++ = 0; 187 value = c; 188 /* Look for end of line (or file) and zero terminate value */ 189 for (; *c && *c != '\n'; c++) 190 continue; 191 *c = 0; 192 193 if (!strncmp(key, "menu", 4)) { 194 /* 195 * Parse "menu=<description>:<command>". If the 196 * description is empty ("menu=:<command>)", 197 * then re-use the command as the description. 198 * Note that the command may contain embedded 199 * colons. 200 */ 201 if (cmenu >= BOOTCFG_MAXMENU) 202 continue; 203 bootcfg_info.desc[cmenu] = value; 204 for (v2 = value; *v2 && *v2 != ':'; v2++) 205 continue; 206 if (*v2) { 207 *v2++ = 0; 208 bootcfg_info.command[cmenu] = v2; 209 if (! *value) 210 bootcfg_info.desc[cmenu] = v2; 211 cmenu++; 212 } else { 213 /* No delimiter means invalid line */ 214 bootcfg_info.desc[cmenu] = NULL; 215 } 216 } else if (!strncmp(key, "banner", 6)) { 217 if (cbanner < BOOTCFG_MAXBANNER) 218 bootcfg_info.banner[cbanner++] = value; 219 } else if (!strncmp(key, "timeout", 7)) { 220 if (!isdigit(*value)) 221 bootcfg_info.timeout = -1; 222 else 223 bootcfg_info.timeout = atoi(value); 224 } else if (!strncmp(key, "default", 7)) { 225 bootcfg_info.def = atoi(value) - 1; 226 } else if (!strncmp(key, "consdev", 7)) { 227 bootcfg_info.consdev = value; 228 } else if (!strncmp(key, "root", 4)) { 229 bootcfg_info.root = value; 230 } else if (!strncmp(key, BOOTCFG_CMD_LOAD, 4)) { 231 command(BOOTCFG_CMD_LOAD, value); 232 } else if (!strncmp(key, "format", 6)) { 233 printf("value:%c\n", *value); 234 switch (*value) { 235 case 'a': 236 case 'A': 237 bootcfg_info.menuformat = MENUFORMAT_AUTO; 238 break; 239 240 case 'n': 241 case 'N': 242 case 'd': 243 case 'D': 244 bootcfg_info.menuformat = MENUFORMAT_NUMBER; 245 break; 246 247 case 'l': 248 case 'L': 249 bootcfg_info.menuformat = MENUFORMAT_LETTER; 250 break; 251 } 252 } else if (!strncmp(key, "clear", 5)) { 253 bootcfg_info.clear = !!atoi(value); 254 } else if (!strncmp(key, BOOTCFG_CMD_USERCONF, 8)) { 255 command(BOOTCFG_CMD_USERCONF, value); 256 } else { 257 command(key, value); 258 } 259 } 260 261 switch (bootcfg_info.menuformat) { 262 case MENUFORMAT_AUTO: 263 if (cmenu > 9 && bootcfg_info.timeout > 0) 264 bootcfg_info.menuformat = MENUFORMAT_LETTER; 265 else 266 bootcfg_info.menuformat = MENUFORMAT_NUMBER; 267 break; 268 269 case MENUFORMAT_NUMBER: 270 if (cmenu > 9 && bootcfg_info.timeout > 0) 271 cmenu = 9; 272 break; 273 } 274 275 bootcfg_info.nummenu = cmenu; 276 if (bootcfg_info.def < 0) 277 bootcfg_info.def = 0; 278 if (bootcfg_info.def >= cmenu) 279 bootcfg_info.def = cmenu - 1; 280 281 return 0; 282 } 283 284 void 285 print_bootcfg_banner(const char *bootprog_name, const char *bootprog_rev) 286 { 287 int n = 0; 288 289 if (bootcfg_info.banner[0]) { 290 for (; n < BOOTCFG_MAXBANNER && bootcfg_info.banner[n]; n++) 291 printf("%s\n", bootcfg_info.banner[n]); 292 return; 293 } 294 295 /* If the user has not specified a banner, print a default one. */ 296 297 printf("\n"); 298 printf(" \\\\-__,------,___.\n"); 299 printf(" \\\\ __,---` %s\n", bootprog_name); 300 printf(" \\\\ `---,_. Revision %s\n", bootprog_rev); 301 printf(" \\\\-,_____,.---`\n"); 302 printf(" \\\\\n"); 303 printf(" \\\\\n"); 304 printf(" \\\\\n\n"); 305 } 306