diff --git a/Cargo.lock b/Cargo.lock index 36de0bd..4f13470 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,10 +263,8 @@ dependencies = [ "humantime", "log", "nix", - "toml", "xcb", "xcb-xembed", - "xdg", ] [[package]] @@ -316,6 +314,7 @@ dependencies = [ "gtk-sys", "log", "pam", + "shell-words", ] [[package]] @@ -339,11 +338,14 @@ dependencies = [ name = "bscreensaver-util" version = "0.1.0" dependencies = [ + "anyhow", "clap", "env_logger", "lazy_static", "libc", + "toml", "xcb", + "xdg", ] [[package]] @@ -1508,6 +1510,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "slab" version = "0.4.6" diff --git a/dialog-gtk3/Cargo.toml b/dialog-gtk3/Cargo.toml index cfbaf6b..e91b1dd 100644 --- a/dialog-gtk3/Cargo.toml +++ b/dialog-gtk3/Cargo.toml @@ -15,4 +15,4 @@ gdk-sys = "0.15" gdkx11 = "0.15" log = "0.4" pam = "0.7" -#x11 = "2.19" +shell-words = "1" diff --git a/dialog-gtk3/src/main.rs b/dialog-gtk3/src/main.rs index e660c3c..4bbf1c1 100644 --- a/dialog-gtk3/src/main.rs +++ b/dialog-gtk3/src/main.rs @@ -3,10 +3,10 @@ use gdkx11::X11Window; use gethostname::gethostname; use glib::GString; use gtk::{prelude::*, Button, Entry, Label, Plug, Window}; -use log::{debug, error}; -use std::{io::{self, Write}, process::exit, rc::Rc, thread, time::Duration}; +use log::{debug, error, warn}; +use std::{io::{self, Write}, process::{exit, Command}, rc::Rc, thread, time::Duration}; -use bscreensaver_util::init_logging; +use bscreensaver_util::{init_logging, load_configuration}; const DIALOG_UPDATE_INTERVAL: Duration = Duration::from_millis(100); const DIALOG_TIMEOUT: Duration = Duration::from_secs(60); @@ -14,6 +14,12 @@ const DIALOG_TIMEOUT: Duration = Duration::from_secs(60); fn main() -> anyhow::Result<()> { init_logging("BSCREENSAVER_DIALOG_GTK3_LOG"); + let mut new_login_command = load_configuration()?.and_then(|config_toml| { + config_toml.get("new-login-command") + .and_then(|nlc| nlc.as_str().map(|s| s.to_string())) + .map(|nlc| shell_words::split(&nlc)) + }).transpose()?.filter(|nlc| !nlc.is_empty()); + let standalone = std::env::var("BSCREENSAVER_DIALOG_STANDALONE").is_ok(); unsafe { glib::log_writer_default_set_use_stderr(true) }; @@ -208,13 +214,30 @@ fn main() -> anyhow::Result<()> { .build(); vbox.pack_start(&hbox, true, true, 2); - let button = Button::builder() + if let Some(new_login_command) = new_login_command.take() { + let new_login_button = Button::builder() + .label("New Login") + .build(); + new_login_button.connect_clicked(move |_| { + let cmd = &new_login_command[0]; + let empty = Vec::::new(); + let args = if new_login_command.len() > 1 { &new_login_command[1..] } else { &empty }; + if let Err(err) = Command::new(cmd).args(args).spawn() { + warn!("Failed to run new login command: {}", err); + } else { + exit(1); + } + }); + hbox.pack_start(&new_login_button, false, true, 8); + } + + let unlock_button = Button::builder() .label("Unlock") .build(); { let password_box = Rc::clone(&password_box); - button.connect_clicked(move |button| { - button.set_sensitive(false); + unlock_button.connect_clicked(move |unlock_button| { + unlock_button.set_sensitive(false); password_box.set_sensitive(false); let username = username.clone(); @@ -229,9 +252,9 @@ fn main() -> anyhow::Result<()> { }); }); } - hbox.pack_end(&button, false, true, 8); - button.set_can_default(true); - button.set_has_default(true); + hbox.pack_end(&unlock_button, false, true, 8); + unlock_button.set_can_default(true); + unlock_button.set_has_default(true); let timer = Rc::new(gtk::ProgressBar::builder() .orientation(gtk::Orientation::Horizontal) diff --git a/locker/Cargo.toml b/locker/Cargo.toml index 72b4fce..588af98 100644 --- a/locker/Cargo.toml +++ b/locker/Cargo.toml @@ -20,8 +20,6 @@ bscreensaver-util = { path = "../util" } humantime = "2" log = "0.4" nix = "0.23" -toml = "0.5" # git source needed until extension event error resolution fix is released xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "d09b5f91bc07d56673f1bc0d6c7ecd72b5ff7b3e", features = ["randr", "xfixes", "xinput"] } xcb-xembed = { path = "../xcb-xembed" } -xdg = "2" diff --git a/locker/src/main.rs b/locker/src/main.rs index 596bd61..1acced7 100644 --- a/locker/src/main.rs +++ b/locker/src/main.rs @@ -15,7 +15,7 @@ use nix::{ use std::{ env, ffi::CString, - fs::{read_link, File}, + fs::read_link, io::{self, Read}, os::{ linux::process::{ChildExt, CommandExt, PidFd}, @@ -277,19 +277,11 @@ fn main() -> anyhow::Result<()> { fn parse_config() -> anyhow::Result { use humantime::parse_duration; - use toml::Value; - - match xdg::BaseDirectories::new()?.find_config_file("bscreensaver.toml") { - None => Ok(Configuration::default()), - Some(config_path) => { - let mut f = File::open(config_path)?; - let mut config = String::new(); - f.read_to_string(&mut config)?; - drop(f); - - let config_toml = config.parse::()?; - let mut config = Configuration::default(); + let mut config = Configuration::default(); + match load_configuration()? { + None => Ok(config), + Some(config_toml) => { config.lock_timeout = match config_toml.get("lock-timeout") { None => config.lock_timeout, Some(val) => parse_duration(val.as_str().ok_or(anyhow!("'lock-timeout' must be a duration string like '10m' or '90s'"))?)?, diff --git a/util/Cargo.toml b/util/Cargo.toml index 4775d94..b7c3a30 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -6,8 +6,11 @@ edition = "2021" [lib] [dependencies] +anyhow = "1" clap = "3" env_logger = "0.9" lazy_static = "1" libc = "0.2" +toml = "0.5" xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "d09b5f91bc07d56673f1bc0d6c7ecd72b5ff7b3e", features = ["randr", "screensaver", "xfixes"] } +xdg = "2" diff --git a/util/src/lib.rs b/util/src/lib.rs index 8a5fbe2..fcaff23 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -1,4 +1,5 @@ -use std::{ffi::CStr, io}; +use std::{ffi::CStr, fs::File, io::{self, Read}}; +use toml::Value; use xcb::x; pub const BSCREENSAVER_WM_CLASS: &[u8] = b"bscreensaver\0Bscreensaver\0"; @@ -10,6 +11,21 @@ pub fn init_logging(env_name: &str) { .init(); } +pub fn load_configuration() -> anyhow::Result> { + match xdg::BaseDirectories::new()?.find_config_file("bscreensaver.toml") { + None => Ok(None), + Some(config_path) => { + let mut f = File::open(config_path)?; + let mut config = String::new(); + f.read_to_string(&mut config)?; + drop(f); + + let config_toml = config.parse::()?; + Ok(Some(config_toml)) + }, + } +} + pub fn create_atom(conn: &xcb::Connection, name: &[u8]) -> xcb::Result { let cookie = conn.send_request(&x::InternAtom { only_if_exists: false,