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 * $FreeBSD: releng/5.1/sys/kern/tty_cons.c 112046 2003-03-09 20:42:49Z 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
64 static d_open_t cnopen;
65 static d_close_t cnclose;
66 static d_read_t cnread;
67 static d_write_t cnwrite;
68 static d_ioctl_t cnioctl;
69 static d_poll_t cnpoll;
70 static d_kqfilter_t cnkqfilter;
71
72 static struct cdevsw cn_cdevsw = {
73 .d_open = cnopen,
74 .d_close = cnclose,
75 .d_read = cnread,
76 .d_write = cnwrite,
77 .d_ioctl = cnioctl,
78 .d_poll = cnpoll,
79 .d_name = "console",
80 .d_maj = 256,
81 /*
82 * XXX: We really want major #0, but zero here means
83 * XXX: allocate a major number automatically.
84 * XXX: kern_conf.c knows what to do when it sees 256.
85 */
86 .d_flags = D_TTY,
87 .d_kqfilter = cnkqfilter,
88 };
89
90 struct 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
99 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
100 static 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
107 static udev_t cn_udev_t;
108 SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
109 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
110
111 int cons_unavail = 0; /* XXX:
112 * physical console not available for
113 * input (i.e., it is in graphics mode)
114 */
115 static int cn_mute;
116 static int openflag; /* how /dev/console was opened */
117 static int cn_is_open;
118 static u_char console_pausing; /* pause after each line during probe */
119 static char *console_pausestr=
120 "<pause; press any key to proceed to next line or '.' to end pause mode>";
121
122 void cndebug(char *);
123
124 CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
125 SET_DECLARE(cons_set, struct consdev);
126
127 void
128 cninit(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
180 void
181 cninit_finish()
182 {
183 console_pausing = 0;
184 }
185
186 /* add a new physical console to back the virtual console */
187 int
188 cnadd(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
208 void
209 cnremove(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
235 void
236 cnselect(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
251 void
252 cndebug(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
263 static int
264 sysctl_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
319 SYSCTL_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 */
326 static int
327 sysctl_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
345 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
346 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
347
348 static int
349 cn_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
383 static int
384 cnopen(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
397 static int
398 cnclose(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
413 static int
414 cnread(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
425 static int
426 cnwrite(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 }
441 done:
442 uio->uio_resid = 0; /* dump the data */
443 return (0);
444 }
445
446 static int
447 cnioctl(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 */
476 static int
477 cnpoll(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
490 static int
491 cnkqfilter(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 */
507 int
508 cngetc(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
521 int
522 cncheckc(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
540 void
541 cnputc(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
571 void
572 cndbctl(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
590 static void
591 cn_drvinit(void *unused)
592 {
593
594 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
595 }
596
597 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
Cache object: b66589387bd3fe802f7fb7a3120071db
|