1#![allow(dead_code)]
12
13#[cfg(feature = "alloc")]
14extern crate alloc;
15
16#[cfg(feature = "alloc")]
17use alloc::{string::String, vec::Vec};
18use core::sync::atomic::{AtomicU64, Ordering};
19
20use spin::Mutex;
21
22use crate::{error::KernelError, process::ProcessId};
23
24const MAX_SESSIONS: usize = 8;
30
31const VT_BASE: u8 = 7;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct SessionId(pub u64);
42
43static NEXT_SESSION_ID: AtomicU64 = AtomicU64::new(1);
45
46fn alloc_session_id() -> SessionId {
47 SessionId(NEXT_SESSION_ID.fetch_add(1, Ordering::Relaxed))
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum SessionState {
57 Active,
59 Locked,
61 Background,
63 LoggingOut,
65}
66
67#[cfg(feature = "alloc")]
73#[derive(Debug, Clone)]
74pub struct Session {
75 pub id: SessionId,
77 pub user_id: u64,
79 pub username: String,
81 pub vt_number: u8,
83 pub state: SessionState,
85 pub foreground_group: u64,
87 pub background_groups: Vec<u64>,
89 pub login_time: u64,
91 pub process_ids: Vec<u64>,
93}
94
95#[cfg(feature = "alloc")]
96impl Session {
97 fn new(id: SessionId, user_id: u64, username: &str, vt_number: u8) -> Self {
99 Self {
100 id,
101 user_id,
102 username: String::from(username),
103 vt_number,
104 state: SessionState::Active,
105 foreground_group: 0,
106 background_groups: Vec::new(),
107 login_time: 0,
108 process_ids: Vec::new(),
109 }
110 }
111
112 pub fn contains_process(&self, pid: u64) -> bool {
114 self.process_ids.contains(&pid)
115 }
116
117 pub fn process_count(&self) -> usize {
119 self.process_ids.len()
120 }
121}
122
123#[cfg(feature = "alloc")]
129pub struct SessionManager {
130 sessions: Vec<Session>,
132 active_session: Option<SessionId>,
134}
135
136#[cfg(feature = "alloc")]
137impl Default for SessionManager {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143#[cfg(feature = "alloc")]
144impl SessionManager {
145 pub const fn new() -> Self {
147 Self {
148 sessions: Vec::new(),
149 active_session: None,
150 }
151 }
152
153 pub fn create_session(
158 &mut self,
159 user_id: u64,
160 username: &str,
161 vt: u8,
162 ) -> Result<SessionId, KernelError> {
163 if self.sessions.len() >= MAX_SESSIONS {
164 return Err(KernelError::InvalidState {
165 expected: "fewer than 8 sessions",
166 actual: "max sessions reached",
167 });
168 }
169
170 let id = alloc_session_id();
171 let vt_number = if vt > 0 {
172 vt
173 } else {
174 VT_BASE + self.sessions.len() as u8
175 };
176
177 let mut session = Session::new(id, user_id, username, vt_number);
178
179 #[cfg(target_arch = "x86_64")]
181 {
182 session.login_time = unsafe { core::arch::x86_64::_rdtsc() };
183 }
184 #[cfg(not(target_arch = "x86_64"))]
185 {
186 session.login_time = 0;
187 }
188
189 if self.active_session.is_none() {
191 session.state = SessionState::Active;
192 self.active_session = Some(id);
193 } else {
194 session.state = SessionState::Background;
195 }
196
197 self.sessions.push(session);
198 Ok(id)
199 }
200
201 pub fn destroy_session(&mut self, id: SessionId) {
203 self.sessions.retain(|s| s.id != id);
204 if self.active_session == Some(id) {
205 self.active_session = self.sessions.first().map(|s| s.id);
207 if let Some(new_active) = self.active_session {
208 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == new_active) {
209 s.state = SessionState::Active;
210 }
211 }
212 }
213 }
214
215 pub fn get_session(&self, id: SessionId) -> Option<&Session> {
217 self.sessions.iter().find(|s| s.id == id)
218 }
219
220 pub fn get_session_mut(&mut self, id: SessionId) -> Option<&mut Session> {
222 self.sessions.iter_mut().find(|s| s.id == id)
223 }
224
225 pub fn get_active_session(&self) -> Option<SessionId> {
227 self.active_session
228 }
229
230 pub fn switch_session(&mut self, id: SessionId) {
235 if let Some(current_id) = self.active_session {
237 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == current_id) {
238 if s.state == SessionState::Active {
239 s.state = SessionState::Background;
240 }
241 }
242 }
243
244 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == id) {
246 s.state = SessionState::Active;
247 self.active_session = Some(id);
248 }
249 }
250
251 pub fn lock_session(&mut self, id: SessionId) {
253 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == id) {
254 s.state = SessionState::Locked;
255 }
256 }
257
258 pub fn unlock_session(&mut self, id: SessionId) {
260 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == id) {
261 if s.state == SessionState::Locked {
262 s.state = SessionState::Active;
263 }
264 }
265 }
266
267 pub fn add_process(&mut self, session_id: SessionId, pid: ProcessId) {
269 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == session_id) {
270 if !s.process_ids.contains(&pid.0) {
271 s.process_ids.push(pid.0);
272 }
273 }
274 }
275
276 pub fn remove_process(&mut self, session_id: SessionId, pid: ProcessId) {
278 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == session_id) {
279 s.process_ids.retain(|&p| p != pid.0);
280 }
281 }
282
283 pub fn get_session_for_vt(&self, vt: u8) -> Option<SessionId> {
285 self.sessions
286 .iter()
287 .find(|s| s.vt_number == vt)
288 .map(|s| s.id)
289 }
290
291 pub fn list_sessions(&self) -> Vec<SessionId> {
293 self.sessions.iter().map(|s| s.id).collect()
294 }
295
296 pub fn session_count(&self) -> usize {
298 self.sessions.len()
299 }
300
301 pub fn can_signal(&self, source_pid: u64, target_pid: u64) -> bool {
306 let source_session = self
307 .sessions
308 .iter()
309 .find(|s| s.process_ids.contains(&source_pid));
310 let target_session = self
311 .sessions
312 .iter()
313 .find(|s| s.process_ids.contains(&target_pid));
314
315 match (source_session, target_session) {
316 (Some(src), Some(tgt)) => src.id == tgt.id,
317 _ => true,
319 }
320 }
321
322 pub fn begin_logout(&mut self, id: SessionId) {
324 if let Some(s) = self.sessions.iter_mut().find(|s| s.id == id) {
325 s.state = SessionState::LoggingOut;
326 }
327 }
328
329 pub fn get_session_processes(&self, id: SessionId) -> Vec<u64> {
331 self.sessions
332 .iter()
333 .find(|s| s.id == id)
334 .map(|s| s.process_ids.clone())
335 .unwrap_or_default()
336 }
337}
338
339#[cfg(feature = "alloc")]
345pub static SESSION_MANAGER: Mutex<SessionManager> = Mutex::new(SessionManager::new());
346
347#[cfg(feature = "alloc")]
349pub fn init() {
350 let _guard = SESSION_MANAGER.lock();
352 }
354
355#[cfg(test)]
360mod tests {
361 use alloc::vec;
362
363 use super::*;
364
365 #[test]
366 fn test_session_id_allocation() {
367 let id1 = alloc_session_id();
368 let id2 = alloc_session_id();
369 assert_ne!(id1, id2);
370 assert!(id2.0 > id1.0);
371 }
372
373 #[test]
374 fn test_create_session() {
375 let mut mgr = SessionManager::new();
376 let id = mgr.create_session(1000, "alice", 0).unwrap();
377 assert_eq!(mgr.session_count(), 1);
378 assert_eq!(mgr.get_active_session(), Some(id));
379
380 let session = mgr.get_session(id).unwrap();
381 assert_eq!(session.user_id, 1000);
382 assert_eq!(session.username, "alice");
383 assert_eq!(session.state, SessionState::Active);
384 }
385
386 #[test]
387 fn test_max_sessions() {
388 let mut mgr = SessionManager::new();
389 for i in 0..MAX_SESSIONS {
390 let name = alloc::format!("user{}", i);
391 mgr.create_session(i as u64, &name, 0).unwrap();
392 }
393 let result = mgr.create_session(100, "overflow", 0);
394 assert!(result.is_err());
395 }
396
397 #[test]
398 fn test_destroy_session() {
399 let mut mgr = SessionManager::new();
400 let id = mgr.create_session(1, "bob", 0).unwrap();
401 assert_eq!(mgr.session_count(), 1);
402 mgr.destroy_session(id);
403 assert_eq!(mgr.session_count(), 0);
404 assert_eq!(mgr.get_active_session(), None);
405 }
406
407 #[test]
408 fn test_switch_session() {
409 let mut mgr = SessionManager::new();
410 let id1 = mgr.create_session(1, "alice", 0).unwrap();
411 let id2 = mgr.create_session(2, "bob", 0).unwrap();
412
413 assert_eq!(mgr.get_active_session(), Some(id1));
414 assert_eq!(
415 mgr.get_session(id2).unwrap().state,
416 SessionState::Background
417 );
418
419 mgr.switch_session(id2);
420 assert_eq!(mgr.get_active_session(), Some(id2));
421 assert_eq!(
422 mgr.get_session(id1).unwrap().state,
423 SessionState::Background
424 );
425 assert_eq!(mgr.get_session(id2).unwrap().state, SessionState::Active);
426 }
427
428 #[test]
429 fn test_lock_unlock_session() {
430 let mut mgr = SessionManager::new();
431 let id = mgr.create_session(1, "charlie", 0).unwrap();
432
433 mgr.lock_session(id);
434 assert_eq!(mgr.get_session(id).unwrap().state, SessionState::Locked);
435
436 mgr.unlock_session(id);
437 assert_eq!(mgr.get_session(id).unwrap().state, SessionState::Active);
438 }
439
440 #[test]
441 fn test_add_remove_process() {
442 let mut mgr = SessionManager::new();
443 let id = mgr.create_session(1, "dave", 0).unwrap();
444
445 mgr.add_process(id, ProcessId(42));
446 mgr.add_process(id, ProcessId(43));
447 assert_eq!(mgr.get_session(id).unwrap().process_count(), 2);
448 assert!(mgr.get_session(id).unwrap().contains_process(42));
449
450 mgr.remove_process(id, ProcessId(42));
451 assert_eq!(mgr.get_session(id).unwrap().process_count(), 1);
452 assert!(!mgr.get_session(id).unwrap().contains_process(42));
453 }
454
455 #[test]
456 fn test_session_isolation() {
457 let mut mgr = SessionManager::new();
458 let id1 = mgr.create_session(1, "alice", 0).unwrap();
459 let id2 = mgr.create_session(2, "bob", 0).unwrap();
460
461 mgr.add_process(id1, ProcessId(10));
462 mgr.add_process(id2, ProcessId(20));
463
464 mgr.add_process(id1, ProcessId(11));
466 assert!(mgr.can_signal(10, 11));
467
468 assert!(!mgr.can_signal(10, 20));
470 }
471
472 #[test]
473 fn test_get_session_for_vt() {
474 let mut mgr = SessionManager::new();
475 let id = mgr.create_session(1, "eve", 9).unwrap();
476 assert_eq!(mgr.get_session_for_vt(9), Some(id));
477 assert_eq!(mgr.get_session_for_vt(10), None);
478 }
479
480 #[test]
481 fn test_list_sessions() {
482 let mut mgr = SessionManager::new();
483 let id1 = mgr.create_session(1, "a", 0).unwrap();
484 let id2 = mgr.create_session(2, "b", 0).unwrap();
485 let list = mgr.list_sessions();
486 assert_eq!(list.len(), 2);
487 assert!(list.contains(&id1));
488 assert!(list.contains(&id2));
489 }
490
491 #[test]
492 fn test_begin_logout() {
493 let mut mgr = SessionManager::new();
494 let id = mgr.create_session(1, "frank", 0).unwrap();
495 mgr.add_process(id, ProcessId(100));
496 mgr.begin_logout(id);
497 assert_eq!(mgr.get_session(id).unwrap().state, SessionState::LoggingOut);
498
499 let pids = mgr.get_session_processes(id);
500 assert_eq!(pids, vec![100u64]);
501 }
502
503 #[test]
504 fn test_destroy_promotes_next() {
505 let mut mgr = SessionManager::new();
506 let id1 = mgr.create_session(1, "a", 0).unwrap();
507 let id2 = mgr.create_session(2, "b", 0).unwrap();
508 assert_eq!(mgr.get_active_session(), Some(id1));
509
510 mgr.destroy_session(id1);
511 assert_eq!(mgr.get_active_session(), Some(id2));
513 assert_eq!(mgr.get_session(id2).unwrap().state, SessionState::Active);
514 }
515}