FreeBSD/Linux Kernel Cross Reference
sys/kern/tty_cons.c
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 */
40
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD: releng/5.2/sys/kern/tty_cons.c 121204 2003-10-18 12:16:17Z phk $");
43
44 #include "opt_ddb.h"
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/conf.h>
49 #include <sys/cons.h>
50 #include <sys/fcntl.h>
51 #include <sys/kernel.h>
52 #include <sys/malloc.h>
53 #include <sys/msgbuf.h>
54 #include <sys/namei.h>
55 #include <sys/proc.h>
56 #include <sys/queue.h>
57 #include <sys/reboot.h>
58 #include <sys/sysctl.h>
59 #include <sys/tty.h>
60 #include <sys/uio.h>
61 #include <sys/vnode.h>
62
63 #include <ddb/ddb.h>
64
65 #include <machine/cpu.h>
66
67 static d_open_t cnopen;
68 static d_close_t cnclose;
69 static d_read_t cnread;
70 static d_write_t cnwrite;
71 static d_ioctl_t cnioctl;
72 static d_poll_t cnpoll;
73 static d_kqfilter_t cnkqfilter;
74
75 static struct cdevsw cn_cdevsw = {
76 .d_open = cnopen,
77 .d_close = cnclose,
78 .d_read = cnread,
79 .d_write = cnwrite,
80 .d_ioctl = cnioctl,
81 .d_poll = cnpoll,
82 .d_name = "console",
83 .d_maj = 256,
84 /*
85 * XXX: We really want major #0, but zero here means
86 * XXX: allocate a major number automatically.
87 * XXX: kern_conf.c knows what to do when it sees 256.
88 */
89 .d_flags = D_TTY,
90 .d_kqfilter = cnkqfilter,
91 };
92
93 struct cn_device {
94 STAILQ_ENTRY(cn_device) cnd_next;
95 struct vnode *cnd_vp;
96 struct consdev *cnd_cn;
97 };
98
99 #define CNDEVPATHMAX 32
100 #define CNDEVTAB_SIZE 4
101 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
102 static STAILQ_HEAD(, cn_device) cn_devlist =
103 STAILQ_HEAD_INITIALIZER(cn_devlist);
104
105 #define CND_INVALID(cnd, td) \
106 (cnd == NULL || cnd->cnd_vp == NULL || \
107 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
108
109 static udev_t cn_udev_t;
110 SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
111 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
112
113 int cons_unavail = 0; /* XXX:
114 * physical console not available for
115 * input (i.e., it is in graphics mode)
116 */
117 static int cn_mute;
118 static int openflag; /* how /dev/console was opened */
119 static int cn_is_open;
120 static char *consbuf; /* buffer used by `consmsgbuf' */
121 static struct callout conscallout; /* callout for outputting to constty */
122 struct msgbuf consmsgbuf; /* message buffer for console tty */
123 static u_char console_pausing; /* pause after each line during probe */
124 static char *console_pausestr=
125 "<pause; press any key to proceed to next line or '.' to end pause mode>";
126 struct tty *constty; /* pointer to console "window" tty */
127
128 void cndebug(char *);
129 static void constty_timeout(void *arg);
130
131 CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
132 SET_DECLARE(cons_set, struct consdev);
133
134 void
135 cninit(void)
136 {
137 struct consdev *best_cn, *cn, **list;
138
139 /*
140 * Check if we should mute the console (for security reasons perhaps)
141 * It can be changes dynamically using sysctl kern.consmute
142 * once we are up and going.
143 *
144 */
145 cn_mute = ((boothowto & (RB_MUTE
146 |RB_SINGLE
147 |RB_VERBOSE
148 |RB_ASKNAME
149 |RB_CONFIG)) == RB_MUTE);
150
151 /*
152 * Find the first console with the highest priority.
153 */
154 best_cn = NULL;
155 SET_FOREACH(list, cons_set) {
156 cn = *list;
157 cnremove(cn);
158 if (cn->cn_probe == NULL)
159 continue;
160 cn->cn_probe(cn);
161 if (cn->cn_pri == CN_DEAD)
162 continue;
163 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
164 best_cn = cn;
165 if (boothowto & RB_MULTIPLE) {
166 /*
167 * Initialize console, and attach to it.
168 */
169 cnadd(cn);
170 cn->cn_init(cn);
171 }
172 }
173 if (best_cn == NULL)
174 return;
175 if ((boothowto & RB_MULTIPLE) == 0) {
176 cnadd(best_cn);
177 best_cn->cn_init(best_cn);
178 }
179 if (boothowto & RB_PAUSE)
180 console_pausing = 1;
181 /*
182 * Make the best console the preferred console.
183 */
184 cnselect(best_cn);
185 }
186
187 void
188 cninit_finish()
189 {
190 console_pausing = 0;
191 }
192
193 /* add a new physical console to back the virtual console */
194 int
195 cnadd(struct consdev *cn)
196 {
197 struct cn_device *cnd;
198 int i;
199
200 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
201 if (cnd->cnd_cn == cn)
202 return (0);
203 for (i = 0; i < CNDEVTAB_SIZE; i++) {
204 cnd = &cn_devtab[i];
205 if (cnd->cnd_cn == NULL)
206 break;
207 }
208 if (cnd->cnd_cn != NULL)
209 return (ENOMEM);
210 cnd->cnd_cn = cn;
211 if (cn->cn_name[0] == '\0') {
212 /* XXX: it is unclear if/where this print might output */
213 printf("WARNING: console at %p has no name\n", cn);
214 }
215 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
216 return (0);
217 }
218
219 void
220 cnremove(struct consdev *cn)
221 {
222 struct cn_device *cnd;
223
224 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
225 if (cnd->cnd_cn != cn)
226 continue;
227 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
228 if (cnd->cnd_vp != NULL)
229 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
230 cnd->cnd_vp = NULL;
231 cnd->cnd_cn = NULL;
232 #if 0
233 /*
234 * XXX
235 * syscons gets really confused if console resources are
236 * freed after the system has initialized.
237 */
238 if (cn->cn_term != NULL)
239 cn->cn_term(cn);
240 #endif
241 return;
242 }
243 }
244
245 void
246 cnselect(struct consdev *cn)
247 {
248 struct cn_device *cnd;
249
250 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
251 if (cnd->cnd_cn != cn)
252 continue;
253 if (cnd == STAILQ_FIRST(&cn_devlist))
254 return;
255 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
256 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
257 return;
258 }
259 }
260
261 void
262 cndebug(char *str)
263 {
264 int i, len;
265
266 len = strlen(str);
267 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
268 for (i = 0; i < len; i++)
269 cnputc(str[i]);
270 cnputc('\n');
271 }
272
273 /*
274 * XXX: rewrite to use sbufs instead
275 */
276
277 static int
278 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
279 {
280 struct cn_device *cnd;
281 struct consdev *cp, **list;
282 char *name, *p;
283 int delete, len, error;
284
285 len = 2;
286 SET_FOREACH(list, cons_set) {
287 cp = *list;
288 if (cp->cn_name[0] != '\0')
289 len += strlen(cp->cn_name) + 1;
290 }
291 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
292 len += strlen(cnd->cnd_cn->cn_name) + 1;
293 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
294 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
295 p = name;
296 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
297 p += sprintf(p, "%s,", cnd->cnd_cn->cn_name);
298 *p++ = '/';
299 SET_FOREACH(list, cons_set) {
300 cp = *list;
301 if (cp->cn_name[0] != '\0')
302 p += sprintf(p, "%s,", cp->cn_name);
303 }
304 error = sysctl_handle_string(oidp, name, len, req);
305 if (error == 0 && req->newptr != NULL) {
306 p = name;
307 error = ENXIO;
308 delete = 0;
309 if (*p == '-') {
310 delete = 1;
311 p++;
312 }
313 SET_FOREACH(list, cons_set) {
314 cp = *list;
315 if (strcmp(p, cp->cn_name) != 0)
316 continue;
317 if (delete) {
318 cnremove(cp);
319 error = 0;
320 } else {
321 error = cnadd(cp);
322 if (error == 0)
323 cnselect(cp);
324 }
325 break;
326 }
327 }
328 FREE(name, M_TEMP);
329 return (error);
330 }
331
332 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
333 0, 0, sysctl_kern_console, "A", "Console device control");
334
335 /*
336 * User has changed the state of the console muting.
337 * This may require us to open or close the device in question.
338 */
339 static int
340 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
341 {
342 int error;
343 int ocn_mute;
344
345 ocn_mute = cn_mute;
346 error = sysctl_handle_int(oidp, &cn_mute, 0, req);
347 if (error != 0 || req->newptr == NULL)
348 return (error);
349 if (ocn_mute && !cn_mute && cn_is_open)
350 error = cnopen(NODEV, openflag, 0, curthread);
351 else if (!ocn_mute && cn_mute && cn_is_open) {
352 error = cnclose(NODEV, openflag, 0, curthread);
353 cn_is_open = 1; /* XXX hack */
354 }
355 return (error);
356 }
357
358 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
359 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
360
361 static int
362 cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
363 {
364 char path[CNDEVPATHMAX];
365 struct nameidata nd;
366 struct vnode *vp;
367 dev_t dev;
368 int error;
369
370 if ((vp = cnd->cnd_vp) != NULL) {
371 if (!forceopen && vp->v_type != VBAD) {
372 dev = vp->v_rdev;
373 return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
374 }
375 cnd->cnd_vp = NULL;
376 vn_close(vp, openflag, td->td_ucred, td);
377 }
378 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
379 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
380 error = vn_open(&nd, &openflag, 0, -1);
381 if (error == 0) {
382 NDFREE(&nd, NDF_ONLY_PNBUF);
383 VOP_UNLOCK(nd.ni_vp, 0, td);
384 if (nd.ni_vp->v_type == VCHR)
385 cnd->cnd_vp = nd.ni_vp;
386 else
387 vn_close(nd.ni_vp, openflag, td->td_ucred, td);
388 }
389 return (cnd->cnd_vp != NULL);
390 }
391
392 static int
393 cnopen(dev_t dev, int flag, int mode, struct thread *td)
394 {
395 struct cn_device *cnd;
396
397 openflag = flag | FWRITE; /* XXX */
398 cn_is_open = 1; /* console is logically open */
399 if (cn_mute)
400 return (0);
401 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
402 cn_devopen(cnd, td, 0);
403 return (0);
404 }
405
406 static int
407 cnclose(dev_t dev, int flag, int mode, struct thread *td)
408 {
409 struct cn_device *cnd;
410 struct vnode *vp;
411
412 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
413 if ((vp = cnd->cnd_vp) == NULL)
414 continue;
415 cnd->cnd_vp = NULL;
416 vn_close(vp, openflag, td->td_ucred, td);
417 }
418 cn_is_open = 0;
419 return (0);
420 }
421
422 static int
423 cnread(dev_t dev, struct uio *uio, int flag)
424 {
425 struct cn_device *cnd;
426
427 cnd = STAILQ_FIRST(&cn_devlist);
428 if (cn_mute || CND_INVALID(cnd, curthread))
429 return (0);
430 dev = cnd->cnd_vp->v_rdev;
431 return ((*devsw(dev)->d_read)(dev, uio, flag));
432 }
433
434 static int
435 cnwrite(dev_t dev, struct uio *uio, int flag)
436 {
437 struct cn_device *cnd;
438
439 cnd = STAILQ_FIRST(&cn_devlist);
440 if (cn_mute || CND_INVALID(cnd, curthread))
441 goto done;
442 if (constty)
443 dev = constty->t_dev;
444 else
445 dev = cnd->cnd_vp->v_rdev;
446 if (dev != NULL) {
447 log_console(uio);
448 return ((*devsw(dev)->d_write)(dev, uio, flag));
449 }
450 done:
451 uio->uio_resid = 0; /* dump the data */
452 return (0);
453 }
454
455 static int
456 cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
457 {
458 struct cn_device *cnd;
459 int error;
460
461 cnd = STAILQ_FIRST(&cn_devlist);
462 if (cn_mute || CND_INVALID(cnd, td))
463 return (0);
464 /*
465 * Superuser can always use this to wrest control of console
466 * output from the "virtual" console.
467 */
468 if (cmd == TIOCCONS && constty) {
469 error = suser(td);
470 if (error)
471 return (error);
472 constty = NULL;
473 return (0);
474 }
475 dev = cnd->cnd_vp->v_rdev;
476 if (dev != NULL)
477 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
478 return (0);
479 }
480
481 /*
482 * XXX
483 * poll/kqfilter do not appear to be correct
484 */
485 static int
486 cnpoll(dev_t dev, int events, struct thread *td)
487 {
488 struct cn_device *cnd;
489
490 cnd = STAILQ_FIRST(&cn_devlist);
491 if (cn_mute || CND_INVALID(cnd, td))
492 return (0);
493 dev = cnd->cnd_vp->v_rdev;
494 if (dev != NULL)
495 return ((*devsw(dev)->d_poll)(dev, events, td));
496 return (0);
497 }
498
499 static int
500 cnkqfilter(dev_t dev, struct knote *kn)
501 {
502 struct cn_device *cnd;
503
504 cnd = STAILQ_FIRST(&cn_devlist);
505 if (cn_mute || CND_INVALID(cnd, curthread))
506 return (1);
507 dev = cnd->cnd_vp->v_rdev;
508 if (dev != NULL)
509 return ((*devsw(dev)->d_kqfilter)(dev, kn));
510 return (1);
511 }
512
513 /*
514 * Low level console routines.
515 */
516 int
517 cngetc(void)
518 {
519 int c;
520
521 if (cn_mute)
522 return (-1);
523 while ((c = cncheckc()) == -1)
524 ;
525 if (c == '\r')
526 c = '\n'; /* console input is always ICRNL */
527 return (c);
528 }
529
530 int
531 cncheckc(void)
532 {
533 struct cn_device *cnd;
534 struct consdev *cn;
535 int c;
536
537 if (cn_mute)
538 return (-1);
539 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
540 cn = cnd->cnd_cn;
541 #ifdef DDB
542 if (!db_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
543 #endif
544 c = cn->cn_checkc(cn);
545 if (c != -1) {
546 return (c);
547 }
548 #ifdef DDB
549 }
550 #endif
551 }
552 return (-1);
553 }
554
555 void
556 cnputc(int c)
557 {
558 struct cn_device *cnd;
559 struct consdev *cn;
560 char *cp;
561
562 if (cn_mute || c == '\0')
563 return;
564 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
565 cn = cnd->cnd_cn;
566 #ifdef DDB
567 if (!db_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
568 #endif
569 if (c == '\n')
570 cn->cn_putc(cn, '\r');
571 cn->cn_putc(cn, c);
572 #ifdef DDB
573 }
574 #endif
575 }
576 #ifdef DDB
577 if (console_pausing && !db_active && (c == '\n')) {
578 #else
579 if (console_pausing && (c == '\n')) {
580 #endif
581 for (cp = console_pausestr; *cp != '\0'; cp++)
582 cnputc(*cp);
583 if (cngetc() == '.')
584 console_pausing = 0;
585 cnputc('\r');
586 for (cp = console_pausestr; *cp != '\0'; cp++)
587 cnputc(' ');
588 cnputc('\r');
589 }
590 }
591
592 void
593 cndbctl(int on)
594 {
595 struct cn_device *cnd;
596 struct consdev *cn;
597 static int refcount;
598
599 if (!on)
600 refcount--;
601 if (refcount == 0)
602 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
603 cn = cnd->cnd_cn;
604 if (cn->cn_dbctl != NULL)
605 cn->cn_dbctl(cn, on);
606 }
607 if (on)
608 refcount++;
609 }
610
611 static int consmsgbuf_size = 8192;
612 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
613 "");
614
615 /*
616 * Redirect console output to a tty.
617 */
618 void
619 constty_set(struct tty *tp)
620 {
621 int size;
622
623 KASSERT(tp != NULL, ("constty_set: NULL tp"));
624 if (consbuf == NULL) {
625 size = consmsgbuf_size;
626 consbuf = malloc(size, M_TTYS, M_WAITOK);
627 msgbuf_init(&consmsgbuf, consbuf, size);
628 callout_init(&conscallout, 0);
629 }
630 constty = tp;
631 constty_timeout(NULL);
632 }
633
634 /*
635 * Disable console redirection to a tty.
636 */
637 void
638 constty_clear(void)
639 {
640 int c;
641
642 constty = NULL;
643 if (consbuf == NULL)
644 return;
645 callout_stop(&conscallout);
646 while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
647 cnputc(c);
648 free(consbuf, M_TTYS);
649 consbuf = NULL;
650 }
651
652 /* Times per second to check for pending console tty messages. */
653 static int constty_wakeups_per_second = 5;
654 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
655 &constty_wakeups_per_second, 0, "");
656
657 static void
658 constty_timeout(void *arg)
659 {
660 int c;
661
662 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
663 if (tputchar(c, constty) < 0)
664 constty = NULL;
665 }
666 if (constty != NULL) {
667 callout_reset(&conscallout, hz / constty_wakeups_per_second,
668 constty_timeout, NULL);
669 } else {
670 /* Deallocate the constty buffer memory. */
671 constty_clear();
672 }
673 }
674
675 static void
676 cn_drvinit(void *unused)
677 {
678
679 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
680 }
681
682 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
Cache object: c932b6304ad005908213a0ea24536e6f
|