2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package tty
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"sync"
|
|
|
|
"unicode/utf8"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/arch"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2018-05-12 00:18:56 +00:00
|
|
|
// canonMaxBytes is the number of bytes that fit into a single line of
|
|
|
|
// terminal input in canonical mode. This corresponds to N_TTY_BUF_SIZE
|
|
|
|
// in include/linux/tty.h.
|
|
|
|
canonMaxBytes = 4096
|
|
|
|
|
|
|
|
// nonCanonMaxBytes is the maximum number of bytes that can be read at
|
|
|
|
// a time in noncanonical mode.
|
|
|
|
nonCanonMaxBytes = canonMaxBytes - 1
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
spacesPerTab = 8
|
|
|
|
)
|
|
|
|
|
|
|
|
// lineDiscipline dictates how input and output are handled between the
|
|
|
|
// pseudoterminal (pty) master and slave. It can be configured to alter I/O,
|
|
|
|
// modify control characters (e.g. Ctrl-C for SIGINT), etc. The following man
|
|
|
|
// pages are good resources for how to affect the line discipline:
|
|
|
|
//
|
|
|
|
// * termios(3)
|
|
|
|
// * tty_ioctl(4)
|
|
|
|
//
|
|
|
|
// This file corresponds most closely to drivers/tty/n_tty.c.
|
|
|
|
//
|
|
|
|
// lineDiscipline has a simple structure but supports a multitude of options
|
|
|
|
// (see the above man pages). It consists of two queues of bytes: one from the
|
|
|
|
// terminal master to slave (the input queue) and one from slave to master (the
|
|
|
|
// output queue). When bytes are written to one end of the pty, the line
|
|
|
|
// discipline reads the bytes, modifies them or takes special action if
|
|
|
|
// required, and enqueues them to be read by the other end of the pty:
|
|
|
|
//
|
|
|
|
// input from terminal +-------------+ input to process (e.g. bash)
|
|
|
|
// +------------------------>| input queue |---------------------------+
|
2018-05-12 00:18:56 +00:00
|
|
|
// | (inputQueueWrite) +-------------+ (inputQueueRead) |
|
2018-04-27 17:37:02 +00:00
|
|
|
// | |
|
|
|
|
// | v
|
|
|
|
// masterFD slaveFD
|
|
|
|
// ^ |
|
|
|
|
// | |
|
|
|
|
// | output to terminal +--------------+ output from process |
|
|
|
|
// +------------------------| output queue |<--------------------------+
|
2018-05-12 00:18:56 +00:00
|
|
|
// (outputQueueRead) +--------------+ (outputQueueWrite)
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Lock order:
|
2018-06-07 17:20:28 +00:00
|
|
|
// termiosMu
|
2018-06-11 18:08:51 +00:00
|
|
|
// inQueue.mu
|
|
|
|
// outQueue.mu
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type lineDiscipline struct {
|
2018-08-27 17:48:02 +00:00
|
|
|
// sizeMu protects size.
|
|
|
|
sizeMu sync.Mutex `state:"nosave"`
|
|
|
|
|
|
|
|
// size is the terminal size (width and height).
|
|
|
|
size linux.WindowSize
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// inQueue is the input queue of the terminal.
|
|
|
|
inQueue queue
|
|
|
|
|
|
|
|
// outQueue is the output queue of the terminal.
|
|
|
|
outQueue queue
|
|
|
|
|
|
|
|
// termiosMu protects termios.
|
2018-06-07 17:20:28 +00:00
|
|
|
termiosMu sync.RWMutex `state:"nosave"`
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// termios is the terminal configuration used by the lineDiscipline.
|
|
|
|
termios linux.KernelTermios
|
|
|
|
|
|
|
|
// column is the location in a row of the cursor. This is important for
|
|
|
|
// handling certain special characters like backspace.
|
|
|
|
column int
|
2018-08-14 23:21:38 +00:00
|
|
|
|
|
|
|
// masterWaiter is used to wait on the master end of the TTY.
|
|
|
|
masterWaiter waiter.Queue `state:"zerovalue"`
|
|
|
|
|
|
|
|
// slaveWaiter is used to wait on the slave end of the TTY.
|
|
|
|
slaveWaiter waiter.Queue `state:"zerovalue"`
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 17:20:28 +00:00
|
|
|
func newLineDiscipline(termios linux.KernelTermios) *lineDiscipline {
|
|
|
|
ld := lineDiscipline{termios: termios}
|
|
|
|
ld.inQueue.transformer = &inputQueueTransformer{}
|
|
|
|
ld.outQueue.transformer = &outputQueueTransformer{}
|
|
|
|
return &ld
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// getTermios gets the linux.Termios for the tty.
|
|
|
|
func (l *lineDiscipline) getTermios(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-04-27 17:37:02 +00:00
|
|
|
// We must copy a Termios struct, not KernelTermios.
|
|
|
|
t := l.termios.ToTermios()
|
|
|
|
_, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), t, usermem.IOOpts{
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
})
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// setTermios sets a linux.Termios for the tty.
|
|
|
|
func (l *lineDiscipline) setTermios(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
|
|
|
l.termiosMu.Lock()
|
|
|
|
defer l.termiosMu.Unlock()
|
2018-05-12 00:18:56 +00:00
|
|
|
oldCanonEnabled := l.termios.LEnabled(linux.ICANON)
|
2018-04-27 17:37:02 +00:00
|
|
|
// We must copy a Termios struct, not KernelTermios.
|
|
|
|
var t linux.Termios
|
|
|
|
_, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &t, usermem.IOOpts{
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
})
|
|
|
|
l.termios.FromTermios(t)
|
2018-05-12 00:18:56 +00:00
|
|
|
|
|
|
|
// If canonical mode is turned off, move bytes from inQueue's wait
|
|
|
|
// buffer to its read buffer. Anything already in the read buffer is
|
|
|
|
// now readable.
|
|
|
|
if oldCanonEnabled && !l.termios.LEnabled(linux.ICANON) {
|
2019-03-22 01:03:49 +00:00
|
|
|
l.inQueue.pushWaitBuf(l)
|
|
|
|
l.inQueue.readable = true
|
|
|
|
l.slaveWaiter.Notify(waiter.EventIn)
|
2018-05-12 00:18:56 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2018-08-27 17:48:02 +00:00
|
|
|
func (l *lineDiscipline) windowSize(ctx context.Context, io usermem.IO, args arch.SyscallArguments) error {
|
|
|
|
l.sizeMu.Lock()
|
|
|
|
defer l.sizeMu.Unlock()
|
|
|
|
_, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), l.size, usermem.IOOpts{
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) setWindowSize(ctx context.Context, io usermem.IO, args arch.SyscallArguments) error {
|
|
|
|
l.sizeMu.Lock()
|
|
|
|
defer l.sizeMu.Unlock()
|
|
|
|
_, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &l.size, usermem.IOOpts{
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
func (l *lineDiscipline) masterReadiness() waiter.EventMask {
|
2018-05-12 00:18:56 +00:00
|
|
|
// We don't have to lock a termios because the default master termios
|
|
|
|
// is immutable.
|
|
|
|
return l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) slaveReadiness() waiter.EventMask {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-05-12 00:18:56 +00:00
|
|
|
return l.outQueue.writeReadiness(&l.termios) | l.inQueue.readReadiness(&l.termios)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 00:18:56 +00:00
|
|
|
func (l *lineDiscipline) inputQueueReadSize(ctx context.Context, io usermem.IO, args arch.SyscallArguments) error {
|
|
|
|
return l.inQueue.readableSize(ctx, io, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) inputQueueRead(ctx context.Context, dst usermem.IOSequence) (int64, error) {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-08-14 23:21:38 +00:00
|
|
|
n, pushed, err := l.inQueue.read(ctx, dst, l)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if n > 0 {
|
|
|
|
l.masterWaiter.Notify(waiter.EventOut)
|
|
|
|
if pushed {
|
|
|
|
l.slaveWaiter.Notify(waiter.EventIn)
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
return 0, syserror.ErrWouldBlock
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) inputQueueWrite(ctx context.Context, src usermem.IOSequence) (int64, error) {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-08-14 23:21:38 +00:00
|
|
|
n, err := l.inQueue.write(ctx, src, l)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if n > 0 {
|
|
|
|
l.slaveWaiter.Notify(waiter.EventIn)
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
return 0, syserror.ErrWouldBlock
|
2018-05-12 00:18:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) outputQueueReadSize(ctx context.Context, io usermem.IO, args arch.SyscallArguments) error {
|
|
|
|
return l.outQueue.readableSize(ctx, io, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) outputQueueRead(ctx context.Context, dst usermem.IOSequence) (int64, error) {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-08-14 23:21:38 +00:00
|
|
|
n, pushed, err := l.outQueue.read(ctx, dst, l)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if n > 0 {
|
|
|
|
l.slaveWaiter.Notify(waiter.EventOut)
|
|
|
|
if pushed {
|
|
|
|
l.masterWaiter.Notify(waiter.EventIn)
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
return 0, syserror.ErrWouldBlock
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lineDiscipline) outputQueueWrite(ctx context.Context, src usermem.IOSequence) (int64, error) {
|
2018-06-07 17:20:28 +00:00
|
|
|
l.termiosMu.RLock()
|
|
|
|
defer l.termiosMu.RUnlock()
|
2018-08-14 23:21:38 +00:00
|
|
|
n, err := l.outQueue.write(ctx, src, l)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if n > 0 {
|
|
|
|
l.masterWaiter.Notify(waiter.EventIn)
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
return 0, syserror.ErrWouldBlock
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-11 18:08:51 +00:00
|
|
|
// transformer is a helper interface to make it easier to stateify queue.
|
|
|
|
type transformer interface {
|
|
|
|
// transform functions require queue's mutex to be held.
|
|
|
|
transform(*lineDiscipline, *queue, []byte) int
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-11 18:08:51 +00:00
|
|
|
// outputQueueTransformer implements transformer. It performs line discipline
|
|
|
|
// transformations on the output queue.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-06-07 17:20:28 +00:00
|
|
|
type outputQueueTransformer struct{}
|
2018-05-12 00:18:56 +00:00
|
|
|
|
2018-06-07 17:20:28 +00:00
|
|
|
// transform does output processing for one end of the pty. See
|
2018-05-03 21:05:25 +00:00
|
|
|
// drivers/tty/n_tty.c:do_output_char for an analogous kernel function.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
2019-03-22 01:03:49 +00:00
|
|
|
// Preconditions:
|
2018-06-07 17:20:28 +00:00
|
|
|
// * l.termiosMu must be held for reading.
|
2018-06-11 18:08:51 +00:00
|
|
|
// * q.mu must be held.
|
2018-06-07 17:20:28 +00:00
|
|
|
func (*outputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte) int {
|
2018-05-12 00:18:56 +00:00
|
|
|
// transformOutput is effectively always in noncanonical mode, as the
|
|
|
|
// master termios never has ICANON set.
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
if !l.termios.OEnabled(linux.OPOST) {
|
2019-03-22 01:03:49 +00:00
|
|
|
q.readBuf = append(q.readBuf, buf...)
|
|
|
|
if len(q.readBuf) > 0 {
|
2018-05-12 00:18:56 +00:00
|
|
|
q.readable = true
|
|
|
|
}
|
2019-03-22 01:03:49 +00:00
|
|
|
return len(buf)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 00:18:56 +00:00
|
|
|
var ret int
|
2018-04-27 17:37:02 +00:00
|
|
|
for len(buf) > 0 {
|
2019-04-03 19:59:27 +00:00
|
|
|
size := l.peek(buf)
|
|
|
|
cBytes := append([]byte{}, buf[:size]...)
|
2018-05-12 00:18:56 +00:00
|
|
|
ret += size
|
|
|
|
buf = buf[size:]
|
2019-04-03 19:59:27 +00:00
|
|
|
// We're guaranteed that cBytes has at least one element.
|
|
|
|
switch cBytes[0] {
|
2018-04-27 17:37:02 +00:00
|
|
|
case '\n':
|
|
|
|
if l.termios.OEnabled(linux.ONLRET) {
|
|
|
|
l.column = 0
|
|
|
|
}
|
|
|
|
if l.termios.OEnabled(linux.ONLCR) {
|
2019-03-22 01:03:49 +00:00
|
|
|
q.readBuf = append(q.readBuf, '\r', '\n')
|
2018-04-27 17:37:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
case '\r':
|
|
|
|
if l.termios.OEnabled(linux.ONOCR) && l.column == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if l.termios.OEnabled(linux.OCRNL) {
|
2019-04-03 19:59:27 +00:00
|
|
|
cBytes[0] = '\n'
|
2018-04-27 17:37:02 +00:00
|
|
|
if l.termios.OEnabled(linux.ONLRET) {
|
|
|
|
l.column = 0
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
l.column = 0
|
|
|
|
case '\t':
|
|
|
|
spaces := spacesPerTab - l.column%spacesPerTab
|
|
|
|
if l.termios.OutputFlags&linux.TABDLY == linux.XTABS {
|
|
|
|
l.column += spaces
|
2019-03-22 01:03:49 +00:00
|
|
|
q.readBuf = append(q.readBuf, bytes.Repeat([]byte{' '}, spacesPerTab)...)
|
2018-04-27 17:37:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
l.column += spaces
|
|
|
|
case '\b':
|
|
|
|
if l.column > 0 {
|
|
|
|
l.column--
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
l.column++
|
|
|
|
}
|
2019-04-03 19:59:27 +00:00
|
|
|
q.readBuf = append(q.readBuf, cBytes...)
|
2018-05-12 00:18:56 +00:00
|
|
|
}
|
2019-03-22 01:03:49 +00:00
|
|
|
if len(q.readBuf) > 0 {
|
2018-05-12 00:18:56 +00:00
|
|
|
q.readable = true
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-05-12 00:18:56 +00:00
|
|
|
return ret
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-11 18:08:51 +00:00
|
|
|
// inputQueueTransformer implements transformer. It performs line discipline
|
|
|
|
// transformations on the input queue.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-06-07 17:20:28 +00:00
|
|
|
type inputQueueTransformer struct{}
|
|
|
|
|
|
|
|
// transform does input processing for one end of the pty. Characters read are
|
|
|
|
// transformed according to flags set in the termios struct. See
|
2018-04-27 17:37:02 +00:00
|
|
|
// drivers/tty/n_tty.c:n_tty_receive_char_special for an analogous kernel
|
|
|
|
// function.
|
|
|
|
//
|
2019-03-22 01:03:49 +00:00
|
|
|
// Preconditions:
|
2018-06-07 17:20:28 +00:00
|
|
|
// * l.termiosMu must be held for reading.
|
2018-06-11 18:08:51 +00:00
|
|
|
// * q.mu must be held.
|
2018-06-07 17:20:28 +00:00
|
|
|
func (*inputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte) int {
|
2018-05-12 00:18:56 +00:00
|
|
|
// If there's a line waiting to be read in canonical mode, don't write
|
|
|
|
// anything else to the read buffer.
|
|
|
|
if l.termios.LEnabled(linux.ICANON) && q.readable {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
maxBytes := nonCanonMaxBytes
|
|
|
|
if l.termios.LEnabled(linux.ICANON) {
|
|
|
|
maxBytes = canonMaxBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
var ret int
|
2019-03-22 01:03:49 +00:00
|
|
|
for len(buf) > 0 && len(q.readBuf) < canonMaxBytes {
|
2019-04-03 19:59:27 +00:00
|
|
|
size := l.peek(buf)
|
|
|
|
cBytes := append([]byte{}, buf[:size]...)
|
|
|
|
// We're guaranteed that cBytes has at least one element.
|
|
|
|
switch cBytes[0] {
|
2018-04-27 17:37:02 +00:00
|
|
|
case '\r':
|
|
|
|
if l.termios.IEnabled(linux.IGNCR) {
|
2018-05-12 00:18:56 +00:00
|
|
|
buf = buf[size:]
|
|
|
|
ret += size
|
2018-04-27 17:37:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if l.termios.IEnabled(linux.ICRNL) {
|
2019-04-03 19:59:27 +00:00
|
|
|
cBytes[0] = '\n'
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
case '\n':
|
|
|
|
if l.termios.IEnabled(linux.INLCR) {
|
2019-04-03 19:59:27 +00:00
|
|
|
cBytes[0] = '\r'
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-12 00:18:56 +00:00
|
|
|
|
|
|
|
// In canonical mode, we discard non-terminating characters
|
|
|
|
// after the first 4095.
|
2019-04-03 19:59:27 +00:00
|
|
|
if l.shouldDiscard(q, cBytes) {
|
2018-05-12 00:18:56 +00:00
|
|
|
buf = buf[size:]
|
|
|
|
ret += size
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop if the buffer would be overfilled.
|
2019-03-22 01:03:49 +00:00
|
|
|
if len(q.readBuf)+size > maxBytes {
|
2018-05-12 00:18:56 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
buf = buf[size:]
|
|
|
|
ret += size
|
|
|
|
|
|
|
|
// If we get EOF, make the buffer available for reading.
|
2019-04-03 19:59:27 +00:00
|
|
|
if l.termios.LEnabled(linux.ICANON) && l.termios.IsEOF(cBytes[0]) {
|
2018-05-12 00:18:56 +00:00
|
|
|
q.readable = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2019-04-03 19:59:27 +00:00
|
|
|
q.readBuf = append(q.readBuf, cBytes...)
|
2019-03-22 01:03:49 +00:00
|
|
|
|
2018-06-07 17:20:28 +00:00
|
|
|
// Anything written to the readBuf will have to be echoed.
|
|
|
|
if l.termios.LEnabled(linux.ECHO) {
|
2019-03-22 01:03:49 +00:00
|
|
|
l.outQueue.writeBytes(cBytes, l)
|
|
|
|
l.masterWaiter.Notify(waiter.EventIn)
|
2018-06-07 17:20:28 +00:00
|
|
|
}
|
2018-05-12 00:18:56 +00:00
|
|
|
|
|
|
|
// If we finish a line, make it available for reading.
|
2019-04-03 19:59:27 +00:00
|
|
|
if l.termios.LEnabled(linux.ICANON) && l.termios.IsTerminating(cBytes) {
|
2018-05-12 00:18:56 +00:00
|
|
|
q.readable = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// In noncanonical mode, everything is readable.
|
2019-03-22 01:03:49 +00:00
|
|
|
if !l.termios.LEnabled(linux.ICANON) && len(q.readBuf) > 0 {
|
2018-05-12 00:18:56 +00:00
|
|
|
q.readable = true
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-05-12 00:18:56 +00:00
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// shouldDiscard returns whether c should be discarded. In canonical mode, if
|
|
|
|
// too many bytes are enqueued, we keep reading input and discarding it until
|
|
|
|
// we find a terminating character. Signal/echo processing still occurs.
|
2018-06-11 18:08:51 +00:00
|
|
|
//
|
|
|
|
// Precondition:
|
|
|
|
// * l.termiosMu must be held for reading.
|
|
|
|
// * q.mu must be held.
|
2019-04-03 19:59:27 +00:00
|
|
|
func (l *lineDiscipline) shouldDiscard(q *queue, cBytes []byte) bool {
|
|
|
|
return l.termios.LEnabled(linux.ICANON) && len(q.readBuf)+len(cBytes) >= canonMaxBytes && !l.termios.IsTerminating(cBytes)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 19:59:27 +00:00
|
|
|
// peek returns the size in bytes of the next character to process. As long as
|
|
|
|
// b isn't empty, peek returns a value of at least 1.
|
|
|
|
func (l *lineDiscipline) peek(b []byte) int {
|
|
|
|
size := 1
|
2018-04-27 17:37:02 +00:00
|
|
|
// If UTF-8 support is enabled, runes might be multiple bytes.
|
|
|
|
if l.termios.IEnabled(linux.IUTF8) {
|
2019-04-03 19:59:27 +00:00
|
|
|
_, size = utf8.DecodeRune(b)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-04-03 19:59:27 +00:00
|
|
|
return size
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|