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, 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::termios,
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    /// The number of active references to the main part of the terminal. Starts as `None`. The
73    /// main part of the terminal is considered closed when this is `Some(0)`.
74    main_references: Option<u32>,
75
76    /// The number of active references to the replica part of the terminal. Starts as `None`. The
77    /// replica part of the terminal is considered closed when this is `Some(0)`.
78    replica_references: Option<u32>,
79
80    /// Input queue of the terminal. Data flow from the main side to the replica side.
81    #[derivative(Default(value = "Queue::input_queue()"))]
82    input_queue: Option<Queue>,
83
84    /// Output queue of the terminal. Data flow from the replica side to the main side.
85    #[derivative(Default(value = "Queue::output_queue()"))]
86    output_queue: Option<Queue>,
87}
88
89/// Helper trait for input/output buffers.
90pub trait InputBuffer {
91    fn available(&self) -> usize;
92    fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno>;
93}
94
95pub trait OutputBuffer {
96    fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
97}
98
99/// Macro to help working with the terminal queues.
100macro_rules! with_queue {
101    ($self_:tt . $name:ident . $fn:ident ( $($param:expr),*$(,)?)) => {
102        {
103        let mut queue = $self_.$name . take().unwrap();
104        let result = queue.$fn( $($param),* );
105        $self_.$name = Some(queue);
106        result
107        }
108    };
109}
110
111/// Keep track of the signals to send when handling terminal content.
112#[must_use]
113pub struct PendingSignals {
114    signals: Vec<Signal>,
115}
116
117impl PendingSignals {
118    pub fn new() -> Self {
119        Self { signals: vec![] }
120    }
121
122    /// Add the given signal to the list of signal to send to the associate process group.
123    fn add(&mut self, signal: Signal) {
124        self.signals.push(signal);
125    }
126
127    /// Append all pending signals in `other` to `self`.
128    fn append(&mut self, mut other: Self) {
129        self.signals.append(&mut other.signals);
130    }
131
132    /// Returns a slice of the pending signals.
133    pub fn signals(&self) -> &[Signal] {
134        &self.signals[..]
135    }
136}
137
138/// Represents the type of erase operation that can be performed on terminal input.
139#[derive(Debug, PartialEq)]
140enum EraseType {
141    /// Erase a single character (typically triggered by backspace)
142    Character,
143    /// Erase a word (typically triggered by Ctrl+W)
144    Word,
145    /// Erase the entire line (typically triggered by Ctrl+U)
146    Line,
147}
148
149impl LineDiscipline {
150    /// Returns the terminal configuration.
151    pub fn termios(&self) -> &uapi::termios {
152        &self.termios
153    }
154
155    pub fn is_canon_enabled(&self) -> bool {
156        self.termios.has_local_flags(ICANON)
157    }
158
159    /// Returns the number of available bytes to read from the side of the terminal described by
160    /// `is_main`.
161    pub fn get_available_read_size(&self, is_main: bool) -> usize {
162        let queue = if is_main { self.output_queue() } else { self.input_queue() };
163        queue.readable_size()
164    }
165
166    /// Sets the terminal configuration.
167    pub fn set_termios(&mut self, termios: uapi::termios) -> PendingSignals {
168        let old_canon_enabled = self.is_canon_enabled();
169        self.termios = termios;
170        if old_canon_enabled && !self.is_canon_enabled() {
171            with_queue!(self.input_queue.on_canon_disabled(self))
172        } else {
173            PendingSignals::new()
174        }
175    }
176
177    /// `close` implementation of the main side of the terminal.
178    pub fn main_close(&mut self) {
179        self.main_references = self.main_references.map(|v| v - 1);
180    }
181
182    /// Called when a new reference to the main side of this terminal is made.
183    pub fn main_open(&mut self) {
184        self.main_references = Some(self.main_references.unwrap_or(0) + 1);
185    }
186
187    pub fn is_main_closed(&self) -> bool {
188        matches!(self.main_references, Some(0))
189    }
190
191    /// `query_events` implementation of the main side of the terminal.
192    pub fn main_query_events(&self) -> FdEvents {
193        if self.is_replica_closed() && self.output_queue().readable_size() == 0 {
194            return FdEvents::POLLOUT | FdEvents::POLLHUP;
195        }
196        self.output_queue().read_readiness() | self.input_queue().write_readiness()
197    }
198
199    /// `read` implementation of the main side of the terminal.
200    pub fn main_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
201        if self.is_replica_closed() && self.output_queue().readable_size() == 0 {
202            return error!(EIO);
203        }
204        with_queue!(self.output_queue.read(self, data))
205    }
206
207    /// `write` implementation of the main side of the terminal.
208    pub fn main_write(
209        &mut self,
210        data: &mut dyn InputBuffer,
211    ) -> Result<(usize, PendingSignals), Errno> {
212        with_queue!(self.input_queue.write(self, data))
213    }
214
215    /// `close` implementation of the replica side of the terminal.
216    pub fn replica_close(&mut self) {
217        self.replica_references = self.replica_references.map(|v| v - 1);
218    }
219
220    /// Called when a new reference to the replica side of this terminal is made.
221    pub fn replica_open(&mut self) {
222        self.replica_references = Some(self.replica_references.unwrap_or(0) + 1);
223    }
224
225    pub fn is_replica_closed(&self) -> bool {
226        matches!(self.replica_references, Some(0))
227    }
228
229    /// `query_events` implementation of the replica side of the terminal.
230    pub fn replica_query_events(&self) -> FdEvents {
231        if self.is_main_closed() {
232            return FdEvents::POLLIN | FdEvents::POLLOUT | FdEvents::POLLERR | FdEvents::POLLHUP;
233        }
234        self.input_queue().read_readiness() | self.output_queue().write_readiness()
235    }
236
237    /// `read` implementation of the replica side of the terminal.
238    pub fn replica_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
239        if self.is_main_closed() {
240            return Ok(0);
241        }
242        with_queue!(self.input_queue.read(self, data))
243    }
244
245    /// `write` implementation of the replica side of the terminal.
246    pub fn replica_write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
247        if self.is_main_closed() {
248            return error!(EIO);
249        }
250        if self.stopped && self.termios.has_input_flags(IXON) {
251            return error!(EAGAIN);
252        }
253        let (read_from_userspace, signals) = with_queue!(self.output_queue.write(self, data))?;
254        // Writing to the replica side never generates signals.
255        assert!(signals.signals().is_empty());
256        Ok(read_from_userspace)
257    }
258
259    /// Returns the input queue.
260    fn input_queue(&self) -> &Queue {
261        self.input_queue.as_ref().unwrap()
262    }
263
264    /// Returns the output_queue. The Option is always filled.
265    fn output_queue(&self) -> &Queue {
266        self.output_queue.as_ref().unwrap()
267    }
268
269    /// Return whether a signal must be send when receiving `byte`, and if yes, which.
270    fn handle_signals(&mut self, byte: RawByte) -> Option<Signal> {
271        if !self.termios.has_local_flags(ISIG) {
272            return None;
273        }
274        self.termios.signal(byte)
275    }
276
277    fn extend_echo_bytes(&self, target: &mut Vec<RawByte>, byte: RawByte) {
278        if self.termios.has_local_flags(ECHOCTL) {
279            if let Some(control_character_echo) = generate_control_character_echo(byte) {
280                target.extend(control_character_echo);
281                return;
282            }
283        }
284        target.push(byte);
285    }
286
287    fn transform(
288        &mut self,
289        is_input: bool,
290        queue: &mut Queue,
291        buffer: &[RawByte],
292    ) -> (usize, PendingSignals) {
293        if is_input {
294            self.transform_input(queue, buffer)
295        } else {
296            (self.transform_output(queue, buffer), PendingSignals::new())
297        }
298    }
299
300    fn transform_output(&mut self, queue: &mut Queue, original_buffer: &[RawByte]) -> usize {
301        let mut buffer = original_buffer;
302
303        // transform_output is effectively always in noncanonical mode, as the
304        // main termios never has ICANON set.
305
306        if !self.termios.has_output_flags(OPOST) {
307            queue.read_queue.push_back(buffer.to_vec());
308            return buffer.len();
309        }
310
311        let mut return_value = 0;
312        while !buffer.is_empty() {
313            let size = compute_next_character_size(buffer, &self.termios);
314            let mut character_bytes = buffer[..size].to_vec();
315            return_value += size;
316            buffer = &buffer[size..];
317
318            if self.termios.has_output_flags(OLCUC) {
319                character_bytes[0].make_ascii_uppercase();
320            }
321            match character_bytes[0] {
322                b'\n' => {
323                    if self.termios.has_output_flags(ONLRET) {
324                        self.column = 0;
325                    }
326                    if self.termios.has_output_flags(ONLCR) {
327                        queue.line_buffer.extend_from_slice(&[b'\r', b'\n']);
328                        continue;
329                    }
330                }
331                b'\r' => {
332                    if self.termios.has_output_flags(ONOCR) && self.column == 0 {
333                        continue;
334                    }
335                    if self.termios.has_output_flags(OCRNL) {
336                        character_bytes[0] = b'\n';
337                        if self.termios.has_output_flags(ONLRET) {
338                            self.column = 0;
339                        }
340                    } else {
341                        self.column = 0;
342                    }
343                }
344                b'\t' => {
345                    let spaces = SPACES_PER_TAB - self.column % SPACES_PER_TAB;
346                    if self.termios.c_oflag & TABDLY == XTABS {
347                        self.column += spaces;
348                        queue.line_buffer.extend(std::iter::repeat(b' ').take(spaces));
349                        continue;
350                    }
351                    self.column += spaces;
352                }
353                BACKSPACE_CHAR => {
354                    if self.column > 0 {
355                        self.column -= 1;
356                    }
357                }
358                _ => {
359                    self.column += 1;
360                }
361            }
362            queue.line_buffer.append(&mut character_bytes);
363        }
364        if !queue.line_buffer.is_empty() {
365            queue.flush_line_buffer();
366        }
367        return_value
368    }
369
370    fn transform_input(
371        &mut self,
372        queue: &mut Queue,
373        original_buffer: &[RawByte],
374    ) -> (usize, PendingSignals) {
375        let mut buffer = original_buffer;
376
377        let max_bytes = if self.termios.has_local_flags(ICANON) {
378            CANON_MAX_BYTES
379        } else {
380            NON_CANON_MAX_BYTES
381        };
382
383        let mut return_value = 0;
384        let mut signals = PendingSignals::new();
385        while !buffer.is_empty() && queue.line_buffer.len() < CANON_MAX_BYTES {
386            let size = compute_next_character_size(buffer, &self.termios);
387            let mut character_bytes = buffer[..size].to_vec();
388            // It is guaranteed that character_bytes has at least one element.
389
390            if self.lnext {
391                self.lnext = false;
392                if self.termios.has_local_flags(ECHO) {
393                    let mut echo_bytes = vec![];
394                    self.extend_echo_bytes(&mut echo_bytes, character_bytes[0]);
395                    signals.append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
396                }
397
398                queue.line_buffer.extend_from_slice(&character_bytes);
399                buffer = &buffer[size..];
400                return_value += size;
401                continue;
402            }
403
404            if self.termios.has_local_flags(IEXTEN) {
405                // VLNEXT
406                if character_bytes[0] == self.termios.c_cc[VLNEXT as usize]
407                    && self.termios.c_cc[VLNEXT as usize] != DISABLED_CHAR
408                {
409                    self.lnext = true;
410                    if self.termios.has_local_flags(ECHO) && self.termios.has_local_flags(ECHOCTL) {
411                        let echo_bytes = vec![b'^', BACKSPACE_CHAR];
412                        signals
413                            .append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
414                    }
415                    buffer = &buffer[size..];
416                    return_value += size;
417                    continue;
418                }
419                // VREPRINT
420                if character_bytes[0] == self.termios.c_cc[VREPRINT as usize]
421                    && self.termios.c_cc[VREPRINT as usize] != DISABLED_CHAR
422                {
423                    if self.termios.has_local_flags(ECHO) {
424                        let mut echo_bytes = vec![];
425                        self.extend_echo_bytes(&mut echo_bytes, character_bytes[0]);
426                        echo_bytes.push(b'\n');
427                        for byte in &queue.line_buffer {
428                            self.extend_echo_bytes(&mut echo_bytes, *byte);
429                        }
430                        signals
431                            .append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
432                    }
433                    buffer = &buffer[size..];
434                    return_value += size;
435                    continue;
436                }
437            }
438
439            if self.termios.has_input_flags(IUCLC) && self.termios.has_local_flags(IEXTEN) {
440                character_bytes[0].make_ascii_lowercase();
441            }
442
443            let mut signal_generated = false;
444            if let Some(signal) = self.handle_signals(character_bytes[0]) {
445                signals.add(signal);
446                signal_generated = true;
447                if !self.termios.has_local_flags(NOFLSH) {
448                    queue.flush();
449                    if let Some(ref mut output_queue) = self.output_queue {
450                        output_queue.flush();
451                    }
452                }
453            }
454
455            // Handle IXON/IXOFF (software flow control)
456            if self.termios.has_input_flags(IXON) {
457                if character_bytes[0] == self.termios.c_cc[VSTOP as usize] {
458                    self.stopped = true;
459                    buffer = &buffer[size..];
460                    return_value += size;
461                    continue;
462                }
463                // POSIX says:
464                // "If IXON is set, start/stop output control is enabled. A received STOP character
465                // suspends output and a received START character restarts output. The STOP and
466                // START characters are not read, but performing the flow control functions."
467                //
468                // "If IXANY is set, any input character restarts output that has been suspended."
469                if self.stopped
470                    && (character_bytes[0] == self.termios.c_cc[VSTART as usize]
471                        || self.termios.has_input_flags(IXANY))
472                {
473                    self.stopped = false;
474                    // If it was START, we consume it. If it was IXANY (and not START), we usually
475                    // process it?
476                    // "The START character is not read".
477                    // If IXANY is set and char != START, we should restart AND process the char.
478                    if character_bytes[0] == self.termios.c_cc[VSTART as usize] {
479                        buffer = &buffer[size..];
480                        return_value += size;
481                        continue;
482                    }
483                }
484            }
485
486            match character_bytes[0] {
487                b'\r' => {
488                    if self.termios.has_input_flags(IGNCR) {
489                        buffer = &buffer[size..];
490                        return_value += size;
491                        continue;
492                    }
493                    if self.termios.has_input_flags(ICRNL) {
494                        character_bytes[0] = b'\n';
495                    }
496                }
497                b'\n' => {
498                    if self.termios.has_input_flags(INLCR) {
499                        character_bytes[0] = b'\r'
500                    }
501                }
502                _ => {}
503            }
504            // In canonical mode, we discard non-terminating characters
505            // after the first 4095.
506            if self.termios.has_local_flags(ICANON)
507                && queue.line_buffer.len() + size >= max_bytes
508                && !self.termios.is_terminating(&character_bytes)
509            {
510                buffer = &buffer[size..];
511                return_value += size;
512                continue;
513            }
514
515            if queue.line_buffer.len() + size > max_bytes {
516                break;
517            }
518
519            buffer = &buffer[size..];
520            return_value += size;
521
522            let first_byte = character_bytes[0];
523
524            // If we get EOF, push whatever we have line_buffer to read_queue, then push an empty datagram.
525            if self.termios.has_local_flags(ICANON) && self.termios.is_eof(first_byte) {
526                if !queue.line_buffer.is_empty() {
527                    queue.flush_line_buffer();
528                }
529                queue.read_queue.push_back(vec![]);
530                break;
531            }
532
533            let mut maybe_erase_span = None;
534            let mut erase_type = None;
535            if self.termios.has_local_flags(ICANON) {
536                if self.termios.is_erase(first_byte) {
537                    maybe_erase_span =
538                        Some(compute_last_character_span(&queue.line_buffer[..], &self.termios));
539                    erase_type = Some(EraseType::Character);
540                } else if self.termios.is_werase(first_byte) {
541                    maybe_erase_span =
542                        Some(compute_last_word_span(&queue.line_buffer[..], &self.termios));
543                    erase_type = Some(EraseType::Word);
544                }
545                if self.termios.is_kill(first_byte) {
546                    maybe_erase_span =
547                        Some(compute_last_line_span(&queue.line_buffer[..], &self.termios));
548                    erase_type = Some(EraseType::Line);
549                }
550            }
551
552            let mut erased_bytes = Option::None;
553            if let Some(erase_span) = maybe_erase_span {
554                if erase_span.bytes == 0 {
555                    continue;
556                }
557                if self.termios.has_local_flags(ECHOPRT) {
558                    erased_bytes = Some(
559                        queue.line_buffer[queue.line_buffer.len() - erase_span.bytes..].to_vec(),
560                    );
561                }
562                queue.line_buffer.truncate(queue.line_buffer.len() - erase_span.bytes);
563            } else if !signal_generated {
564                queue.line_buffer.extend_from_slice(&character_bytes);
565            }
566
567            // Anything written to the read buffer will have to be echoed.
568            let mut echo_bytes = vec![];
569            if self.termios.has_local_flags(ECHO) {
570                if let Some(erase_span) = maybe_erase_span {
571                    match erase_type {
572                        Some(EraseType::Character) | Some(EraseType::Word) => {
573                            if self.termios.has_local_flags(ECHOPRT) {
574                                if let Some(bytes) = erased_bytes {
575                                    if !self.erasing {
576                                        echo_bytes.push(b'\\');
577                                        self.erasing = true;
578                                    }
579                                    for byte in bytes.iter().rev() {
580                                        self.extend_echo_bytes(&mut echo_bytes, *byte);
581                                    }
582                                }
583                            } else if self.termios.has_local_flags(ECHOE) {
584                                echo_bytes = generate_erase_echo(&erase_span);
585                            }
586                        }
587                        Some(EraseType::Line) => {
588                            if self.termios.has_local_flags(ECHOKE) {
589                                echo_bytes = generate_erase_echo(&erase_span);
590                            } else if self.termios.has_local_flags(ECHOK) {
591                                self.extend_echo_bytes(&mut echo_bytes, first_byte);
592                                echo_bytes.push(b'\n');
593                            }
594                        }
595                        None => {
596                            unreachable!("Erase type should be Some when maybe_erase_span is Some")
597                        }
598                    }
599                    if self.erasing && queue.line_buffer.is_empty() {
600                        echo_bytes.push(b'/');
601                        self.erasing = false;
602                    }
603                } else {
604                    if self.erasing && first_byte != b'\n' {
605                        echo_bytes.push(b'/');
606                        self.erasing = false;
607                    }
608                }
609
610                let needs_normal_echo =
611                    if maybe_erase_span.is_some() { echo_bytes.is_empty() } else { true };
612
613                if needs_normal_echo {
614                    let mut char_echo = vec![];
615                    if self.termios.has_local_flags(ECHOCTL) {
616                        if let Some(control_character_echo) =
617                            generate_control_character_echo(first_byte)
618                        {
619                            char_echo = control_character_echo;
620                        }
621                    }
622                    if char_echo.is_empty() {
623                        char_echo = character_bytes.clone();
624                    }
625                    echo_bytes.extend(char_echo);
626                }
627            } else if self.termios.has_local_flags(ECHONL) && first_byte == b'\n' {
628                echo_bytes.extend_from_slice(&character_bytes);
629            }
630
631            if !echo_bytes.is_empty() {
632                signals.append(with_queue!(self.output_queue.write_bytes(self, &echo_bytes)));
633            }
634
635            // If we finish a line, make it available for reading.
636            if self.termios.has_local_flags(ICANON) && self.termios.is_terminating(&character_bytes)
637            {
638                queue.flush_line_buffer();
639            }
640        }
641        // In noncanonical mode, everything is readable.
642        if !self.termios.has_local_flags(ICANON) && !queue.line_buffer.is_empty() {
643            queue.flush_line_buffer();
644        }
645
646        (return_value, signals)
647    }
648}
649
650/// Alias used to mark bytes in the queues that have not yet been processed and pushed into the
651/// read buffer. See `Queue`.
652type RawByte = u8;
653
654#[derive(Debug, Default)]
655struct Queue {
656    /// The queue of data ready to be read. Each element is a "datagram" (line or chunk).
657    /// Empty byte vectors represent EOF markers (read returns 0).
658    read_queue: VecDeque<Vec<u8>>,
659
660    /// The incomplete line/chunk being processed but not yet ready for the read_queue.
661    /// In Canonical mode, this holds the current line being edited.
662    /// In Non-Canonical mode, this holds data until it is pushed to the read_queue.
663    line_buffer: Vec<u8>,
664
665    /// Data that can't fit into readBuf. It is put here until it can be loaded into the read
666    /// buffer. Contains data that hasn't been processed.
667    wait_buffers: VecDeque<Vec<RawByte>>,
668
669    /// The length of the data in `wait_buffers`.
670    total_wait_buffer_length: usize,
671
672    /// Whether this queue in the input queue. Needed to know how to transform received data.
673    is_input: bool,
674}
675
676impl Queue {
677    fn output_queue() -> Option<Self> {
678        Some(Queue { is_input: false, ..Default::default() })
679    }
680
681    fn input_queue() -> Option<Self> {
682        Some(Queue { is_input: true, ..Default::default() })
683    }
684
685    /// Returns whether the queue is ready to be written to.
686    fn write_readiness(&self) -> FdEvents {
687        if self.total_wait_buffer_length < WAIT_BUFFER_MAX_BYTES {
688            FdEvents::POLLOUT
689        } else {
690            FdEvents::empty()
691        }
692    }
693
694    /// Returns whether the queue is ready to be read from.
695    fn read_readiness(&self) -> FdEvents {
696        // If there's an empty "datagram" in read_queue, it means EOF, which is "readable" (returns 0).
697        if !self.read_queue.is_empty() { FdEvents::POLLIN } else { FdEvents::empty() }
698    }
699
700    /// Returns the number of bytes ready to be read.
701    fn readable_size(&self) -> usize {
702        // We sum up everything in the read_queue.
703        // 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".
704        self.read_queue.iter().map(|v| v.len()).sum()
705    }
706
707    /// Read from the queue into `data`. Returns the number of bytes copied.
708    fn read(
709        &mut self,
710        terminal: &mut LineDiscipline,
711        data: &mut dyn OutputBuffer,
712    ) -> Result<usize, Errno> {
713        if self.read_queue.is_empty() {
714            return error!(EAGAIN);
715        }
716
717        let mut total_written = 0;
718        while let Some(mut packet) = self.read_queue.pop_front() {
719            if packet.is_empty() {
720                if total_written > 0 {
721                    // We've already read some data. We need to complete the read with that data and
722                    // leave the empty datagram in the queue to signal EOF on the next read.
723                    self.read_queue.push_front(packet);
724                }
725                break;
726            }
727
728            match data.write(&packet) {
729                Ok(written) => {
730                    total_written += written;
731                    if written < packet.len() {
732                        // Put back the unread part.
733                        let remaining = packet.split_off(written);
734                        self.read_queue.push_front(remaining);
735                        // Destination full.
736                        break;
737                    }
738
739                    // If we are in canonical input mode, we stop after one packet (one line).
740                    if self.is_input && terminal.termios.has_local_flags(ICANON) {
741                        break;
742                    }
743                }
744                Err(e) => {
745                    // If write failed, push back the whole packet.
746                    self.read_queue.push_front(packet);
747                    if total_written > 0 {
748                        // If we managed to write something before error, return success.
749                        return Ok(total_written);
750                    }
751                    return Err(e);
752                }
753            }
754        }
755
756        let signals = self.drain_waiting_buffer(terminal);
757        assert!(signals.signals().is_empty());
758        Ok(total_written)
759    }
760
761    /// Writes to the queue from `data`. Returns the number of bytes copied.
762    fn write(
763        &mut self,
764        terminal: &mut LineDiscipline,
765        data: &mut dyn InputBuffer,
766    ) -> Result<(usize, PendingSignals), Errno> {
767        let room = WAIT_BUFFER_MAX_BYTES - self.total_wait_buffer_length;
768        let data_length = data.available();
769        if room == 0 && data_length > 0 {
770            return error!(EAGAIN);
771        }
772        let buffer = data.read_to_vec_exact(std::cmp::min(room, data_length))?;
773        let read_from_userspace = buffer.len();
774        let signals = self.push_to_waiting_buffer(terminal, buffer);
775        Ok((read_from_userspace, signals))
776    }
777
778    /// Writes the given `buffer` to the queue.
779    fn write_bytes(&mut self, terminal: &mut LineDiscipline, buffer: &[RawByte]) -> PendingSignals {
780        self.push_to_waiting_buffer(terminal, buffer.to_vec())
781    }
782
783    /// Pushes the given buffer into the wait_buffers, and process the wait_buffers.
784    fn push_to_waiting_buffer(
785        &mut self,
786        terminal: &mut LineDiscipline,
787        buffer: Vec<RawByte>,
788    ) -> PendingSignals {
789        self.total_wait_buffer_length += buffer.len();
790        self.wait_buffers.push_back(buffer);
791        self.drain_waiting_buffer(terminal)
792    }
793
794    /// Processes the wait_buffers, filling the read buffer.
795    fn drain_waiting_buffer(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
796        let mut signals_to_return = PendingSignals::new();
797        while let Some(wait_buffer) = self.wait_buffers.pop_front() {
798            self.total_wait_buffer_length -= wait_buffer.len();
799            let (count, signals) = terminal.transform(self.is_input, self, &wait_buffer);
800            signals_to_return.append(signals);
801            if count != wait_buffer.len() {
802                let remaining = wait_buffer[count..].to_vec();
803                self.total_wait_buffer_length += remaining.len();
804                self.wait_buffers.push_front(remaining);
805                break;
806            }
807        }
808        signals_to_return
809    }
810
811    /// Flushed the line buffer to the read queue.
812    fn flush_line_buffer(&mut self) {
813        self.read_queue.push_back(std::mem::take(&mut self.line_buffer));
814    }
815
816    /// Flush the content of the queue.
817    fn flush(&mut self) {
818        self.read_queue.clear();
819        self.line_buffer.clear();
820        self.wait_buffers.clear();
821        self.total_wait_buffer_length = 0;
822    }
823
824    /// Called when the queue is moved from canonical mode, to non canonical mode.
825    fn on_canon_disabled(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
826        let signals = self.drain_waiting_buffer(terminal);
827        if !self.line_buffer.is_empty() {
828            self.flush_line_buffer();
829        }
830        signals
831    }
832}
833
834// Helper functions (copied from terminal.rs)
835// Returns the ASCII representation of the given char. This will assert if the character is not
836// ascii.
837fn get_ascii(c: char) -> u8 {
838    let mut dest: [u8; 1] = [0];
839    c.encode_utf8(&mut dest);
840    dest[0]
841}
842
843// Returns the control character associated with the given letter.
844fn get_control_character(c: char) -> cc_t {
845    get_ascii(c) - get_ascii('A') + 1
846}
847
848// Returns the default control characters of a terminal.
849fn get_default_control_characters() -> [cc_t; 19usize] {
850    [
851        get_control_character('C'),  // VINTR = ^C
852        get_control_character('\\'), // VQUIT = ^\
853        get_ascii('\x7f'),           // VERASE = DEL
854        get_control_character('U'),  // VKILL = ^U
855        get_control_character('D'),  // VEOF = ^D
856        0,                           // VTIME
857        1,                           // VMIN
858        0,                           // VSWTC
859        get_control_character('Q'),  // VSTART = ^Q
860        get_control_character('S'),  // VSTOP = ^S
861        get_control_character('Z'),  // VSUSP = ^Z
862        0,                           // VEOL
863        get_control_character('R'),  // VREPRINT = ^R
864        get_control_character('O'),  // VDISCARD = ^O
865        get_control_character('W'),  // VWERASE = ^W
866        get_control_character('V'),  // VLNEXT = ^V
867        0,                           // VEOL2
868        0,                           // Remaining data in the array,
869        0,                           // Remaining data in the array,
870    ]
871}
872
873// Returns the default replica terminal configuration.
874pub fn get_default_termios() -> uapi::termios {
875    uapi::termios {
876        c_iflag: uapi::ICRNL | uapi::IXON,
877        c_oflag: uapi::OPOST | uapi::ONLCR,
878        c_cflag: uapi::B38400 | uapi::CS8 | uapi::CREAD,
879        c_lflag: uapi::ISIG
880            | uapi::ICANON
881            | uapi::ECHO
882            | uapi::ECHOE
883            | uapi::ECHOK
884            | uapi::ECHOCTL
885            | uapi::ECHOKE
886            | uapi::IEXTEN,
887        c_line: 0,
888        c_cc: get_default_control_characters(),
889    }
890}
891
892/// Helper trait for termios to help parse the configuration.
893trait TermIOS {
894    fn has_input_flags(&self, flags: tcflag_t) -> bool;
895    fn has_output_flags(&self, flags: tcflag_t) -> bool;
896    fn has_local_flags(&self, flags: tcflag_t) -> bool;
897    fn is_eof(&self, c: RawByte) -> bool;
898    fn is_erase(&self, c: RawByte) -> bool;
899    fn is_werase(&self, c: RawByte) -> bool;
900    fn is_kill(&self, c: RawByte) -> bool;
901    fn is_terminating(&self, character_bytes: &[RawByte]) -> bool;
902    fn signal(&self, c: RawByte) -> Option<Signal>;
903}
904
905impl TermIOS for uapi::termios {
906    fn has_input_flags(&self, flags: tcflag_t) -> bool {
907        self.c_iflag & flags == flags
908    }
909    fn has_output_flags(&self, flags: tcflag_t) -> bool {
910        self.c_oflag & flags == flags
911    }
912    fn has_local_flags(&self, flags: tcflag_t) -> bool {
913        self.c_lflag & flags == flags
914    }
915    fn is_eof(&self, c: RawByte) -> bool {
916        c == self.c_cc[VEOF as usize] && self.c_cc[VEOF as usize] != DISABLED_CHAR
917    }
918    fn is_erase(&self, c: RawByte) -> bool {
919        c == self.c_cc[VERASE as usize] && self.c_cc[VERASE as usize] != DISABLED_CHAR
920    }
921    fn is_werase(&self, c: RawByte) -> bool {
922        c == self.c_cc[VWERASE as usize]
923            && self.c_cc[VWERASE as usize] != DISABLED_CHAR
924            && self.has_local_flags(IEXTEN)
925    }
926    fn is_kill(&self, c: RawByte) -> bool {
927        c == self.c_cc[VKILL as usize] && self.c_cc[VKILL as usize] != DISABLED_CHAR
928    }
929    fn is_terminating(&self, character_bytes: &[RawByte]) -> bool {
930        // All terminating characters are 1 byte.
931        if character_bytes.len() != 1 {
932            return false;
933        }
934        let c = character_bytes[0];
935
936        // Is this the user-set EOF character?
937        if self.is_eof(c) {
938            return true;
939        }
940
941        if c == DISABLED_CHAR {
942            return false;
943        }
944        if c == b'\n' || c == self.c_cc[VEOL as usize] {
945            return true;
946        }
947        if c == self.c_cc[VEOL2 as usize] {
948            return self.has_local_flags(IEXTEN);
949        }
950        false
951    }
952    fn signal(&self, c: RawByte) -> Option<Signal> {
953        if c == DISABLED_CHAR {
954            return None;
955        }
956        if c == self.c_cc[VINTR as usize] {
957            return Some(SIGINT);
958        }
959        if c == self.c_cc[VQUIT as usize] {
960            return Some(SIGQUIT);
961        }
962        if c == self.c_cc[VSUSP as usize] {
963            return Some(SIGSTOP);
964        }
965        None
966    }
967}
968
969fn compute_next_character_size(buffer: &[RawByte], termios: &uapi::termios) -> usize {
970    if !termios.has_input_flags(IUTF8) {
971        return 1;
972    }
973
974    #[derive(Default)]
975    struct Receiver {
976        done: Option<bool>,
977    }
978
979    impl utf8parse::Receiver for Receiver {
980        fn codepoint(&mut self, _c: char) {
981            self.done = Some(true);
982        }
983        fn invalid_sequence(&mut self) {
984            self.done = Some(false);
985        }
986    }
987
988    let mut byte_count = 0;
989    let mut receiver = Receiver::default();
990    let mut parser = utf8parse::Parser::new();
991    while receiver.done.is_none() && byte_count < buffer.len() {
992        parser.advance(&mut receiver, buffer[byte_count]);
993        byte_count += 1;
994    }
995    if receiver.done == Some(true) { byte_count } else { 1 }
996}
997
998fn is_ascii(c: RawByte) -> bool {
999    c & 0x80 == 0
1000}
1001
1002fn is_utf8_start(c: RawByte) -> bool {
1003    c & 0xC0 == 0xC0
1004}
1005
1006fn generate_erase_echo(erase_span: &BufferSpan) -> Vec<RawByte> {
1007    let erase_echo = [BACKSPACE_CHAR, b' ', BACKSPACE_CHAR];
1008    erase_echo.iter().cycle().take(erase_echo.len() * erase_span.characters).map(|c| *c).collect()
1009}
1010
1011fn generate_control_character_echo(c: RawByte) -> Option<Vec<RawByte>> {
1012    if matches!(c, 0..=0x8 | 0xB..=0xC | 0xE..=0x1F) {
1013        Some(vec![b'^', c + CONTROL_OFFSET])
1014    } else {
1015        None
1016    }
1017}
1018
1019#[derive(Default, Debug, Clone, Copy)]
1020struct BufferSpan {
1021    bytes: usize,
1022    characters: usize,
1023}
1024
1025impl std::ops::AddAssign<Self> for BufferSpan {
1026    fn add_assign(&mut self, rhs: Self) {
1027        self.bytes += rhs.bytes;
1028        self.characters += rhs.characters;
1029    }
1030}
1031
1032fn compute_last_character_span(buffer: &[RawByte], termios: &uapi::termios) -> BufferSpan {
1033    if buffer.is_empty() {
1034        return BufferSpan::default();
1035    }
1036    if termios.has_input_flags(IUTF8) {
1037        let mut bytes = 0;
1038        for c in buffer.iter().rev() {
1039            bytes += 1;
1040            if is_ascii(*c) || is_utf8_start(*c) {
1041                return BufferSpan { bytes, characters: 1 };
1042            }
1043        }
1044        BufferSpan::default()
1045    } else {
1046        BufferSpan { bytes: 1, characters: 1 }
1047    }
1048}
1049
1050fn compute_last_word_span(buffer: &[RawByte], termios: &uapi::termios) -> BufferSpan {
1051    fn is_whitespace(c: RawByte) -> bool {
1052        c == b' ' || c == b'\t'
1053    }
1054
1055    let mut in_word = false;
1056    let mut word_span = BufferSpan::default();
1057    let mut remaining = buffer.len();
1058    loop {
1059        let span = compute_last_character_span(&buffer[..remaining], termios);
1060        if span.bytes == 0 {
1061            break;
1062        }
1063        if span.bytes == 1 {
1064            let c = buffer[remaining - 1];
1065            if in_word {
1066                if is_whitespace(c) {
1067                    break;
1068                }
1069            } else {
1070                if !is_whitespace(c) {
1071                    in_word = true;
1072                }
1073            }
1074        }
1075        remaining -= span.bytes;
1076        word_span += span;
1077    }
1078
1079    word_span
1080}
1081
1082fn compute_last_line_span(buffer: &[RawByte], termios: &uapi::termios) -> BufferSpan {
1083    let mut line_span = BufferSpan::default();
1084    let mut remaining = buffer.len();
1085
1086    loop {
1087        let span = compute_last_character_span(&buffer[..remaining], termios);
1088        if span.bytes == 0 {
1089            break;
1090        }
1091        if span.bytes == 1 {
1092            let c = buffer[remaining - 1];
1093            if c == b'\n' {
1094                break;
1095            }
1096        }
1097        remaining -= span.bytes;
1098        line_span += span;
1099    }
1100
1101    line_span
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106    use super::*;
1107
1108    #[::fuchsia::test]
1109    fn test_ascii_conversion() {
1110        assert_eq!(get_ascii(' '), 32);
1111    }
1112
1113    #[::fuchsia::test]
1114    fn test_control_character() {
1115        assert_eq!(get_control_character('C'), 3);
1116    }
1117
1118    #[::fuchsia::test]
1119    fn test_compute_next_character_size_non_utf8() {
1120        let termios = get_default_termios();
1121        for i in 0..=255 {
1122            let array: &[u8] = &[i, 0xa9, 0];
1123            assert_eq!(compute_next_character_size(array, &termios), 1);
1124        }
1125    }
1126
1127    #[::fuchsia::test]
1128    fn test_compute_next_character_size_utf8() {
1129        let mut termios = get_default_termios();
1130        termios.c_iflag |= IUTF8;
1131        for i in 0..128 {
1132            let array: &[RawByte] = &[i, 0xa9, 0];
1133            assert_eq!(compute_next_character_size(array, &termios), 1);
1134        }
1135        let array: &[RawByte] = &[0xc2, 0xa9, 0];
1136        assert_eq!(compute_next_character_size(array, &termios), 2);
1137        let array: &[RawByte] = &[0xc2, 255, 0];
1138        assert_eq!(compute_next_character_size(array, &termios), 1);
1139    }
1140
1141    #[::fuchsia::test]
1142    fn test_signal_handling_with_disabled_chars() {
1143        let mut termios = get_default_termios();
1144        termios.c_cc[VINTR as usize] = DISABLED_CHAR;
1145        termios.c_cc[VQUIT as usize] = DISABLED_CHAR;
1146        termios.c_cc[VSUSP as usize] = DISABLED_CHAR;
1147
1148        assert_eq!(termios.signal(0), None);
1149        assert_eq!(termios.signal(3), None); // Normally ^C (SIGINT)
1150        assert_eq!(termios.signal(28), None); // Normally ^\ (SIGQUIT)
1151        assert_eq!(termios.signal(26), None); // Normally ^Z (SIGSTOP)
1152    }
1153}
1154
1155pub mod testing;