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_inq.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) 2008 Ed Schouten <ed@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Portions of this software were developed under sponsorship from Snow
    6  * B.V., the Netherlands.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: releng/11.2/sys/kern/tty_inq.c 314538 2017-03-02 04:23:53Z ian $");
   32 
   33 #include <sys/param.h>
   34 #include <sys/kernel.h>
   35 #include <sys/lock.h>
   36 #include <sys/queue.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/systm.h>
   39 #include <sys/tty.h>
   40 #include <sys/uio.h>
   41 
   42 #include <vm/uma.h>
   43 
   44 /*
   45  * TTY input queue buffering.
   46  *
   47  * Unlike the output queue, the input queue has more features that are
   48  * needed to properly implement various features offered by the TTY
   49  * interface:
   50  *
   51  * - Data can be removed from the tail of the queue, which is used to
   52  *   implement backspace.
   53  * - Once in a while, input has to be `canonicalized'. When ICANON is
   54  *   turned on, this will be done after a CR has been inserted.
   55  *   Otherwise, it should be done after any character has been inserted.
   56  * - The input queue can store one bit per byte, called the quoting bit.
   57  *   This bit is used by TTYDISC to make backspace work on quoted
   58  *   characters.
   59  *
   60  * In most cases, there is probably less input than output, so unlike
   61  * the outq, we'll stick to 128 byte blocks here.
   62  */
   63 
   64 static int ttyinq_flush_secure = 1;
   65 SYSCTL_INT(_kern, OID_AUTO, tty_inq_flush_secure, CTLFLAG_RW,
   66         &ttyinq_flush_secure, 0, "Zero buffers while flushing");
   67 
   68 #define TTYINQ_QUOTESIZE        (TTYINQ_DATASIZE / BMSIZE)
   69 #define BMSIZE                  32
   70 #define GETBIT(tib,boff) \
   71         ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE)))
   72 #define SETBIT(tib,boff) \
   73         ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE)))
   74 #define CLRBIT(tib,boff) \
   75         ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE)))
   76 
   77 struct ttyinq_block {
   78         struct ttyinq_block     *tib_prev;
   79         struct ttyinq_block     *tib_next;
   80         uint32_t                tib_quotes[TTYINQ_QUOTESIZE];
   81         char                    tib_data[TTYINQ_DATASIZE];
   82 };
   83 
   84 static uma_zone_t ttyinq_zone;
   85 
   86 #define TTYINQ_INSERT_TAIL(ti, tib) do {                                \
   87         if (ti->ti_end == 0) {                                          \
   88                 tib->tib_prev = NULL;                                   \
   89                 tib->tib_next = ti->ti_firstblock;                      \
   90                 ti->ti_firstblock = tib;                                \
   91         } else {                                                        \
   92                 tib->tib_prev = ti->ti_lastblock;                       \
   93                 tib->tib_next = ti->ti_lastblock->tib_next;             \
   94                 ti->ti_lastblock->tib_next = tib;                       \
   95         }                                                               \
   96         if (tib->tib_next != NULL)                                      \
   97                 tib->tib_next->tib_prev = tib;                          \
   98         ti->ti_nblocks++;                                               \
   99 } while (0)
  100 
  101 #define TTYINQ_REMOVE_HEAD(ti) do {                                     \
  102         ti->ti_firstblock = ti->ti_firstblock->tib_next;                \
  103         if (ti->ti_firstblock != NULL)                                  \
  104                 ti->ti_firstblock->tib_prev = NULL;                     \
  105         ti->ti_nblocks--;                                               \
  106 } while (0)
  107 
  108 #define TTYINQ_RECYCLE(ti, tib) do {                                    \
  109         if (ti->ti_quota <= ti->ti_nblocks)                             \
  110                 uma_zfree(ttyinq_zone, tib);                            \
  111         else                                                            \
  112                 TTYINQ_INSERT_TAIL(ti, tib);                            \
  113 } while (0)
  114 
  115 int 
  116 ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t size)
  117 {
  118         struct ttyinq_block *tib;
  119 
  120         ti->ti_quota = howmany(size, TTYINQ_DATASIZE);
  121 
  122         while (ti->ti_quota > ti->ti_nblocks) {
  123                 /*
  124                  * List is getting bigger.
  125                  * Add new blocks to the tail of the list.
  126                  *
  127                  * We must unlock the TTY temporarily, because we need
  128                  * to allocate memory. This won't be a problem, because
  129                  * in the worst case, another thread ends up here, which
  130                  * may cause us to allocate too many blocks, but this
  131                  * will be caught by the loop below.
  132                  */
  133                 tty_unlock(tp);
  134                 tib = uma_zalloc(ttyinq_zone, M_WAITOK);
  135                 tty_lock(tp);
  136 
  137                 if (tty_gone(tp)) {
  138                         uma_zfree(ttyinq_zone, tib);
  139                         return (ENXIO);
  140                 }
  141 
  142                 TTYINQ_INSERT_TAIL(ti, tib);
  143         }
  144         return (0);
  145 }
  146 
  147 void
  148 ttyinq_free(struct ttyinq *ti)
  149 {
  150         struct ttyinq_block *tib;
  151 
  152         ttyinq_flush(ti);
  153         ti->ti_quota = 0;
  154 
  155         while ((tib = ti->ti_firstblock) != NULL) {
  156                 TTYINQ_REMOVE_HEAD(ti);
  157                 uma_zfree(ttyinq_zone, tib);
  158         }
  159 
  160         MPASS(ti->ti_nblocks == 0);
  161 }
  162 
  163 int
  164 ttyinq_read_uio(struct ttyinq *ti, struct tty *tp, struct uio *uio,
  165     size_t rlen, size_t flen)
  166 {
  167 
  168         MPASS(rlen <= uio->uio_resid);
  169 
  170         while (rlen > 0) {
  171                 int error;
  172                 struct ttyinq_block *tib;
  173                 size_t cbegin, cend, clen;
  174 
  175                 /* See if there still is data. */
  176                 if (ti->ti_begin == ti->ti_linestart)
  177                         return (0);
  178                 tib = ti->ti_firstblock;
  179                 if (tib == NULL)
  180                         return (0);
  181 
  182                 /*
  183                  * The end address should be the lowest of these three:
  184                  * - The write pointer
  185                  * - The blocksize - we can't read beyond the block
  186                  * - The end address if we could perform the full read
  187                  */
  188                 cbegin = ti->ti_begin;
  189                 cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen),
  190                     TTYINQ_DATASIZE);
  191                 clen = cend - cbegin;
  192                 MPASS(clen >= flen);
  193                 rlen -= clen;
  194 
  195                 /*
  196                  * We can prevent buffering in some cases:
  197                  * - We need to read the block until the end.
  198                  * - We don't need to read the block until the end, but
  199                  *   there is no data beyond it, which allows us to move
  200                  *   the write pointer to a new block.
  201                  */
  202                 if (cend == TTYINQ_DATASIZE || cend == ti->ti_end) {
  203                         /*
  204                          * Fast path: zero copy. Remove the first block,
  205                          * so we can unlock the TTY temporarily.
  206                          */
  207                         TTYINQ_REMOVE_HEAD(ti);
  208                         ti->ti_begin = 0;
  209 
  210                         /*
  211                          * Because we remove the first block, we must
  212                          * fix up the block offsets.
  213                          */
  214 #define CORRECT_BLOCK(t) do {                   \
  215         if (t <= TTYINQ_DATASIZE)               \
  216                 t = 0;                          \
  217         else                                    \
  218                 t -= TTYINQ_DATASIZE;           \
  219 } while (0)
  220                         CORRECT_BLOCK(ti->ti_linestart);
  221                         CORRECT_BLOCK(ti->ti_reprint);
  222                         CORRECT_BLOCK(ti->ti_end);
  223 #undef CORRECT_BLOCK
  224 
  225                         /*
  226                          * Temporary unlock and copy the data to
  227                          * userspace. We may need to flush trailing
  228                          * bytes, like EOF characters.
  229                          */
  230                         tty_unlock(tp);
  231                         error = uiomove(tib->tib_data + cbegin,
  232                             clen - flen, uio);
  233                         tty_lock(tp);
  234 
  235                         /* Block can now be readded to the list. */
  236                         TTYINQ_RECYCLE(ti, tib);
  237                 } else {
  238                         char ob[TTYINQ_DATASIZE - 1];
  239 
  240                         /*
  241                          * Slow path: store data in a temporary buffer.
  242                          */
  243                         memcpy(ob, tib->tib_data + cbegin, clen - flen);
  244                         ti->ti_begin += clen;
  245                         MPASS(ti->ti_begin < TTYINQ_DATASIZE);
  246 
  247                         /* Temporary unlock and copy the data to userspace. */
  248                         tty_unlock(tp);
  249                         error = uiomove(ob, clen - flen, uio);
  250                         tty_lock(tp);
  251                 }
  252 
  253                 if (error != 0)
  254                         return (error);
  255                 if (tty_gone(tp))
  256                         return (ENXIO);
  257         }
  258 
  259         return (0);
  260 }
  261 
  262 static __inline void
  263 ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset,
  264     size_t length, int value)
  265 {
  266 
  267         if (value) {
  268                 /* Set the bits. */
  269                 for (; length > 0; length--, offset++)
  270                         SETBIT(tib, offset);
  271         } else {
  272                 /* Unset the bits. */
  273                 for (; length > 0; length--, offset++)
  274                         CLRBIT(tib, offset);
  275         }
  276 }
  277 
  278 size_t
  279 ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes, int quote)
  280 {
  281         const char *cbuf = buf;
  282         struct ttyinq_block *tib;
  283         unsigned int boff;
  284         size_t l;
  285 
  286         while (nbytes > 0) {
  287                 boff = ti->ti_end % TTYINQ_DATASIZE;
  288 
  289                 if (ti->ti_end == 0) {
  290                         /* First time we're being used or drained. */
  291                         MPASS(ti->ti_begin == 0);
  292                         tib = ti->ti_firstblock;
  293                         if (tib == NULL) {
  294                                 /* Queue has no blocks. */
  295                                 break;
  296                         }
  297                         ti->ti_lastblock = tib;
  298                 } else if (boff == 0) {
  299                         /* We reached the end of this block on last write. */
  300                         tib = ti->ti_lastblock->tib_next;
  301                         if (tib == NULL) {
  302                                 /* We've reached the watermark. */
  303                                 break;
  304                         }
  305                         ti->ti_lastblock = tib;
  306                 } else {
  307                         tib = ti->ti_lastblock;
  308                 }
  309 
  310                 /* Don't copy more than was requested. */
  311                 l = MIN(nbytes, TTYINQ_DATASIZE - boff);
  312                 MPASS(l > 0);
  313                 memcpy(tib->tib_data + boff, cbuf, l);
  314 
  315                 /* Set the quoting bits for the proper region. */
  316                 ttyinq_set_quotes(tib, boff, l, quote);
  317 
  318                 cbuf += l;
  319                 nbytes -= l;
  320                 ti->ti_end += l;
  321         }
  322 
  323         return (cbuf - (const char *)buf);
  324 }
  325 
  326 int
  327 ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes, int quote)
  328 {
  329         size_t ret;
  330 
  331         if (ttyinq_bytesleft(ti) < nbytes)
  332                 return (-1);
  333 
  334         /* We should always be able to write it back. */
  335         ret = ttyinq_write(ti, buf, nbytes, quote);
  336         MPASS(ret == nbytes);
  337 
  338         return (0);
  339 }
  340 
  341 void
  342 ttyinq_canonicalize(struct ttyinq *ti)
  343 {
  344 
  345         ti->ti_linestart = ti->ti_reprint = ti->ti_end;
  346         ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock;
  347 }
  348 
  349 size_t
  350 ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
  351     char *lastc)
  352 {
  353         struct ttyinq_block *tib = ti->ti_firstblock;
  354         unsigned int boff = ti->ti_begin;
  355         unsigned int bend = MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart),
  356             ti->ti_begin + maxlen);
  357 
  358         MPASS(maxlen > 0);
  359 
  360         if (tib == NULL)
  361                 return (0);
  362 
  363         while (boff < bend) {
  364                 if (strchr(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff)) {
  365                         *lastc = tib->tib_data[boff];
  366                         return (boff - ti->ti_begin + 1);
  367                 }
  368                 boff++;
  369         }
  370 
  371         /* Not found - just process the entire block. */
  372         return (bend - ti->ti_begin);
  373 }
  374 
  375 void
  376 ttyinq_flush(struct ttyinq *ti)
  377 {
  378         struct ttyinq_block *tib;
  379 
  380         ti->ti_begin = 0;
  381         ti->ti_linestart = 0;
  382         ti->ti_reprint = 0;
  383         ti->ti_end = 0;
  384 
  385         /* Zero all data in the input queue to get rid of passwords. */
  386         if (ttyinq_flush_secure) {
  387                 for (tib = ti->ti_firstblock; tib != NULL; tib = tib->tib_next)
  388                         bzero(&tib->tib_data, sizeof tib->tib_data);
  389         }
  390 }
  391 
  392 int
  393 ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote)
  394 {
  395         unsigned int boff;
  396         struct ttyinq_block *tib = ti->ti_lastblock;
  397 
  398         if (ti->ti_linestart == ti->ti_end)
  399                 return (-1);
  400 
  401         MPASS(ti->ti_end > 0);
  402         boff = (ti->ti_end - 1) % TTYINQ_DATASIZE;
  403 
  404         *c = tib->tib_data[boff];
  405         *quote = GETBIT(tib, boff);
  406 
  407         return (0);
  408 }
  409 
  410 void
  411 ttyinq_unputchar(struct ttyinq *ti)
  412 {
  413 
  414         MPASS(ti->ti_linestart < ti->ti_end);
  415 
  416         if (--ti->ti_end % TTYINQ_DATASIZE == 0) {
  417                 /* Roll back to the previous block. */
  418                 ti->ti_lastblock = ti->ti_lastblock->tib_prev;
  419                 /*
  420                  * This can only fail if we are unputchar()'ing the
  421                  * first character in the queue.
  422                  */
  423                 MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0));
  424         }
  425 }
  426 
  427 void
  428 ttyinq_reprintpos_set(struct ttyinq *ti)
  429 {
  430 
  431         ti->ti_reprint = ti->ti_end;
  432         ti->ti_reprintblock = ti->ti_lastblock;
  433 }
  434 
  435 void
  436 ttyinq_reprintpos_reset(struct ttyinq *ti)
  437 {
  438 
  439         ti->ti_reprint = ti->ti_linestart;
  440         ti->ti_reprintblock = ti->ti_startblock;
  441 }
  442 
  443 static void
  444 ttyinq_line_iterate(struct ttyinq *ti,
  445     ttyinq_line_iterator_t *iterator, void *data,
  446     unsigned int offset, struct ttyinq_block *tib)
  447 {
  448         unsigned int boff;
  449 
  450         /* Use the proper block when we're at the queue head. */
  451         if (offset == 0)
  452                 tib = ti->ti_firstblock;
  453 
  454         /* Iterate all characters and call the iterator function. */
  455         for (; offset < ti->ti_end; offset++) {
  456                 boff = offset % TTYINQ_DATASIZE;
  457                 MPASS(tib != NULL);
  458 
  459                 /* Call back the iterator function. */
  460                 iterator(data, tib->tib_data[boff], GETBIT(tib, boff));
  461 
  462                 /* Last byte iterated - go to the next block. */
  463                 if (boff == TTYINQ_DATASIZE - 1)
  464                         tib = tib->tib_next;
  465                 MPASS(tib != NULL);
  466         }
  467 }
  468 
  469 void
  470 ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
  471     ttyinq_line_iterator_t *iterator, void *data)
  472 {
  473 
  474         ttyinq_line_iterate(ti, iterator, data,
  475             ti->ti_linestart, ti->ti_startblock);
  476 }
  477 
  478 void
  479 ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
  480     ttyinq_line_iterator_t *iterator, void *data)
  481 {
  482 
  483         ttyinq_line_iterate(ti, iterator, data,
  484             ti->ti_reprint, ti->ti_reprintblock);
  485 }
  486 
  487 static void
  488 ttyinq_startup(void *dummy)
  489 {
  490 
  491         ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block),
  492             NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
  493 }
  494 
  495 SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL);

Cache object: 4017b0856f160964b23d8a32ad003556


[ 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.