1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 5 * All Rights Reserved. 6 * Copyright (c) 1998 Robert Nordier 7 * All Rights Reserved. 8 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 9 * All rights reserved. 10 * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com> 11 * All Rights Reserved. 12 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 13 * Copyright (c) 2018 Netflix, Inc. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer 20 * in this position and unchanged. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 /* Note: This is compiled in both the kernel and boot loader contexts */ 40 41 #include <sys/param.h> 42 #ifdef _KERNEL 43 #include <sys/systm.h> 44 #else 45 #include <stand.h> 46 #endif 47 #include <sys/reboot.h> 48 #include <sys/boot.h> 49 #include <sys/tslog.h> 50 51 #ifdef _KERNEL 52 #define SETENV(k, v) kern_setenv(k, v) 53 #define GETENV(k) kern_getenv(k) 54 #define FREE(v) freeenv(v) 55 #else /* Boot loader */ 56 #define SETENV(k, v) setenv(k, v, 1) 57 #define GETENV(k) getenv(k) 58 #define FREE(v) 59 #endif 60 61 static struct 62 { 63 const char *ev; 64 int mask; 65 } howto_names[] = { 66 { "boot_askname", RB_ASKNAME}, 67 { "boot_cdrom", RB_CDROM}, 68 { "boot_ddb", RB_KDB}, 69 { "boot_dfltroot", RB_DFLTROOT}, 70 { "boot_gdb", RB_GDB}, 71 { "boot_multicons", RB_MULTIPLE}, 72 { "boot_mute", RB_MUTE}, 73 { "boot_mutemsgs", RB_MUTEMSGS}, 74 { "boot_pause", RB_PAUSE}, 75 { "boot_serial", RB_SERIAL}, 76 { "boot_single", RB_SINGLE}, 77 { "boot_verbose", RB_VERBOSE}, 78 { NULL, 0} 79 }; 80 81 /* 82 * In the boot environment, we often parse a command line and have to throw away 83 * its contents. As we do so, we set environment variables that correspond to 84 * the flags we encounter. Later, to get a howto mask, we grovel through these 85 * to reconstruct it. This also allows users in their loader.conf to set them 86 * and have the kernel see them. 87 */ 88 89 /** 90 * @brief convert the env vars in howto_names into a howto mask 91 */ 92 int 93 boot_env_to_howto(void) 94 { 95 int i, howto; 96 char *val; 97 98 TSENTER(); 99 for (howto = 0, i = 0; howto_names[i].ev != NULL; i++) { 100 val = GETENV(howto_names[i].ev); 101 if (val != NULL && strcasecmp(val, "no") != 0) 102 howto |= howto_names[i].mask; 103 FREE(val); 104 } 105 TSEXIT(); 106 return (howto); 107 } 108 109 /** 110 * @brief Set env vars from howto_names based on howto passed in 111 */ 112 void 113 boot_howto_to_env(int howto) 114 { 115 int i; 116 117 for (i = 0; howto_names[i].ev != NULL; i++) 118 if (howto & howto_names[i].mask) 119 SETENV(howto_names[i].ev, "YES"); 120 } 121 122 /** 123 * @brief Helper routine to parse a single arg and return its mask 124 * 125 * Parse all the - options to create a mask (or a serial speed in the 126 * case of -S). If the arg doesn't start with '-' assume it's an env 127 * variable and set that instead. 128 */ 129 int 130 boot_parse_arg(const char *v) 131 { 132 char *n; 133 int howto; 134 135 #if 0 136 /* Need to see if this is better or worse than the meat of the #else */ 137 static const char howto_switches[] = "aCdrgDmMphsv"; 138 static int howto_masks[] = { 139 RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE, 140 RB_MUTE, RB_MUTEMSGS, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE 141 }; 142 143 opts = strchr(kargs, '-'); 144 while (opts != NULL) { 145 while (*(++opts) != '\0') { 146 sw = strchr(howto_switches, *opts); 147 if (sw == NULL) 148 break; 149 howto |= howto_masks[sw - howto_switches]; 150 } 151 opts = strchr(opts, '-'); 152 } 153 #else 154 howto = 0; 155 if (*v == '-') { 156 while (*v != '\0') { 157 v++; 158 switch (*v) { 159 case 'a': howto |= RB_ASKNAME; break; 160 case 'C': howto |= RB_CDROM; break; 161 case 'd': howto |= RB_KDB; break; 162 case 'D': howto |= RB_MULTIPLE; break; 163 case 'm': howto |= RB_MUTE; break; 164 case 'M': howto |= RB_MUTEMSGS; break; 165 case 'g': howto |= RB_GDB; break; 166 case 'h': howto |= RB_SERIAL; break; 167 case 'p': howto |= RB_PAUSE; break; 168 case 'P': howto |= RB_PROBE; break; 169 case 'r': howto |= RB_DFLTROOT; break; 170 case 's': howto |= RB_SINGLE; break; 171 case 'S': SETENV("comconsole_speed", v + 1); v += strlen(v); break; 172 case 'v': howto |= RB_VERBOSE; break; 173 } 174 } 175 } else { 176 char buf[128]; 177 char *vv = buf; 178 179 strlcpy(buf, v, sizeof(buf)); 180 n = strsep(&vv, "="); 181 if (vv == NULL) 182 SETENV(n, "1"); 183 else 184 SETENV(n, vv); 185 } 186 #endif 187 return (howto); 188 } 189 190 /** 191 * @brief breakup the command line into args, and pass to boot_parse_arg 192 */ 193 int 194 boot_parse_cmdline_delim(char *cmdline, const char *delim) 195 { 196 char *v; 197 int howto; 198 199 TSENTER(); 200 howto = 0; 201 while ((v = strsep(&cmdline, delim)) != NULL) { 202 if (*v == '\0') 203 continue; 204 howto |= boot_parse_arg(v); 205 } 206 TSEXIT(); 207 return (howto); 208 } 209 210 /** 211 * @brief Simplified interface for common 'space or tab separated' args 212 */ 213 int 214 boot_parse_cmdline(char *cmdline) 215 { 216 217 return (boot_parse_cmdline_delim(cmdline, " \t\n")); 218 } 219 220 /** 221 * @brief Pass a vector of strings to boot_parse_arg 222 */ 223 int 224 boot_parse_args(int argc, char *argv[]) 225 { 226 int i, howto; 227 228 howto = 0; 229 for (i = 1; i < argc; i++) 230 howto |= boot_parse_arg(argv[i]); 231 return (howto); 232 } 233