Skip to main content

starnix_core/task/
session.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 macro_rules_attribute::apply;
6use starnix_sync::{LockBefore, Locked, ProcessGroupState, RwLock};
7use std::collections::BTreeMap;
8use std::sync::{Arc, Weak};
9
10use crate::device::terminal::Terminal;
11use crate::mutable_state::{state_accessor, state_implementation};
12use crate::task::ProcessGroup;
13use starnix_uapi::pid_t;
14use starnix_uapi::signals::{SIGCONT, SIGHUP};
15
16#[derive(Debug)]
17pub struct SessionMutableState {
18    /// The process groups in the session
19    ///
20    /// The references to ProcessGroup is weak to prevent cycles as ProcessGroup have a Arc reference to their
21    /// session.
22    /// It is still expected that these weak references are always valid, as process groups must unregister
23    /// themselves before they are deleted.
24    process_groups: BTreeMap<pid_t, Weak<ProcessGroup>>,
25
26    /// The leader of the foreground process group. This is necessary because the leader must
27    /// be returned even if the process group has already been deleted.
28    foreground_process_group: pid_t,
29
30    /// The controlling terminal of the session.
31    pub controlling_terminal: Option<ControllingTerminal>,
32}
33
34/// A session is a collection of `ProcessGroup` objects that are related to each other. Each
35/// session has a session ID (`sid`), which is a unique identifier for the session.
36///
37/// The session leader is the first `ProcessGroup` in a session. It is responsible for managing the
38/// session, including sending signals to all processes in the session and controlling the
39/// foreground and background process groups.
40///
41/// When a `ProcessGroup` is created, it is automatically added to the session of its parent.
42/// See `setsid(2)` for information about creating sessions.
43///
44/// A session can be destroyed when the session leader exits or when all process groups in the
45/// session are destroyed.
46#[derive(Debug)]
47pub struct Session {
48    /// The leader of the session
49    pub leader: pid_t,
50
51    /// The mutable state of the Session.
52    mutable_state: RwLock<SessionMutableState>,
53}
54
55impl PartialEq for Session {
56    fn eq(&self, other: &Self) -> bool {
57        self.leader == other.leader
58    }
59}
60
61impl Session {
62    pub fn new(leader: pid_t) -> Arc<Session> {
63        Arc::new(Session {
64            leader,
65            mutable_state: RwLock::new(SessionMutableState {
66                process_groups: BTreeMap::new(),
67                foreground_process_group: leader,
68                controlling_terminal: None,
69            }),
70        })
71    }
72
73    /// Disassociates the controlling terminal from the session.
74    pub fn disassociate_controlling_terminal<L>(&self, locked: &mut Locked<L>)
75    where
76        L: LockBefore<ProcessGroupState>,
77    {
78        loop {
79            // THREAD SAFETY: The controlling terminal must be extracted from the Session state
80            // lock. Respect Terminal => Session lock ordering by dropping the Session lock before
81            // acquiring the Terminal lock. The controlling terminal may change while reacquiring
82            // locks.
83            let Some(controlling_terminal) = self.read().controlling_terminal.clone() else {
84                return;
85            };
86            let mut terminal_state = controlling_terminal.terminal.write();
87            let mut state = self.write();
88
89            // THREAD SAFETY: Check whether the controlling terminal changed while the Session lock
90            // was dropped.
91            if !state.controlling_terminal.as_ref().map_or(false, |current_ct| {
92                current_ct.matches(&controlling_terminal.terminal, controlling_terminal.is_main)
93            }) {
94                // Drop the lock for the old terminal and try again.
95                continue;
96            }
97
98            state.controlling_terminal = None;
99            terminal_state.controller = None;
100
101            // THREAD SAFETY: Respect ThreadGroup => Terminal => Session lock ordering by dropping
102            // the Terminal and Session locks before signaling.
103            let process_group = state.get_foreground_process_group();
104            drop(state);
105            drop(terminal_state);
106            if let Some(pg) = process_group {
107                pg.send_signals(locked, &[SIGHUP, SIGCONT]);
108            }
109            return;
110        }
111    }
112
113    state_accessor!(Session, mutable_state);
114}
115
116#[apply(state_implementation!)]
117impl SessionMutableState<Base = Session> {
118    /// Removes the process group from the session. Returns whether the session is empty.
119    pub fn remove(&mut self, pid: pid_t) {
120        self.process_groups.remove(&pid);
121    }
122
123    pub fn insert(&mut self, process_group: &Arc<ProcessGroup>) {
124        self.process_groups.insert(process_group.leader, Arc::downgrade(process_group));
125    }
126
127    pub fn get_foreground_process_group_leader(&self) -> pid_t {
128        self.foreground_process_group
129    }
130
131    pub fn get_foreground_process_group(&self) -> Option<Arc<ProcessGroup>> {
132        self.process_groups.get(&self.foreground_process_group).and_then(Weak::upgrade)
133    }
134
135    pub fn set_foreground_process_group(&mut self, process_group: &Arc<ProcessGroup>) {
136        self.foreground_process_group = process_group.leader;
137    }
138}
139
140/// The controlling terminal of a session.
141#[derive(Clone, Debug)]
142pub struct ControllingTerminal {
143    /// The controlling terminal.
144    pub terminal: Arc<Terminal>,
145    /// Whether the session is associated to the main or replica side of the terminal.
146    pub is_main: bool,
147}
148
149impl ControllingTerminal {
150    pub fn new(terminal: &Terminal, is_main: bool) -> Self {
151        Self { terminal: terminal.to_owned(), is_main }
152    }
153
154    pub fn matches(&self, terminal: &Terminal, is_main: bool) -> bool {
155        std::ptr::eq(terminal, Arc::as_ptr(&self.terminal)) && is_main == self.is_main
156    }
157}