1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <stand.h> 31 #include <string.h> 32 33 #include "bootstrap.h" 34 /* 35 * Core console support 36 */ 37 38 static int cons_set(struct env_var *ev, int flags, const void *value); 39 static int cons_find(const char *name); 40 static int cons_check(const char *string); 41 static int cons_change(const char *string); 42 static int twiddle_set(struct env_var *ev, int flags, const void *value); 43 44 /* 45 * Detect possible console(s) to use. If preferred console(s) have been 46 * specified, mark them as active. Else, mark the first probed console 47 * as active. Also create the console variable. 48 */ 49 void 50 cons_probe(void) 51 { 52 int cons; 53 int active; 54 char *prefconsole; 55 56 /* We want a callback to install the new value when this var changes. */ 57 env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, env_nounset); 58 59 /* Do all console probes */ 60 for (cons = 0; consoles[cons] != NULL; cons++) { 61 consoles[cons]->c_flags = 0; 62 consoles[cons]->c_probe(consoles[cons]); 63 } 64 /* Now find the first working one */ 65 active = -1; 66 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 67 consoles[cons]->c_flags = 0; 68 consoles[cons]->c_probe(consoles[cons]); 69 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 70 active = cons; 71 } 72 /* Force a console even if all probes failed */ 73 if (active == -1) 74 active = 0; 75 76 /* Check to see if a console preference has already been registered */ 77 prefconsole = getenv("console"); 78 if (prefconsole != NULL) 79 prefconsole = strdup(prefconsole); 80 if (prefconsole != NULL) { 81 unsetenv("console"); /* we want to replace this */ 82 cons_change(prefconsole); 83 } else { 84 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 85 consoles[active]->c_init(consoles[active], 0); 86 prefconsole = strdup(consoles[active]->c_name); 87 } 88 89 printf("Consoles: "); 90 for (cons = 0; consoles[cons] != NULL; cons++) 91 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 92 printf("%s ", consoles[cons]->c_desc); 93 printf("\n"); 94 95 if (prefconsole != NULL) { 96 env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 97 env_nounset); 98 free(prefconsole); 99 } 100 } 101 102 void 103 cons_mode(int raw) 104 { 105 int cons; 106 107 for (cons = 0; consoles[cons] != NULL; cons++) { 108 if (raw == 0) 109 consoles[cons]->c_flags &= ~C_MODERAW; 110 else 111 consoles[cons]->c_flags |= C_MODERAW; 112 } 113 } 114 115 int 116 getchar(void) 117 { 118 int cons; 119 int rv; 120 121 /* Loop forever polling all active consoles */ 122 for(;;) 123 for (cons = 0; consoles[cons] != NULL; cons++) 124 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 125 (C_PRESENTIN | C_ACTIVEIN) && 126 ((rv = consoles[cons]->c_in(consoles[cons])) != -1)) 127 return(rv); 128 } 129 130 int 131 ischar(void) 132 { 133 int cons; 134 135 for (cons = 0; consoles[cons] != NULL; cons++) 136 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 137 (C_PRESENTIN | C_ACTIVEIN) && 138 (consoles[cons]->c_ready(consoles[cons]) != 0)) 139 return(1); 140 return(0); 141 } 142 143 void 144 putchar(int c) 145 { 146 int cons; 147 148 /* Expand newlines if not in raw mode */ 149 for (cons = 0; consoles[cons] != NULL; cons++) 150 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 151 (C_PRESENTOUT | C_ACTIVEOUT)) { 152 if (c == '\n' && (consoles[cons]->c_flags & C_MODERAW) == 0) 153 consoles[cons]->c_out(consoles[cons], '\r'); 154 consoles[cons]->c_out(consoles[cons], c); 155 } 156 } 157 158 /* 159 * Find the console with the specified name. 160 */ 161 static int 162 cons_find(const char *name) 163 { 164 int cons; 165 166 for (cons = 0; consoles[cons] != NULL; cons++) 167 if (!strcmp(consoles[cons]->c_name, name)) 168 return (cons); 169 return (-1); 170 } 171 172 /* 173 * Select one or more consoles. 174 */ 175 static int 176 cons_set(struct env_var *ev, int flags, const void *value) 177 { 178 int ret; 179 180 if ((value == NULL) || (cons_check(value) == 0)) { 181 /* 182 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax error, 183 * which would prevent it processing any further loader.conf entries. 184 */ 185 return (CMD_OK); 186 } 187 188 ret = cons_change(value); 189 if (ret != CMD_OK) 190 return (ret); 191 192 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 193 return (CMD_OK); 194 } 195 196 /* 197 * Check that at least one the consoles listed in *string is valid 198 */ 199 static int 200 cons_check(const char *string) 201 { 202 int cons, found, failed; 203 char *curpos, *dup, *next; 204 205 dup = next = strdup(string); 206 found = failed = 0; 207 while (next != NULL) { 208 curpos = strsep(&next, " ,"); 209 if (*curpos != '\0') { 210 cons = cons_find(curpos); 211 if (cons == -1) { 212 printf("console %s is invalid!\n", curpos); 213 failed++; 214 } else { 215 found++; 216 } 217 } 218 } 219 220 free(dup); 221 222 if (found == 0) 223 printf("no valid consoles!\n"); 224 225 if (found == 0 || failed != 0) { 226 printf("Available consoles:\n"); 227 for (cons = 0; consoles[cons] != NULL; cons++) 228 printf(" %s\n", consoles[cons]->c_name); 229 } 230 231 return (found); 232 } 233 234 /* 235 * Activate all the valid consoles listed in *string and disable all others. 236 */ 237 static int 238 cons_change(const char *string) 239 { 240 int cons, active; 241 char *curpos, *dup, *next; 242 243 /* Disable all consoles */ 244 for (cons = 0; consoles[cons] != NULL; cons++) { 245 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 246 } 247 248 /* Enable selected consoles */ 249 dup = next = strdup(string); 250 active = 0; 251 while (next != NULL) { 252 curpos = strsep(&next, " ,"); 253 if (*curpos == '\0') 254 continue; 255 cons = cons_find(curpos); 256 if (cons >= 0) { 257 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 258 consoles[cons]->c_init(consoles[cons], 0); 259 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 260 (C_PRESENTIN | C_PRESENTOUT)) { 261 active++; 262 continue; 263 } 264 265 if (active != 0) { 266 /* If no consoles have initialised we wouldn't see this. */ 267 printf("console %s failed to initialize\n", consoles[cons]->c_name); 268 } 269 } 270 } 271 272 free(dup); 273 274 if (active == 0) { 275 /* All requested consoles failed to initialise, try to recover. */ 276 for (cons = 0; consoles[cons] != NULL; cons++) { 277 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 278 consoles[cons]->c_init(consoles[cons], 0); 279 if ((consoles[cons]->c_flags & 280 (C_PRESENTIN | C_PRESENTOUT)) == 281 (C_PRESENTIN | C_PRESENTOUT)) 282 active++; 283 } 284 285 if (active == 0) 286 return (CMD_ERROR); /* Recovery failed. */ 287 } 288 289 return (CMD_OK); 290 } 291 292 /* 293 * Change the twiddle divisor. 294 * 295 * The user can set the twiddle_divisor variable to directly control how fast 296 * the progress twiddle spins, useful for folks with slow serial consoles. The 297 * code to monitor changes to the variable and propagate them to the twiddle 298 * routines has to live somewhere. Twiddling is console-related so it's here. 299 */ 300 static int 301 twiddle_set(struct env_var *ev, int flags, const void *value) 302 { 303 u_long tdiv; 304 char * eptr; 305 306 tdiv = strtoul(value, &eptr, 0); 307 if (*(const char *)value == 0 || *eptr != 0) { 308 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 309 return (CMD_ERROR); 310 } 311 twiddle_divisor((u_int)tdiv); 312 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 313 314 return(CMD_OK); 315 } 316