diff --git a/CHANGELOG.md b/CHANGELOG.md index 9134b116..6f315ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -# [3.1.5] - 2021-03-11 +# [3.1.6] - 2021-03-11 +### Changed +- Graphics switching will now wait until all users logged out before switching + ### Changed - Further tweaks to gfx switching - More logging on gfx switching diff --git a/Cargo.lock b/Cargo.lock index 3ca06e01..d02416e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,7 +188,7 @@ dependencies = [ [[package]] name = "daemon" -version = "3.1.5" +version = "3.1.6" dependencies = [ "env_logger", "intel-pstate", diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 86d1ba53..63928170 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -1,4 +1,7 @@ -use daemon::{ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions, ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions}; +use daemon::{ + ctrl_fan_cpu::FanCpuSupportedFunctions, ctrl_leds::LedSupportedFunctions, + ctrl_rog_bios::RogBiosSupportedFunctions, ctrl_supported::SupportedFunctions, +}; use gumdrop::{Opt, Options}; use rog_dbus::AuraDbusClient; use rog_types::{ @@ -98,7 +101,11 @@ struct BiosCommand { post_sound_set: Option, #[options(no_long, help = "read bios POST sound")] post_sound_get: bool, - #[options(meta = "", no_long, help = "activate dGPU dedicated/G-Sync ")] + #[options( + meta = "", + no_long, + help = "activate dGPU dedicated/G-Sync " + )] dedicated_gfx_set: Option, #[options(no_long, help = "get GPU mode")] dedicated_gfx_get: bool, @@ -277,20 +284,15 @@ fn do_gfx( } } - println!("Changing graphics modes..."); - println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`"); + println!( + "Your display-manager will restart in requested mode when all users are logged out" + ); + println!("If anything fails check `journalctl -b -u asusd`"); - if do_gfx_action( - command.force, - "This will restart your display-manager. Please save all work!", - "Setting graphics mode...", - ) { - dbus.proxies().gfx().gfx_write_mode(mode.into())?; - let res = dbus.gfx_wait_changed()?; - println!("{}", res); - std::process::exit(1) - } - std::process::exit(-1) + dbus.proxies().gfx().gfx_write_mode(mode.into())?; + let res = dbus.gfx_wait_changed()?; + println!("{}", res); + std::process::exit(0) } if command.get { let res = dbus.proxies().gfx().gfx_get_mode()?; @@ -307,21 +309,6 @@ fn do_gfx( Ok(()) } -fn do_gfx_action(no_confirm: bool, ask_msg: &str, ok_msg: &str) -> bool { - println!("{}", Red.paint(&format!("{} Continue?", ask_msg))); - - let mut buf = String::new(); - - std::io::stdin().read_line(&mut buf).expect("Input failed"); - let input = buf.chars().next().unwrap() as char; - - if input == 'Y' || input == 'y' || no_confirm { - println!("{}", Green.paint(&format!("{}", ok_msg))); - return true; - } - false -} - fn handle_led_mode( dbus: &AuraDbusClient, supported: &LedSupportedFunctions, @@ -423,15 +410,14 @@ fn handle_bios_option( println!("Missing arg or command\n"); let usage: Vec = BiosCommand::usage() - .lines() - .map(|s| s.to_string()) - .collect(); + .lines() + .map(|s| s.to_string()) + .collect(); - for line in usage - .iter() - .filter(|line| !(line.contains("sound") && !supported.post_sound_toggle) - || !(line.contains("GPU") && !supported.dedicated_gfx_toggle)) - { + for line in usage.iter().filter(|line| { + !(line.contains("sound") && !supported.post_sound_toggle) + || !(line.contains("GPU") && !supported.dedicated_gfx_toggle) + }) { println!("{}", line); } } @@ -450,9 +436,11 @@ fn handle_bios_option( if let Some(opt) = cmd.dedicated_gfx_set { println!("Rebuilding initrd to include drivers"); dbus.proxies().rog_bios().set_dedicated_gfx(opt)?; - println!("The mode change is not active until you reboot, on boot the bios will make the required change"); + println!("The mode change is not active until you reboot, on boot the bios will make the required change"); if opt { - println!("NOTE: on reboot your display manager will be forced to use Nvidia drivers"); + println!( + "NOTE: on reboot your display manager will be forced to use Nvidia drivers" + ); } else { println!("NOTE: after reboot you can then select regular graphics modes"); } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index a972fa89..33635af2 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daemon" -version = "3.1.5" +version = "3.1.6" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] @@ -40,4 +40,4 @@ toml = "^0.5" sysfs-class = "^0.1.2" # used for backlight control and baord ID rog_fan_curve = { version = "0.1", features = ["serde"] } # cpu power management -intel-pstate = "^0.2" +intel-pstate = "^0.2" \ No newline at end of file diff --git a/daemon/src/ctrl_gfx/error.rs b/daemon/src/ctrl_gfx/error.rs index 995867fb..cdc5a0b1 100644 --- a/daemon/src/ctrl_gfx/error.rs +++ b/daemon/src/ctrl_gfx/error.rs @@ -24,7 +24,10 @@ impl fmt::Display for GfxError { GfxError::DisplayManagerTimeout(state) => { write!(f, "Timed out waiting for display-manager {} state", state) } - GfxError::GsyncModeActive => write!(f, "Can not switch gfx modes when dedicated/G-Sync mode is active"), + GfxError::GsyncModeActive => write!( + f, + "Can not switch gfx modes when dedicated/G-Sync mode is active" + ), } } } diff --git a/daemon/src/ctrl_gfx/gfx.rs b/daemon/src/ctrl_gfx/gfx.rs index fcdcf8b1..d882669a 100644 --- a/daemon/src/ctrl_gfx/gfx.rs +++ b/daemon/src/ctrl_gfx/gfx.rs @@ -3,10 +3,11 @@ use ctrl_gfx::*; use ctrl_rog_bios::CtrlRogBios; use log::{error, info, warn}; use rog_types::gfx_vendors::GfxVendors; -use std::iter::FromIterator; -use std::process::Command; -use std::str::FromStr; +use session_manager::{are_gfx_sessions_alive, get_sessions}; use std::{io::Write, ops::Add, path::Path}; +use std::{iter::FromIterator, thread::JoinHandle}; +use std::{process::Command, thread::sleep, time::Duration}; +use std::{str::FromStr, sync::mpsc}; use std::{sync::Arc, sync::Mutex}; use sysfs_class::{PciDevice, SysClass}; use system::{GraphicsDevice, PciBus}; @@ -22,6 +23,7 @@ pub struct CtrlGraphics { #[allow(dead_code)] other: Vec, config: Arc>, + thread_kill: Arc>>>, } trait Dbus { @@ -48,15 +50,15 @@ impl Dbus for CtrlGraphics { fn set_vendor(&mut self, vendor: String) { if let Ok(tmp) = GfxVendors::from_str(&vendor) { - info!("Switching gfx mode to {}", vendor); + info!("GFX: Switching gfx mode to {}", vendor); let msg = self.set_gfx_config(tmp).unwrap_or_else(|err| { - error!("{}", err); + error!("GFX: {}", err); format!("Failed: {}", err.to_string()) }); self.notify_gfx(&vendor) - .unwrap_or_else(|err| warn!("{}", err)); + .unwrap_or_else(|err| warn!("GFX: {}", err)); self.notify_action(&msg) - .unwrap_or_else(|err| warn!("{}", err)); + .unwrap_or_else(|err| warn!("GFX: {}", err)); } } @@ -77,7 +79,7 @@ impl ZbusAdd for CtrlGraphics { self, ) .map_err(|err| { - warn!("CtrlGraphics: add_to_server {}", err); + warn!("GFX: CtrlGraphics: add_to_server {}", err); err }) .ok(); @@ -87,7 +89,7 @@ impl ZbusAdd for CtrlGraphics { impl Reloadable for CtrlGraphics { fn reload(&mut self) -> Result<(), RogError> { self.auto_power()?; - info!("Reloaded gfx mode: {:?}", self.get_gfx_mode()?); + info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?); Ok(()) } } @@ -96,7 +98,7 @@ impl CtrlGraphics { pub fn new(config: Arc>) -> std::io::Result { let bus = PciBus::new()?; - info!("Rescanning PCI bus"); + info!("GFX: Rescanning PCI bus"); bus.rescan()?; let devs = PciDevice::all()?; @@ -107,7 +109,7 @@ impl CtrlGraphics { for func in devs.iter() { if let Some(func_slot) = func.id().split('.').next() { if func_slot == parent_slot { - info!("{}: Function for {}", func.id(), parent.id()); + info!("GFX: {}: Function for {}", func.id(), parent.id()); functions.push(func.clone()); } } @@ -125,19 +127,19 @@ impl CtrlGraphics { if 0x03 == (c >> 16) & 0xFF { match dev.vendor()? { 0x1002 => { - info!("{}: AMD graphics", dev.id()); + info!("GFX: {}: AMD graphics", dev.id()); amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev))); } 0x10DE => { - info!("{}: NVIDIA graphics", dev.id()); + info!("GFX: {}: NVIDIA graphics", dev.id()); nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev))); } 0x8086 => { - info!("{}: Intel graphics", dev.id()); + info!("GFX: {}: Intel graphics", dev.id()); intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev))); } vendor => { - info!("{}: Other({:X}) graphics", dev.id(), vendor); + info!("GFX: {}: Other({:X}) graphics", dev.id(), vendor); other.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev))); } } @@ -151,11 +153,20 @@ impl CtrlGraphics { nvidia, other, config, + thread_kill: Arc::new(Mutex::new(None)), }) } - fn save_gfx_mode(&self, vendor: GfxVendors) -> Result<(), RogError> { - if let Ok(mut config) = self.config.lock() { + pub fn bus(&self) -> PciBus { + self.bus.clone() + } + + pub fn devices(&self) -> Vec { + self.nvidia.clone() + } + + fn save_gfx_mode(vendor: GfxVendors, config: Arc>) -> Result<(), RogError> { + if let Ok(mut config) = config.lock() { config.gfx_mode = vendor.clone(); config.write(); return Ok(()); @@ -181,10 +192,10 @@ impl CtrlGraphics { fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> { let action = if vendor == GfxVendors::Nvidia { - info!("Enabling nvidia-fallback.service"); + info!("GFX: Enabling nvidia-fallback.service"); "enable" } else { - info!("Disabling nvidia-fallback.service"); + info!("GFX: Disabling nvidia-fallback.service"); "disable" }; @@ -217,7 +228,7 @@ impl CtrlGraphics { } let file = XORG_PATH.to_string().add(XORG_FILE); - info!("Writing {}", file); + info!("GFX: Writing {}", file); let mut file = std::fs::OpenOptions::new() .create(true) .truncate(true) @@ -232,7 +243,7 @@ impl CtrlGraphics { } fn write_modprobe_conf() -> Result<(), RogError> { - info!("Writing {}", MODPROBE_PATH); + info!("GFX: Writing {}", MODPROBE_PATH); let mut file = std::fs::OpenOptions::new() .create(true) @@ -248,12 +259,12 @@ impl CtrlGraphics { Ok(()) } - fn unbind_remove_nvidia(&self) -> Result<(), RogError> { + fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), RogError> { // Unbind NVIDIA graphics devices and their functions - let unbinds = self.nvidia.iter().map(|dev| dev.unbind()); + let unbinds = devices.iter().map(|dev| dev.unbind()); // Remove NVIDIA graphics devices and their functions - let removes = self.nvidia.iter().map(|dev| dev.remove()); + let removes = devices.iter().map(|dev| dev.remove()); Result::from_iter(unbinds.chain(removes)) .map_err(|err| RogError::Command("device unbind error".into(), err))?; @@ -269,12 +280,15 @@ impl CtrlGraphics { match cmd.output() { Ok(output) => { if !output.status.success() { - error!("Failed to list uses of nvidia devices: {}", String::from_utf8_lossy(&output.stderr)); + error!( + "Failed to list uses of nvidia devices: {}", + String::from_utf8_lossy(&output.stderr) + ); } else if output.status.success() { - warn!("{}", String::from_utf8_lossy(&output.stdout)); + warn!("GFX: {}", String::from_utf8_lossy(&output.stdout)); } } - Err(err) => error!("Failed to list uses of nvidia devices: {}", err), + Err(err) => error!("GFX: Failed to list uses of nvidia devices: {}", err), } } @@ -287,7 +301,7 @@ impl CtrlGraphics { loop { if count > MAX_TRIES { let msg = format!("{} {} failed for unknown reason", action, driver); - error!("{}", msg); + error!("GFX: {}", msg); return Ok(()); //Err(RogError::Modprobe(msg)); } @@ -308,8 +322,8 @@ impl CtrlGraphics { driver, String::from_utf8_lossy(&output.stderr) ); - warn!("{}", msg); - warn!("It may be safe to ignore the above error, run `lsmod |grep nvidia` to confirm modules loaded"); + warn!("GFX: {}", msg); + warn!("GFX: It may be safe to ignore the above error, run `lsmod |grep nvidia` to confirm modules loaded"); return Ok(()); } if count >= MAX_TRIES { @@ -368,13 +382,16 @@ impl CtrlGraphics { return Err(GfxError::DisplayManagerTimeout(state.into()).into()); } - pub fn do_vendor_tasks(&mut self, vendor: GfxVendors) -> Result<(), RogError> { + pub fn do_vendor_tasks( + vendor: GfxVendors, + devices: &[GraphicsDevice], + bus: &PciBus, + ) -> Result<(), RogError> { Self::write_xorg_conf(vendor)?; Self::write_modprobe_conf()?; // TODO: Not required here, should put in startup? // Rescan before doing remove or add drivers - self.bus - .rescan() + bus.rescan() .map_err(|err| GfxError::Bus("bus rescan error".into(), err))?; match vendor { @@ -392,53 +409,105 @@ impl CtrlGraphics { for driver in NVIDIA_DRIVERS.iter() { Self::do_driver_action(driver, "rmmod")?; } - self.unbind_remove_nvidia()?; + Self::unbind_remove_nvidia(&devices)?; } } - self.save_gfx_mode(vendor)?; Ok(()) } + /// Spools until all user sessions are ended + fn fire_starter( + vendor: GfxVendors, + devices: Vec, + bus: PciBus, + sessions: Vec, + killer: mpsc::Receiver, + ) -> Result { + info!("GFX: display-manager thread started"); + while are_gfx_sessions_alive(&sessions) { + if let Ok(stop) = killer.try_recv() { + if stop { + return Ok("Graphics mode change was cancelled".into()); + } + } + sleep(Duration::from_millis(300)); + } + info!("GFX: all graphical user sessions ended, continuing"); + Self::do_display_manager_action("stop")?; + + match Self::wait_display_manager_state("inactive") { + Ok(_) => info!("GFX: display-manager stopped"), + Err(err) => { + warn!("GFX: {}", err); + warn!("GFX: Retry stop display manager"); + Self::do_display_manager_action("stop")?; + Self::wait_display_manager_state("inactive")?; + } + } + + Self::do_vendor_tasks(vendor, &devices, &bus)?; + Self::do_display_manager_action("start")?; + + if Self::wait_display_manager_state("active").is_err() { + error!("GFX: display-manager failed to start normally, attempting restart"); + Self::do_display_manager_action("restart")?; + Self::wait_display_manager_state("active")?; + } + info!("GFX: display-manager started"); + + let v: &str = vendor.into(); + info!("GFX: Graphics mode changed to {} successfully", v); + Ok(format!("Graphics mode changed to {} successfully", v)) + } + /// For manually calling (not on boot/startup) /// /// Will stop and start display manager without warning pub fn set_gfx_config(&mut self, vendor: GfxVendors) -> Result { if let Ok(gsync) = CtrlRogBios::get_gfx_mode() { - if gsync == 1{ + if gsync == 1 { return Err(GfxError::GsyncModeActive.into()); } } - Self::do_display_manager_action("stop")?; - match Self::wait_display_manager_state("inactive") { - Ok(_) => info!("display-manager stopped"), - Err(err) => { - warn!("{}", err); - warn!("Retry stop display manager"); - Self::do_display_manager_action("stop")?; - Self::wait_display_manager_state("inactive")?; + if let Ok(lock) = self.thread_kill.lock() { + if let Some(tx) = lock.as_ref() { + // Cancel the running thread + info!("GFX: Cancelling previous thread"); + tx.send(true) + .map_err(|err| { + warn!("GFX: {}", err); + }) + .ok(); } } - self.do_vendor_tasks(vendor)?; + let devices = self.nvidia.clone(); + let bus = self.bus.clone(); + let sessions = get_sessions().unwrap(); + let (tx, rx) = mpsc::channel(); + if let Ok(mut lock) = self.thread_kill.lock() { + *lock = Some(tx); + } + let killer = self.thread_kill.clone(); - Self::do_display_manager_action("start")?; - match Self::wait_display_manager_state("active") { - Ok(_) => info!("display-manager started"), - Err(err) => { - warn!("{}", err); - warn!("Retry start display manager"); - Self::do_display_manager_action("restart")?; - Self::wait_display_manager_state("active")?; + // Save selected mode in case of reboot + Self::save_gfx_mode(vendor, self.config.clone())?; + + let _join: JoinHandle<()> = std::thread::spawn(move || { + Self::fire_starter(vendor, devices, bus, sessions, rx) + .map_err(|err| { + error!("GFX: {}", err); + }) + .ok(); + // clear the tx/rx when done + if let Ok(mut lock) = killer.try_lock() { + *lock = None; } - } + return; + }); - if Self::wait_display_manager_state("active").is_err() { - error!("display-manager failed to start normally, attempting restart"); - Self::do_display_manager_action("restart")?; - } - Self::wait_display_manager_state("active")?; // TODO: undo if failed? Save last mode, catch errors... let v: &str = vendor.into(); Ok(format!("Graphics mode changed to {} successfully", v)) @@ -449,12 +518,12 @@ impl CtrlGraphics { // // Switch to dedicated if config says to do so // if config.gfx_nv_mode_is_dedicated && vendor == GfxVendors::Nvidia { // CtrlRogBios::set_gfx_mode(true) - // .unwrap_or_else(|err| warn!("Gfx controller: {}", err)); + // .unwrap_or_else(|err| warn!("GFX: Gfx controller: {}", err)); // } else if let Ok(ded) = CtrlRogBios::get_gfx_mode() { // // otherwise if switching to non-Nvidia mode turn off dedicated mode // if ded == 1 && vendor != GfxVendors::Nvidia { // CtrlRogBios::set_gfx_mode(false) - // .unwrap_or_else(|err| warn!("Gfx controller: {}", err)); + // .unwrap_or_else(|err| warn!("GFX: Gfx controller: {}", err)); // } // } // } @@ -462,7 +531,9 @@ impl CtrlGraphics { fn auto_power(&mut self) -> Result<(), RogError> { let vendor = self.get_gfx_mode()?; - self.do_vendor_tasks(vendor)?; + let devices = self.nvidia.clone(); + let bus = self.bus.clone(); + Self::do_vendor_tasks(vendor, &devices, &bus)?; Self::toggle_fallback_service(vendor)?; Ok(()) } diff --git a/daemon/src/ctrl_gfx/system.rs b/daemon/src/ctrl_gfx/system.rs index f70d6eb6..82f1f2bb 100644 --- a/daemon/src/ctrl_gfx/system.rs +++ b/daemon/src/ctrl_gfx/system.rs @@ -33,6 +33,7 @@ impl Module { } } +#[derive(Clone)] pub struct PciBus { path: PathBuf, } @@ -56,6 +57,7 @@ impl PciBus { } } +#[derive(Clone)] pub struct GraphicsDevice { _id: String, functions: Vec, diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 49826d1c..fafdbccb 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -112,7 +112,9 @@ fn start_daemon() -> Result<(), Box> { if let Ok(vendor) = ctrl.get_gfx_mode() { if ded == 1 && vendor != GfxVendors::Nvidia { warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); - ctrl.do_vendor_tasks(GfxVendors::Nvidia)?; + let devices = ctrl.devices(); + let bus = ctrl.bus(); + CtrlGraphics::do_vendor_tasks(GfxVendors::Nvidia, &devices, &bus)?; } else if ded == 0 { info!("Dedicated GFX toggle is off"); } diff --git a/daemon/src/error.rs b/daemon/src/error.rs index 971e8f66..f4635847 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -4,7 +4,7 @@ use rog_types::error::GraphicsError; use std::convert::From; use std::fmt; -use crate::ctrl_gfx::error::GfxError; +use crate::{ctrl_gfx::error::GfxError, session_manager::SessionError}; #[derive(Debug)] pub enum RogError { @@ -28,6 +28,7 @@ pub enum RogError { Initramfs(String), Modprobe(String), Command(String, std::io::Error), + Session(SessionError), } impl fmt::Display for RogError { @@ -54,6 +55,7 @@ impl fmt::Display for RogError { RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), + RogError::Session(detail) => write!(f, "Session error: {}", detail), } } } diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index aa59cd58..d6c16788 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -30,6 +30,8 @@ pub mod laptops; /// Fetch all supported functions for the laptop pub mod ctrl_supported; +pub mod session_manager; + mod error; use crate::error::RogError; diff --git a/daemon/src/session_manager.rs b/daemon/src/session_manager.rs new file mode 100644 index 00000000..5244cd60 --- /dev/null +++ b/daemon/src/session_manager.rs @@ -0,0 +1,175 @@ +use log::{error, warn}; +use serde_derive::{Deserialize, Serialize}; +use std::process::Command; + +#[derive(Debug, PartialEq)] +enum SessionType { + X11, + Wayland, + TTY, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct UserSession { + pub session: String, + pub uid: u32, + pub user: String, + pub seat: String, + pub tty: String, +} + +#[derive(Debug)] +pub struct Session { + session_id: String, + session_type: SessionType, +} + +pub fn are_gfx_sessions_alive(sessions: &[Session]) -> bool { + for session in sessions { + match is_gfx_alive(session) { + Ok(alive) => { + if alive { + return true; + } + } + Err(err) => warn!("Error checking sessions: {}", err), + } + } + false +} + +pub fn get_sessions() -> Result, SessionError> { + // loginctl list-sessions --no-legend + let mut cmd = Command::new("loginctl"); + cmd.arg("list-sessions"); + cmd.arg("--output"); + cmd.arg("json"); + + let mut sessions = Vec::new(); + + match cmd.output() { + Ok(output) => { + if !output.status.success() { + error!( + "Couldn't get sessions: {}", + String::from_utf8_lossy(&output.stderr) + ); + } else if output.status.success() { + if let Ok(data) = serde_json::from_slice::>(&output.stdout) { + for s in &data { + if let Ok(t) = get_session_type(&s.session) { + sessions.push(Session { + session_id: s.session.to_owned(), + session_type: t, + }) + } + } + return Ok(sessions); + } + } + } + Err(err) => error!("Couldn't get sessions: {}", err), + } + Err(SessionError::NoSessions) +} + +fn is_gfx_alive(session: &Session) -> Result { + if session.session_type == SessionType::TTY { + return Ok(false); + } + let session_id = session.session_id.to_owned(); + let mut cmd = Command::new("loginctl"); + cmd.arg("show-session"); + cmd.arg(&session_id); + cmd.arg("--property"); + cmd.arg("Type"); + + match cmd.output() { + Ok(output) => { + if !output.status.success() { + let msg = String::from_utf8_lossy(&output.stderr); + if msg.contains("No session") { + return Ok(false); + } + error!( + "Couldn't get session: {}", + String::from_utf8_lossy(&output.stderr) + ); + } else if output.status.success() { + return Ok(true); + } + } + Err(err) => error!("Couldn't get session: {}", err), + } + Ok(false) +} + +fn get_session_type(session_id: &str) -> Result { + //loginctl show-session 2 --property Type + let mut cmd = Command::new("loginctl"); + cmd.arg("show-session"); + cmd.arg(session_id); + cmd.arg("--property"); + cmd.arg("Type"); + cmd.arg("--property"); + cmd.arg("Class"); + + match cmd.output() { + Ok(output) => { + if !output.status.success() { + let msg = String::from_utf8_lossy(&output.stderr); + if msg.contains("No session") { + return Err(SessionError::NoSession(session_id.into())); + } + error!( + "Couldn't get session: {}", + String::from_utf8_lossy(&output.stderr) + ); + } else if output.status.success() { + let what = String::from_utf8_lossy(&output.stdout); + let mut stype = SessionType::TTY; + let mut user = false; + for line in what.lines() { + if let Some(is_it) = line.split("=").last() { + match is_it.trim() { + "user" => user = true, + "wayland" => stype = SessionType::Wayland, + "x11" => stype = SessionType::X11, + "tty" => stype = SessionType::TTY, + _ => return Err(SessionError::NoSession(session_id.into())), + } + } + } + if user { + return Ok(stype); + } + } + } + Err(err) => error!("Couldn't get session: {}", err), + } + Err(SessionError::NoSession(session_id.into())) +} + +use std::fmt; + +#[derive(Debug)] +pub enum SessionError { + NoSession(String), + NoSessions, + Command(String, std::io::Error), +} + +impl fmt::Display for SessionError { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SessionError::NoSession(id) => write!(f, "Session {} not active", id), + SessionError::NoSessions => write!(f, "No active sessions"), + SessionError::Command(func, error) => { + write!(f, "Command exec error: {}: {}", func, error) + } + } + } +} + +impl std::error::Error for SessionError {}