diff --git a/src/configuration.rs b/src/configuration.rs index 2d91a54..188ce77 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -37,6 +37,15 @@ impl Configuration { /// * `Ok(Configuration)` if the configuration was successfully loaded and parsed. /// * `Err(Box)` if there was an error reading the file or /// parsing the TOML content. + /// + /// # Examples + /// + /// ``` + /// use dysm_rs::configuration::Configuration; + /// use std::path::PathBuf; + /// + /// let _ = Configuration::from(&PathBuf::from("references/daemon.conf")).unwrap(); + /// ``` pub fn from(file: &PathBuf) -> Result> { g_info!( APPLICATION_NAME, diff --git a/src/session.rs b/src/session.rs index eb033ac..fcc15e7 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,13 +1,13 @@ //! 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 +//! managing user sessions. 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 gio::{self, DBusConnection, OwnerId, RegistrationId}; +use glib::{g_critical, g_debug, g_info, g_warning}; use lazy_static::*; use std::path::PathBuf; use std::sync::{Arc, Mutex}; @@ -21,14 +21,22 @@ 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). + /// A global instance of the `SessionManager` wrapped in a `Mutex` for thread safety. + /// This allows the session manager to be accessed and modified from different parts of the application + /// while ensuring that only one instance is active at a time. static ref G_SESSION_MANAGER: Arc> = Arc::new(Mutex::::new(SessionManagerState::End)); } +/// SessionManagerState +/// +/// An enum representing the state of the session manager. It can be in a running state with an active `SessionManager`, +/// or it can be in an end or reload state. pub enum SessionManagerState { + /// The session manager is currently running and managing user sessions. Running(SessionManager), + /// The session manager has been stopped and is no longer managing sessions. End, + /// The session manager is in the process of reloading its configuration or restarting. Reload, } @@ -40,20 +48,87 @@ pub struct SessionManager { config: Configuration, /// The main loop for handling session events. main_loop: glib::MainLoop, - // introspection: gio::DBusNodeInfo, + /// The D-Bus owner ID for the session manager's D-Bus name. bus_owner_id: Option, + /// A list of signal handler IDs for any signals the session manager has connected to. signals: Vec, + /// The D-Bus connection and registration ID for the session manager's D-Bus object. + bus_registration: Option<(DBusConnection, RegistrationId)>, } +/// Public interface for SessionManager +/// pub trait SessionManagerInterface { + /// Run the session manager with an input config file + /// + /// # Arguments + /// + /// - `&self` - Object that implements the Trait + /// - `config` (`&PathBuf`) - Config file. + /// - `is_dbus_session` (`bool`) - run the session manager using session DBUS or system DBUS + /// + /// # Returns + /// + /// - `bool` - if True service shall be restarted after finished + /// fn run(&self, config: &PathBuf, is_dbus_session: bool) -> bool; + + /// Quit the current session manager service + /// + /// # Arguments + /// + /// - `&self` - Object that implements the Trait + /// - `reload` (`bool`) - if true the service shall be restarted after finished + /// fn quit(&self, reload: bool); } +/// Trait that defines callbacks for SessionManager +/// trait SessionManagerCallback { + /// Handler for DBUS name acquired event + /// + /// # Arguments + /// + /// - `&self` - Object that implements the Trait. + /// - `connection` (`DBusConnection`) - DBUS connection. + /// - `name` (`&str`) - DBUS name. + /// fn on_name_acquired(&self, connection: DBusConnection, name: &str); + + /// Handler for DBUS bus acquired event + /// + /// # Arguments + /// + /// - `&self` - Object that implements this Trait + /// - `connection` (`DBusConnection`) - DBUS connection + /// - `name` (`&str`) - DBUS name + /// fn on_bus_acquired(&self, connection: DBusConnection, name: &str); + + /// Hander for DBUS name lost event + /// + /// # Arguments + /// + /// - `&self` - Object that implements this Trait + /// - `connection` (`Option`) - DBUS connection. + /// - `name` (`&str`) - DBUS name. + /// fn on_name_lost(&self, connection: Option, name: &str); + + /// Handler for DBUS method call event + /// + /// # Arguments + /// + /// - `&self` (`undefined`) - Object that implements this Trait. + /// - `connection` (`DBusConnection`) - DBUS connection. + /// - `sender` (`Option<&str>`) - Sender name. + /// - `object_path` (`&str`) - DBUS Object path. + /// - `interface_name` (`Option<&str>`) - DBUS interface name. + /// - `method_name` (`&str`) - DBUS method name. + /// - `parameters` (`gio::glib::Variant`) - DBUS method parameters. + /// - `invocation` (`gio::DBusMethodInvocation`) - DBUS method invocation object. + /// fn on_method_call( &self, connection: DBusConnection, @@ -64,7 +139,14 @@ trait SessionManagerCallback { parameters: gio::glib::Variant, invocation: gio::DBusMethodInvocation, ); - fn on_process_monitor(&self); + + /// Hander for service processes monitor + /// + /// # Arguments + /// + /// - `&self` - Object that implements this Trait + /// + fn on_processes_monitor(&self); } impl SessionManagerInterface for Arc> { @@ -75,6 +157,7 @@ impl SessionManagerInterface for Arc> { main_loop: glib::MainLoop::new(None, false), bus_owner_id: None, signals: Vec::new(), + bus_registration: None, }); let evtloop = match self.lock().as_deref_mut().unwrap() { SessionManagerState::Running(session) => { @@ -84,10 +167,9 @@ impl SessionManagerInterface for Arc> { _ => panic!("No SessionManager created"), }; evtloop.run(); - if let SessionManagerState::Reload = self.lock().as_deref().unwrap() { - true - } else { - false + match self.lock().as_deref().unwrap() { + SessionManagerState::Reload => true, + _ => false, } } @@ -110,9 +192,21 @@ impl SessionManagerCallback for Arc> { 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 introspection = match gio::DBusNodeInfo::for_xml(G_DBUS_INTROSPECTION) { + Ok(info) => info, + Err(err) => { + g_critical!( + APPLICATION_NAME, + "Failed to parse D-Bus introspection data: {}", + err + ); + self.quit(false); + return; + } + }; let interface = &introspection.interfaces()[0]; - let _ = connection + + let registration_id = match connection .register_object(G_DBUS_SERVER_PATH, interface) .method_call( |connection: DBusConnection, @@ -134,11 +228,25 @@ impl SessionManagerCallback for Arc> { }, ) .build() - .expect("Failed to register D-Bus object"); + { + Err(error) => { + g_critical!( + APPLICATION_NAME, + "Failed to register D-Bus object at path {}: {}", + G_DBUS_SERVER_PATH, + error + ); + self.quit(false); + return; + } + Ok(resource) => resource, + }; + if let SessionManagerState::Running(session) = self.lock().as_deref_mut().unwrap() { + session.bus_registration = Some((connection, registration_id)); + } } - fn on_name_lost(&self, _connection: Option, name: &str) { - g_warning!(APPLICATION_NAME, "D-Bus name lost: {}", name); + g_critical!(APPLICATION_NAME, "D-Bus name lost: {}", name); if let SessionManagerState::Running(session) = self.lock().as_deref_mut().unwrap() { session.bus_owner_id = None; } @@ -147,9 +255,9 @@ impl SessionManagerCallback for Arc> { fn on_method_call( &self, - connection: DBusConnection, - sender: Option<&str>, - object_path: &str, + _connection: DBusConnection, + _sender: Option<&str>, + _object_path: &str, interface_name: Option<&str>, method_name: &str, parameters: gio::glib::Variant, @@ -183,17 +291,38 @@ impl SessionManagerCallback for Arc> { } } - fn on_process_monitor(&self) {} + fn on_processes_monitor(&self) { + if let SessionManagerState::Running(session) = self.lock().as_deref_mut().unwrap() { + session.monitor(); + } + } } impl SessionManager { + /// Get a threadsafe singleton instance of the SessionManager + /// + /// # Returns + /// + /// - `Arc>` - Smart pointer to SessionManager + /// + /// # Examples + /// + /// ``` + /// use dysm_rs::SessionManager; + /// + /// let instance = SessionManager::instance(); + /// ``` pub fn instance() -> Arc> { G_SESSION_MANAGER.clone() } - /// Set up the session manager. + + /// Setup the session manager before running + /// + /// # Arguments + /// + /// - `&mut self` (`&mut SessionManager`) - Mutable reference to SessionManager. + /// - `is_dbus_session` (`bool`) - if true setup service using DBUS session, otherwise SBUS system /// - /// 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, @@ -245,17 +374,37 @@ impl SessionManager { // 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(); + SessionManager::instance().on_processes_monitor(); glib::ControlFlow::Continue }); self.signals.push(processes_monitor_id); // Implementation for running the session manager } + /// Process DBUS login method cal + /// + /// # Arguments + /// + /// - `&self` (`&mut SessionManager`) - Mutable reference to current SessionManager. + /// - `parameters` (`&gio::glib::Variant`) - DBUS method parameters. + /// - `invocation` (`gio::DBusMethodInvocation`) - DBUS method invocation object. + /// 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"); } + + /// Monitor session processes + /// + /// # Arguments + /// + /// - `&self` (`&mut SessionManager`) - Mutable reference to current SessionManager. + /// + fn monitor(&self) { + // Here you would implement the logic to monitor child processes. + // For example, you could check if any child processes have exited and handle that accordingly. + g_debug!(APPLICATION_NAME, "Monitoring child processes..."); + } } impl Drop for SessionManager { @@ -265,6 +414,19 @@ impl Drop for SessionManager { g_debug!(APPLICATION_NAME, "Releasing acquired D-Bus name"); gio::bus_unown_name(bus_owner_id); } + if let Some((connection, registration_id)) = self.bus_registration.take() { + g_debug!(APPLICATION_NAME, "Unregistering D-Bus object"); + connection + .unregister_object(registration_id) + .unwrap_or_else(|e| { + g_critical!( + APPLICATION_NAME, + "Unable to unregister object from D-Bus connection: {}", + e + ) + }); + } + g_debug!(APPLICATION_NAME, "Removing signal handlers"); for signal_id in self.signals.drain(..) { signal_id.remove(); }