Compare commits

...

3 Commits
3.1.5 ... 3.1.6

Author SHA1 Message Date
Luke D Jones
8a63dce85f Bugfix: destroy the deref clone stackoverflow 2021-03-11 23:42:38 +13:00
Luke Jones
01386599f4 Merge branch 'fluke/hotfixing' into 'main'
Graphics switching now waits for user sessions to end

See merge request asus-linux/asus-nb-ctrl!31
2021-03-11 08:17:41 +00:00
Luke D Jones
4310b4b742 Graphics switching now waits for user sessions to end 2021-03-11 21:13:41 +13:00
13 changed files with 387 additions and 138 deletions

View File

@@ -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

4
Cargo.lock generated
View File

@@ -33,7 +33,7 @@ dependencies = [
[[package]]
name = "asusctl"
version = "3.1.2"
version = "3.1.3"
dependencies = [
"daemon",
"gumdrop",
@@ -188,7 +188,7 @@ dependencies = [
[[package]]
name = "daemon"
version = "3.1.5"
version = "3.1.6"
dependencies = [
"env_logger",
"intel-pstate",

View File

@@ -1,6 +1,6 @@
[package]
name = "asusctl"
version = "3.1.2"
version = "3.1.3"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"

View File

@@ -1,12 +1,10 @@
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::{
anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN},
cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin},
gfx_vendors::GfxVendors,
profile::{FanLevel, ProfileCommand, ProfileEvent},
};
use rog_types::{anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN}, aura_modes::AuraModes, cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin}, gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}};
use std::env::args;
use yansi_term::Colour::Green;
use yansi_term::Colour::Red;
@@ -98,7 +96,11 @@ struct BiosCommand {
post_sound_set: Option<bool>,
#[options(no_long, help = "read bios POST sound")]
post_sound_get: bool,
#[options(meta = "", no_long, help = "activate dGPU dedicated/G-Sync <true/false>")]
#[options(
meta = "",
no_long,
help = "activate dGPU dedicated/G-Sync <true/false>"
)]
dedicated_gfx_set: Option<bool>,
#[options(no_long, help = "get GPU mode")]
dedicated_gfx_get: bool,
@@ -277,20 +279,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 +304,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,
@@ -349,17 +331,23 @@ fn handle_led_mode(
}
println!("\nHelp can also be requested on modes, e.g: static --help");
std::process::exit(1);
return Ok(());
}
if mode.next_mode && mode.prev_mode {
println!("Please specify either next or previous")
println!("Please specify either next or previous");
return Ok(());
}
if mode.next_mode {
dbus.proxies().led().next_led_mode()?;
} else if mode.prev_mode {
dbus.proxies().led().prev_led_mode()?;
} else if let Some(command) = mode.command.as_ref() {
dbus.proxies().led().set_led_mode(&command.into())?
} else if let Some(mode) = mode.command.as_ref() {
if mode.help_requested() {
println!("{}", mode.self_usage());
return Ok(());
}
dbus.proxies().led().set_led_mode(&<AuraModes>::from(mode))?;
}
Ok(())
}
@@ -423,15 +411,14 @@ fn handle_bios_option(
println!("Missing arg or command\n");
let usage: Vec<String> = 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 +437,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");
}

View File

@@ -1,6 +1,6 @@
[package]
name = "daemon"
version = "3.1.5"
version = "3.1.6"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -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"

View File

@@ -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"
),
}
}
}

View File

@@ -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<GraphicsDevice>,
config: Arc<Mutex<Config>>,
thread_kill: Arc<Mutex<Option<mpsc::Sender<bool>>>>,
}
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<Mutex<Config>>) -> std::io::Result<CtrlGraphics> {
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<GraphicsDevice> {
self.nvidia.clone()
}
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<Config>>) -> 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<GraphicsDevice>,
bus: PciBus,
sessions: Vec<session_manager::Session>,
killer: mpsc::Receiver<bool>,
) -> Result<String, RogError> {
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<String, RogError> {
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(())
}

View File

@@ -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<PciDevice>,

View File

@@ -112,7 +112,9 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
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");
}

View File

@@ -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),
}
}
}

View File

@@ -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;

View File

@@ -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<Vec<Session>, 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::<Vec<UserSession>>(&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<bool, SessionError> {
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<SessionType, SessionError> {
//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 {}

View File

@@ -204,28 +204,28 @@ pub enum AuraModes {
impl From<SetAuraBuiltin> for AuraModes {
fn from(mode: SetAuraBuiltin) -> Self {
match mode {
SetAuraBuiltin::Static(x) => AuraModes::Static(x),
SetAuraBuiltin::Breathe(x) => AuraModes::Breathe(x),
SetAuraBuiltin::Strobe(x) => AuraModes::Strobe(x),
SetAuraBuiltin::Rainbow(x) => AuraModes::Rainbow(x),
SetAuraBuiltin::Star(x) => AuraModes::Star(x),
SetAuraBuiltin::Rain(x) => AuraModes::Rain(x),
SetAuraBuiltin::Highlight(x) => AuraModes::Highlight(x),
SetAuraBuiltin::Laser(x) => AuraModes::Laser(x),
SetAuraBuiltin::Ripple(x) => AuraModes::Ripple(x),
SetAuraBuiltin::Pulse(x) => AuraModes::Pulse(x),
SetAuraBuiltin::Comet(x) => AuraModes::Comet(x),
SetAuraBuiltin::Flash(x) => AuraModes::Flash(x),
SetAuraBuiltin::MultiStatic(x) => AuraModes::MultiStatic(x),
SetAuraBuiltin::MultiBreathe(x) => AuraModes::MultiBreathe(x),
}
(&mode).into()
}
}
impl From<&SetAuraBuiltin> for AuraModes {
fn from(mode: &SetAuraBuiltin) -> Self {
mode.clone().into()
match mode {
SetAuraBuiltin::Static(x) => AuraModes::Static(x.clone()),
SetAuraBuiltin::Breathe(x) => AuraModes::Breathe(x.clone()),
SetAuraBuiltin::Strobe(x) => AuraModes::Strobe(x.clone()),
SetAuraBuiltin::Rainbow(x) => AuraModes::Rainbow(x.clone()),
SetAuraBuiltin::Star(x) => AuraModes::Star(x.clone()),
SetAuraBuiltin::Rain(x) => AuraModes::Rain(x.clone()),
SetAuraBuiltin::Highlight(x) => AuraModes::Highlight(x.clone()),
SetAuraBuiltin::Laser(x) => AuraModes::Laser(x.clone()),
SetAuraBuiltin::Ripple(x) => AuraModes::Ripple(x.clone()),
SetAuraBuiltin::Pulse(x) => AuraModes::Pulse(x.clone()),
SetAuraBuiltin::Comet(x) => AuraModes::Comet(x.clone()),
SetAuraBuiltin::Flash(x) => AuraModes::Flash(x.clone()),
SetAuraBuiltin::MultiStatic(x) => AuraModes::MultiStatic(x.clone()),
SetAuraBuiltin::MultiBreathe(x) => AuraModes::MultiBreathe(x.clone()),
}
}
}