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