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: stable/12/stand/common/console.c 370860 2021-10-17 18:47:33Z kevans $");
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
cons_probe(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,
58 env_nounset);
59
60 /* Do all console probes */
61 for (cons = 0; consoles[cons] != NULL; cons++) {
62 consoles[cons]->c_flags = 0;
63 consoles[cons]->c_probe(consoles[cons]);
64 }
65 /* Now find the first working one */
66 active = -1;
67 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) {
68 consoles[cons]->c_flags = 0;
69 consoles[cons]->c_probe(consoles[cons]);
70 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT))
71 active = cons;
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 prefconsole = getenv("console");
79 if (prefconsole != NULL)
80 prefconsole = strdup(prefconsole);
81 if (prefconsole != NULL) {
82 unsetenv("console"); /* we want to replace this */
83 cons_change(prefconsole);
84 } else {
85 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
86 consoles[active]->c_init(0);
87 prefconsole = strdup(consoles[active]->c_name);
88 }
89
90 printf("Consoles: ");
91 for (cons = 0; consoles[cons] != NULL; cons++)
92 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT))
93 printf("%s ", consoles[cons]->c_desc);
94 printf("\n");
95
96 if (prefconsole != NULL) {
97 env_setenv("console", EV_VOLATILE, prefconsole, cons_set,
98 env_nounset);
99 free(prefconsole);
100 }
101 }
102
103 int
getchar(void)104 getchar(void)
105 {
106 int cons;
107 int rv;
108
109 /* Loop forever polling all active consoles */
110 for (;;) {
111 for (cons = 0; consoles[cons] != NULL; cons++) {
112 if ((consoles[cons]->c_flags &
113 (C_PRESENTIN | C_ACTIVEIN)) ==
114 (C_PRESENTIN | C_ACTIVEIN) &&
115 ((rv = consoles[cons]->c_in()) != -1))
116 return (rv);
117 }
118 }
119 }
120
121 int
ischar(void)122 ischar(void)
123 {
124 int cons;
125
126 for (cons = 0; consoles[cons] != NULL; cons++)
127 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
128 (C_PRESENTIN | C_ACTIVEIN) &&
129 (consoles[cons]->c_ready() != 0))
130 return (1);
131 return (0);
132 }
133
134 void
putchar(int c)135 putchar(int c)
136 {
137 int cons;
138
139 /* Expand newlines */
140 if (c == '\n')
141 putchar('\r');
142
143 for (cons = 0; consoles[cons] != NULL; cons++) {
144 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) ==
145 (C_PRESENTOUT | C_ACTIVEOUT))
146 consoles[cons]->c_out(c);
147 }
148 }
149
150 /*
151 * Find the console with the specified name.
152 */
153 static int
cons_find(const char * name)154 cons_find(const char *name)
155 {
156 int cons;
157
158 for (cons = 0; consoles[cons] != NULL; cons++)
159 if (strcmp(consoles[cons]->c_name, name) == 0)
160 return (cons);
161 return (-1);
162 }
163
164 /*
165 * Select one or more consoles.
166 */
167 static int
cons_set(struct env_var * ev,int flags,const void * value)168 cons_set(struct env_var *ev, int flags, const void *value)
169 {
170 int ret;
171
172 if ((value == NULL) || (cons_check(value) == 0)) {
173 /*
174 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax
175 * error, which would prevent it processing any further
176 * loader.conf entries.
177 */
178 return (CMD_OK);
179 }
180
181 ret = cons_change(value);
182 if (ret != CMD_OK)
183 return (ret);
184
185 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
186 return (CMD_OK);
187 }
188
189 /*
190 * Check that at least one the consoles listed in *string is valid
191 */
192 static int
cons_check(const char * string)193 cons_check(const char *string)
194 {
195 int cons, found, failed;
196 char *curpos, *dup, *next;
197
198 dup = next = strdup(string);
199 found = failed = 0;
200 while (next != NULL) {
201 curpos = strsep(&next, " ,");
202 if (*curpos != '\0') {
203 cons = cons_find(curpos);
204 if (cons == -1) {
205 printf("console %s is invalid!\n", curpos);
206 failed++;
207 } else {
208 found++;
209 }
210 }
211 }
212
213 free(dup);
214
215 if (found == 0)
216 printf("no valid consoles!\n");
217
218 if (found == 0 || failed != 0) {
219 printf("Available consoles:\n");
220 for (cons = 0; consoles[cons] != NULL; cons++)
221 printf(" %s\n", consoles[cons]->c_name);
222 }
223
224 return (found);
225 }
226
227 /*
228 * Activate all the valid consoles listed in *string and disable all others.
229 */
230 static int
cons_change(const char * string)231 cons_change(const char *string)
232 {
233 int cons, active;
234 char *curpos, *dup, *next;
235
236 /* Disable all consoles */
237 for (cons = 0; consoles[cons] != NULL; cons++) {
238 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
239 }
240
241 /* Enable selected consoles */
242 dup = next = strdup(string);
243 active = 0;
244 while (next != NULL) {
245 curpos = strsep(&next, " ,");
246 if (*curpos == '\0')
247 continue;
248 cons = cons_find(curpos);
249 if (cons >= 0) {
250 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
251 consoles[cons]->c_init(0);
252 if ((consoles[cons]->c_flags &
253 (C_PRESENTIN | C_PRESENTOUT)) ==
254 (C_PRESENTIN | C_PRESENTOUT)) {
255 active++;
256 continue;
257 }
258
259 if (active != 0) {
260 /*
261 * If no consoles have initialised we
262 * wouldn't see this.
263 */
264 printf("console %s failed to initialize\n",
265 consoles[cons]->c_name);
266 }
267 }
268 }
269
270 free(dup);
271
272 if (active == 0) {
273 /*
274 * All requested consoles failed to initialise,
275 * try to recover.
276 */
277 for (cons = 0; consoles[cons] != NULL; cons++) {
278 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
279 consoles[cons]->c_init(0);
280 if ((consoles[cons]->c_flags &
281 (C_PRESENTIN | C_PRESENTOUT)) ==
282 (C_PRESENTIN | C_PRESENTOUT))
283 active++;
284 }
285
286 if (active == 0)
287 return (CMD_ERROR); /* Recovery failed. */
288 }
289
290 return (CMD_OK);
291 }
292
293 /*
294 * Change the twiddle divisor.
295 *
296 * The user can set the twiddle_divisor variable to directly control how fast
297 * the progress twiddle spins, useful for folks with slow serial consoles. The
298 * code to monitor changes to the variable and propagate them to the twiddle
299 * routines has to live somewhere. Twiddling is console-related so it's here.
300 */
301 static int
twiddle_set(struct env_var * ev,int flags,const void * value)302 twiddle_set(struct env_var *ev, int flags, const void *value)
303 {
304 u_long tdiv;
305 char *eptr;
306
307 tdiv = strtoul(value, &eptr, 0);
308 if (*(const char *)value == 0 || *eptr != 0) {
309 printf("invalid twiddle_divisor '%s'\n", (const char *)value);
310 return (CMD_ERROR);
311 }
312 twiddle_divisor((u_int)tdiv);
313 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
314
315 return (CMD_OK);
316 }
317