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        // Read the controlling terminal. We must drop the read lock on the session
79        // before acquiring the write lock on the terminal to respect the lock ordering
80        // (Terminal before Session).
81        let controlling_terminal = {
82            let session_reader = self.read();
83            session_reader.controlling_terminal.clone()
84        };
85
86        if let Some(ct) = controlling_terminal {
87            let mut terminal_state = ct.terminal.write();
88            let mut session_writer = self.write();
89
90            // Verify it is still the same terminal
91            if session_writer
92                .controlling_terminal
93                .as_ref()
94                .map_or(false, |current_ct| current_ct.matches(&ct.terminal, ct.is_main))
95            {
96                session_writer.controlling_terminal = None;
97                terminal_state.controller = None;
98
99                if let Some(pg) = session_writer.get_foreground_process_group() {
100                    pg.send_signals(locked, &[SIGHUP, SIGCONT]);
101                }
102            }
103        }
104    }
105
106    state_accessor!(Session, mutable_state);
107}
108
109#[apply(state_implementation!)]
110impl SessionMutableState<Base = Session> {
111    /// Removes the process group from the session. Returns whether the session is empty.
112    pub fn remove(&mut self, pid: pid_t) {
113        self.process_groups.remove(&pid);
114    }
115
116    pub fn insert(&mut self, process_group: &Arc<ProcessGroup>) {
117        self.process_groups.insert(process_group.leader, Arc::downgrade(process_group));
118    }
119
120    pub fn get_foreground_process_group_leader(&self) -> pid_t {
121        self.foreground_process_group
122    }
123
124    pub fn get_foreground_process_group(&self) -> Option<Arc<ProcessGroup>> {
125        self.process_groups.get(&self.foreground_process_group).and_then(Weak::upgrade)
126    }
127
128    pub fn set_foreground_process_group(&mut self, process_group: &Arc<ProcessGroup>) {
129        self.foreground_process_group = process_group.leader;
130    }
131}
132
133/// The controlling terminal of a session.
134#[derive(Clone, Debug)]
135pub struct ControllingTerminal {
136    /// The controlling terminal.
137    pub terminal: Arc<Terminal>,
138    /// Whether the session is associated to the main or replica side of the terminal.
139    pub is_main: bool,
140}
141
142impl ControllingTerminal {
143    pub fn new(terminal: &Terminal, is_main: bool) -> Self {
144        Self { terminal: terminal.to_owned(), is_main }
145    }
146
147    pub fn matches(&self, terminal: &Terminal, is_main: bool) -> bool {
148        std::ptr::eq(terminal, Arc::as_ptr(&self.terminal)) && is_main == self.is_main
149    }
150}