From 08202f15725e6c31cbcce0feda340d3274ea25d2 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 7 Nov 2024 09:37:16 -0700 Subject: [PATCH] Recover previous locked state --- Cargo.lock | 1 + Cargo.toml | 1 + src/locker.rs | 103 ++++++++++++++++++++++++++++++++++++++------------ src/logind.rs | 5 ++- 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76da554..28a9c30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -996,6 +996,7 @@ dependencies = [ "cosmic-dbus-networkmanager", "cosmic-greeter-config", "cosmic-greeter-daemon", + "dirs", "env_logger", "freedesktop_entry_parser", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index bfb12ba..b2970b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ cosmic-comp-config.workspace = true cosmic-config = { workspace = true, features = ["calloop", "macro"] } cosmic-greeter-config.workspace = true cosmic-greeter-daemon = { path = "daemon" } +dirs = "5" env_logger.workspace = true freedesktop_entry_parser = "1.3.0" libcosmic = { workspace = true, features = ["tokio", "wayland"] } diff --git a/src/locker.rs b/src/locker.rs index 71a0dde..2dc25d3 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -22,16 +22,30 @@ use cosmic_config::CosmicConfigEntry; use std::{ any::TypeId, collections::HashMap, + env, ffi::{CStr, CString}, fs, os::fd::OwnedFd, - path::Path, + path::{Path, PathBuf}, process, sync::Arc, }; use tokio::{sync::mpsc, task, time}; use wayland_client::{protocol::wl_output::WlOutput, Proxy}; +fn lockfile_opt() -> Option { + let runtime_dir = dirs::runtime_dir()?; + let session_id_str = env::var("XDG_SESSION_ID").ok()?; + let session_id = match session_id_str.parse::() { + Ok(ok) => ok, + Err(err) => { + log::warn!("failed to parse session ID {:?}: {}", session_id_str, err); + return None; + } + }; + Some(runtime_dir.join(format!("cosmic-greeter-{}.lock", session_id))) +} + pub fn main(current_user: pwd::Passwd) -> Result<(), Box> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); @@ -69,6 +83,7 @@ pub fn main(current_user: pwd::Passwd) -> Result<(), Box> let flags = Flags { current_user, icon_opt, + lockfile_opt: lockfile_opt(), wallpapers, }; @@ -189,6 +204,7 @@ impl pam_client::ConversationHandler for Conversation { pub struct Flags { current_user: pwd::Passwd, icon_opt: Option, + lockfile_opt: Option, wallpapers: Vec<(String, cosmic_bg_config::Source)>, } @@ -315,31 +331,46 @@ impl cosmic::Application for App { core.window.show_minimize = false; core.window.use_template = false; - ( - App { - core, - flags, - state: State::Unlocked, - surface_ids: HashMap::new(), - active_surface_id_opt: None, - surface_images: HashMap::new(), - surface_names: HashMap::new(), - text_input_ids: HashMap::new(), - inhibit_opt: None, - network_icon_opt: None, - power_info_opt: None, - value_tx_opt: None, - prompt_opt: None, - error_opt: None, - }, - if cfg!(feature = "logind") { + let already_locked = match flags.lockfile_opt { + Some(ref lockfile) => lockfile.exists(), + None => false, + }; + + let mut app = App { + core, + flags, + state: State::Unlocked, + surface_ids: HashMap::new(), + active_surface_id_opt: None, + surface_images: HashMap::new(), + surface_names: HashMap::new(), + text_input_ids: HashMap::new(), + inhibit_opt: None, + network_icon_opt: None, + power_info_opt: None, + value_tx_opt: None, + prompt_opt: None, + error_opt: None, + }; + + let command = if cfg!(feature = "logind") { + if already_locked { + // Recover previously locked state + log::info!("recovering previous locked state"); + app.state = State::Locking; + lock() + } else { // When logind feature is used, wait for lock signal Command::none() - } else { - // When logind feature not used, lock immediately - lock() - }, - ) + } + } else { + // When logind feature not used, lock immediately + log::info!("locking immediately"); + app.state = State::Locking; + lock() + }; + + (app, command) } /// Handle application events here. @@ -425,6 +456,7 @@ impl cosmic::Application for App { self.state = State::Locked; // Allow suspend self.inhibit_opt = None; + // Create lock surfaces let mut commands = Vec::with_capacity(self.surface_ids.len()); for (output, surface_id) in self.surface_ids.iter() { commands.push(get_lock_surface(*surface_id, output.clone())); @@ -513,6 +545,13 @@ impl cosmic::Application for App { self.error_opt = None; // Clear value_tx self.value_tx_opt = None; + // Try to create lockfile when locking + if let Some(ref lockfile) = self.flags.lockfile_opt { + if let Err(err) = fs::File::create(lockfile) { + log::warn!("failed to create lockfile {:?}: {}", lockfile, err); + } + } + // Tell compositor to lock return lock(); } State::Unlocking => { @@ -531,10 +570,18 @@ impl cosmic::Application for App { self.error_opt = None; // Clear value_tx self.value_tx_opt = None; + // Try to delete lockfile when unlocking + if let Some(ref lockfile) = self.flags.lockfile_opt { + if let Err(err) = fs::remove_file(lockfile) { + log::warn!("failed to remove lockfile {:?}: {}", lockfile, err); + } + } + // Destroy lock surfaces let mut commands = Vec::with_capacity(self.surface_ids.len() + 1); for (_output, surface_id) in self.surface_ids.iter() { commands.push(destroy_lock_surface(*surface_id)); } + // Tell compositor to unlock commands.push(unlock()); // Wait to exit until `Unlocked` event, when server has processed unlock return Command::batch(commands); @@ -642,7 +689,13 @@ impl cosmic::Application for App { } None => {} } - match self.flags.current_user.gecos.as_ref().filter(|s| !s.is_empty()) { + match self + .flags + .current_user + .gecos + .as_ref() + .filter(|s| !s.is_empty()) + { Some(gecos) => { let full_name = gecos.split(",").next().unwrap_or_default(); column = column.push( diff --git a/src/logind.rs b/src/logind.rs index 44f24da..ee5418e 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -7,7 +7,6 @@ use logind_zbus::{ session::SessionProxy, }; use std::{any::TypeId, error::Error, os::fd::OwnedFd, sync::Arc}; -use tokio::time; use zbus::Connection; use crate::locker::Message; @@ -68,7 +67,9 @@ pub fn subscription() -> Subscription { pub async fn handler(msg_tx: &mut mpsc::Sender) -> Result<(), Box> { let connection = Connection::system().await?; let manager = ManagerProxy::new(&connection).await?; - let session_path = manager.get_session_by_PID(std::os::unix::process::parent_id()).await?; + let session_path = manager + .get_session_by_PID(std::os::unix::process::parent_id()) + .await?; let session = SessionProxy::builder(&connection) .path(&session_path)? .build()