1 /* $NetBSD: bootcfg.c,v 1.2 2014/08/10 07:40:49 isaki 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 #define DEFAULT_TIMEOUT 10 42 43 struct bootcfg_def bootcfg_info; 44 45 void 46 bootcfg_do_noop(const char *cmd, char *arg) 47 { 48 /* noop, do nothing */ 49 } 50 51 /* 52 * This function parses a boot.cfg file in the root of the filesystem 53 * (if present) and populates the global boot configuration. 54 * 55 * The file consists of a number of lines each terminated by \n 56 * The lines are in the format keyword=value. There should not be spaces 57 * around the = sign. 58 * 59 * perform_bootcfg(conf, command, maxsz) 60 * 61 * conf Path to boot.cfg to be passed verbatim to open() 62 * 63 * command Pointer to a function that will be called when 64 * perform_bootcfg() encounters a key (command) it does not 65 * recognize. 66 * The command function is provided both the keyword and 67 * value parsed as arguments to the function. 68 * 69 * maxsz Limit the size of the boot.cfg perform_bootcfg() will parse. 70 * - If maxsz is < 0 boot.cfg will not be processed. 71 * - If maxsz is = 0 no limit will be imposed but parsing may 72 * fail due to platform or other constraints e.g. maximum 73 * segment size. 74 * - If 0 < maxsz and boot.cfg exceeds maxsz it will not be 75 * parsed, otherwise it will be parsed. 76 * 77 * The recognised keywords are: 78 * banner: text displayed instead of the normal welcome text 79 * menu: Descriptive text:command to use 80 * timeout: Timeout in seconds (overrides that set by installboot) 81 * default: the default menu option to use if Return is pressed 82 * consdev: the console device to use 83 * format: how menu choices are displayed: (a)utomatic, (n)umbers or (l)etters 84 * clear: whether to clear the screen or not 85 * 86 * Example boot.cfg file: 87 * banner=Welcome to NetBSD 88 * banner=Please choose the boot type from the following menu 89 * menu=Boot NetBSD:boot netbsd 90 * menu=Boot into single user mode:boot netbsd -s 91 * menu=:boot hd1a:netbsd -cs 92 * menu=Goto boot comand line:prompt 93 * timeout=10 94 * consdev=com0 95 * default=1 96 */ 97 void 98 perform_bootcfg(const char *conf, bootcfg_command command, const off_t maxsz) 99 { 100 char *bc, *c; 101 int cmenu, cbanner, len; 102 int fd, err, off; 103 struct stat st; 104 char *next, *key, *value, *v2; 105 106 /* clear bootcfg structure */ 107 memset(&bootcfg_info, 0, sizeof(bootcfg_info)); 108 109 /* set default timeout */ 110 bootcfg_info.timeout = DEFAULT_TIMEOUT; 111 112 /* automatically switch between letter and numbers on menu */ 113 bootcfg_info.menuformat = DEFAULT_FORMAT; 114 115 fd = open(conf, 0); 116 if (fd < 0) 117 return; 118 119 err = fstat(fd, &st); 120 if (err == -1) { 121 close(fd); 122 return; 123 } 124 125 /* if a maximum size is being requested for the boot.cfg enforce it. */ 126 if (0 < maxsz && st.st_size > maxsz) { 127 close(fd); 128 return; 129 } 130 131 bc = alloc(st.st_size + 1); 132 if (bc == NULL) { 133 printf("Could not allocate memory for boot configuration\n"); 134 close(fd); 135 return; 136 } 137 138 /* 139 * XXX original code, assumes error or eof return from read() 140 * results in the entire boot.cfg being buffered. 141 * - should bail out on read() failing. 142 * - assumption is made that the file size doesn't change between 143 * fstat() and read()ing. probably safe in this context 144 * arguably should check that reading the file won't overflow 145 * the storage anyway. 146 */ 147 off = 0; 148 do { 149 len = read(fd, bc + off, 1024); 150 if (len <= 0) 151 break; 152 off += len; 153 } while (len > 0); 154 bc[off] = '\0'; 155 156 close(fd); 157 158 /* bc is now assumed to contain the whole boot.cfg file (see above) */ 159 160 cmenu = 0; 161 cbanner = 0; 162 for (c = bc; *c; c = next) { 163 key = c; 164 /* find end of line */ 165 for (; *c && *c != '\n'; c++) 166 /* zero terminate line on start of comment */ 167 if (*c == '#') 168 *c = 0; 169 /* zero terminate line */ 170 if (*(next = c)) 171 *next++ = 0; 172 /* Look for = separator between key and value */ 173 for (c = key; *c && *c != '='; c++) 174 continue; 175 /* Ignore lines with no key=value pair */ 176 if (*c == '\0') 177 continue; 178 179 /* zero terminate key which points to keyword */ 180 *c++ = 0; 181 value = c; 182 /* Look for end of line (or file) and zero terminate value */ 183 for (; *c && *c != '\n'; c++) 184 continue; 185 *c = 0; 186 187 if (!strncmp(key, "menu", 4)) { 188 /* 189 * Parse "menu=<description>:<command>". If the 190 * description is empty ("menu=:<command>)", 191 * then re-use the command as the description. 192 * Note that the command may contain embedded 193 * colons. 194 */ 195 if (cmenu >= BOOTCFG_MAXMENU) 196 continue; 197 bootcfg_info.desc[cmenu] = value; 198 for (v2 = value; *v2 && *v2 != ':'; v2++) 199 continue; 200 if (*v2) { 201 *v2++ = 0; 202 bootcfg_info.command[cmenu] = v2; 203 if (! *value) 204 bootcfg_info.desc[cmenu] = v2; 205 cmenu++; 206 } else { 207 /* No delimiter means invalid line */ 208 bootcfg_info.desc[cmenu] = NULL; 209 } 210 } else if (!strncmp(key, "banner", 6)) { 211 if (cbanner < BOOTCFG_MAXBANNER) 212 bootcfg_info.banner[cbanner++] = value; 213 } else if (!strncmp(key, "timeout", 7)) { 214 if (!isdigit(*value)) 215 bootcfg_info.timeout = -1; 216 else 217 bootcfg_info.timeout = atoi(value); 218 } else if (!strncmp(key, "default", 7)) { 219 bootcfg_info.def = atoi(value) - 1; 220 } else if (!strncmp(key, "consdev", 7)) { 221 bootcfg_info.consdev = value; 222 } else if (!strncmp(key, BOOTCFG_CMD_LOAD, 4)) { 223 command(BOOTCFG_CMD_LOAD, value); 224 } else if (!strncmp(key, "format", 6)) { 225 printf("value:%c\n", *value); 226 switch (*value) { 227 case 'a': 228 case 'A': 229 bootcfg_info.menuformat = MENUFORMAT_AUTO; 230 break; 231 232 case 'n': 233 case 'N': 234 case 'd': 235 case 'D': 236 bootcfg_info.menuformat = MENUFORMAT_NUMBER; 237 break; 238 239 case 'l': 240 case 'L': 241 bootcfg_info.menuformat = MENUFORMAT_LETTER; 242 break; 243 } 244 } else if (!strncmp(key, "clear", 5)) { 245 bootcfg_info.clear = !!atoi(value); 246 } else if (!strncmp(key, BOOTCFG_CMD_USERCONF, 8)) { 247 command(BOOTCFG_CMD_USERCONF, value); 248 } else { 249 command(key, value); 250 } 251 } 252 253 switch (bootcfg_info.menuformat) { 254 case MENUFORMAT_AUTO: 255 if (cmenu > 9 && bootcfg_info.timeout > 0) 256 bootcfg_info.menuformat = MENUFORMAT_LETTER; 257 else 258 bootcfg_info.menuformat = MENUFORMAT_NUMBER; 259 break; 260 261 case MENUFORMAT_NUMBER: 262 if (cmenu > 9 && bootcfg_info.timeout > 0) 263 cmenu = 9; 264 break; 265 } 266 267 bootcfg_info.nummenu = cmenu; 268 if (bootcfg_info.def < 0) 269 bootcfg_info.def = 0; 270 if (bootcfg_info.def >= cmenu) 271 bootcfg_info.def = cmenu - 1; 272 } 273