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 29 #include <stand.h> 30 #include <string.h> 31 32 #include "bootstrap.h" 33 /* 34 * Core console support 35 */ 36 37 static int cons_set(struct env_var *ev, int flags, const void *value); 38 static int cons_find(const char *name); 39 static int cons_check(const char *string); 40 static int cons_change(const char *string, char **); 41 static int twiddle_set(struct env_var *ev, int flags, const void *value); 42 43 /* 44 * Detect possible console(s) to use. If preferred console(s) have been 45 * specified, mark them as active. Else, mark the first probed console 46 * as active. Also create the console variable. 47 */ 48 void 49 cons_probe(void) 50 { 51 int cons; 52 int active; 53 char *prefconsole, *list, *console; 54 55 /* We want a callback to install the new value when this var changes. */ 56 env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, 57 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; cons++) { 67 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) { 68 active = cons; 69 break; 70 } 71 } 72 73 /* Force a console even if all probes failed */ 74 if (active == -1) 75 active = 0; 76 77 /* Check to see if a console preference has already been registered */ 78 list = NULL; 79 prefconsole = getenv("console"); 80 if (prefconsole != NULL) 81 prefconsole = strdup(prefconsole); 82 if (prefconsole == NULL) 83 prefconsole = strdup(consoles[active]->c_name); 84 85 /* 86 * unset "console", we need to create one with callbacks. 87 */ 88 unsetenv("console"); 89 cons_change(prefconsole, &list); 90 91 printf("Consoles: "); 92 for (cons = 0; consoles[cons] != NULL; cons++) 93 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 94 printf("%s ", consoles[cons]->c_desc); 95 printf("\n"); 96 97 if (list != NULL) 98 console = list; 99 else 100 console = prefconsole; 101 102 env_setenv("console", EV_VOLATILE, console, cons_set, 103 env_nounset); 104 105 free(prefconsole); 106 free(list); 107 } 108 109 void 110 cons_mode(int raw) 111 { 112 int cons; 113 114 for (cons = 0; consoles[cons] != NULL; cons++) { 115 if (raw == 0) 116 consoles[cons]->c_flags &= ~C_MODERAW; 117 else 118 consoles[cons]->c_flags |= C_MODERAW; 119 } 120 } 121 122 int 123 getchar(void) 124 { 125 int cons; 126 int flags = C_PRESENTIN | C_ACTIVEIN; 127 int rv; 128 129 /* 130 * Loop forever polling all active consoles. Somewhat strangely, 131 * this code expects all ->c_in() implementations to effectively do an 132 * ischar() check first, returning -1 if there's not a char ready. 133 */ 134 for (;;) { 135 for (cons = 0; consoles[cons] != NULL; cons++) { 136 if ((consoles[cons]->c_flags & flags) == flags && 137 ((rv = consoles[cons]->c_in(consoles[cons])) != -1)) 138 return (rv); 139 } 140 delay(30 * 1000); /* delay 30ms */ 141 } 142 } 143 144 int 145 ischar(void) 146 { 147 int cons; 148 149 for (cons = 0; consoles[cons] != NULL; cons++) 150 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 151 (C_PRESENTIN | C_ACTIVEIN) && 152 (consoles[cons]->c_ready(consoles[cons]) != 0)) 153 return (1); 154 return (0); 155 } 156 157 void 158 putchar(int c) 159 { 160 int cons; 161 162 /* Expand newlines if not in raw mode */ 163 for (cons = 0; consoles[cons] != NULL; cons++) 164 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 165 (C_PRESENTOUT | C_ACTIVEOUT)) { 166 if (c == '\n' && 167 (consoles[cons]->c_flags & C_MODERAW) == 0) 168 consoles[cons]->c_out(consoles[cons], '\r'); 169 consoles[cons]->c_out(consoles[cons], c); 170 } 171 } 172 173 /* 174 * Find the console with the specified name. 175 */ 176 static int 177 cons_find(const char *name) 178 { 179 int cons; 180 181 for (cons = 0; consoles[cons] != NULL; cons++) 182 if (strcmp(consoles[cons]->c_name, name) == 0) 183 return (cons); 184 return (-1); 185 } 186 187 /* 188 * Select one or more consoles. 189 */ 190 static int 191 cons_set(struct env_var *ev, int flags, const void *value) 192 { 193 int ret; 194 char *list; 195 196 if ((value == NULL) || (cons_check(value) == 0)) { 197 /* 198 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax 199 * error, which would prevent it processing any further 200 * loader.conf entries. 201 */ 202 return (CMD_OK); 203 } 204 205 list = NULL; 206 ret = cons_change(value, &list); 207 if (ret != CMD_OK) 208 return (ret); 209 210 /* 211 * set console variable. 212 */ 213 if (list != NULL) { 214 (void) env_setenv(ev->ev_name, flags | EV_NOHOOK, list, 215 NULL, NULL); 216 } else { 217 (void) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, 218 NULL, NULL); 219 } 220 free(list); 221 return (ret); 222 } 223 224 /* 225 * Check that at least one the consoles listed in *string is valid 226 */ 227 static int 228 cons_check(const char *string) 229 { 230 int cons, found, failed; 231 char *curpos, *dup, *next; 232 233 dup = next = strdup(string); 234 found = failed = 0; 235 while (next != NULL) { 236 curpos = strsep(&next, " ,"); 237 if (*curpos != '\0') { 238 cons = cons_find(curpos); 239 if (cons == -1) { 240 printf("console %s is invalid!\n", curpos); 241 failed++; 242 } else { 243 if ((consoles[cons]->c_flags & 244 (C_PRESENTIN | C_PRESENTOUT)) != 245 (C_PRESENTIN | C_PRESENTOUT)) { 246 failed++; 247 } else 248 found++; 249 } 250 } 251 } 252 253 free(dup); 254 255 if (found == 0) 256 printf("no valid consoles!\n"); 257 258 if (found == 0 || failed != 0) { 259 printf("Available consoles:\n"); 260 for (cons = 0; consoles[cons] != NULL; cons++) { 261 printf(" %s", consoles[cons]->c_name); 262 if (consoles[cons]->c_devinfo != NULL) 263 consoles[cons]->c_devinfo(consoles[cons]); 264 printf("\n"); 265 } 266 } 267 268 return (found); 269 } 270 271 /* 272 * Helper function to build string with list of console names. 273 */ 274 static char * 275 cons_add_list(char *list, const char *value) 276 { 277 char *tmp; 278 279 if (list == NULL) 280 return (strdup(value)); 281 282 if (asprintf(&tmp, "%s,%s", list, value) > 0) { 283 free(list); 284 list = tmp; 285 } 286 return (list); 287 } 288 289 /* 290 * Activate all the valid consoles listed in string and disable all others. 291 * Return comma separated string with list of activated console names. 292 */ 293 static int 294 cons_change(const char *string, char **list) 295 { 296 int cons, active, rv; 297 char *curpos, *dup, *next; 298 299 /* Disable all consoles */ 300 for (cons = 0; consoles[cons] != NULL; cons++) { 301 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 302 } 303 304 /* Enable selected consoles */ 305 dup = next = strdup(string); 306 active = 0; 307 *list = NULL; 308 rv = CMD_OK; 309 while (next != NULL) { 310 curpos = strsep(&next, " ,"); 311 if (*curpos == '\0') 312 continue; 313 cons = cons_find(curpos); 314 if (cons >= 0) { 315 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 316 consoles[cons]->c_init(consoles[cons], 0); 317 if ((consoles[cons]->c_flags & 318 (C_ACTIVEIN | C_ACTIVEOUT)) == 319 (C_ACTIVEIN | C_ACTIVEOUT)) { 320 active++; 321 *list = cons_add_list(*list, curpos); 322 continue; 323 } 324 325 if (active != 0) { 326 /* 327 * If no consoles have initialised we wouldn't 328 * see this. 329 */ 330 printf("console %s failed to initialize\n", 331 consoles[cons]->c_name); 332 } 333 } 334 } 335 336 free(dup); 337 338 if (active == 0) { 339 /* 340 * All requested consoles failed to initialise, try to recover. 341 */ 342 for (cons = 0; consoles[cons] != NULL; cons++) { 343 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 344 consoles[cons]->c_init(consoles[cons], 0); 345 if ((consoles[cons]->c_flags & 346 (C_ACTIVEIN | C_ACTIVEOUT)) == 347 (C_ACTIVEIN | C_ACTIVEOUT)) { 348 active++; 349 *list = cons_add_list(*list, 350 consoles[cons]->c_name); 351 } 352 } 353 354 if (active == 0) 355 rv = CMD_ERROR; /* Recovery failed. */ 356 } 357 358 return (rv); 359 } 360 361 /* 362 * Change the twiddle divisor. 363 * 364 * The user can set the twiddle_divisor variable to directly control how fast 365 * the progress twiddle spins, useful for folks with slow serial consoles. The 366 * code to monitor changes to the variable and propagate them to the twiddle 367 * routines has to live somewhere. Twiddling is console-related so it's here. 368 */ 369 static int 370 twiddle_set(struct env_var *ev, int flags, const void *value) 371 { 372 ulong_t tdiv; 373 char *eptr; 374 375 tdiv = strtoul(value, &eptr, 0); 376 if (*(const char *)value == 0 || *eptr != 0) { 377 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 378 return (CMD_ERROR); 379 } 380 twiddle_divisor((uint_t)tdiv); 381 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 382 383 return (CMD_OK); 384 } 385 386 COMMAND_SET(console, "console", "console info", command_console); 387 388 static int 389 command_console(int argc, char *argv[]) 390 { 391 if (argc > 1) 392 printf("%s: list info about available consoles\n", argv[0]); 393 394 printf("Current console: %s\n", getenv("console")); 395 printf("Available consoles:\n"); 396 for (int cons = 0; consoles[cons] != NULL; cons++) { 397 printf(" %s", consoles[cons]->c_name); 398 if (consoles[cons]->c_devinfo != NULL) 399 consoles[cons]->c_devinfo(consoles[cons]); 400 printf("\n"); 401 } 402 403 return (CMD_OK); 404 } 405