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