Deleted Added
full compact
kern_cons.c (111194) kern_cons.c (111815)
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * from: @(#)cons.c 7.2 (Berkeley) 5/9/91
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * from: @(#)cons.c 7.2 (Berkeley) 5/9/91
39 * $FreeBSD: head/sys/kern/tty_cons.c 111194 2003-02-20 20:54:45Z phk $
39 * $FreeBSD: head/sys/kern/tty_cons.c 111815 2003-03-03 12:15:54Z phk $
40 */
41
42#include "opt_ddb.h"
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/conf.h>
47#include <sys/cons.h>
48#include <sys/fcntl.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/namei.h>
52#include <sys/proc.h>
53#include <sys/queue.h>
54#include <sys/reboot.h>
55#include <sys/sysctl.h>
56#include <sys/tty.h>
57#include <sys/uio.h>
58#include <sys/vnode.h>
59
60#include <ddb/ddb.h>
61
62#include <machine/cpu.h>
63
64static d_open_t cnopen;
65static d_close_t cnclose;
66static d_read_t cnread;
67static d_write_t cnwrite;
68static d_ioctl_t cnioctl;
69static d_poll_t cnpoll;
70static d_kqfilter_t cnkqfilter;
71
72#define CDEV_MAJOR 0
73static struct cdevsw cn_cdevsw = {
40 */
41
42#include "opt_ddb.h"
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/conf.h>
47#include <sys/cons.h>
48#include <sys/fcntl.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/namei.h>
52#include <sys/proc.h>
53#include <sys/queue.h>
54#include <sys/reboot.h>
55#include <sys/sysctl.h>
56#include <sys/tty.h>
57#include <sys/uio.h>
58#include <sys/vnode.h>
59
60#include <ddb/ddb.h>
61
62#include <machine/cpu.h>
63
64static d_open_t cnopen;
65static d_close_t cnclose;
66static d_read_t cnread;
67static d_write_t cnwrite;
68static d_ioctl_t cnioctl;
69static d_poll_t cnpoll;
70static d_kqfilter_t cnkqfilter;
71
72#define CDEV_MAJOR 0
73static struct cdevsw cn_cdevsw = {
74 /* open */ cnopen,
75 /* close */ cnclose,
76 /* read */ cnread,
77 /* write */ cnwrite,
78 /* ioctl */ cnioctl,
79 /* poll */ cnpoll,
80 /* mmap */ nommap,
81 /* strategy */ nostrategy,
82 /* name */ "console",
83 /* maj */ CDEV_MAJOR,
84 /* dump */ nodump,
85 /* psize */ nopsize,
86 /* flags */ D_TTY | D_KQFILTER,
87 /* kqfilter */ cnkqfilter,
74 .d_open = cnopen,
75 .d_close = cnclose,
76 .d_read = cnread,
77 .d_write = cnwrite,
78 .d_ioctl = cnioctl,
79 .d_poll = cnpoll,
80 .d_name = "console",
81 .d_maj = CDEV_MAJOR,
82 .d_flags = D_TTY | D_KQFILTER,
83 .d_kqfilter = cnkqfilter,
88};
89
90struct cn_device {
91 STAILQ_ENTRY(cn_device) cnd_next;
92 char cnd_name[16];
93 struct vnode *cnd_vp;
94 struct consdev *cnd_cn;
95};
96
97#define CNDEVPATHMAX 32
98#define CNDEVTAB_SIZE 4
99static struct cn_device cn_devtab[CNDEVTAB_SIZE];
100static STAILQ_HEAD(, cn_device) cn_devlist =
101 STAILQ_HEAD_INITIALIZER(cn_devlist);
102
103#define CND_INVALID(cnd, td) \
104 (cnd == NULL || cnd->cnd_vp == NULL || \
105 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
106
107static udev_t cn_udev_t;
108SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
109 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
110
111int cons_unavail = 0; /* XXX:
112 * physical console not available for
113 * input (i.e., it is in graphics mode)
114 */
115static int cn_mute;
116static int openflag; /* how /dev/console was opened */
117static int cn_is_open;
118static u_char console_pausing; /* pause after each line during probe */
119static char *console_pausestr=
120"<pause; press any key to proceed to next line or '.' to end pause mode>";
121
122void cndebug(char *);
123
124CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
125SET_DECLARE(cons_set, struct consdev);
126
127void
128cninit(void)
129{
130 struct consdev *best_cn, *cn, **list;
131
132 /*
133 * Check if we should mute the console (for security reasons perhaps)
134 * It can be changes dynamically using sysctl kern.consmute
135 * once we are up and going.
136 *
137 */
138 cn_mute = ((boothowto & (RB_MUTE
139 |RB_SINGLE
140 |RB_VERBOSE
141 |RB_ASKNAME
142 |RB_CONFIG)) == RB_MUTE);
143
144 /*
145 * Find the first console with the highest priority.
146 */
147 best_cn = NULL;
148 SET_FOREACH(list, cons_set) {
149 cn = *list;
150 cnremove(cn);
151 if (cn->cn_probe == NULL)
152 continue;
153 cn->cn_probe(cn);
154 if (cn->cn_pri == CN_DEAD)
155 continue;
156 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
157 best_cn = cn;
158 if (boothowto & RB_MULTIPLE) {
159 /*
160 * Initialize console, and attach to it.
161 */
162 cnadd(cn);
163 cn->cn_init(cn);
164 }
165 }
166 if (best_cn == NULL)
167 return;
168 if ((boothowto & RB_MULTIPLE) == 0) {
169 cnadd(best_cn);
170 best_cn->cn_init(best_cn);
171 }
172 if (boothowto & RB_PAUSE)
173 console_pausing = 1;
174 /*
175 * Make the best console the preferred console.
176 */
177 cnselect(best_cn);
178}
179
180void
181cninit_finish()
182{
183 console_pausing = 0;
184}
185
186/* add a new physical console to back the virtual console */
187int
188cnadd(struct consdev *cn)
189{
190 struct cn_device *cnd;
191 int i;
192
193 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
194 if (cnd->cnd_cn == cn)
195 return (0);
196 for (i = 0; i < CNDEVTAB_SIZE; i++) {
197 cnd = &cn_devtab[i];
198 if (cnd->cnd_cn == NULL)
199 break;
200 }
201 if (cnd->cnd_cn != NULL)
202 return (ENOMEM);
203 cnd->cnd_cn = cn;
204 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
205 return (0);
206}
207
208void
209cnremove(struct consdev *cn)
210{
211 struct cn_device *cnd;
212
213 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
214 if (cnd->cnd_cn != cn)
215 continue;
216 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
217 if (cnd->cnd_vp != NULL)
218 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
219 cnd->cnd_vp = NULL;
220 cnd->cnd_cn = NULL;
221 cnd->cnd_name[0] = '\0';
222#if 0
223 /*
224 * XXX
225 * syscons gets really confused if console resources are
226 * freed after the system has initialized.
227 */
228 if (cn->cn_term != NULL)
229 cn->cn_term(cn);
230#endif
231 return;
232 }
233}
234
235void
236cnselect(struct consdev *cn)
237{
238 struct cn_device *cnd;
239
240 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
241 if (cnd->cnd_cn != cn)
242 continue;
243 if (cnd == STAILQ_FIRST(&cn_devlist))
244 return;
245 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
246 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
247 return;
248 }
249}
250
251void
252cndebug(char *str)
253{
254 int i, len;
255
256 len = strlen(str);
257 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
258 for (i = 0; i < len; i++)
259 cnputc(str[i]);
260 cnputc('\n');
261}
262
263static int
264sysctl_kern_console(SYSCTL_HANDLER_ARGS)
265{
266 struct cn_device *cnd;
267 struct consdev *cp, **list;
268 char *name, *p;
269 int delete, len, error;
270
271 len = 2;
272 SET_FOREACH(list, cons_set) {
273 cp = *list;
274 if (cp->cn_dev != NULL)
275 len += strlen(devtoname(cp->cn_dev)) + 1;
276 }
277 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
278 len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
279 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
280 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
281 p = name;
282 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
283 p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
284 *p++ = '/';
285 SET_FOREACH(list, cons_set) {
286 cp = *list;
287 if (cp->cn_dev != NULL)
288 p += sprintf(p, "%s,", devtoname(cp->cn_dev));
289 }
290 error = sysctl_handle_string(oidp, name, len, req);
291 if (error == 0 && req->newptr != NULL) {
292 p = name;
293 error = ENXIO;
294 delete = 0;
295 if (*p == '-') {
296 delete = 1;
297 p++;
298 }
299 SET_FOREACH(list, cons_set) {
300 cp = *list;
301 if (cp->cn_dev == NULL ||
302 strcmp(p, devtoname(cp->cn_dev)) != 0)
303 continue;
304 if (delete) {
305 cnremove(cp);
306 error = 0;
307 } else {
308 error = cnadd(cp);
309 if (error == 0)
310 cnselect(cp);
311 }
312 break;
313 }
314 }
315 FREE(name, M_TEMP);
316 return (error);
317}
318
319SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
320 0, 0, sysctl_kern_console, "A", "Console device control");
321
322/*
323 * User has changed the state of the console muting.
324 * This may require us to open or close the device in question.
325 */
326static int
327sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
328{
329 int error;
330 int ocn_mute;
331
332 ocn_mute = cn_mute;
333 error = sysctl_handle_int(oidp, &cn_mute, 0, req);
334 if (error != 0 || req->newptr == NULL)
335 return (error);
336 if (ocn_mute && !cn_mute && cn_is_open)
337 error = cnopen(NODEV, openflag, 0, curthread);
338 else if (!ocn_mute && cn_mute && cn_is_open) {
339 error = cnclose(NODEV, openflag, 0, curthread);
340 cn_is_open = 1; /* XXX hack */
341 }
342 return (error);
343}
344
345SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
346 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
347
348static int
349cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
350{
351 char path[CNDEVPATHMAX];
352 struct nameidata nd;
353 struct vnode *vp;
354 dev_t dev;
355 int error;
356
357 if ((vp = cnd->cnd_vp) != NULL) {
358 if (!forceopen && vp->v_type != VBAD) {
359 dev = vp->v_rdev;
360 return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
361 }
362 cnd->cnd_vp = NULL;
363 vn_close(vp, openflag, td->td_ucred, td);
364 }
365 if (cnd->cnd_name[0] == '\0') {
366 strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
367 sizeof(cnd->cnd_name));
368 }
369 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
370 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
371 error = vn_open(&nd, &openflag, 0);
372 if (error == 0) {
373 NDFREE(&nd, NDF_ONLY_PNBUF);
374 VOP_UNLOCK(nd.ni_vp, 0, td);
375 if (nd.ni_vp->v_type == VCHR)
376 cnd->cnd_vp = nd.ni_vp;
377 else
378 vn_close(nd.ni_vp, openflag, td->td_ucred, td);
379 }
380 return (cnd->cnd_vp != NULL);
381}
382
383static int
384cnopen(dev_t dev, int flag, int mode, struct thread *td)
385{
386 struct cn_device *cnd;
387
388 openflag = flag | FWRITE; /* XXX */
389 cn_is_open = 1; /* console is logically open */
390 if (cn_mute)
391 return (0);
392 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
393 cn_devopen(cnd, td, 0);
394 return (0);
395}
396
397static int
398cnclose(dev_t dev, int flag, int mode, struct thread *td)
399{
400 struct cn_device *cnd;
401 struct vnode *vp;
402
403 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
404 if ((vp = cnd->cnd_vp) == NULL)
405 continue;
406 cnd->cnd_vp = NULL;
407 vn_close(vp, openflag, td->td_ucred, td);
408 }
409 cn_is_open = 0;
410 return (0);
411}
412
413static int
414cnread(dev_t dev, struct uio *uio, int flag)
415{
416 struct cn_device *cnd;
417
418 cnd = STAILQ_FIRST(&cn_devlist);
419 if (cn_mute || CND_INVALID(cnd, curthread))
420 return (0);
421 dev = cnd->cnd_vp->v_rdev;
422 return ((*devsw(dev)->d_read)(dev, uio, flag));
423}
424
425static int
426cnwrite(dev_t dev, struct uio *uio, int flag)
427{
428 struct cn_device *cnd;
429
430 cnd = STAILQ_FIRST(&cn_devlist);
431 if (cn_mute || CND_INVALID(cnd, curthread))
432 goto done;
433 if (constty)
434 dev = constty->t_dev;
435 else
436 dev = cnd->cnd_vp->v_rdev;
437 if (dev != NULL) {
438 log_console(uio);
439 return ((*devsw(dev)->d_write)(dev, uio, flag));
440 }
441done:
442 uio->uio_resid = 0; /* dump the data */
443 return (0);
444}
445
446static int
447cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
448{
449 struct cn_device *cnd;
450 int error;
451
452 cnd = STAILQ_FIRST(&cn_devlist);
453 if (cn_mute || CND_INVALID(cnd, td))
454 return (0);
455 /*
456 * Superuser can always use this to wrest control of console
457 * output from the "virtual" console.
458 */
459 if (cmd == TIOCCONS && constty) {
460 error = suser(td);
461 if (error)
462 return (error);
463 constty = NULL;
464 return (0);
465 }
466 dev = cnd->cnd_vp->v_rdev;
467 if (dev != NULL)
468 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
469 return (0);
470}
471
472/*
473 * XXX
474 * poll/kqfilter do not appear to be correct
475 */
476static int
477cnpoll(dev_t dev, int events, struct thread *td)
478{
479 struct cn_device *cnd;
480
481 cnd = STAILQ_FIRST(&cn_devlist);
482 if (cn_mute || CND_INVALID(cnd, td))
483 return (0);
484 dev = cnd->cnd_vp->v_rdev;
485 if (dev != NULL)
486 return ((*devsw(dev)->d_poll)(dev, events, td));
487 return (0);
488}
489
490static int
491cnkqfilter(dev_t dev, struct knote *kn)
492{
493 struct cn_device *cnd;
494
495 cnd = STAILQ_FIRST(&cn_devlist);
496 if (cn_mute || CND_INVALID(cnd, curthread))
497 return (1);
498 dev = cnd->cnd_vp->v_rdev;
499 if (dev != NULL)
500 return ((*devsw(dev)->d_kqfilter)(dev, kn));
501 return (1);
502}
503
504/*
505 * Low level console routines.
506 */
507int
508cngetc(void)
509{
510 int c;
511
512 if (cn_mute)
513 return (-1);
514 while ((c = cncheckc()) == -1)
515 ;
516 if (c == '\r')
517 c = '\n'; /* console input is always ICRNL */
518 return (c);
519}
520
521int
522cncheckc(void)
523{
524 struct cn_device *cnd;
525 struct consdev *cn;
526 int c;
527
528 if (cn_mute)
529 return (-1);
530 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
531 cn = cnd->cnd_cn;
532 c = cn->cn_checkc(cn);
533 if (c != -1) {
534 return (c);
535 }
536 }
537 return (-1);
538}
539
540void
541cnputc(int c)
542{
543 struct cn_device *cnd;
544 struct consdev *cn;
545 char *cp;
546
547 if (cn_mute || c == '\0')
548 return;
549 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
550 cn = cnd->cnd_cn;
551 if (c == '\n')
552 cn->cn_putc(cn, '\r');
553 cn->cn_putc(cn, c);
554 }
555#ifdef DDB
556 if (console_pausing && !db_active && (c == '\n')) {
557#else
558 if (console_pausing && (c == '\n')) {
559#endif
560 for (cp = console_pausestr; *cp != '\0'; cp++)
561 cnputc(*cp);
562 if (cngetc() == '.')
563 console_pausing = 0;
564 cnputc('\r');
565 for (cp = console_pausestr; *cp != '\0'; cp++)
566 cnputc(' ');
567 cnputc('\r');
568 }
569}
570
571void
572cndbctl(int on)
573{
574 struct cn_device *cnd;
575 struct consdev *cn;
576 static int refcount;
577
578 if (!on)
579 refcount--;
580 if (refcount == 0)
581 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
582 cn = cnd->cnd_cn;
583 if (cn->cn_dbctl != NULL)
584 cn->cn_dbctl(cn, on);
585 }
586 if (on)
587 refcount++;
588}
589
590static void
591cn_drvinit(void *unused)
592{
593
594 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
595 "console");
596}
597
598SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
84};
85
86struct cn_device {
87 STAILQ_ENTRY(cn_device) cnd_next;
88 char cnd_name[16];
89 struct vnode *cnd_vp;
90 struct consdev *cnd_cn;
91};
92
93#define CNDEVPATHMAX 32
94#define CNDEVTAB_SIZE 4
95static struct cn_device cn_devtab[CNDEVTAB_SIZE];
96static STAILQ_HEAD(, cn_device) cn_devlist =
97 STAILQ_HEAD_INITIALIZER(cn_devlist);
98
99#define CND_INVALID(cnd, td) \
100 (cnd == NULL || cnd->cnd_vp == NULL || \
101 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
102
103static udev_t cn_udev_t;
104SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
105 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
106
107int cons_unavail = 0; /* XXX:
108 * physical console not available for
109 * input (i.e., it is in graphics mode)
110 */
111static int cn_mute;
112static int openflag; /* how /dev/console was opened */
113static int cn_is_open;
114static u_char console_pausing; /* pause after each line during probe */
115static char *console_pausestr=
116"<pause; press any key to proceed to next line or '.' to end pause mode>";
117
118void cndebug(char *);
119
120CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
121SET_DECLARE(cons_set, struct consdev);
122
123void
124cninit(void)
125{
126 struct consdev *best_cn, *cn, **list;
127
128 /*
129 * Check if we should mute the console (for security reasons perhaps)
130 * It can be changes dynamically using sysctl kern.consmute
131 * once we are up and going.
132 *
133 */
134 cn_mute = ((boothowto & (RB_MUTE
135 |RB_SINGLE
136 |RB_VERBOSE
137 |RB_ASKNAME
138 |RB_CONFIG)) == RB_MUTE);
139
140 /*
141 * Find the first console with the highest priority.
142 */
143 best_cn = NULL;
144 SET_FOREACH(list, cons_set) {
145 cn = *list;
146 cnremove(cn);
147 if (cn->cn_probe == NULL)
148 continue;
149 cn->cn_probe(cn);
150 if (cn->cn_pri == CN_DEAD)
151 continue;
152 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
153 best_cn = cn;
154 if (boothowto & RB_MULTIPLE) {
155 /*
156 * Initialize console, and attach to it.
157 */
158 cnadd(cn);
159 cn->cn_init(cn);
160 }
161 }
162 if (best_cn == NULL)
163 return;
164 if ((boothowto & RB_MULTIPLE) == 0) {
165 cnadd(best_cn);
166 best_cn->cn_init(best_cn);
167 }
168 if (boothowto & RB_PAUSE)
169 console_pausing = 1;
170 /*
171 * Make the best console the preferred console.
172 */
173 cnselect(best_cn);
174}
175
176void
177cninit_finish()
178{
179 console_pausing = 0;
180}
181
182/* add a new physical console to back the virtual console */
183int
184cnadd(struct consdev *cn)
185{
186 struct cn_device *cnd;
187 int i;
188
189 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
190 if (cnd->cnd_cn == cn)
191 return (0);
192 for (i = 0; i < CNDEVTAB_SIZE; i++) {
193 cnd = &cn_devtab[i];
194 if (cnd->cnd_cn == NULL)
195 break;
196 }
197 if (cnd->cnd_cn != NULL)
198 return (ENOMEM);
199 cnd->cnd_cn = cn;
200 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
201 return (0);
202}
203
204void
205cnremove(struct consdev *cn)
206{
207 struct cn_device *cnd;
208
209 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
210 if (cnd->cnd_cn != cn)
211 continue;
212 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
213 if (cnd->cnd_vp != NULL)
214 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
215 cnd->cnd_vp = NULL;
216 cnd->cnd_cn = NULL;
217 cnd->cnd_name[0] = '\0';
218#if 0
219 /*
220 * XXX
221 * syscons gets really confused if console resources are
222 * freed after the system has initialized.
223 */
224 if (cn->cn_term != NULL)
225 cn->cn_term(cn);
226#endif
227 return;
228 }
229}
230
231void
232cnselect(struct consdev *cn)
233{
234 struct cn_device *cnd;
235
236 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
237 if (cnd->cnd_cn != cn)
238 continue;
239 if (cnd == STAILQ_FIRST(&cn_devlist))
240 return;
241 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
242 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
243 return;
244 }
245}
246
247void
248cndebug(char *str)
249{
250 int i, len;
251
252 len = strlen(str);
253 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
254 for (i = 0; i < len; i++)
255 cnputc(str[i]);
256 cnputc('\n');
257}
258
259static int
260sysctl_kern_console(SYSCTL_HANDLER_ARGS)
261{
262 struct cn_device *cnd;
263 struct consdev *cp, **list;
264 char *name, *p;
265 int delete, len, error;
266
267 len = 2;
268 SET_FOREACH(list, cons_set) {
269 cp = *list;
270 if (cp->cn_dev != NULL)
271 len += strlen(devtoname(cp->cn_dev)) + 1;
272 }
273 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
274 len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
275 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
276 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
277 p = name;
278 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
279 p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
280 *p++ = '/';
281 SET_FOREACH(list, cons_set) {
282 cp = *list;
283 if (cp->cn_dev != NULL)
284 p += sprintf(p, "%s,", devtoname(cp->cn_dev));
285 }
286 error = sysctl_handle_string(oidp, name, len, req);
287 if (error == 0 && req->newptr != NULL) {
288 p = name;
289 error = ENXIO;
290 delete = 0;
291 if (*p == '-') {
292 delete = 1;
293 p++;
294 }
295 SET_FOREACH(list, cons_set) {
296 cp = *list;
297 if (cp->cn_dev == NULL ||
298 strcmp(p, devtoname(cp->cn_dev)) != 0)
299 continue;
300 if (delete) {
301 cnremove(cp);
302 error = 0;
303 } else {
304 error = cnadd(cp);
305 if (error == 0)
306 cnselect(cp);
307 }
308 break;
309 }
310 }
311 FREE(name, M_TEMP);
312 return (error);
313}
314
315SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
316 0, 0, sysctl_kern_console, "A", "Console device control");
317
318/*
319 * User has changed the state of the console muting.
320 * This may require us to open or close the device in question.
321 */
322static int
323sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
324{
325 int error;
326 int ocn_mute;
327
328 ocn_mute = cn_mute;
329 error = sysctl_handle_int(oidp, &cn_mute, 0, req);
330 if (error != 0 || req->newptr == NULL)
331 return (error);
332 if (ocn_mute && !cn_mute && cn_is_open)
333 error = cnopen(NODEV, openflag, 0, curthread);
334 else if (!ocn_mute && cn_mute && cn_is_open) {
335 error = cnclose(NODEV, openflag, 0, curthread);
336 cn_is_open = 1; /* XXX hack */
337 }
338 return (error);
339}
340
341SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
342 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
343
344static int
345cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
346{
347 char path[CNDEVPATHMAX];
348 struct nameidata nd;
349 struct vnode *vp;
350 dev_t dev;
351 int error;
352
353 if ((vp = cnd->cnd_vp) != NULL) {
354 if (!forceopen && vp->v_type != VBAD) {
355 dev = vp->v_rdev;
356 return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
357 }
358 cnd->cnd_vp = NULL;
359 vn_close(vp, openflag, td->td_ucred, td);
360 }
361 if (cnd->cnd_name[0] == '\0') {
362 strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
363 sizeof(cnd->cnd_name));
364 }
365 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
366 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
367 error = vn_open(&nd, &openflag, 0);
368 if (error == 0) {
369 NDFREE(&nd, NDF_ONLY_PNBUF);
370 VOP_UNLOCK(nd.ni_vp, 0, td);
371 if (nd.ni_vp->v_type == VCHR)
372 cnd->cnd_vp = nd.ni_vp;
373 else
374 vn_close(nd.ni_vp, openflag, td->td_ucred, td);
375 }
376 return (cnd->cnd_vp != NULL);
377}
378
379static int
380cnopen(dev_t dev, int flag, int mode, struct thread *td)
381{
382 struct cn_device *cnd;
383
384 openflag = flag | FWRITE; /* XXX */
385 cn_is_open = 1; /* console is logically open */
386 if (cn_mute)
387 return (0);
388 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
389 cn_devopen(cnd, td, 0);
390 return (0);
391}
392
393static int
394cnclose(dev_t dev, int flag, int mode, struct thread *td)
395{
396 struct cn_device *cnd;
397 struct vnode *vp;
398
399 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
400 if ((vp = cnd->cnd_vp) == NULL)
401 continue;
402 cnd->cnd_vp = NULL;
403 vn_close(vp, openflag, td->td_ucred, td);
404 }
405 cn_is_open = 0;
406 return (0);
407}
408
409static int
410cnread(dev_t dev, struct uio *uio, int flag)
411{
412 struct cn_device *cnd;
413
414 cnd = STAILQ_FIRST(&cn_devlist);
415 if (cn_mute || CND_INVALID(cnd, curthread))
416 return (0);
417 dev = cnd->cnd_vp->v_rdev;
418 return ((*devsw(dev)->d_read)(dev, uio, flag));
419}
420
421static int
422cnwrite(dev_t dev, struct uio *uio, int flag)
423{
424 struct cn_device *cnd;
425
426 cnd = STAILQ_FIRST(&cn_devlist);
427 if (cn_mute || CND_INVALID(cnd, curthread))
428 goto done;
429 if (constty)
430 dev = constty->t_dev;
431 else
432 dev = cnd->cnd_vp->v_rdev;
433 if (dev != NULL) {
434 log_console(uio);
435 return ((*devsw(dev)->d_write)(dev, uio, flag));
436 }
437done:
438 uio->uio_resid = 0; /* dump the data */
439 return (0);
440}
441
442static int
443cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
444{
445 struct cn_device *cnd;
446 int error;
447
448 cnd = STAILQ_FIRST(&cn_devlist);
449 if (cn_mute || CND_INVALID(cnd, td))
450 return (0);
451 /*
452 * Superuser can always use this to wrest control of console
453 * output from the "virtual" console.
454 */
455 if (cmd == TIOCCONS && constty) {
456 error = suser(td);
457 if (error)
458 return (error);
459 constty = NULL;
460 return (0);
461 }
462 dev = cnd->cnd_vp->v_rdev;
463 if (dev != NULL)
464 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
465 return (0);
466}
467
468/*
469 * XXX
470 * poll/kqfilter do not appear to be correct
471 */
472static int
473cnpoll(dev_t dev, int events, struct thread *td)
474{
475 struct cn_device *cnd;
476
477 cnd = STAILQ_FIRST(&cn_devlist);
478 if (cn_mute || CND_INVALID(cnd, td))
479 return (0);
480 dev = cnd->cnd_vp->v_rdev;
481 if (dev != NULL)
482 return ((*devsw(dev)->d_poll)(dev, events, td));
483 return (0);
484}
485
486static int
487cnkqfilter(dev_t dev, struct knote *kn)
488{
489 struct cn_device *cnd;
490
491 cnd = STAILQ_FIRST(&cn_devlist);
492 if (cn_mute || CND_INVALID(cnd, curthread))
493 return (1);
494 dev = cnd->cnd_vp->v_rdev;
495 if (dev != NULL)
496 return ((*devsw(dev)->d_kqfilter)(dev, kn));
497 return (1);
498}
499
500/*
501 * Low level console routines.
502 */
503int
504cngetc(void)
505{
506 int c;
507
508 if (cn_mute)
509 return (-1);
510 while ((c = cncheckc()) == -1)
511 ;
512 if (c == '\r')
513 c = '\n'; /* console input is always ICRNL */
514 return (c);
515}
516
517int
518cncheckc(void)
519{
520 struct cn_device *cnd;
521 struct consdev *cn;
522 int c;
523
524 if (cn_mute)
525 return (-1);
526 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
527 cn = cnd->cnd_cn;
528 c = cn->cn_checkc(cn);
529 if (c != -1) {
530 return (c);
531 }
532 }
533 return (-1);
534}
535
536void
537cnputc(int c)
538{
539 struct cn_device *cnd;
540 struct consdev *cn;
541 char *cp;
542
543 if (cn_mute || c == '\0')
544 return;
545 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
546 cn = cnd->cnd_cn;
547 if (c == '\n')
548 cn->cn_putc(cn, '\r');
549 cn->cn_putc(cn, c);
550 }
551#ifdef DDB
552 if (console_pausing && !db_active && (c == '\n')) {
553#else
554 if (console_pausing && (c == '\n')) {
555#endif
556 for (cp = console_pausestr; *cp != '\0'; cp++)
557 cnputc(*cp);
558 if (cngetc() == '.')
559 console_pausing = 0;
560 cnputc('\r');
561 for (cp = console_pausestr; *cp != '\0'; cp++)
562 cnputc(' ');
563 cnputc('\r');
564 }
565}
566
567void
568cndbctl(int on)
569{
570 struct cn_device *cnd;
571 struct consdev *cn;
572 static int refcount;
573
574 if (!on)
575 refcount--;
576 if (refcount == 0)
577 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
578 cn = cnd->cnd_cn;
579 if (cn->cn_dbctl != NULL)
580 cn->cn_dbctl(cn, on);
581 }
582 if (on)
583 refcount++;
584}
585
586static void
587cn_drvinit(void *unused)
588{
589
590 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
591 "console");
592}
593
594SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)