273 lines
10 KiB
Rust
273 lines
10 KiB
Rust
//! Session management for the display manager.
|
|
//!
|
|
//! This module defines the `SessionManager` struct, which is responsible for
|
|
//! managing user sessions in the display manager. It includes functionality
|
|
//! for loading configuration, starting and stopping sessions, and
|
|
//! handling session-related events.
|
|
use crate::configuration::Configuration;
|
|
use crate::{APPLICATION_NAME, VERSION};
|
|
use gio::{self, DBusConnection, OwnerId, ResourceLookupFlags};
|
|
use glib::{g_debug, g_info, g_warning};
|
|
use lazy_static::*;
|
|
use std::path::PathBuf;
|
|
use std::sync::{Arc, Mutex};
|
|
use std::time::Duration;
|
|
|
|
static G_DBUS_INTROSPECTION: &str = include_str!("../resources/introspection.xml");
|
|
const G_DBUS_SERVER_NAME: &str = "dev.iohub.diya.SessionManager";
|
|
const G_DBUS_SERVER_PATH: &str = "/dev/iohub/diya/SessionManager";
|
|
const G_DBUS_SERVER_ERROR_NAME: &str = "dev.iohub.diya.SessionManager.Error";
|
|
const G_DBUS_METHOD_LOGIN: &str = "login";
|
|
const G_PROCESSES_MON_TO: u64 = 500; // Interval for monitoring child processes in milliseconds
|
|
|
|
lazy_static! {
|
|
/// static global mutex variable that stores the current state of the session manager process, which can be either Running, End, or Reload.
|
|
/// This variable is used to control the behavior of the session manager based on signals received (e.g., SIGHUP for reload, SIGINT/SIGTERM for shutdown).
|
|
static ref G_SESSION_MANAGER: Arc<Mutex<SessionManagerState>> = Arc::new(Mutex::<SessionManagerState>::new(SessionManagerState::End));
|
|
}
|
|
|
|
pub enum SessionManagerState {
|
|
Running(SessionManager),
|
|
End,
|
|
Reload,
|
|
}
|
|
|
|
/// SessionManager
|
|
///
|
|
/// The `SessionManager` struct is responsible for managing user sessions in the display manager.
|
|
pub struct SessionManager {
|
|
/// the configuration for the session manager
|
|
config: Configuration,
|
|
/// The main loop for handling session events.
|
|
main_loop: glib::MainLoop,
|
|
// introspection: gio::DBusNodeInfo,
|
|
bus_owner_id: Option<OwnerId>,
|
|
signals: Vec<glib::SourceId>,
|
|
}
|
|
|
|
pub trait SessionManagerInterface {
|
|
fn run(&self, config: &PathBuf, is_dbus_session: bool) -> bool;
|
|
fn quit(&self, reload: bool);
|
|
}
|
|
|
|
trait SessionManagerCallback {
|
|
fn on_name_acquired(&self, connection: DBusConnection, name: &str);
|
|
fn on_bus_acquired(&self, connection: DBusConnection, name: &str);
|
|
fn on_name_lost(&self, connection: Option<DBusConnection>, name: &str);
|
|
fn on_method_call(
|
|
&self,
|
|
connection: DBusConnection,
|
|
sender: Option<&str>,
|
|
object_path: &str,
|
|
interface_name: Option<&str>,
|
|
method_name: &str,
|
|
parameters: gio::glib::Variant,
|
|
invocation: gio::DBusMethodInvocation,
|
|
);
|
|
fn on_process_monitor(&self);
|
|
}
|
|
|
|
impl SessionManagerInterface for Arc<Mutex<SessionManagerState>> {
|
|
fn run(&self, config: &PathBuf, is_dbus_session: bool) -> bool {
|
|
self.quit(false);
|
|
*self.lock().as_deref_mut().unwrap() = SessionManagerState::Running(SessionManager {
|
|
config: Configuration::from(&config).expect("Failed to load configuration"),
|
|
main_loop: glib::MainLoop::new(None, false),
|
|
bus_owner_id: None,
|
|
signals: Vec::new(),
|
|
});
|
|
let evtloop = match self.lock().as_deref_mut().unwrap() {
|
|
SessionManagerState::Running(session) => {
|
|
session.setup(is_dbus_session);
|
|
session.main_loop.clone()
|
|
}
|
|
_ => panic!("No SessionManager created"),
|
|
};
|
|
evtloop.run();
|
|
if let SessionManagerState::Reload = self.lock().as_deref().unwrap() {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn quit(&self, reload: bool) {
|
|
if let SessionManagerState::Running(session) = self.lock().as_deref().unwrap() {
|
|
session.main_loop.quit();
|
|
}
|
|
*self.lock().as_deref_mut().unwrap() = if reload {
|
|
SessionManagerState::Reload
|
|
} else {
|
|
SessionManagerState::End
|
|
};
|
|
}
|
|
}
|
|
|
|
impl SessionManagerCallback for Arc<Mutex<SessionManagerState>> {
|
|
fn on_name_acquired(&self, _connection: DBusConnection, name: &str) {
|
|
g_debug!(APPLICATION_NAME, "D-Bus name acquired: {}", name);
|
|
}
|
|
|
|
fn on_bus_acquired(&self, connection: DBusConnection, name: &str) {
|
|
g_debug!(APPLICATION_NAME, "D-BUS acquired for name {}", name);
|
|
let introspection = gio::DBusNodeInfo::for_xml(G_DBUS_INTROSPECTION).unwrap();
|
|
let interface = &introspection.interfaces()[0];
|
|
let _ = connection
|
|
.register_object(G_DBUS_SERVER_PATH, interface)
|
|
.method_call(
|
|
|connection: DBusConnection,
|
|
sender: Option<&str>,
|
|
object_path: &str,
|
|
interface_name: Option<&str>,
|
|
method_name: &str,
|
|
parameters: gio::glib::Variant,
|
|
invocation: gio::DBusMethodInvocation| {
|
|
SessionManager::instance().on_method_call(
|
|
connection,
|
|
sender,
|
|
object_path,
|
|
interface_name,
|
|
method_name,
|
|
parameters,
|
|
invocation,
|
|
);
|
|
},
|
|
)
|
|
.build()
|
|
.expect("Failed to register D-Bus object");
|
|
}
|
|
|
|
fn on_name_lost(&self, _connection: Option<DBusConnection>, name: &str) {
|
|
g_warning!(APPLICATION_NAME, "D-Bus name lost: {}", name);
|
|
if let SessionManagerState::Running(session) = self.lock().as_deref_mut().unwrap() {
|
|
session.bus_owner_id = None;
|
|
}
|
|
self.quit(false);
|
|
}
|
|
|
|
fn on_method_call(
|
|
&self,
|
|
connection: DBusConnection,
|
|
sender: Option<&str>,
|
|
object_path: &str,
|
|
interface_name: Option<&str>,
|
|
method_name: &str,
|
|
parameters: gio::glib::Variant,
|
|
invocation: gio::DBusMethodInvocation,
|
|
) {
|
|
if let SessionManagerState::Running(session) = self.lock().as_deref_mut().unwrap() {
|
|
if !matches!(interface_name, Some(G_DBUS_SERVER_NAME)) {
|
|
let msg = format!(
|
|
"Interface name mismatch: got {}, but expected {}",
|
|
interface_name.unwrap(),
|
|
G_DBUS_SERVER_NAME
|
|
);
|
|
g_warning!(APPLICATION_NAME, "{}", msg);
|
|
invocation.return_dbus_error(G_DBUS_SERVER_ERROR_NAME, &msg);
|
|
return;
|
|
}
|
|
g_debug!(
|
|
APPLICATION_NAME,
|
|
"Received method call: {}, interface: {}",
|
|
method_name,
|
|
interface_name.unwrap()
|
|
);
|
|
match method_name {
|
|
G_DBUS_METHOD_LOGIN => session.login(¶meters, invocation),
|
|
_ => {
|
|
let msg = format!("Unsupported method {}", method_name);
|
|
g_warning!(APPLICATION_NAME, "{}", msg);
|
|
invocation.return_dbus_error(G_DBUS_SERVER_ERROR_NAME, &msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_process_monitor(&self) {}
|
|
}
|
|
|
|
impl SessionManager {
|
|
pub fn instance() -> Arc<Mutex<SessionManagerState>> {
|
|
G_SESSION_MANAGER.clone()
|
|
}
|
|
/// Set up the session manager.
|
|
///
|
|
/// This method starts the main loop and begins handling session events.
|
|
/// It will block until the main loop is quit, at which point it will return.
|
|
fn setup(&mut self, is_dbus_session: bool) {
|
|
g_info!(
|
|
APPLICATION_NAME,
|
|
"Starting SessionManager version {}",
|
|
VERSION
|
|
);
|
|
g_debug!(APPLICATION_NAME, "{}", self.config);
|
|
g_debug!(APPLICATION_NAME, "Use session DBUS: {}", is_dbus_session);
|
|
self.bus_owner_id = Some(gio::bus_own_name(
|
|
if is_dbus_session {
|
|
gio::BusType::Session
|
|
} else {
|
|
gio::BusType::System
|
|
},
|
|
G_DBUS_SERVER_NAME,
|
|
gio::BusNameOwnerFlags::NONE,
|
|
move |connection, name| {
|
|
SessionManager::instance().on_bus_acquired(connection, name);
|
|
},
|
|
|connection, name| {
|
|
SessionManager::instance().on_name_acquired(connection, name);
|
|
},
|
|
move |connection, name| {
|
|
SessionManager::instance().on_name_lost(connection, name);
|
|
},
|
|
));
|
|
|
|
for signal in vec![libc::SIGINT, libc::SIGTERM, libc::SIGHUP] {
|
|
let signal_id = match signal {
|
|
libc::SIGHUP => glib::unix_signal_add_local(signal, move || {
|
|
g_warning!(APPLICATION_NAME, "Received SIGHUP, reloading service...");
|
|
SessionManager::instance().quit(true);
|
|
glib::ControlFlow::Continue
|
|
}),
|
|
_ => glib::unix_signal_add_local(signal, move || {
|
|
g_info!(
|
|
APPLICATION_NAME,
|
|
"Received signal {}, shutting down...",
|
|
signal
|
|
);
|
|
SessionManager::instance().quit(false);
|
|
glib::ControlFlow::Continue
|
|
}),
|
|
};
|
|
self.signals.push(signal_id);
|
|
}
|
|
let processes_monitor_id =
|
|
glib::timeout_add_local(Duration::from_millis(G_PROCESSES_MON_TO), move || {
|
|
// Monitor child processes here
|
|
// For example, you could check if any child processes have exited and handle that accordingly
|
|
// g_debug!(APPLICATION_NAME, "Monitoring child processes...");
|
|
SessionManager::instance().on_process_monitor();
|
|
glib::ControlFlow::Continue
|
|
});
|
|
self.signals.push(processes_monitor_id);
|
|
// Implementation for running the session manager
|
|
}
|
|
|
|
fn login(&self, parameters: &gio::glib::Variant, invocation: gio::DBusMethodInvocation) {
|
|
g_debug!(APPLICATION_NAME, "Logging called");
|
|
invocation.return_dbus_error(G_DBUS_SERVER_ERROR_NAME, "Unimplemented");
|
|
}
|
|
}
|
|
|
|
impl Drop for SessionManager {
|
|
fn drop(&mut self) {
|
|
g_info!(APPLICATION_NAME, "Shutting down SessionManager");
|
|
if let Some(bus_owner_id) = self.bus_owner_id.take() {
|
|
g_debug!(APPLICATION_NAME, "Releasing acquired D-Bus name");
|
|
gio::bus_unown_name(bus_owner_id);
|
|
}
|
|
for signal_id in self.signals.drain(..) {
|
|
signal_id.remove();
|
|
}
|
|
}
|
|
}
|