Skip to main content

line_discipline/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use derivative::Derivative;
6use starnix_uapi::errors::Errno;
7use starnix_uapi::signals::{SIGINT, SIGQUIT, SIGSTOP, Signal};
8use starnix_uapi::vfs::FdEvents;
9use starnix_uapi::{
10    ECHO, ECHOCTL, ECHOE, ECHOK, ECHOKE, ECHONL, ECHOPRT, ICANON, ICRNL, IEXTEN, IGNCR, INLCR,
11    ISIG, IUCLC, IUTF8, IXANY, IXON, NOFLSH, OCRNL, OLCUC, ONLCR, ONLRET, ONOCR, OPOST, TABDLY,
12    VEOF, VEOL, VEOL2, VERASE, VINTR, VKILL, VLNEXT, VQUIT, VREPRINT, VSTART, VSTOP, VSUSP,
13    VWERASE, XTABS, cc_t, errno, error, tcflag_t, uapi,
14};
15use std::collections::VecDeque;
16
17// CANON_MAX_BYTES is the number of bytes that fit into a single line of
18// terminal input in canonical mode. See https://github.com/google/gvisor/blob/master/pkg/sentry/fs/tty/line_discipline.go
19const CANON_MAX_BYTES: usize = 4096;
20
21// NON_CANON_MAX_BYTES is the maximum number of bytes that can be read at
22// a time in non canonical mode.
23const NON_CANON_MAX_BYTES: usize = CANON_MAX_BYTES - 1;
24
25// WAIT_BUFFER_MAX_BYTES is the maximum size of a wait buffer. It is based on
26// https://github.com/google/gvisor/blob/master/pkg/sentry/fsimpl/devpts/queue.go
27const WAIT_BUFFER_MAX_BYTES: usize = 131072;
28
29const SPACES_PER_TAB: usize = 8;
30
31// DISABLED_CHAR is used to indicate that a control character is disabled.
32const DISABLED_CHAR: u8 = 0;
33
34const BACKSPACE_CHAR: u8 = 8; // \b
35
36/// The offset in ASCII between a control character and it's character name.
37/// For example, typing CTRL-C on a keyboard generates the value
38/// b'C' - CONTROL_OFFSET
39const CONTROL_OFFSET: u8 = 0x40;
40
41#[derive(Derivative)]
42#[derivative(Default)]
43#[derivative(Debug)]
44pub struct LineDiscipline {
45    /// |true| is the terminal is locked.
46    #[derivative(Default(value = "true"))]
47    pub locked: bool,
48
49    /// |true| if the output is stopped (due to IXON).
50    #[derivative(Default(value = "false"))]
51    pub stopped: bool,
52
53    /// Terminal size.
54    pub window_size: uapi::winsize,
55
56    /// Terminal configuration.
57    #[derivative(Default(value = "get_default_termios()"))]
58    termios: uapi::termios2,
59
60    /// True if the terminal is currently in the middle of an erase sequence (ECHOPRT).
61    #[derivative(Default(value = "false"))]
62    erasing: bool,
63
64    /// True if the next character should be treated literally.
65    #[derivative(Default(value = "false"))]
66    lnext: bool,
67
68    /// Location in a row of the cursor. Needed to handle certain special characters like
69    /// backspace.
70    column: usize,
71
72    /// Packet mode state (TIOCPKT).
73    #[derivative(Default(value = "false"))]
74    packet_mode_enabled: bool,
75
76    /// Packet mode pending events.
77    #[derivative(Default(value = "0"))]
78    packet_mode_pending_events: u8,
79
80    /// The number of active references to the main part of the terminal. Starts as `None`. The
81    /// main part of the terminal is considered closed when this is `Some(0)`.
82    main_references: Option<u32>,
83
84    /// The number of active references to the replica part of the terminal. Starts as `None`. The
85    /// replica part of the terminal is considered closed when this is `Some(0)`.
86    replica_references: Option<u32>,
87
88    /// Input queue of the terminal. Data flow from the main side to the replica side.
89    #[derivative(Default(value = "Queue::input_queue()"))]
90    input_queue: Option<Queue>,
91
92    /// Output queue of the terminal. Data flow from the replica side to the main side.
93    #[derivative(Default(value = "Queue::output_queue()"))]
94    output_queue: Option<Queue>,
95}
96
97/// Helper trait for input/output buffers.
98pub trait InputBuffer {
99    fn available(&self) -> usize;
100    fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno>;
101}
102
103pub trait OutputBuffer {
104    fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
105}
106
107/// Macro to help working with the terminal queues.
108macro_rules! with_queue {
109    ($self_:tt . $name:ident . $fn:ident ( $($param:expr),*$(,)?)) => {
110        {
111        let mut queue = $self_.$name . take().unwrap();
112        let result = queue.$fn( $($param),* );
113        $self_.$name = Some(queue);
114        result
115        }
116    };
117}
118
119/// Keep track of the signals to send when handling terminal content.
120#[must_use]
121pub struct PendingSignals {
122    signals: Vec<Signal>,
123}
124
125impl PendingSignals {
126    pub fn new() -> Self {
127        Self { signals: vec![] }
128    }
129
130    /// Add the given signal to the list of signal to send to the associate process group.
131    fn add(&mut self, signal: Signal) {
132        self.signals.push(signal);
133    }
134
135    /// Append all pending signals in `other` to `self`.
136    fn append(&mut self, mut other: Self) {
137        self.signals.append(&mut other.signals);
138    }
139
140    /// Returns a slice of the pending signals.
141    pub fn signals(&self) -> &[Signal] {
142        &self.signals[..]
143    }
144}
145
146/// Represents the type of erase operation that can be performed on terminal input.
147#[derive(Debug, PartialEq)]
148enum EraseType {
149    /// Erase a single character (typically triggered by backspace)
150    Character,
151    /// Erase a word (typically triggered by Ctrl+W)
152    Word,
153    /// Erase the entire line (typically triggered by Ctrl+U)
154    Line,
155}
156
157impl LineDiscipline {
158    /// Returns the terminal configuration.
159    pub fn termios(&self) -> &uapi::termios2 {
160        &self.termios
161    }
162
163    pub fn is_canon_enabled(&self) -> bool {
164        self.termios.has_local_flags(ICANON)
165    }
166
167    pub fn is_packet_mode_enabled(&self) -> bool {
168        self.packet_mode_enabled
169    }
170
171    pub fn set_packet_mode(&mut self, enabled: bool) {
172        self.packet_mode_enabled = enabled;
173        if !enabled {
174            self.packet_mode_pending_events = 0;
175        }
176    }
177
178    pub fn has_packet_mode_pending_events(&self) -> bool {
179        self.packet_mode_enabled && self.packet_mode_pending_events != 0
180    }
181
182    /// Returns the number of available bytes to read from the side of the terminal described by
183    /// `is_main`.
184    pub fn get_available_read_size(&self, is_main: bool) -> usize {
185        let queue = if is_main { self.output_queue() } else { self.input_queue() };
186        queue.readable_size()
187    }
188
189    /// Sets the terminal configuration.
190    pub fn set_termios(&mut self, termios: uapi::termios2) -> PendingSignals {
191        let old_canon_enabled = self.is_canon_enabled();
192        let old_ixon = self.termios.c_iflag & uapi::IXON != 0;
193        self.termios = termios;
194
195        if self.packet_mode_enabled {
196            let new_ixon = self.termios.c_iflag & uapi::IXON != 0;
197            if old_ixon != new_ixon {
198                let event = if new_ixon { uapi::TIOCPKT_DOSTOP } else { uapi::TIOCPKT_NOSTOP };
199                self.packet_mode_pending_events |= event as u8;
200            }
201        }
202
203        if old_canon_enabled && !self.is_canon_enabled() {
204            with_queue!(self.input_queue.on_canon_disabled(self))
205        } else {
206            PendingSignals::new()
207        }
208    }
209
210    /// Flushes queues according to `queue_selector` (TCIFLUSH, TCOFLUSH, TCIOFLUSH).
211    pub fn flush(&mut self, is_main: bool, queue_selector: u32) -> Result<(), Errno> {
212        // We can receive a flush request from either the main or the replica which switch what the
213        // input and output queues are referring to.
214        let (input_queue, output_queue) = if is_main {
215            (self.output_queue.as_mut().unwrap(), self.input_queue.as_mut().unwrap())
216        } else {
217            (self.input_queue.as_mut().unwrap(), self.output_queue.as_mut().unwrap())
218        };
219
220        let event;
221        // For input flushes, we discard all data in the pipeline. Data that's already been
222        // delivered to us, and data that's waiting to be delivered.
223        //
224        // For output flushes, we want to only discard data that has been sent, but not yet
225        // delivered to the other side's read_queue. Since this we're a pty and sending is just a
226        // memcpy rather than actually going across a wire, the only time this will happen when the
227        // read_queue fills up and there is backpressure due to full buffers.
228        match queue_selector {
229            uapi::TCIFLUSH => {
230                input_queue.flush();
231                event = uapi::TIOCPKT_FLUSHREAD;
232            }
233            uapi::TCOFLUSH => {
234                output_queue.flush_unprocessed();
235                event = uapi::TIOCPKT_FLUSHWRITE;
236            }
237            uapi::TCIOFLUSH => {
238                input_queue.flush();
239                output_queue.flush_unprocessed();
240                event = uapi::TIOCPKT_FLUSHREAD | uapi::TIOCPKT_FLUSHWRITE;
241            }
242            _ => return error!(EINVAL),
243        };
244
245        if !is_main && self.packet_mode_enabled {
246            self.packet_mode_pending_events |= event as u8;
247        }
248
249        Ok(())
250    }
251
252    /// `close` implementation of the main side of the terminal.
253    pub fn main_close(&mut self) {
254        self.main_references = self.main_references.map(|v| v - 1);
255    }
256
257    /// Called when a new reference to the main side of this terminal is made.
258    pub fn main_open(&mut self) {
259        self.main_references = Some(self.main_references.unwrap_or(0) + 1);
260    }
261
262    pub fn is_main_closed(&self) -> bool {
263        matches!(self.main_references, Some(0))
264    }
265
266    /// `query_events` implementation of the main side of the terminal.
267    pub fn main_query_events(&self) -> FdEvents {
268        if self.is_replica_closed() && self.output_queue().readable_size() == 0 {
269            return FdEvents::POLLOUT | FdEvents::POLLHUP;
270        }
271        let mut events =
272            self.output_queue().read_readiness() | self.input_queue().write_readiness();
273        if self.packet_mode_enabled && self.packet_mode_pending_events != 0 {
274            events |= FdEvents::POLLIN | FdEvents::POLLPRI;
275        }
276        events
277    }
278
279    /// `read` implementation of the main side of the terminal.
280    pub fn main_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
281        if self.is_replica_closed() && self.output_queue().readable_size() == 0 {
282            return error!(EIO);
283        }
284        if self.packet_mode_enabled {
285            if self.packet_mode_pending_events != 0 {
286                let event = self.packet_mode_pending_events;
287                self.packet_mode_pending_events = 0;
288                return data.write(&[event]);
289            }
290            if self.output_queue().readable_size() == 0 {
291                return error!(EAGAIN);
292            }
293            let written = data.write(&[0])?;
294            if written == 0 {
295                return Ok(0);
296            }
297            let res = with_queue!(self.output_queue.read(self, data));
298            match res {
299                Ok(n) => return Ok(n + 1),
300                Err(e) if e == errno!(EAGAIN) => return Ok(1),
301                Err(e) => return Err(e),
302            }
303        }
304        with_queue!(self.output_queue.read(self, data))
305    }
306
307    /// `write` implementation of the main side of the terminal.
308    pub fn main_write(
309        &mut self,
310        data: &mut dyn InputBuffer,
311    ) -> Result<(usize, PendingSignals), Errno> {
312        with_queue!(self.input_queue.write(self, data))
313    }
314
315    /// `close` implementation of the replica side of the terminal.
316    pub fn replica_close(&mut self) {
317        self.replica_references = self.replica_references.map(|v| v - 1);
318    }
319
320    /// Called when a new reference to the replica side of this terminal is made.
321    pub fn replica_open(&mut self) {
322        self.replica_references = Some(self.replica_references.unwrap_or(0) + 1);
323    }
324
325    pub fn is_replica_closed(&self) -> bool {
326        matches!(self.replica_references, Some(0))
327    }
328
329    /// `query_events` implementation of the replica side of the terminal.
330    pub fn replica_query_events(&self) -> FdEvents {
331        if self.is_main_closed() {
332            return FdEvents::POLLIN | FdEvents::POLLOUT | FdEvents::POLLERR | FdEvents::POLLHUP;
333        }
334        self.input_queue().read_readiness() | self.output_queue().write_readiness()
335    }
336
337    /// `read` implementation of the replica side of the terminal.
338    pub fn replica_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
339        if self.is_main_closed() {
340            return Ok(0);
341        }
342        with_queue!(self.input_queue.read(self, data))
343    }
344
345    /// `write` implementation of the replica side of the terminal.
346    pub fn replica_write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
347        if self.is_main_closed() {
348            return error!(EIO);
349        }
350        if self.stopped && self.termios.has_input_flags(IXON) {
351            return error!(EAGAIN);
352        }
353        let (read_from_userspace, signals) = with_queue!(self.output_queue.write(self, data))?;
354        // Writing to the replica side never generates signals.
355        assert!(signals.signals().is_empty());
356        Ok(read_from_userspace)
357    }
358
359    /// Returns the input queue.
360    fn input_queue(&self) -> &Queue {
361        self.input_queue.as_ref().unwrap()
362    }
363
364    /// Returns the output_queue. The Option is always filled.
365    fn output_queue(&self) -> &Queue {
366        self.output_queue.as_ref().unwrap()
367    }
368
369    /// Return whether a signal must be send when receiving `byte`, and if yes, which.
370    fn handle_signals(&mut self, byte: RawByte) -> Option<Signal> {
371        if !self.termios.has_local_flags(ISIG) {
372            return None;
373        }
374        self.termios.signal(byte)
375    }
376
377    fn extend_echo_bytes(&self, target: &mut Vec<RawByte>, byte: RawByte) {
378        if self.termios.has_local_flags(ECHOCTL) {
379            if let Some(control_character_echo) = generate_control_character_echo(byte) {
380                target.extend(control_character_echo);
381                return;
382            }
383        }
384        target.push(byte);
385    }
386
387    fn transform(
388        &mut self,
389        is_input: bool,
390        queue: &mut Queue,
391        buffer: &[RawByte],
392    ) -> (usize, PendingSignals) {
393        if is_input {
394            self.transform_input(queue, buffer)
395        } else {
396            (self.transform_output(queue, buffer), PendingSignals::new())
397        }
398    }
399
400    fn transform_output(&mut self, queue: &mut Queue, original_buffer: &[RawByte]) -> usize {
401        let mut buffer = original_buffer;
402
403        // transform_output is effectively always in noncanonical mode, as the
404        // main termios never has ICANON set.
405
406        if !self.termios.has_output_flags(OPOST) {
407            let limit = CANON_MAX_BYTES.saturating_sub(queue.readable_size());
408            if limit == 0 {
409                return 0;
410            }
411            let to_write = std::cmp::min(limit, buffer.len());
412            queue.read_queue.push_back(buffer[..to_write].to_vec());
413            return to_write;
414        }
415
416        let mut return_value = 0;
417        while !buffer.is_empty()
418            && queue.readable_size() + queue.line_buffer.len() < CANON_MAX_BYTES
419        {
420            let size = compute_next_character_size(buffer, &self.termios);
421            let mut character_bytes = buffer[..size].to_vec();
422            return_value += size;
423            buffer = &buffer[size..];
424
425            if self.termios.has_output_flags(OLCUC) {
426                character_bytes[0].make_ascii_uppercase();
427            }
428            match character_bytes[0] {
429                b'\n' => {
430                    if self.termios.has_output_flags(ONLRET) {
431                        self.column = 0;
432                    }
433                    if self.termios.has_output_flags(ONLCR) {
434                        queue.line_buffer.extend_from_slice(&[b'\r', b'\n']);
435                        continue;
436                    }
437                }
438                b'\r' => {
439                    if self.termios.has_output_flags(ONOCR) && self.column == 0 {
440                        continue;
441                    }
442                    if self.termios.has_output_flags(OCRNL) {
443                        character_bytes[0] = b'\n';
444                        if self.termios.has_output_flags(ONLRET) {
445                            self.column = 0;
446                        }
447                    } else {
448                        self.column = 0;
449                    }
450                }
451                b'\t' => {
452                    let spaces = SPACES_PER_TAB - self.column % SPACES_PER_TAB;
453                    if self.termios.c_oflag & TABDLY == XTABS {
454                        self.column += spaces;
455                        queue.line_buffer.extend(std::iter::repeat(b' ').take(spaces));
456                        continue;
457                    }
458                    self.column += spaces;
459                }
460                BACKSPACE_CHAR => {
461                    if self.column > 0 {
462                        self.column -= 1;
463                    }
464                }
465                _ => {
466                    self.column += 1;
467                }
468            }
469            queue.line_buffer.append(&mut character_bytes);
470        }
471        if !queue.line_buffer.is_empty() {
472            queue.flush_line_buffer();
473        }
474        return_value
475    }
476
477    fn transform_input(
478        &mut self,
479        queue: &mut Queue,
480        original_buffer: &[RawByte],
481    ) -> (usize, PendingSignals) {
482        let mut buffer = original_buffer;
483
484        let max_bytes = if self.termios.has_local_flags(ICANON) {
485            CANON_MAX_BYTES
486        } else {
487            NON_CANON_MAX_BYTES
488        };
489
490        let mut return_value = 0;
491        let mut signals = PendingSignals::new();
492        while !buffer.is_empty()
493            && queue.readable_size() + queue.line_buffer.len() < CANON_MAX_BYTES
494        {
495            let size = compute_next_character_size(buffer, &self.termios);
496            let mut character_bytes = buffer[..size].to_vec();
497            // It is guaranteed that character_bytes has at least one element.
498
499            if self.lnext {
500                self.lnext = false;
501                if self.termios.has_local_flags(ECHO) {
502                    let mut echo_bytes = vec![];
503                    self.extend_echo_bytes(&mut echo_bytes, character_bytes[0]);
504                    signals.append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
505                }
506
507                queue.line_buffer.extend_from_slice(&character_bytes);
508                buffer = &buffer[size..];
509                return_value += size;
510                continue;
511            }
512
513            if self.termios.has_local_flags(IEXTEN) {
514                // VLNEXT
515                if character_bytes[0] == self.termios.c_cc[VLNEXT as usize]
516                    && self.termios.c_cc[VLNEXT as usize] != DISABLED_CHAR
517                {
518                    self.lnext = true;
519                    if self.termios.has_local_flags(ECHO) && self.termios.has_local_flags(ECHOCTL) {
520                        let echo_bytes = vec![b'^', BACKSPACE_CHAR];
521                        signals
522                            .append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
523                    }
524                    buffer = &buffer[size..];
525                    return_value += size;
526                    continue;
527                }
528                // VREPRINT
529                if character_bytes[0] == self.termios.c_cc[VREPRINT as usize]
530                    && self.termios.c_cc[VREPRINT as usize] != DISABLED_CHAR
531                {
532                    if self.termios.has_local_flags(ECHO) {
533                        let mut echo_bytes = vec![];
534                        self.extend_echo_bytes(&mut echo_bytes, character_bytes[0]);
535                        echo_bytes.push(b'\n');
536                        for byte in &queue.line_buffer {
537                            self.extend_echo_bytes(&mut echo_bytes, *byte);
538                        }
539                        signals
540                            .append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
541                    }
542                    buffer = &buffer[size..];
543                    return_value += size;
544                    continue;
545                }
546            }
547
548            if self.termios.has_input_flags(IUCLC) && self.termios.has_local_flags(IEXTEN) {
549                character_bytes[0].make_ascii_lowercase();
550            }
551
552            let mut signal_generated = false;
553            if let Some(signal) = self.handle_signals(character_bytes[0]) {
554                signals.add(signal);
555                signal_generated = true;
556                if !self.termios.has_local_flags(NOFLSH) {
557                    queue.flush();
558                    if let Some(ref mut output_queue) = self.output_queue {
559                        output_queue.flush();
560                    }
561                }
562            }
563
564            // Handle IXON/IXOFF (software flow control)
565            if self.termios.has_input_flags(IXON) {
566                if character_bytes[0] == self.termios.c_cc[VSTOP as usize] {
567                    self.stopped = true;
568                    buffer = &buffer[size..];
569                    return_value += size;
570                    continue;
571                }
572                // POSIX says:
573                // "If IXON is set, start/stop output control is enabled. A received STOP character
574                // suspends output and a received START character restarts output. The STOP and
575                // START characters are not read, but performing the flow control functions."
576                //
577                // "If IXANY is set, any input character restarts output that has been suspended."
578                if self.stopped
579                    && (character_bytes[0] == self.termios.c_cc[VSTART as usize]
580                        || self.termios.has_input_flags(IXANY))
581                {
582                    self.stopped = false;
583                    // If it was START, we consume it. If it was IXANY (and not START), we usually
584                    // process it?
585                    // "The START character is not read".
586                    // If IXANY is set and char != START, we should restart AND process the char.
587                    if character_bytes[0] == self.termios.c_cc[VSTART as usize] {
588                        buffer = &buffer[size..];
589                        return_value += size;
590                        continue;
591                    }
592                }
593            }
594
595            match character_bytes[0] {
596                b'\r' => {
597                    if self.termios.has_input_flags(IGNCR) {
598                        buffer = &buffer[size..];
599                        return_value += size;
600                        continue;
601                    }
602                    if self.termios.has_input_flags(ICRNL) {
603                        character_bytes[0] = b'\n';
604                    }
605                }
606                b'\n' => {
607                    if self.termios.has_input_flags(INLCR) {
608                        character_bytes[0] = b'\r'
609                    }
610                }
611                _ => {}
612            }
613            // In canonical mode, we discard non-terminating characters
614            // after the first 4095.
615            if self.termios.has_local_flags(ICANON)
616                && queue.line_buffer.len() + size >= max_bytes
617                && !self.termios.is_terminating(&character_bytes)
618            {
619                buffer = &buffer[size..];
620                return_value += size;
621                continue;
622            }
623
624            if queue.line_buffer.len() + size > max_bytes {
625                break;
626            }
627
628            buffer = &buffer[size..];
629            return_value += size;
630
631            let first_byte = character_bytes[0];
632
633            // If we get EOF, push whatever we have line_buffer to read_queue, then push an empty datagram.
634            if self.termios.has_local_flags(ICANON) && self.termios.is_eof(first_byte) {
635                if !queue.line_buffer.is_empty() {
636                    queue.flush_line_buffer();
637                }
638                queue.read_queue.push_back(vec![]);
639                break;
640            }
641
642            let mut maybe_erase_span = None;
643            let mut erase_type = None;
644            if self.termios.has_local_flags(ICANON) {
645                if self.termios.is_erase(first_byte) {
646                    maybe_erase_span =
647                        Some(compute_last_character_span(&queue.line_buffer[..], &self.termios));
648                    erase_type = Some(EraseType::Character);
649                } else if self.termios.is_werase(first_byte) {
650                    maybe_erase_span =
651                        Some(compute_last_word_span(&queue.line_buffer[..], &self.termios));
652                    erase_type = Some(EraseType::Word);
653                }
654                if self.termios.is_kill(first_byte) {
655                    maybe_erase_span =
656                        Some(compute_last_line_span(&queue.line_buffer[..], &self.termios));
657                    erase_type = Some(EraseType::Line);
658                }
659            }
660
661            let mut erased_bytes = Option::None;
662            if let Some(erase_span) = maybe_erase_span {
663                if erase_span.bytes == 0 {
664                    continue;
665                }
666                if self.termios.has_local_flags(ECHOPRT) {
667                    erased_bytes = Some(
668                        queue.line_buffer[queue.line_buffer.len() - erase_span.bytes..].to_vec(),
669                    );
670                }
671                queue.line_buffer.truncate(queue.line_buffer.len() - erase_span.bytes);
672            } else if !signal_generated {
673                queue.line_buffer.extend_from_slice(&character_bytes);
674            }
675
676            // Anything written to the read buffer will have to be echoed.
677            let mut echo_bytes = vec![];
678            if self.termios.has_local_flags(ECHO) {
679                if let Some(erase_span) = maybe_erase_span {
680                    match erase_type {
681                        Some(EraseType::Character) | Some(EraseType::Word) => {
682                            if self.termios.has_local_flags(ECHOPRT) {
683                                if let Some(bytes) = erased_bytes {
684                                    if !self.erasing {
685                                        echo_bytes.push(b'\\');
686                                        self.erasing = true;
687                                    }
688                                    for byte in bytes.iter().rev() {
689                                        self.extend_echo_bytes(&mut echo_bytes, *byte);
690                                    }
691                                }
692                            } else if self.termios.has_local_flags(ECHOE) {
693                                echo_bytes = generate_erase_echo(&erase_span);
694                            }
695                        }
696                        Some(EraseType::Line) => {
697                            if self.termios.has_local_flags(ECHOKE) {
698                                echo_bytes = generate_erase_echo(&erase_span);
699                            } else if self.termios.has_local_flags(ECHOK) {
700                                self.extend_echo_bytes(&mut echo_bytes, first_byte);
701                                echo_bytes.push(b'\n');
702                            }
703                        }
704                        None => {
705                            unreachable!("Erase type should be Some when maybe_erase_span is Some")
706                        }
707                    }
708                    if self.erasing && queue.line_buffer.is_empty() {
709                        echo_bytes.push(b'/');
710                        self.erasing = false;
711                    }
712                } else {
713                    if self.erasing && first_byte != b'\n' {
714                        echo_bytes.push(b'/');
715                        self.erasing = false;
716                    }
717                }
718
719                let needs_normal_echo =
720                    if maybe_erase_span.is_some() { echo_bytes.is_empty() } else { true };
721
722                if needs_normal_echo {
723                    let mut char_echo = vec![];
724                    if self.termios.has_local_flags(ECHOCTL) {
725                        if let Some(control_character_echo) =
726                            generate_control_character_echo(first_byte)
727                        {
728                            char_echo = control_character_echo;
729                        }
730                    }
731                    if char_echo.is_empty() {
732                        char_echo = character_bytes.clone();
733                    }
734                    echo_bytes.extend(char_echo);
735                }
736            } else if self.termios.has_local_flags(ECHONL) && first_byte == b'\n' {
737                echo_bytes.extend_from_slice(&character_bytes);
738            }
739
740            if !echo_bytes.is_empty() {
741                signals.append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
742            }
743
744            // If we finish a line, make it available for reading.
745            if self.termios.has_local_flags(ICANON) && self.termios.is_terminating(&character_bytes)
746            {
747                queue.flush_line_buffer();
748            }
749        }
750        // In noncanonical mode, everything is readable.
751        if !self.termios.has_local_flags(ICANON) && !queue.line_buffer.is_empty() {
752            queue.flush_line_buffer();
753        }
754
755        (return_value, signals)
756    }
757}
758
759/// Alias used to mark bytes in the queues that have not yet been processed and pushed into the
760/// read buffer. See `Queue`.
761type RawByte = u8;
762
763#[derive(Debug, Default)]
764struct Queue {
765    /// The queue of data ready to be read. Each element is a "datagram" (line or chunk).
766    /// Empty byte vectors represent EOF markers (read returns 0).
767    read_queue: VecDeque<Vec<u8>>,
768
769    /// The incomplete line/chunk being processed but not yet ready for the read_queue.
770    /// In Canonical mode, this holds the current line being edited.
771    /// In Non-Canonical mode, this holds data until it is pushed to the read_queue.
772    line_buffer: Vec<u8>,
773
774    /// Data that can't fit into readBuf. It is put here until it can be loaded into the read
775    /// buffer. Contains data that hasn't been processed.
776    wait_buffers: VecDeque<Vec<RawByte>>,
777
778    /// The length of the data in `wait_buffers`.
779    total_wait_buffer_length: usize,
780
781    /// Whether this queue in the input queue. Needed to know how to transform received data.
782    is_input: bool,
783}
784
785impl Queue {
786    fn output_queue() -> Option<Self> {
787        Some(Queue { is_input: false, ..Default::default() })
788    }
789
790    fn input_queue() -> Option<Self> {
791        Some(Queue { is_input: true, ..Default::default() })
792    }
793
794    /// Returns whether the queue is ready to be written to.
795    fn write_readiness(&self) -> FdEvents {
796        if self.total_wait_buffer_length < WAIT_BUFFER_MAX_BYTES {
797            FdEvents::POLLOUT
798        } else {
799            FdEvents::empty()
800        }
801    }
802
803    /// Returns whether the queue is ready to be read from.
804    fn read_readiness(&self) -> FdEvents {
805        // If there's an empty "datagram" in read_queue, it means EOF, which is "readable" (returns 0).
806        if !self.read_queue.is_empty() { FdEvents::POLLIN } else { FdEvents::empty() }
807    }
808
809    /// Returns the number of bytes ready to be read.
810    fn readable_size(&self) -> usize {
811        // We sum up everything in the read_queue.
812        // NOTE: This might over-report if we only return one datagram at a time, but for poll/FIONREAD it's generally answering "how much is there".
813        self.read_queue.iter().map(|v| v.len()).sum()
814    }
815
816    /// Read from the queue into `data`. Returns the number of bytes copied.
817    fn read(
818        &mut self,
819        terminal: &mut LineDiscipline,
820        data: &mut dyn OutputBuffer,
821    ) -> Result<usize, Errno> {
822        if self.read_queue.is_empty() {
823            return error!(EAGAIN);
824        }
825
826        let mut total_written = 0;
827        while let Some(mut packet) = self.read_queue.pop_front() {
828            if packet.is_empty() {
829                if total_written > 0 {
830                    // We've already read some data. We need to complete the read with that data and
831                    // leave the empty datagram in the queue to signal EOF on the next read.
832                    self.read_queue.push_front(packet);
833                }
834                break;
835            }
836
837            match data.write(&packet) {
838                Ok(written) => {
839                    total_written += written;
840                    if written < packet.len() {
841                        // Put back the unread part.
842                        let remaining = packet.split_off(written);
843                        self.read_queue.push_front(remaining);
844                        // Destination full.
845                        break;
846                    }
847
848                    // If we are in canonical input mode, we stop after one packet (one line).
849                    if self.is_input && terminal.termios.has_local_flags(ICANON) {
850                        break;
851                    }
852                }
853                Err(e) => {
854                    // If write failed, push back the whole packet.
855                    self.read_queue.push_front(packet);
856                    if total_written > 0 {
857                        // If we managed to write something before error, return success.
858                        return Ok(total_written);
859                    }
860                    return Err(e);
861                }
862            }
863        }
864
865        let signals = self.drain_waiting_buffer(terminal);
866        assert!(signals.signals().is_empty());
867        Ok(total_written)
868    }
869
870    /// Writes to the queue from `data`. Returns the number of bytes copied.
871    fn write(
872        &mut self,
873        terminal: &mut LineDiscipline,
874        data: &mut dyn InputBuffer,
875    ) -> Result<(usize, PendingSignals), Errno> {
876        let room = WAIT_BUFFER_MAX_BYTES - self.total_wait_buffer_length;
877        let data_length = data.available();
878        if room == 0 && data_length > 0 {
879            return error!(EAGAIN);
880        }
881        let buffer = data.read_to_vec_exact(std::cmp::min(room, data_length))?;
882        let read_from_userspace = buffer.len();
883        let signals = self.push_to_waiting_buffer(terminal, buffer);
884        Ok((read_from_userspace, signals))
885    }
886
887    /// Writes the given `buffer` to the queue.
888    fn write_bytes(&mut self, terminal: &mut LineDiscipline, buffer: &[RawByte]) -> PendingSignals {
889        self.push_to_waiting_buffer(terminal, buffer.to_vec())
890    }
891
892    /// Pushes the given buffer into the wait_buffers, and process the wait_buffers.
893    fn push_to_waiting_buffer(
894        &mut self,
895        terminal: &mut LineDiscipline,
896        buffer: Vec<RawByte>,
897    ) -> PendingSignals {
898        self.total_wait_buffer_length += buffer.len();
899        self.wait_buffers.push_back(buffer);
900        self.drain_waiting_buffer(terminal)
901    }
902
903    /// Processes the wait_buffers, filling the read buffer.
904    fn drain_waiting_buffer(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
905        let mut signals_to_return = PendingSignals::new();
906        while let Some(wait_buffer) = self.wait_buffers.pop_front() {
907            self.total_wait_buffer_length -= wait_buffer.len();
908            let (count, signals) = terminal.transform(self.is_input, self, &wait_buffer);
909            signals_to_return.append(signals);
910            if count != wait_buffer.len() {
911                let remaining = wait_buffer[count..].to_vec();
912                self.total_wait_buffer_length += remaining.len();
913                self.wait_buffers.push_front(remaining);
914                break;
915            }
916        }
917        signals_to_return
918    }
919
920    /// Flushed the line buffer to the read queue.
921    fn flush_line_buffer(&mut self) {
922        self.read_queue.push_back(std::mem::take(&mut self.line_buffer));
923    }
924
925    /// Flush the content of the queue.
926    fn flush(&mut self) {
927        self.read_queue.clear();
928        self.line_buffer.clear();
929        self.wait_buffers.clear();
930        self.total_wait_buffer_length = 0;
931    }
932
933    /// Flush only the part of the queue which has not yet been processed.
934    fn flush_unprocessed(&mut self) {
935        self.wait_buffers.clear();
936        self.total_wait_buffer_length = 0;
937    }
938
939    /// Called when the queue is moved from canonical mode, to non canonical mode.
940    fn on_canon_disabled(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
941        let signals = self.drain_waiting_buffer(terminal);
942        if !self.line_buffer.is_empty() {
943            self.flush_line_buffer();
944        }
945        signals
946    }
947}
948
949// Helper functions (copied from terminal.rs)
950// Returns the ASCII representation of the given char. This will assert if the character is not
951// ascii.
952fn get_ascii(c: char) -> u8 {
953    let mut dest: [u8; 1] = [0];
954    c.encode_utf8(&mut dest);
955    dest[0]
956}
957
958// Returns the control character associated with the given letter.
959fn get_control_character(c: char) -> cc_t {
960    get_ascii(c) - get_ascii('A') + 1
961}
962
963// Returns the default control characters of a terminal.
964fn get_default_control_characters() -> [cc_t; 19usize] {
965    [
966        get_control_character('C'),  // VINTR = ^C
967        get_control_character('\\'), // VQUIT = ^\
968        get_ascii('\x7f'),           // VERASE = DEL
969        get_control_character('U'),  // VKILL = ^U
970        get_control_character('D'),  // VEOF = ^D
971        0,                           // VTIME
972        1,                           // VMIN
973        0,                           // VSWTC
974        get_control_character('Q'),  // VSTART = ^Q
975        get_control_character('S'),  // VSTOP = ^S
976        get_control_character('Z'),  // VSUSP = ^Z
977        0,                           // VEOL
978        get_control_character('R'),  // VREPRINT = ^R
979        get_control_character('O'),  // VDISCARD = ^O
980        get_control_character('W'),  // VWERASE = ^W
981        get_control_character('V'),  // VLNEXT = ^V
982        0,                           // VEOL2
983        0,                           // Remaining data in the array,
984        0,                           // Remaining data in the array,
985    ]
986}
987
988const DEFAULT_SPEED: u32 = 38400;
989
990// Returns the default replica terminal configuration.
991pub fn get_default_termios() -> uapi::termios2 {
992    uapi::termios2 {
993        c_iflag: uapi::ICRNL | uapi::IXON,
994        c_oflag: uapi::OPOST | uapi::ONLCR,
995        c_cflag: uapi::B38400 | uapi::CS8 | uapi::CREAD,
996        c_lflag: uapi::ISIG
997            | uapi::ICANON
998            | uapi::ECHO
999            | uapi::ECHOE
1000            | uapi::ECHOK
1001            | uapi::ECHOCTL
1002            | uapi::ECHOKE
1003            | uapi::IEXTEN,
1004        c_line: 0,
1005        c_cc: get_default_control_characters(),
1006        c_ispeed: DEFAULT_SPEED,
1007        c_ospeed: DEFAULT_SPEED,
1008    }
1009}
1010
1011/// Helper trait for termios to help parse the configuration.
1012trait TermIOS {
1013    fn has_input_flags(&self, flags: tcflag_t) -> bool;
1014    fn has_output_flags(&self, flags: tcflag_t) -> bool;
1015    fn has_local_flags(&self, flags: tcflag_t) -> bool;
1016    fn is_eof(&self, c: RawByte) -> bool;
1017    fn is_erase(&self, c: RawByte) -> bool;
1018    fn is_werase(&self, c: RawByte) -> bool;
1019    fn is_kill(&self, c: RawByte) -> bool;
1020    fn is_terminating(&self, character_bytes: &[RawByte]) -> bool;
1021    fn signal(&self, c: RawByte) -> Option<Signal>;
1022}
1023
1024impl TermIOS for uapi::termios2 {
1025    fn has_input_flags(&self, flags: tcflag_t) -> bool {
1026        self.c_iflag & flags == flags
1027    }
1028    fn has_output_flags(&self, flags: tcflag_t) -> bool {
1029        self.c_oflag & flags == flags
1030    }
1031    fn has_local_flags(&self, flags: tcflag_t) -> bool {
1032        self.c_lflag & flags == flags
1033    }
1034    fn is_eof(&self, c: RawByte) -> bool {
1035        c == self.c_cc[VEOF as usize] && self.c_cc[VEOF as usize] != DISABLED_CHAR
1036    }
1037    fn is_erase(&self, c: RawByte) -> bool {
1038        c == self.c_cc[VERASE as usize] && self.c_cc[VERASE as usize] != DISABLED_CHAR
1039    }
1040    fn is_werase(&self, c: RawByte) -> bool {
1041        c == self.c_cc[VWERASE as usize]
1042            && self.c_cc[VWERASE as usize] != DISABLED_CHAR
1043            && self.has_local_flags(IEXTEN)
1044    }
1045    fn is_kill(&self, c: RawByte) -> bool {
1046        c == self.c_cc[VKILL as usize] && self.c_cc[VKILL as usize] != DISABLED_CHAR
1047    }
1048    fn is_terminating(&self, character_bytes: &[RawByte]) -> bool {
1049        // All terminating characters are 1 byte.
1050        if character_bytes.len() != 1 {
1051            return false;
1052        }
1053        let c = character_bytes[0];
1054
1055        // Is this the user-set EOF character?
1056        if self.is_eof(c) {
1057            return true;
1058        }
1059
1060        if c == DISABLED_CHAR {
1061            return false;
1062        }
1063        if c == b'\n' || c == self.c_cc[VEOL as usize] {
1064            return true;
1065        }
1066        if c == self.c_cc[VEOL2 as usize] {
1067            return self.has_local_flags(IEXTEN);
1068        }
1069        false
1070    }
1071    fn signal(&self, c: RawByte) -> Option<Signal> {
1072        if c == DISABLED_CHAR {
1073            return None;
1074        }
1075        if c == self.c_cc[VINTR as usize] {
1076            return Some(SIGINT);
1077        }
1078        if c == self.c_cc[VQUIT as usize] {
1079            return Some(SIGQUIT);
1080        }
1081        if c == self.c_cc[VSUSP as usize] {
1082            return Some(SIGSTOP);
1083        }
1084        None
1085    }
1086}
1087
1088fn compute_next_character_size(buffer: &[RawByte], termios: &uapi::termios2) -> usize {
1089    if !termios.has_input_flags(IUTF8) {
1090        return 1;
1091    }
1092
1093    #[derive(Default)]
1094    struct Receiver {
1095        done: Option<bool>,
1096    }
1097
1098    impl utf8parse::Receiver for Receiver {
1099        fn codepoint(&mut self, _c: char) {
1100            self.done = Some(true);
1101        }
1102        fn invalid_sequence(&mut self) {
1103            self.done = Some(false);
1104        }
1105    }
1106
1107    let mut byte_count = 0;
1108    let mut receiver = Receiver::default();
1109    let mut parser = utf8parse::Parser::new();
1110    while receiver.done.is_none() && byte_count < buffer.len() {
1111        parser.advance(&mut receiver, buffer[byte_count]);
1112        byte_count += 1;
1113    }
1114    if receiver.done == Some(true) { byte_count } else { 1 }
1115}
1116
1117fn is_ascii(c: RawByte) -> bool {
1118    c & 0x80 == 0
1119}
1120
1121fn is_utf8_start(c: RawByte) -> bool {
1122    c & 0xC0 == 0xC0
1123}
1124
1125fn generate_erase_echo(erase_span: &BufferSpan) -> Vec<RawByte> {
1126    let erase_echo = [BACKSPACE_CHAR, b' ', BACKSPACE_CHAR];
1127    erase_echo.iter().cycle().take(erase_echo.len() * erase_span.characters).map(|c| *c).collect()
1128}
1129
1130fn generate_control_character_echo(c: RawByte) -> Option<Vec<RawByte>> {
1131    if matches!(c, 0..=0x8 | 0xB..=0xC | 0xE..=0x1F) {
1132        Some(vec![b'^', c + CONTROL_OFFSET])
1133    } else {
1134        None
1135    }
1136}
1137
1138#[derive(Default, Debug, Clone, Copy)]
1139struct BufferSpan {
1140    bytes: usize,
1141    characters: usize,
1142}
1143
1144impl std::ops::AddAssign<Self> for BufferSpan {
1145    fn add_assign(&mut self, rhs: Self) {
1146        self.bytes += rhs.bytes;
1147        self.characters += rhs.characters;
1148    }
1149}
1150
1151fn compute_last_character_span(buffer: &[RawByte], termios: &uapi::termios2) -> BufferSpan {
1152    if buffer.is_empty() {
1153        return BufferSpan::default();
1154    }
1155    if termios.has_input_flags(IUTF8) {
1156        let mut bytes = 0;
1157        for c in buffer.iter().rev() {
1158            bytes += 1;
1159            if is_ascii(*c) || is_utf8_start(*c) {
1160                return BufferSpan { bytes, characters: 1 };
1161            }
1162        }
1163        BufferSpan::default()
1164    } else {
1165        BufferSpan { bytes: 1, characters: 1 }
1166    }
1167}
1168
1169fn compute_last_word_span(buffer: &[RawByte], termios: &uapi::termios2) -> BufferSpan {
1170    fn is_whitespace(c: RawByte) -> bool {
1171        c == b' ' || c == b'\t'
1172    }
1173
1174    let mut in_word = false;
1175    let mut word_span = BufferSpan::default();
1176    let mut remaining = buffer.len();
1177    loop {
1178        let span = compute_last_character_span(&buffer[..remaining], termios);
1179        if span.bytes == 0 {
1180            break;
1181        }
1182        if span.bytes == 1 {
1183            let c = buffer[remaining - 1];
1184            if in_word {
1185                if is_whitespace(c) {
1186                    break;
1187                }
1188            } else {
1189                if !is_whitespace(c) {
1190                    in_word = true;
1191                }
1192            }
1193        }
1194        remaining -= span.bytes;
1195        word_span += span;
1196    }
1197
1198    word_span
1199}
1200
1201fn compute_last_line_span(buffer: &[RawByte], termios: &uapi::termios2) -> BufferSpan {
1202    let mut line_span = BufferSpan::default();
1203    let mut remaining = buffer.len();
1204
1205    loop {
1206        let span = compute_last_character_span(&buffer[..remaining], termios);
1207        if span.bytes == 0 {
1208            break;
1209        }
1210        if span.bytes == 1 {
1211            let c = buffer[remaining - 1];
1212            if c == b'\n' {
1213                break;
1214            }
1215        }
1216        remaining -= span.bytes;
1217        line_span += span;
1218    }
1219
1220    line_span
1221}
1222
1223#[cfg(test)]
1224mod tests {
1225    use super::*;
1226
1227    #[::fuchsia::test]
1228    fn test_ascii_conversion() {
1229        assert_eq!(get_ascii(' '), 32);
1230    }
1231
1232    #[::fuchsia::test]
1233    fn test_control_character() {
1234        assert_eq!(get_control_character('C'), 3);
1235    }
1236
1237    #[::fuchsia::test]
1238    fn test_compute_next_character_size_non_utf8() {
1239        let termios = get_default_termios();
1240        for i in 0..=255 {
1241            let array: &[u8] = &[i, 0xa9, 0];
1242            assert_eq!(compute_next_character_size(array, &termios), 1);
1243        }
1244    }
1245
1246    #[::fuchsia::test]
1247    fn test_compute_next_character_size_utf8() {
1248        let mut termios = get_default_termios();
1249        termios.c_iflag |= IUTF8;
1250        for i in 0..128 {
1251            let array: &[RawByte] = &[i, 0xa9, 0];
1252            assert_eq!(compute_next_character_size(array, &termios), 1);
1253        }
1254        let array: &[RawByte] = &[0xc2, 0xa9, 0];
1255        assert_eq!(compute_next_character_size(array, &termios), 2);
1256        let array: &[RawByte] = &[0xc2, 255, 0];
1257        assert_eq!(compute_next_character_size(array, &termios), 1);
1258    }
1259
1260    #[::fuchsia::test]
1261    fn test_signal_handling_with_disabled_chars() {
1262        let mut termios = get_default_termios();
1263        termios.c_cc[VINTR as usize] = DISABLED_CHAR;
1264        termios.c_cc[VQUIT as usize] = DISABLED_CHAR;
1265        termios.c_cc[VSUSP as usize] = DISABLED_CHAR;
1266
1267        assert_eq!(termios.signal(0), None);
1268        assert_eq!(termios.signal(3), None); // Normally ^C (SIGINT)
1269        assert_eq!(termios.signal(28), None); // Normally ^\ (SIGQUIT)
1270        assert_eq!(termios.signal(26), None); // Normally ^Z (SIGSTOP)
1271    }
1272
1273    struct TestBuffer {
1274        data: Vec<u8>,
1275    }
1276
1277    impl InputBuffer for TestBuffer {
1278        fn available(&self) -> usize {
1279            self.data.len()
1280        }
1281        fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
1282            if size > self.data.len() {
1283                return error!(EAGAIN);
1284            }
1285            Ok(self.data.drain(0..size).collect())
1286        }
1287    }
1288
1289    impl OutputBuffer for TestBuffer {
1290        fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
1291            self.data.extend_from_slice(data);
1292            Ok(data.len())
1293        }
1294    }
1295
1296    #[::fuchsia::test]
1297    fn test_flush() {
1298        fn make_ld() -> LineDiscipline {
1299            let mut ld = LineDiscipline::default();
1300            ld.main_open();
1301            ld.replica_open();
1302
1303            let mut termios = get_default_termios();
1304            termios.c_lflag &= !ECHO;
1305            termios.c_oflag &= !OPOST;
1306            let _ = ld.set_termios(termios);
1307
1308            // Write some data from main to the replica.
1309            // This goes to input_queue.
1310            let mut input = TestBuffer { data: b"ping\n".to_vec() };
1311            let (written, signals) = ld.main_write(&mut input).unwrap();
1312            assert_eq!(written, 5);
1313            assert!(signals.signals().is_empty());
1314
1315            // Write some data from replica to main.
1316            // This goes to output_queue.
1317            let mut output = TestBuffer { data: b"pong\n".to_vec() };
1318            let written = ld.replica_write(&mut output).unwrap();
1319            assert_eq!(written, 5);
1320            ld
1321        }
1322
1323        let mut read_buf = TestBuffer { data: vec![] };
1324
1325        // A TCIFLUSH from the main side should flush only the main's input (output_queue)
1326        let mut ld = make_ld();
1327        ld.flush(true, uapi::TCIFLUSH).unwrap();
1328        read_buf.data.clear();
1329        assert_eq!(error!(EAGAIN), ld.main_read(&mut read_buf));
1330        assert!(read_buf.data.is_empty());
1331        read_buf.data.clear();
1332        assert!(ld.replica_read(&mut read_buf).is_ok());
1333        assert_eq!(read_buf.data, b"ping\n");
1334
1335        // A TCIFLUSH from the replica side should flush only the replica's input (input_queue)
1336        let mut ld = make_ld();
1337        ld.flush(false, uapi::TCIFLUSH).unwrap();
1338        read_buf.data.clear();
1339        assert!(ld.main_read(&mut read_buf).is_ok());
1340        assert_eq!(read_buf.data, b"pong\n");
1341        read_buf.data.clear();
1342        assert_eq!(error!(EAGAIN), ld.replica_read(&mut read_buf));
1343        assert!(read_buf.data.is_empty());
1344
1345        // A TCOFLUSH from the main side should do nothing (instantaneous transmission)
1346        let mut ld = make_ld();
1347        ld.flush(true, uapi::TCOFLUSH).unwrap();
1348        read_buf.data.clear();
1349        assert!(ld.main_read(&mut read_buf).is_ok());
1350        assert_eq!(read_buf.data, b"pong\n");
1351        read_buf.data.clear();
1352        assert!(ld.replica_read(&mut read_buf).is_ok());
1353        assert_eq!(read_buf.data, b"ping\n");
1354
1355        // A TCOFLUSH from the replica side should do nothing
1356        let mut ld = make_ld();
1357        ld.flush(false, uapi::TCOFLUSH).unwrap();
1358        read_buf.data.clear();
1359        assert!(ld.main_read(&mut read_buf).is_ok());
1360        assert_eq!(read_buf.data, b"pong\n");
1361        read_buf.data.clear();
1362        assert!(ld.replica_read(&mut read_buf).is_ok());
1363        assert_eq!(read_buf.data, b"ping\n");
1364
1365        // A TCIOFLUSH from main should flush only main's input (output_queue)
1366        let mut ld = make_ld();
1367        ld.flush(true, uapi::TCIOFLUSH).unwrap();
1368        read_buf.data.clear();
1369        assert_eq!(error!(EAGAIN), ld.main_read(&mut read_buf));
1370        read_buf.data.clear();
1371        assert!(ld.replica_read(&mut read_buf).is_ok());
1372        assert_eq!(read_buf.data, b"ping\n");
1373
1374        // A TCIOFLUSH from replica should flush only replica's input (input_queue)
1375        let mut ld = make_ld();
1376        ld.flush(false, uapi::TCIOFLUSH).unwrap();
1377        read_buf.data.clear();
1378        assert!(ld.main_read(&mut read_buf).is_ok());
1379        assert_eq!(read_buf.data, b"pong\n");
1380        read_buf.data.clear();
1381        assert_eq!(error!(EAGAIN), ld.replica_read(&mut read_buf));
1382    }
1383}
1384
1385pub mod testing;