Skip to main content

starnix_core/device/
terminal.rs

1// Copyright 2022 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 crate::mutable_state::{state_accessor, state_implementation};
6use crate::task::{EventHandler, Pid, WaitCanceler, WaitQueue, Waiter};
7use crate::vfs::buffers::{InputBuffer, InputBufferExt as _, OutputBuffer};
8use crate::vfs::{DirEntryHandle, FsString, Mounts};
9use derivative::Derivative;
10pub use line_discipline::TerminalSide;
11use line_discipline::{LineDiscipline, PendingSignals};
12use macro_rules_attribute::apply;
13use starnix_sync::{DeviceTerminalsLock, LockDepMutex, LockDepRwLock, PtsIdsSetLock};
14use starnix_uapi::auth::FsCred;
15use starnix_uapi::device_id::DeviceId;
16use starnix_uapi::errors::Errno;
17use starnix_uapi::signals::{SIGCONT, SIGHUP, Signal};
18use starnix_uapi::vfs::FdEvents;
19use starnix_uapi::{error, pid_t, uapi};
20use std::collections::{BTreeSet, HashMap};
21use std::sync::{Arc, Weak};
22
23const DEVPTS_FIRST_MAJOR: u32 = 136;
24const DEVPTS_MAJOR_COUNT: u32 = 4;
25pub const DEVPTS_COUNT: u32 = DEVPTS_MAJOR_COUNT * 256;
26
27// Construct the DeviceId associated with the given pts replicas.
28pub fn get_device_type_for_pts(id: u32) -> DeviceId {
29    DeviceId::new(DEVPTS_FIRST_MAJOR + id / 256, id % 256)
30}
31
32/// Global state of the devpts filesystem.
33pub struct TtyState {
34    /// The terminal objects indexed by their identifier.
35    pub terminals: LockDepRwLock<HashMap<u32, Weak<Terminal>>, DeviceTerminalsLock>,
36
37    /// The set of allocated terminal identifiers.
38    pts_ids_set: LockDepMutex<PtsIdsSet, PtsIdsSetLock>,
39}
40
41impl TtyState {
42    /// Allocates a new terminal and returns it.
43    pub fn get_next_terminal(
44        self: &Arc<Self>,
45        dev_pts_root: DirEntryHandle,
46        creds: FsCred,
47    ) -> Result<Arc<Terminal>, Errno> {
48        let id = self.pts_ids_set.lock().acquire()?;
49        let terminal = Terminal::new(self.clone(), dev_pts_root, creds, id);
50        assert!(self.terminals.write().insert(id, Arc::downgrade(&terminal)).is_none());
51        Ok(terminal)
52    }
53
54    /// Release the terminal identifier into the set of available identifier.
55    pub fn release_terminal(&self, id: u32) -> Result<(), Errno> {
56        // Remove this terminal id from the set of terminals before releasing the
57        // identifier. Otherwise, a new terminal might reuse the id and get removed
58        // instead.
59        assert!(self.terminals.write().remove(&id).is_some());
60        self.pts_ids_set.lock().release(id);
61        Ok(())
62    }
63}
64
65impl Default for TtyState {
66    fn default() -> Self {
67        Self { terminals: Default::default(), pts_ids_set: PtsIdsSet::new(DEVPTS_COUNT).into() }
68    }
69}
70
71#[derive(Derivative)]
72#[derivative(Default)]
73#[derivative(Debug)]
74pub struct TerminalMutableState {
75    pub line_discipline: LineDiscipline,
76
77    /// Wait queue for the main side of the terminal.
78    main_wait_queue: WaitQueue,
79
80    /// Wait queue for the replica side of the terminal.
81    replica_wait_queue: WaitQueue,
82
83    /// The controlling session and foreground process group of the terminal.
84    pub controlling_session: Option<ControllingSession>,
85}
86
87/// State of a given terminal. This object handles both the main and the replica terminal.
88#[derive(Derivative)]
89#[derivative(Debug)]
90pub struct Terminal {
91    /// Weak self to allow cloning.
92    weak_self: Weak<Self>,
93
94    /// The global devpts state.
95    #[derivative(Debug = "ignore")]
96    state: Arc<TtyState>,
97
98    /// The root of the devpts fs responsible for this terminal.
99    pub dev_pts_root: DirEntryHandle,
100
101    /// The owner of the terminal.
102    pub fscred: FsCred,
103
104    /// The identifier of the terminal.
105    pub id: u32,
106
107    /// The mutable state of the Terminal.
108    mutable_state:
109        starnix_sync::LockDepRwLock<TerminalMutableState, starnix_sync::TerminalMutableStateLock>,
110}
111
112impl Terminal {
113    pub fn new(
114        state: Arc<TtyState>,
115        dev_pts_root: DirEntryHandle,
116        fscred: FsCred,
117        id: u32,
118    ) -> Arc<Self> {
119        Arc::new_cyclic(|weak_self| Self {
120            weak_self: weak_self.clone(),
121            state,
122            dev_pts_root,
123            fscred,
124            id,
125            mutable_state: Default::default(),
126        })
127    }
128
129    pub fn to_owned(&self) -> Arc<Terminal> {
130        self.weak_self.upgrade().expect("This should never be called while releasing the terminal")
131    }
132
133    /// Sets the terminal configuration.
134    pub fn set_termios(&self, termios: uapi::termios2) {
135        let signals = self.write().set_termios(termios);
136        self.send_signals(signals.signals());
137    }
138
139    pub fn flush(&self, is_main: bool, arg: u32) -> Result<(), Errno> {
140        self.write().flush(is_main, arg)
141    }
142
143    /// `close` implementation of the main side of the terminal.
144    pub fn main_close(&self) {
145        // Remove the entry in the file system.
146        let id = FsString::from(self.id.to_string());
147        // The child is not a directory, the mount doesn't matter.
148        self.dev_pts_root.remove_child(id.as_ref(), &Mounts::new());
149        self.write().main_close();
150    }
151
152    /// Called when a new reference to the main side of this terminal is made.
153    pub fn main_open(&self) {
154        self.write().main_open();
155    }
156
157    /// `wait_async` implementation of the main side of the terminal.
158    pub fn main_wait_async(
159        &self,
160        waiter: &Waiter,
161        events: FdEvents,
162        handler: EventHandler,
163    ) -> WaitCanceler {
164        self.read().main_wait_async(waiter, events, handler)
165    }
166
167    /// `query_events` implementation of the main side of the terminal.
168    pub fn main_query_events(&self) -> FdEvents {
169        self.read().main_query_events()
170    }
171
172    /// `read` implementation of the main side of the terminal.
173    pub fn main_read(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
174        self.write().main_read(data)
175    }
176
177    /// `write` implementation of the main side of the terminal.
178    pub fn main_write(&self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
179        let (bytes, signals) = self.write().main_write(data)?;
180        self.send_signals(signals.signals());
181        Ok(bytes)
182    }
183
184    /// `close` implementation of the replica side of the terminal.
185    pub fn replica_close(&self) {
186        self.write().replica_close();
187    }
188
189    /// Called when a new reference to the replica side of this terminal is made.
190    pub fn replica_open(&self) {
191        self.write().replica_open();
192    }
193
194    /// `wait_async` implementation of the replica side of the terminal.
195    pub fn replica_wait_async(
196        &self,
197        waiter: &Waiter,
198        events: FdEvents,
199        handler: EventHandler,
200    ) -> WaitCanceler {
201        self.read().replica_wait_async(waiter, events, handler)
202    }
203
204    /// `query_events` implementation of the replica side of the terminal.
205    pub fn replica_query_events(&self) -> FdEvents {
206        self.read().replica_query_events()
207    }
208
209    /// `read` implementation of the replica side of the terminal.
210    pub fn replica_read(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
211        let (bytes, signals) = self.write().replica_read(data)?;
212        self.send_signals(signals.signals());
213        Ok(bytes)
214    }
215
216    /// `write` implementation of the replica side of the terminal.
217    pub fn replica_write(&self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
218        self.write().replica_write(data)
219    }
220
221    /// Disassociates the controlling session from the terminal.
222    ///
223    /// Clears the controlling session, clears `controlling_terminal` on all member
224    /// thread groups in the session, and sends SIGHUP and SIGCONT to the foreground
225    /// process group outside of any terminal locks.
226    pub fn disassociate_controlling_session(&self) {
227        self.disassociate_controlling_session_if(None, None);
228    }
229
230    /// Disassociates the controlling session from the terminal if `expected_session` matches
231    /// (or is `None`), clearing matching member `controlling_terminal` references and sending
232    /// `SIGHUP` and `SIGCONT` to the foreground process group outside of any terminal locks.
233    pub fn disassociate_controlling_session_if(
234        &self,
235        expected_session: Option<&Pid>,
236        side: Option<TerminalSide>,
237    ) {
238        let controlling_session = {
239            let mut terminal_state = self.write();
240            if expected_session.is_none_or(|sid| terminal_state.controlling_session() == Some(sid))
241            {
242                terminal_state.controlling_session.take()
243            } else {
244                None
245            }
246        };
247        if let Some(controlling_session) = controlling_session {
248            controlling_session.disassociate(self, side);
249        }
250    }
251
252    /// Sends the specified signals to the foreground process group of this terminal.
253    ///
254    /// The terminal state lock is held only to extract the foreground PID and is dropped
255    /// before signal dispatch to prevent lock inversion with ThreadGroup locks.
256    pub fn send_signals(&self, signals: &[Signal]) {
257        if signals.is_empty() {
258            return;
259        }
260        let foreground_pg = {
261            let state = self.read();
262            state.controlling_session.as_ref().map(|cs| cs.foreground_process_group.clone())
263        };
264        if let Some(pgid) = foreground_pg {
265            pgid.send_signals_to_pgid(signals);
266        }
267    }
268
269    pub fn device(&self) -> DeviceId {
270        get_device_type_for_pts(self.id)
271    }
272
273    state_accessor!(Terminal, mutable_state);
274}
275
276struct InputBufferWrapper<'a>(&'a mut dyn crate::vfs::buffers::InputBuffer);
277
278impl<'a> line_discipline::InputBuffer for InputBufferWrapper<'a> {
279    fn available(&self) -> usize {
280        self.0.available()
281    }
282    fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
283        self.0.read_to_vec_exact(size)
284    }
285}
286
287struct OutputBufferWrapper<'a>(&'a mut dyn crate::vfs::buffers::OutputBuffer);
288
289impl<'a> line_discipline::OutputBuffer for OutputBufferWrapper<'a> {
290    fn available(&self) -> usize {
291        self.0.available()
292    }
293    fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
294        self.0.write(data)
295    }
296}
297
298#[apply(state_implementation!)]
299impl TerminalMutableState<Base = Terminal> {
300    /// Returns the controlling session PID if one is associated.
301    pub fn controlling_session(&self) -> Option<&Pid> {
302        self.controlling_session.as_ref().map(|cs| &cs.session)
303    }
304
305    /// Returns the terminal configuration.
306    pub fn termios(&self) -> &uapi::termios2 {
307        self.line_discipline.termios()
308    }
309
310    pub fn set_packet_mode(&mut self, enabled: bool) {
311        if enabled != self.line_discipline.is_packet_mode_enabled() {
312            self.line_discipline.set_packet_mode(enabled);
313            self.notify_waiters();
314        }
315    }
316
317    /// Returns the number of available bytes to read from the side of the terminal described by
318    /// `is_main`.
319    pub fn get_available_read_size(&self, is_main: bool) -> usize {
320        self.line_discipline.get_available_read_size(TerminalSide::from(is_main))
321    }
322
323    /// Sets the terminal configuration.
324    fn set_termios(&mut self, termios: uapi::termios2) -> PendingSignals {
325        let signals = self.line_discipline.set_termios(termios);
326        self.notify_waiters();
327        signals
328    }
329
330    pub fn flush(&mut self, is_main: bool, arg: u32) -> Result<(), Errno> {
331        self.line_discipline.flush(TerminalSide::from(is_main), arg)?;
332        self.notify_waiters();
333        Ok(())
334    }
335
336    /// `close` implementation of the main side of the terminal.
337    pub fn main_close(&mut self) {
338        self.line_discipline.main_close();
339        self.notify_waiters();
340    }
341
342    /// Called when a new reference to the main side of this terminal is made.
343    pub fn main_open(&mut self) {
344        self.line_discipline.main_open();
345    }
346
347    pub fn is_main_closed(&self) -> bool {
348        self.line_discipline.is_main_closed()
349    }
350
351    /// `wait_async` implementation of the main side of the terminal.
352    fn main_wait_async(
353        &self,
354        waiter: &Waiter,
355        events: FdEvents,
356        handler: EventHandler,
357    ) -> WaitCanceler {
358        self.main_wait_queue.wait_async_fd_events(waiter, events, handler)
359    }
360
361    /// `query_events` implementation of the main side of the terminal.
362    fn main_query_events(&self) -> FdEvents {
363        self.line_discipline.main_query_events()
364    }
365
366    /// `read` implementation of the main side of the terminal.
367    fn main_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
368        let mut wrapper = OutputBufferWrapper(data);
369        let result = self.line_discipline.main_read(&mut wrapper)?;
370        self.notify_waiters();
371        Ok(result)
372    }
373
374    /// `write` implementation of the main side of the terminal.
375    ///
376    /// Returns the number of bytes written and any signals generated while processing the input.
377    fn main_write(&mut self, data: &mut dyn InputBuffer) -> Result<(usize, PendingSignals), Errno> {
378        let mut wrapper = InputBufferWrapper(data);
379        let (result, signals) = self.line_discipline.main_write(&mut wrapper)?;
380        self.notify_waiters();
381        Ok((result, signals))
382    }
383
384    /// `close` implementation of the replica side of the terminal.
385    pub fn replica_close(&mut self) {
386        self.line_discipline.replica_close();
387        self.notify_waiters();
388    }
389
390    /// Called when a new reference to the replica side of this terminal is made.
391    pub fn replica_open(&mut self) {
392        self.line_discipline.replica_open();
393    }
394
395    /// `wait_async` implementation of the replica side of the terminal.
396    fn replica_wait_async(
397        &self,
398        waiter: &Waiter,
399        events: FdEvents,
400        handler: EventHandler,
401    ) -> WaitCanceler {
402        self.replica_wait_queue.wait_async_fd_events(waiter, events, handler)
403    }
404
405    /// `query_events` implementation of the replica side of the terminal.
406    fn replica_query_events(&self) -> FdEvents {
407        self.line_discipline.replica_query_events()
408    }
409
410    /// `read` implementation of the replica side of the terminal.
411    ///
412    /// Returns the number of bytes read and any signals generated while draining waiting input
413    /// buffers.
414    fn replica_read(
415        &mut self,
416        data: &mut dyn OutputBuffer,
417    ) -> Result<(usize, PendingSignals), Errno> {
418        let mut wrapper = OutputBufferWrapper(data);
419        let (result, signals) = self.line_discipline.replica_read(&mut wrapper)?;
420        self.notify_waiters();
421        Ok((result, signals))
422    }
423
424    /// `write` implementation of the replica side of the terminal.
425    fn replica_write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
426        let mut wrapper = InputBufferWrapper(data);
427        let result = self.line_discipline.replica_write(&mut wrapper)?;
428        self.notify_waiters();
429        Ok(result)
430    }
431
432    /// Notify any waiters if the state of the terminal changes.
433    fn notify_waiters(&mut self) {
434        let main_events = self.line_discipline.main_query_events();
435        if main_events.bits() != 0 {
436            self.main_wait_queue.notify_fd_events(main_events);
437        }
438        let replica_events = self.line_discipline.replica_query_events();
439        if replica_events.bits() != 0 {
440            self.replica_wait_queue.notify_fd_events(replica_events);
441        }
442    }
443}
444
445impl Drop for Terminal {
446    fn drop(&mut self) {
447        self.state.release_terminal(self.id).unwrap()
448    }
449}
450
451/// The controlling terminal of a process.
452#[derive(Clone, Debug)]
453pub struct ControllingTerminal {
454    /// The controlling terminal.
455    pub terminal: Arc<Terminal>,
456    /// The side of the terminal associated with the session.
457    pub side: TerminalSide,
458}
459
460impl ControllingTerminal {
461    pub fn new(terminal: &Terminal, side: TerminalSide) -> Self {
462        Self { terminal: terminal.to_owned(), side }
463    }
464
465    pub fn matches(&self, terminal: &Terminal, side: TerminalSide) -> bool {
466        std::ptr::eq(terminal, Arc::as_ptr(&self.terminal)) && side == self.side
467    }
468}
469
470/// The controlling session and foreground process group of a terminal.
471#[derive(Clone, Debug)]
472pub struct ControllingSession {
473    /// The controlling session leader PID.
474    pub session: Pid,
475
476    /// The foreground process group PID.
477    pub foreground_process_group: Pid,
478}
479
480impl ControllingSession {
481    pub fn new(session: &Pid, foreground_pgrp: &Pid) -> Self {
482        Self { session: Arc::clone(session), foreground_process_group: Arc::clone(foreground_pgrp) }
483    }
484
485    pub fn foreground_process_group(&self) -> &Pid {
486        &self.foreground_process_group
487    }
488
489    pub fn foreground_pgid(&self) -> pid_t {
490        self.foreground_process_group.id
491    }
492
493    pub fn set_foreground_process_group(&mut self, pgid: Pid) {
494        self.foreground_process_group = pgid;
495    }
496
497    /// Clears matching member `controlling_terminal` references and sends `SIGHUP` and `SIGCONT`
498    /// to the foreground process group after this session has been detached from `terminal`.
499    pub fn disassociate(self, terminal: &Terminal, side: Option<TerminalSide>) {
500        self.session.clear_controlling_terminal(Some((terminal, side)));
501        self.foreground_process_group.send_signals_to_pgid(&[SIGHUP, SIGCONT]);
502    }
503}
504
505#[derive(Debug)]
506struct PtsIdsSet {
507    pts_count: u32,
508    next_id: u32,
509    reclaimed_ids: BTreeSet<u32>,
510}
511
512impl PtsIdsSet {
513    fn new(pts_count: u32) -> Self {
514        Self { pts_count, next_id: 0, reclaimed_ids: BTreeSet::new() }
515    }
516
517    fn release(&mut self, id: u32) {
518        assert!(self.reclaimed_ids.insert(id))
519    }
520
521    fn acquire(&mut self) -> Result<u32, Errno> {
522        match self.reclaimed_ids.iter().next() {
523            Some(e) => {
524                let value = *e;
525                self.reclaimed_ids.remove(&value);
526                Ok(value)
527            }
528            None => {
529                if self.next_id < self.pts_count {
530                    let id = self.next_id;
531                    self.next_id += 1;
532                    Ok(id)
533                } else {
534                    error!(ENOSPC)
535                }
536            }
537        }
538    }
539}