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