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::fs::devpts::{DEVPTS_COUNT, get_device_type_for_pts};
6use crate::mutable_state::{state_accessor, state_implementation};
7use crate::task::{EventHandler, ProcessGroup, Session, WaitCanceler, WaitQueue, Waiter};
8use crate::vfs::buffers::{InputBuffer, InputBufferExt as _, OutputBuffer};
9use crate::vfs::{DirEntryHandle, FsString, Mounts};
10use derivative::Derivative;
11use macro_rules_attribute::apply;
12
13use line_discipline::{LineDiscipline, PendingSignals};
14use starnix_sync::{LockBefore, Locked, Mutex, ProcessGroupState, RwLock};
15use starnix_uapi::auth::FsCred;
16use starnix_uapi::device_type::DeviceType;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::vfs::FdEvents;
19use starnix_uapi::{error, uapi};
20use std::collections::{BTreeSet, HashMap};
21use std::sync::{Arc, Weak};
22
23/// Global state of the devpts filesystem.
24pub struct TtyState {
25    /// The terminal objects indexed by their identifier.
26    pub terminals: RwLock<HashMap<u32, Weak<Terminal>>>,
27
28    /// The set of available terminal identifier.
29    pts_ids_set: Mutex<PtsIdsSet>,
30}
31
32impl TtyState {
33    /// Returns the next available terminal.
34    pub fn get_next_terminal(
35        self: &Arc<Self>,
36        dev_pts_root: DirEntryHandle,
37        creds: FsCred,
38    ) -> Result<Arc<Terminal>, Errno> {
39        let id = self.pts_ids_set.lock().acquire()?;
40        let terminal = Terminal::new(self.clone(), dev_pts_root, creds, id);
41        assert!(self.terminals.write().insert(id, Arc::downgrade(&terminal)).is_none());
42        Ok(terminal)
43    }
44
45    /// Release the terminal identifier into the set of available identifier.
46    pub fn release_terminal(&self, id: u32) -> Result<(), Errno> {
47        // We need to remove this terminal id from the set of terminals before we release the
48        // identifier. Otherwise, the id might be reused for a new terminal and we'll remove
49        // the *new* terminal with that identifier instead of the old one.
50        assert!(self.terminals.write().remove(&id).is_some());
51        self.pts_ids_set.lock().release(id);
52        Ok(())
53    }
54}
55
56impl Default for TtyState {
57    fn default() -> Self {
58        Self {
59            terminals: RwLock::new(HashMap::new()),
60            pts_ids_set: Mutex::new(PtsIdsSet::new(DEVPTS_COUNT)),
61        }
62    }
63}
64
65#[derive(Derivative)]
66#[derivative(Default)]
67#[derivative(Debug)]
68pub struct TerminalMutableState {
69    pub line_discipline: LineDiscipline,
70
71    /// Wait queue for the main side of the terminal.
72    main_wait_queue: WaitQueue,
73
74    /// Wait queue for the replica side of the terminal.
75    replica_wait_queue: WaitQueue,
76
77    /// The controller for the terminal.
78    pub controller: Option<TerminalController>,
79}
80
81/// State of a given terminal. This object handles both the main and the replica terminal.
82#[derive(Derivative)]
83#[derivative(Debug)]
84pub struct Terminal {
85    /// Weak self to allow cloning.
86    weak_self: Weak<Self>,
87
88    /// The global devpts state.
89    #[derivative(Debug = "ignore")]
90    state: Arc<TtyState>,
91
92    /// The root of the devpts fs responsible for this terminal.
93    pub dev_pts_root: DirEntryHandle,
94
95    /// The owner of the terminal.
96    pub fscred: FsCred,
97
98    /// The identifier of the terminal.
99    pub id: u32,
100
101    /// The mutable state of the Terminal.
102    mutable_state: RwLock<TerminalMutableState>,
103}
104
105impl Terminal {
106    pub fn new(
107        state: Arc<TtyState>,
108        dev_pts_root: DirEntryHandle,
109        fscred: FsCred,
110        id: u32,
111    ) -> Arc<Self> {
112        Arc::new_cyclic(|weak_self| Self {
113            weak_self: weak_self.clone(),
114            state,
115            dev_pts_root,
116            fscred,
117            id,
118            mutable_state: RwLock::new(Default::default()),
119        })
120    }
121
122    pub fn to_owned(&self) -> Arc<Terminal> {
123        self.weak_self.upgrade().expect("This should never be called while releasing the terminal")
124    }
125
126    /// Sets the terminal configuration.
127    pub fn set_termios<L>(&self, locked: &mut Locked<L>, termios: uapi::termios)
128    where
129        L: LockBefore<ProcessGroupState>,
130    {
131        let signals = self.write().set_termios(termios);
132        self.send_signals(locked, signals);
133    }
134
135    /// `close` implementation of the main side of the terminal.
136    pub fn main_close(&self) {
137        // Remove the entry in the file system.
138        let id = FsString::from(self.id.to_string());
139        // The child is not a directory, the mount doesn't matter.
140        self.dev_pts_root.remove_child(id.as_ref(), &Mounts::new());
141        self.write().main_close();
142    }
143
144    /// Called when a new reference to the main side of this terminal is made.
145    pub fn main_open(&self) {
146        self.write().main_open();
147    }
148
149    /// `wait_async` implementation of the main side of the terminal.
150    pub fn main_wait_async(
151        &self,
152        waiter: &Waiter,
153        events: FdEvents,
154        handler: EventHandler,
155    ) -> WaitCanceler {
156        self.read().main_wait_async(waiter, events, handler)
157    }
158
159    /// `query_events` implementation of the main side of the terminal.
160    pub fn main_query_events(&self) -> FdEvents {
161        self.read().main_query_events()
162    }
163
164    /// `read` implementation of the main side of the terminal.
165    pub fn main_read<L>(
166        &self,
167        _locked: &mut Locked<L>,
168        data: &mut dyn OutputBuffer,
169    ) -> Result<usize, Errno>
170    where
171        L: LockBefore<ProcessGroupState>,
172    {
173        self.write().main_read(data)
174    }
175
176    /// `write` implementation of the main side of the terminal.
177    pub fn main_write<L>(
178        &self,
179        locked: &mut Locked<L>,
180        data: &mut dyn InputBuffer,
181    ) -> Result<usize, Errno>
182    where
183        L: LockBefore<ProcessGroupState>,
184    {
185        let (bytes, signals) = self.write().main_write(data)?;
186        self.send_signals(locked, signals);
187        Ok(bytes)
188    }
189
190    /// `close` implementation of the replica side of the terminal.
191    pub fn replica_close(&self) {
192        self.write().replica_close();
193    }
194
195    /// Called when a new reference to the replica side of this terminal is made.
196    pub fn replica_open(&self) {
197        self.write().replica_open();
198    }
199
200    /// `wait_async` implementation of the replica side of the terminal.
201    pub fn replica_wait_async(
202        &self,
203        waiter: &Waiter,
204        events: FdEvents,
205        handler: EventHandler,
206    ) -> WaitCanceler {
207        self.read().replica_wait_async(waiter, events, handler)
208    }
209
210    /// `query_events` implementation of the replica side of the terminal.
211    pub fn replica_query_events(&self) -> FdEvents {
212        self.read().replica_query_events()
213    }
214
215    /// `read` implementation of the replica side of the terminal.
216    pub fn replica_read<L>(
217        &self,
218        _locked: &mut Locked<L>,
219        data: &mut dyn OutputBuffer,
220    ) -> Result<usize, Errno>
221    where
222        L: LockBefore<ProcessGroupState>,
223    {
224        self.write().replica_read(data)
225    }
226
227    /// `write` implementation of the replica side of the terminal.
228    pub fn replica_write<L>(
229        &self,
230        _locked: &mut Locked<L>,
231        data: &mut dyn InputBuffer,
232    ) -> Result<usize, Errno>
233    where
234        L: LockBefore<ProcessGroupState>,
235    {
236        self.write().replica_write(data)
237    }
238
239    /// Send the pending signals to the associated foreground process groups if they exist.
240    fn send_signals<L>(&self, locked: &mut Locked<L>, signals: PendingSignals)
241    where
242        L: LockBefore<ProcessGroupState>,
243    {
244        let signals = signals.signals();
245        if !signals.is_empty() {
246            let process_group = {
247                let terminal_state = self.read();
248                let Some(controller) = terminal_state.controller.as_ref() else {
249                    return;
250                };
251                let Some(session) = controller.session.upgrade() else {
252                    return;
253                };
254                let Some(process_group) = session.read().get_foreground_process_group() else {
255                    return;
256                };
257                process_group
258            };
259            process_group.send_signals(locked, signals);
260        }
261    }
262
263    pub fn device(&self) -> DeviceType {
264        get_device_type_for_pts(self.id)
265    }
266
267    state_accessor!(Terminal, mutable_state);
268}
269
270struct InputBufferWrapper<'a>(&'a mut dyn crate::vfs::buffers::InputBuffer);
271
272impl<'a> line_discipline::InputBuffer for InputBufferWrapper<'a> {
273    fn available(&self) -> usize {
274        self.0.available()
275    }
276    fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
277        self.0.read_to_vec_exact(size)
278    }
279}
280
281struct OutputBufferWrapper<'a>(&'a mut dyn crate::vfs::buffers::OutputBuffer);
282
283impl<'a> line_discipline::OutputBuffer for OutputBufferWrapper<'a> {
284    fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
285        self.0.write(data)
286    }
287}
288
289#[apply(state_implementation!)]
290impl TerminalMutableState<Base = Terminal> {
291    /// Returns the terminal configuration.
292    pub fn termios(&self) -> &uapi::termios {
293        self.line_discipline.termios()
294    }
295
296    /// Returns the number of available bytes to read from the side of the terminal described by
297    /// `is_main`.
298    pub fn get_available_read_size(&self, is_main: bool) -> usize {
299        self.line_discipline.get_available_read_size(is_main)
300    }
301
302    /// Sets the terminal configuration.
303    fn set_termios(&mut self, termios: uapi::termios) -> PendingSignals {
304        let old_canon_enabled = self.line_discipline.is_canon_enabled();
305        let signals = self.line_discipline.set_termios(termios);
306        if old_canon_enabled && !self.line_discipline.is_canon_enabled() {
307            self.notify_waiters();
308        }
309        signals
310    }
311
312    /// `close` implementation of the main side of the terminal.
313    pub fn main_close(&mut self) {
314        self.line_discipline.main_close();
315        self.notify_waiters();
316    }
317
318    /// Called when a new reference to the main side of this terminal is made.
319    pub fn main_open(&mut self) {
320        self.line_discipline.main_open();
321    }
322
323    pub fn is_main_closed(&self) -> bool {
324        self.line_discipline.is_main_closed()
325    }
326
327    /// `wait_async` implementation of the main side of the terminal.
328    fn main_wait_async(
329        &self,
330        waiter: &Waiter,
331        events: FdEvents,
332        handler: EventHandler,
333    ) -> WaitCanceler {
334        self.main_wait_queue.wait_async_fd_events(waiter, events, handler)
335    }
336
337    /// `query_events` implementation of the main side of the terminal.
338    fn main_query_events(&self) -> FdEvents {
339        self.line_discipline.main_query_events()
340    }
341
342    /// `read` implementation of the main side of the terminal.
343    fn main_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
344        let mut wrapper = OutputBufferWrapper(data);
345        let result = self.line_discipline.main_read(&mut wrapper)?;
346        self.notify_waiters();
347        Ok(result)
348    }
349
350    /// `write` implementation of the main side of the terminal.
351    fn main_write(&mut self, data: &mut dyn InputBuffer) -> Result<(usize, PendingSignals), Errno> {
352        let mut wrapper = InputBufferWrapper(data);
353        let (result, signals) = self.line_discipline.main_write(&mut wrapper)?;
354        self.notify_waiters();
355        Ok((result, signals))
356    }
357
358    /// `close` implementation of the replica side of the terminal.
359    pub fn replica_close(&mut self) {
360        self.line_discipline.replica_close();
361        self.notify_waiters();
362    }
363
364    /// Called when a new reference to the replica side of this terminal is made.
365    pub fn replica_open(&mut self) {
366        self.line_discipline.replica_open();
367    }
368
369    /// `wait_async` implementation of the replica side of the terminal.
370    fn replica_wait_async(
371        &self,
372        waiter: &Waiter,
373        events: FdEvents,
374        handler: EventHandler,
375    ) -> WaitCanceler {
376        self.replica_wait_queue.wait_async_fd_events(waiter, events, handler)
377    }
378
379    /// `query_events` implementation of the replica side of the terminal.
380    fn replica_query_events(&self) -> FdEvents {
381        self.line_discipline.replica_query_events()
382    }
383
384    /// `read` implementation of the replica side of the terminal.
385    fn replica_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
386        let mut wrapper = OutputBufferWrapper(data);
387        let result = self.line_discipline.replica_read(&mut wrapper)?;
388        self.notify_waiters();
389        Ok(result)
390    }
391
392    /// `write` implementation of the replica side of the terminal.
393    fn replica_write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
394        let mut wrapper = InputBufferWrapper(data);
395        let result = self.line_discipline.replica_write(&mut wrapper)?;
396        self.notify_waiters();
397        Ok(result)
398    }
399
400    /// Notify any waiters if the state of the terminal changes.
401    fn notify_waiters(&mut self) {
402        let main_events = self.line_discipline.main_query_events();
403        if main_events.bits() != 0 {
404            self.main_wait_queue.notify_fd_events(main_events);
405        }
406        let replica_events = self.line_discipline.replica_query_events();
407        if replica_events.bits() != 0 {
408            self.replica_wait_queue.notify_fd_events(replica_events);
409        }
410    }
411}
412
413impl Drop for Terminal {
414    fn drop(&mut self) {
415        self.state.release_terminal(self.id).unwrap()
416    }
417}
418
419/// The controlling session of a terminal. Is is associated to a single side of the terminal,
420/// either main or replica.
421#[derive(Debug)]
422pub struct TerminalController {
423    pub session: Weak<Session>,
424}
425
426impl TerminalController {
427    pub fn new(session: &Arc<Session>) -> Option<Self> {
428        Some(Self { session: Arc::downgrade(&session) })
429    }
430
431    pub fn get_foreground_process_group(&self) -> Option<Arc<ProcessGroup>> {
432        self.session.upgrade().and_then(|session| session.read().get_foreground_process_group())
433    }
434}
435
436#[derive(Debug)]
437struct PtsIdsSet {
438    pts_count: u32,
439    next_id: u32,
440    reclaimed_ids: BTreeSet<u32>,
441}
442
443impl PtsIdsSet {
444    fn new(pts_count: u32) -> Self {
445        Self { pts_count, next_id: 0, reclaimed_ids: BTreeSet::new() }
446    }
447
448    fn release(&mut self, id: u32) {
449        assert!(self.reclaimed_ids.insert(id))
450    }
451
452    fn acquire(&mut self) -> Result<u32, Errno> {
453        match self.reclaimed_ids.iter().next() {
454            Some(e) => {
455                let value = *e;
456                self.reclaimed_ids.remove(&value);
457                Ok(value)
458            }
459            None => {
460                if self.next_id < self.pts_count {
461                    let id = self.next_id;
462                    self.next_id += 1;
463                    Ok(id)
464                } else {
465                    error!(ENOSPC)
466                }
467            }
468        }
469    }
470}