kern_cons.c revision 85458
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 85458 2001-10-25 04:51:37Z jlemon $ 40 */ 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/conf.h> 45#include <sys/cons.h> 46#include <sys/fcntl.h> 47#include <sys/kernel.h> 48#include <sys/malloc.h> 49#include <sys/namei.h> 50#include <sys/proc.h> 51#include <sys/queue.h> 52#include <sys/reboot.h> 53#include <sys/sysctl.h> 54#include <sys/tty.h> 55#include <sys/uio.h> 56#include <sys/vnode.h> 57 58#include <machine/cpu.h> 59 60static d_open_t cnopen; 61static d_close_t cnclose; 62static d_read_t cnread; 63static d_write_t cnwrite; 64static d_ioctl_t cnioctl; 65static d_poll_t cnpoll; 66static d_kqfilter_t cnkqfilter; 67 68#define CDEV_MAJOR 0 69static struct cdevsw cn_cdevsw = { 70 /* open */ cnopen, 71 /* close */ cnclose, 72 /* read */ cnread, 73 /* write */ cnwrite, 74 /* ioctl */ cnioctl, 75 /* poll */ cnpoll, 76 /* mmap */ nommap, 77 /* strategy */ nostrategy, 78 /* name */ "console", 79 /* maj */ CDEV_MAJOR, 80 /* dump */ nodump, 81 /* psize */ nopsize, 82 /* flags */ D_TTY | D_KQFILTER, 83 /* kqfilter */ cnkqfilter, 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 dev_t cn_devfsdev; /* represents the device private info */ 115 116void cndebug(char *); 117 118CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 119SET_DECLARE(cons_set, struct consdev); 120 121void 122cninit(void) 123{ 124 struct consdev *best_cn, *cn, **list; 125 126 /* 127 * Check if we should mute the console (for security reasons perhaps) 128 * It can be changes dynamically using sysctl kern.consmute 129 * once we are up and going. 130 * 131 */ 132 cn_mute = ((boothowto & (RB_MUTE 133 |RB_SINGLE 134 |RB_VERBOSE 135 |RB_ASKNAME 136 |RB_CONFIG)) == RB_MUTE); 137 138 /* 139 * Find the first console with the highest priority. 140 */ 141 best_cn = NULL; 142 SET_FOREACH(list, cons_set) { 143 cn = *list; 144 if (cn->cn_probe == NULL) 145 continue; 146 cn->cn_probe(cn); 147 if (cn->cn_pri == CN_DEAD) 148 continue; 149 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 150 best_cn = cn; 151 if (boothowto & RB_MULTIPLE) { 152 /* 153 * Initialize console, and attach to it. 154 */ 155 cnadd(cn); 156 cn->cn_init(cn); 157 } 158 } 159 if (best_cn == NULL) 160 return; 161 if ((boothowto & RB_MULTIPLE) == 0) { 162 cnadd(best_cn); 163 best_cn->cn_init(best_cn); 164 } 165 /* 166 * Make the best console the preferred console. 167 */ 168 cnselect(best_cn); 169} 170 171/* add a new physical console to back the virtual console */ 172int 173cnadd(struct consdev *cn) 174{ 175 struct cn_device *cnd; 176 int i; 177 178 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 179 if (cnd->cnd_cn == cn) 180 return (0); 181 for (i = 0; i < CNDEVTAB_SIZE; i++) { 182 cnd = &cn_devtab[i]; 183 if (cnd->cnd_cn == NULL) 184 break; 185 } 186 if (cnd->cnd_cn != NULL) 187 return (ENOMEM); 188 cnd->cnd_cn = cn; 189 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 190 return (0); 191} 192 193void 194cnremove(struct consdev *cn) 195{ 196 struct cn_device *cnd; 197 198 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 199 if (cnd->cnd_cn != cn) 200 continue; 201 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 202 if (cnd->cnd_vp != NULL) 203 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL); 204 cnd->cnd_vp = NULL; 205 cnd->cnd_cn = NULL; 206 cnd->cnd_name[0] = '\0'; 207#if 0 208 /* 209 * XXX 210 * syscons gets really confused if console resources are 211 * freed after the system has initialized. 212 */ 213 if (cn->cn_term != NULL) 214 cn->cn_term(cn); 215#endif 216 return; 217 } 218} 219 220void 221cnselect(struct consdev *cn) 222{ 223 struct cn_device *cnd; 224 225 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 226 if (cnd->cnd_cn != cn) 227 continue; 228 if (cnd == STAILQ_FIRST(&cn_devlist)) 229 return; 230 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 231 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 232 return; 233 } 234} 235 236void 237cndebug(char *str) 238{ 239 int i, len; 240 241 len = strlen(str); 242 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' '); 243 for (i = 0; i < len; i++) 244 cnputc(str[i]); 245 cnputc('\n'); 246} 247 248static int 249sysctl_kern_console(SYSCTL_HANDLER_ARGS) 250{ 251 struct cn_device *cnd; 252 struct consdev *cp, **list; 253 char *name, *p; 254 int delete, len, error; 255 256 len = 2; 257 SET_FOREACH(list, cons_set) { 258 cp = *list; 259 if (cp->cn_dev != NULL) 260 len += strlen(devtoname(cp->cn_dev)) + 1; 261 } 262 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 263 len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1; 264 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX; 265 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO); 266 p = name; 267 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 268 p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev)); 269 *p++ = '/'; 270 SET_FOREACH(list, cons_set) { 271 cp = *list; 272 if (cp->cn_dev != NULL) 273 p += sprintf(p, "%s,", devtoname(cp->cn_dev)); 274 } 275 error = sysctl_handle_string(oidp, name, len, req); 276 if (error == 0 && req->newptr != NULL) { 277 p = name; 278 error = ENXIO; 279 delete = 0; 280 if (*p == '-') { 281 delete = 1; 282 p++; 283 } 284 SET_FOREACH(list, cons_set) { 285 cp = *list; 286 if (cp->cn_dev == NULL || 287 strcmp(p, devtoname(cp->cn_dev)) != 0) 288 continue; 289 if (delete) { 290 cnremove(cp); 291 error = 0; 292 } else { 293 error = cnadd(cp); 294 if (error == 0) 295 cnselect(cp); 296 } 297 break; 298 } 299 } 300 FREE(name, M_TEMP); 301 return (error); 302} 303 304SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 305 0, 0, sysctl_kern_console, "A", "Console device control"); 306 307/* 308 * User has changed the state of the console muting. 309 * This may require us to open or close the device in question. 310 */ 311static int 312sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) 313{ 314 int error; 315 int ocn_mute; 316 317 ocn_mute = cn_mute; 318 error = sysctl_handle_int(oidp, &cn_mute, 0, req); 319 if (error != 0 || req->newptr == NULL) 320 return (error); 321 if (ocn_mute && !cn_mute && cn_is_open) 322 error = cnopen(NODEV, openflag, 0, curthread); 323 else if (!ocn_mute && cn_mute && cn_is_open) { 324 error = cnclose(NODEV, openflag, 0, curthread); 325 cn_is_open = 1; /* XXX hack */ 326 } 327 return (error); 328} 329 330SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 331 0, sizeof(cn_mute), sysctl_kern_consmute, "I", ""); 332 333static int 334cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen) 335{ 336 char path[CNDEVPATHMAX]; 337 struct nameidata nd; 338 dev_t dev; 339 int error; 340 341 if (cnd->cnd_vp != NULL) { 342 if (!forceopen) { 343 dev = cnd->cnd_vp->v_rdev; 344 return ((*devsw(dev)->d_open)(dev, openflag, 0, td)); 345 } 346 vn_close(cnd->cnd_vp, openflag, td->td_proc->p_ucred, td); 347 cnd->cnd_vp = NULL; 348 } 349 if (cnd->cnd_name[0] == '\0') 350 strncpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev), 351 sizeof(cnd->cnd_name)); 352 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name); 353 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); 354 error = vn_open(&nd, &openflag, 0); 355 if (error == 0) { 356 NDFREE(&nd, NDF_ONLY_PNBUF); 357 VOP_UNLOCK(nd.ni_vp, 0, td); 358 if (nd.ni_vp->v_type == VCHR) 359 cnd->cnd_vp = nd.ni_vp; 360 else 361 vn_close(nd.ni_vp, openflag, td->td_proc->p_ucred, td); 362 } 363 return (cnd->cnd_vp != NULL); 364} 365 366static int 367cnopen(dev_t dev, int flag, int mode, struct thread *td) 368{ 369 struct cn_device *cnd; 370 371 openflag = flag | FWRITE; /* XXX */ 372 cn_is_open = 1; /* console is logically open */ 373 if (cn_mute) 374 return (0); 375 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 376 cn_devopen(cnd, td, 0); 377 return (0); 378} 379 380static int 381cnclose(dev_t dev, int flag, int mode, struct thread *td) 382{ 383 struct cn_device *cnd; 384 struct vnode *vp; 385 386 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 387 if ((vp = cnd->cnd_vp) == NULL) 388 continue; 389 cnd->cnd_vp = NULL; 390 vn_close(vp, openflag, td->td_proc->p_ucred, td); 391 } 392 cn_is_open = 0; 393 return (0); 394} 395 396static int 397cnread(dev_t dev, struct uio *uio, int flag) 398{ 399 struct cn_device *cnd; 400 401 cnd = STAILQ_FIRST(&cn_devlist); 402 if (cn_mute || CND_INVALID(cnd, curthread)) 403 return (0); 404 dev = cnd->cnd_vp->v_rdev; 405 return ((*devsw(dev)->d_read)(dev, uio, flag)); 406} 407 408static int 409cnwrite(dev_t dev, struct uio *uio, int flag) 410{ 411 struct cn_device *cnd; 412 413 cnd = STAILQ_FIRST(&cn_devlist); 414 if (cn_mute || CND_INVALID(cnd, curthread)) 415 goto done; 416 if (constty) 417 dev = constty->t_dev; 418 else 419 dev = cnd->cnd_vp->v_rdev; 420 if (dev != NULL) { 421 log_console(uio); 422 return ((*devsw(dev)->d_write)(dev, uio, flag)); 423 } 424done: 425 uio->uio_resid = 0; /* dump the data */ 426 return (0); 427} 428 429static int 430cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 431{ 432 struct cn_device *cnd; 433 int error; 434 435 cnd = STAILQ_FIRST(&cn_devlist); 436 if (cn_mute || CND_INVALID(cnd, td)) 437 return (0); 438 /* 439 * Superuser can always use this to wrest control of console 440 * output from the "virtual" console. 441 */ 442 if (cmd == TIOCCONS && constty) { 443 error = suser_td(td); 444 if (error) 445 return (error); 446 constty = NULL; 447 return (0); 448 } 449 dev = cnd->cnd_vp->v_rdev; 450 if (dev != NULL) 451 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td)); 452 return (0); 453} 454 455/* 456 * XXX 457 * poll/kqfilter do not appear to be correct 458 */ 459static int 460cnpoll(dev_t dev, int events, struct thread *td) 461{ 462 struct cn_device *cnd; 463 464 cnd = STAILQ_FIRST(&cn_devlist); 465 if (cn_mute || CND_INVALID(cnd, td)) 466 return (0); 467 dev = cnd->cnd_vp->v_rdev; 468 if (dev != NULL) 469 return ((*devsw(dev)->d_poll)(dev, events, td)); 470 return (0); 471} 472 473static int 474cnkqfilter(dev_t dev, struct knote *kn) 475{ 476 struct cn_device *cnd; 477 478 cnd = STAILQ_FIRST(&cn_devlist); 479 if (cn_mute || CND_INVALID(cnd, curthread)) 480 return (1); 481 dev = cnd->cnd_vp->v_rdev; 482 if (dev != NULL) 483 return ((*devsw(dev)->d_kqfilter)(dev, kn)); 484 return (1); 485} 486 487/* 488 * Low level console routines. 489 */ 490int 491cngetc(void) 492{ 493 int c; 494 495 if (cn_mute) 496 return (-1); 497 while ((c = cncheckc()) == -1) 498 ; 499 if (c == '\r') 500 c = '\n'; /* console input is always ICRNL */ 501 return (c); 502} 503 504int 505cncheckc(void) 506{ 507 struct cn_device *cnd; 508 struct consdev *cn; 509 int c; 510 511 if (cn_mute) 512 return (-1); 513 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 514 cn = cnd->cnd_cn; 515 c = cn->cn_checkc(cn->cn_dev); 516 if (c != -1) { 517 return (c); 518 } 519 } 520 return (-1); 521} 522 523void 524cnputc(int c) 525{ 526 struct cn_device *cnd; 527 struct consdev *cn; 528 529 if (cn_mute || c == '\0') 530 return; 531 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 532 cn = cnd->cnd_cn; 533 if (c == '\n') 534 cn->cn_putc(cn->cn_dev, '\r'); 535 cn->cn_putc(cn->cn_dev, c); 536 } 537} 538 539void 540cndbctl(int on) 541{ 542 struct cn_device *cnd; 543 struct consdev *cn; 544 static int refcount; 545 546 if (!on) 547 refcount--; 548 if (refcount == 0) 549 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 550 cn = cnd->cnd_cn; 551 if (cn->cn_dbctl != NULL) 552 cn->cn_dbctl(cn->cn_dev, on); 553 } 554 if (on) 555 refcount++; 556} 557 558static void 559cn_drvinit(void *unused) 560{ 561 562 cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 563 "console"); 564} 565 566SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL) 567