The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/tty_cons.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.