session and seat management daemon
2

Configure Feed

Select the types of activity you want to include in your feed.

harden graceful shutdown

r0chd (Jul 6, 2026, 10:57 AM +0200) 252ce1a4 5c176a8f

+54 -24
+10
daemon/src/cgroup.rs
··· 84 84 85 85 fs::remove_dir(&cgroup.path)?; 86 86 log::info!(session_id = session_id.as_raw(), path = cgroup.path.to_str(); "cgroup destroyed"); 87 + 88 + if state.shutting_down && state.cgroup_manager.is_empty() { 89 + state.loop_signal.stop(); 90 + } 91 + 87 92 return Ok(calloop::PostAction::Remove); 88 93 } 89 94 ··· 122 127 .find(|cgroup| cgroup.session_id == session_id) 123 128 .context("session not found")?; 124 129 let procs = fs::read_to_string(cgroup.path.join("cgroup.procs"))?; 130 + 125 131 for line in procs.lines() { 126 132 let pid = line.trim().parse::<i32>()?; 127 133 let pid = process::Pid::from_raw(pid).context("invalid pid in cgroup.procs")?; ··· 148 154 } 149 155 150 156 Ok(None) 157 + } 158 + 159 + pub const fn is_empty(&self) -> bool { 160 + self.cgroups.is_empty() 151 161 } 152 162 } 153 163
+44 -24
daemon/src/main.rs
··· 93 93 seat_entities: Vec<ecs::Entity>, 94 94 client_entities: Vec<ecs::Entity>, 95 95 seat_access: SeatAccess, 96 + shutting_down: bool, 97 + loop_signal: calloop::LoopSignal, 96 98 world: ecs::World, 97 99 } 98 100 ··· 398 400 399 401 log::info!(uid = client.creds().uid.as_raw(), pid = client.creds().pid.as_raw_pid(), session_count; "reboot accepted"); 400 402 401 - let session_ids = session::entities(&self.world, &self.session_entities) 403 + for session_id in session::entities(&self.world, &self.session_entities) 402 404 .filter_map(|entity| self.world.get::<ecs::Session>(entity)) 403 - .map(|session| session.0) 404 - .collect::<Vec<_>>(); 405 - 406 - for session_id in session_ids { 407 - if let Err(err) = self.cgroup_manager.terminate_session(session_id) { 408 - log::error!(session_id = session_id.as_raw(), error = err.to_string().as_str(); "failed to terminate session before reboot"); 405 + { 406 + if let Err(err) = self.cgroup_manager.terminate_session(session_id.0) { 407 + log::error!(session_id = session_id.0.as_raw(), error = err.to_string().as_str(); "failed to terminate session before reboot"); 409 408 object.send_error( 410 409 session_core_v1::ErrorCode::Io, 411 - format!("failed to terminate session {}: {err}", session_id.as_raw()), 410 + format!( 411 + "failed to terminate session {}: {err}", 412 + session_id.0.as_raw() 413 + ), 412 414 ); 413 415 return; 414 416 } ··· 445 447 446 448 log::info!(uid = client.creds().uid.as_raw(), pid = client.creds().pid.as_raw_pid(), session_count; "poweroff accepted"); 447 449 448 - let session_ids = session::entities(&self.world, &self.session_entities) 450 + for session_id in session::entities(&self.world, &self.session_entities) 449 451 .filter_map(|entity| self.world.get::<ecs::Session>(entity)) 450 - .map(|session| session.0) 451 - .collect::<Vec<_>>(); 452 - 453 - for session_id in session_ids { 454 - if let Err(err) = self.cgroup_manager.terminate_session(session_id) { 455 - log::error!(session_id = session_id.as_raw(), error = err.to_string().as_str(); "failed to terminate session before poweroff"); 452 + { 453 + if let Err(err) = self.cgroup_manager.terminate_session(session_id.0) { 454 + log::error!(session_id = session_id.0.as_raw(), error = err.to_string().as_str(); "failed to terminate session before poweroff"); 456 455 object.send_error( 457 456 session_core_v1::ErrorCode::Io, 458 - format!("failed to terminate session {}: {err}", session_id.as_raw()), 457 + format!( 458 + "failed to terminate session {}: {err}", 459 + session_id.0.as_raw() 460 + ), 459 461 ); 460 462 return; 461 463 } ··· 506 508 ); 507 509 508 510 let mut event_loop = calloop::EventLoop::try_new()?; 511 + let loop_signal = event_loop.get_signal(); 509 512 510 513 let cgroup_manager = cgroup::CgroupManager::new(event_loop.handle())?; 511 514 ··· 521 524 seat_entities: Vec::new(), 522 525 client_entities: Vec::new(), 523 526 seat_access, 527 + shutting_down: false, 528 + loop_signal, 524 529 world: ecs::World::new(), 525 530 }; 526 531 ··· 556 561 event_loop 557 562 .handle() 558 563 .insert_source(source, move |_, _, state| { 564 + if state.shutting_down { 565 + return Ok(calloop::PostAction::Continue); 566 + } 567 + 559 568 _ = socket.dispatch_events(state, false); 560 569 Ok(calloop::PostAction::Continue) 561 570 })?; 562 - 563 - let loop_signal = event_loop.get_signal(); 564 571 565 572 let signals = signals::Signals::new(&[ 566 573 signals::Signal::SIGUSR1, ··· 576 583 seat::handle_vt_signal(&mut state.world, &state.seat_entities, event.signal()) 577 584 } 578 585 signals::Signal::SIGTERM | signals::Signal::SIGINT => { 579 - loop_signal.stop(); 580 - Vec::new() 586 + if state.shutting_down { 587 + return; 588 + } 589 + 590 + state.shutting_down = true; 591 + 592 + for session_id in session::entities(&state.world, &state.session_entities) 593 + .filter_map(|entity| state.world.get::<ecs::Session>(entity)) 594 + { 595 + if let Err(err) = state.cgroup_manager.terminate_session(session_id.0) { 596 + log::error!(session_id = session_id.0.as_raw(), error = err.to_string().as_str(); "failed to terminate session during shutdown"); 597 + } 598 + } 599 + 600 + if state.cgroup_manager.is_empty() { 601 + state.loop_signal.stop(); 602 + } 603 + 604 + return; 581 605 } 582 606 _ => unreachable!(), 583 607 }; ··· 616 640 })?; 617 641 618 642 event_loop.run(None, &mut sessiond, |_| {})?; 619 - 620 - session::entities(&sessiond.world, &sessiond.session_entities) 621 - .filter_map(|entity| sessiond.world.get::<ecs::Session>(entity)) 622 - .for_each(|session| _ = sessiond.cgroup_manager.terminate_session(session.0)); 623 643 624 644 let _ = fs::remove_file("/run/sessiond.sock"); 625 645