xref: /freebsd/stand/common/console.c (revision ccfd87fe2ac0e2e6aeb1911a7d7cce6712a8564f)
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 <sys/param.h>
31 #include <stand.h>
32 #include <string.h>
33 
34 #include "bootstrap.h"
35 /*
36  * Core console support
37  */
38 
39 static int	cons_set(struct env_var *ev, int flags, const void *value);
40 static int	cons_find(const char *name);
41 static int	cons_check(const char *string);
42 static int	cons_change(const char *string);
43 static int	twiddle_set(struct env_var *ev, int flags, const void *value);
44 
45 #ifndef MODULE_VERBOSE
46 # define MODULE_VERBOSE MODULE_VERBOSE_TWIDDLE
47 #endif
48 int module_verbose = MODULE_VERBOSE;
49 
50 static int
51 module_verbose_set(struct env_var *ev, int flags, const void *value)
52 {
53 	u_long v;
54 	char *eptr;
55 
56 	v = strtoul(value, &eptr, 0);
57 	if (*(const char *)value == 0 || *eptr != 0) {
58 		printf("invalid module_verbose '%s'\n", (const char *)value);
59 		return (CMD_ERROR);
60 	}
61 	module_verbose = (int)v;
62 	if (module_verbose < MODULE_VERBOSE_TWIDDLE) {
63 		/* A hack for now; we do not want twiddling */
64 		twiddle_divisor(UINT_MAX);
65 	}
66 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
67 
68 	return (CMD_OK);
69 }
70 
71 /*
72  * Detect possible console(s) to use.  If preferred console(s) have been
73  * specified, mark them as active. Else, mark the first probed console
74  * as active.  Also create the console variable.
75  */
76 void
77 cons_probe(void)
78 {
79 	int	cons;
80 	int	active;
81 	char	*prefconsole;
82 	char	module_verbose_buf[8];
83 
84 	TSENTER();
85 
86 	/* We want a callback to install the new value when these vars change. */
87 	snprintf(module_verbose_buf, sizeof(module_verbose_buf), "%d",
88 	    module_verbose);
89 	env_setenv("module_verbose", EV_VOLATILE, module_verbose_buf,
90 	    module_verbose_set, env_nounset);
91 	env_setenv("twiddle_divisor", EV_VOLATILE, "16", twiddle_set,
92 	    env_nounset);
93 
94 	/* Do all console probes */
95 	for (cons = 0; consoles[cons] != NULL; cons++) {
96 		consoles[cons]->c_flags = 0;
97 		consoles[cons]->c_probe(consoles[cons]);
98 	}
99 	/* Now find the first working one */
100 	active = -1;
101 	for (cons = 0; consoles[cons] != NULL && active == -1; cons++) {
102 		consoles[cons]->c_flags = 0;
103 		consoles[cons]->c_probe(consoles[cons]);
104 		if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT))
105 			active = cons;
106 	}
107 	/* Force a console even if all probes failed */
108 	if (active == -1)
109 		active = 0;
110 
111 	/* Check to see if a console preference has already been registered */
112 	prefconsole = getenv("console");
113 	if (prefconsole != NULL)
114 		prefconsole = strdup(prefconsole);
115 	if (prefconsole != NULL) {
116 		unsetenv("console");		/* we want to replace this */
117 		cons_change(prefconsole);
118 	} else {
119 		consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
120 		consoles[active]->c_init(0);
121 		prefconsole = strdup(consoles[active]->c_name);
122 	}
123 
124 	printf("Consoles: ");
125 	for (cons = 0; consoles[cons] != NULL; cons++)
126 		if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT))
127 			printf("%s  ", consoles[cons]->c_desc);
128 	printf("\n");
129 
130 	if (prefconsole != NULL) {
131 		env_setenv("console", EV_VOLATILE, prefconsole, cons_set,
132 		    env_nounset);
133 		free(prefconsole);
134 	}
135 
136 	TSEXIT();
137 }
138 
139 int
140 getchar(void)
141 {
142 	int	cons;
143 	int	rv;
144 
145 	/* Loop forever polling all active consoles */
146 	for (;;) {
147 		for (cons = 0; consoles[cons] != NULL; cons++) {
148 			if ((consoles[cons]->c_flags &
149 			    (C_PRESENTIN | C_ACTIVEIN)) ==
150 			    (C_PRESENTIN | C_ACTIVEIN) &&
151 			    ((rv = consoles[cons]->c_in()) != -1))
152 				return (rv);
153 		}
154 	}
155 }
156 
157 int
158 ischar(void)
159 {
160 	int	cons;
161 
162 	for (cons = 0; consoles[cons] != NULL; cons++)
163 		if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
164 		    (C_PRESENTIN | C_ACTIVEIN) &&
165 		    (consoles[cons]->c_ready() != 0))
166 			return (1);
167 	return (0);
168 }
169 
170 void
171 putchar(int c)
172 {
173 	int	cons;
174 
175 	/* Expand newlines */
176 	if (c == '\n')
177 		putchar('\r');
178 
179 	for (cons = 0; consoles[cons] != NULL; cons++) {
180 		if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) ==
181 		    (C_PRESENTOUT | C_ACTIVEOUT))
182 			consoles[cons]->c_out(c);
183 	}
184 }
185 
186 /*
187  * Find the console with the specified name.
188  */
189 static int
190 cons_find(const char *name)
191 {
192 	int	cons;
193 
194 	for (cons = 0; consoles[cons] != NULL; cons++)
195 		if (strcmp(consoles[cons]->c_name, name) == 0)
196 			return (cons);
197 	return (-1);
198 }
199 
200 /*
201  * Select one or more consoles.
202  */
203 static int
204 cons_set(struct env_var *ev, int flags, const void *value)
205 {
206 	int	ret;
207 
208 	if ((value == NULL) || (cons_check(value) == 0)) {
209 		/*
210 		 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax
211 		 * error, which would prevent it processing any further
212 		 * loader.conf entries.
213 		 */
214 		return (CMD_OK);
215 	}
216 
217 	ret = cons_change(value);
218 	if (ret != CMD_OK)
219 		return (ret);
220 
221 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
222 	return (CMD_OK);
223 }
224 
225 /*
226  * Check that at least one the consoles listed in *string is valid
227  */
228 static int
229 cons_check(const char *string)
230 {
231 	int	cons, found, failed;
232 	char	*curpos, *dup, *next;
233 
234 	dup = next = strdup(string);
235 	found = failed = 0;
236 	while (next != NULL) {
237 		curpos = strsep(&next, " ,");
238 		if (*curpos != '\0') {
239 			cons = cons_find(curpos);
240 			if (cons == -1) {
241 				printf("console %s is unavailable\n", curpos);
242 				failed++;
243 			} else {
244 				found++;
245 			}
246 		}
247 	}
248 
249 	free(dup);
250 
251 	if (found == 0)
252 		printf("no valid consoles!\n");
253 
254 	if (found == 0 && failed != 0) {
255 		printf("Available consoles:\n");
256 		for (cons = 0; consoles[cons] != NULL; cons++)
257 			printf("    %s\n", consoles[cons]->c_name);
258 	}
259 
260 	return (found);
261 }
262 
263 /*
264  * Activate all the valid consoles listed in *string and disable all others.
265  */
266 static int
267 cons_change(const char *string)
268 {
269 	int	cons, active;
270 	char	*curpos, *dup, *next;
271 
272 	/* Disable all consoles */
273 	for (cons = 0; consoles[cons] != NULL; cons++) {
274 		consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
275 	}
276 
277 	/* Enable selected consoles */
278 	dup = next = strdup(string);
279 	active = 0;
280 	while (next != NULL) {
281 		curpos = strsep(&next, " ,");
282 		if (*curpos == '\0')
283 			continue;
284 		cons = cons_find(curpos);
285 		if (cons >= 0) {
286 			consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
287 			consoles[cons]->c_init(0);
288 			if ((consoles[cons]->c_flags &
289 			    (C_PRESENTIN | C_PRESENTOUT)) ==
290 			    (C_PRESENTIN | C_PRESENTOUT)) {
291 				active++;
292 				continue;
293 			}
294 
295 			if (active != 0) {
296 				/*
297 				 * If no consoles have initialised we
298 				 * wouldn't see this.
299 				 */
300 				printf("console %s failed to initialize\n",
301 				    consoles[cons]->c_name);
302 			}
303 		}
304 	}
305 
306 	free(dup);
307 
308 	if (active == 0) {
309 		/*
310 		 * All requested consoles failed to initialise,
311 		 * try to recover.
312 		 */
313 		for (cons = 0; consoles[cons] != NULL; cons++) {
314 			consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
315 			consoles[cons]->c_init(0);
316 			if ((consoles[cons]->c_flags &
317 			    (C_PRESENTIN | C_PRESENTOUT)) ==
318 			    (C_PRESENTIN | C_PRESENTOUT))
319 				active++;
320 		}
321 
322 		if (active == 0)
323 			return (CMD_ERROR); /* Recovery failed. */
324 	}
325 
326 	return (CMD_OK);
327 }
328 
329 /*
330  * Change the twiddle divisor.
331  *
332  * The user can set the twiddle_divisor variable to directly control how fast
333  * the progress twiddle spins, useful for folks with slow serial consoles.  The
334  * code to monitor changes to the variable and propagate them to the twiddle
335  * routines has to live somewhere.  Twiddling is console-related so it's here.
336  */
337 static int
338 twiddle_set(struct env_var *ev, int flags, const void *value)
339 {
340 	u_long tdiv;
341 	char *eptr;
342 
343 	tdiv = strtoul(value, &eptr, 0);
344 	if (*(const char *)value == 0 || *eptr != 0) {
345 		printf("invalid twiddle_divisor '%s'\n", (const char *)value);
346 		return (CMD_ERROR);
347 	}
348 	twiddle_divisor((u_int)tdiv);
349 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
350 
351 	return (CMD_OK);
352 }
353