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