diff --git a/Cargo.lock b/Cargo.lock index ec263af..eb2d91c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1635,7 +1635,7 @@ checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" [[package]] name = "tuigreet" -version = "0.9.0" +version = "0.9.1" dependencies = [ "chrono", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index f1de39b..85221e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuigreet" -version = "0.9.0" +version = "0.9.1" authors = ["Antoine POPINEAU "] edition = "2018" build = "build.rs" diff --git a/contrib/locales/en-US/tuigreet.ftl b/contrib/locales/en-US/tuigreet.ftl index 26187d6..c6e48fd 100644 --- a/contrib/locales/en-US/tuigreet.ftl +++ b/contrib/locales/en-US/tuigreet.ftl @@ -21,6 +21,7 @@ new_command = New command: shutdown = Shut down reboot = Reboot +command_missing = No command configured command_exited = Command exited with command_failed = Command failed diff --git a/contrib/locales/fr-FR/tuigreet.ftl b/contrib/locales/fr-FR/tuigreet.ftl index c3205d3..fe364e9 100644 --- a/contrib/locales/fr-FR/tuigreet.ftl +++ b/contrib/locales/fr-FR/tuigreet.ftl @@ -21,6 +21,7 @@ command = Nouvelle commande : shutdown = Éteindre reboot = Redémarrer +command_missing = Aucune commande configurée command_exited = La commande a retourné command_failed = Échec de la commande diff --git a/src/greeter.rs b/src/greeter.rs index c64d230..0c8e01a 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -22,9 +22,7 @@ use zeroize::Zeroize; use crate::{ event::Event, - info::{ - get_issue, get_last_session, get_last_session_path, get_last_user_name, get_last_user_session, get_last_user_session_path, get_last_user_username, get_min_max_uids, get_sessions, get_users, - }, + info::{get_issue, get_last_command, get_last_session_path, get_last_user_command, get_last_user_name, get_last_user_session, get_last_user_username, get_min_max_uids, get_sessions, get_users}, power::PowerOption, ui::{ common::{masked::MaskedString, menu::Menu, style::Theme}, @@ -96,7 +94,7 @@ pub enum GreetAlign { #[default] Center, Left, - Right + Right, } #[derive(SmartDefault)] @@ -204,9 +202,17 @@ impl Greeter { greeter.parse_options().await; + let sessions = get_sessions(&greeter).unwrap_or_default(); + + if let SessionSource::None = greeter.session_source { + if !sessions.is_empty() { + greeter.session_source = SessionSource::Session(0); + } + } + greeter.sessions = Menu { title: fl!("title_session"), - options: get_sessions(&greeter).unwrap_or_default(), + options: sessions, selected: 0, }; @@ -217,29 +223,34 @@ impl Greeter { // If, on top of that, we should remember their last session. if greeter.remember_user_session { - if let Ok(ref session_path) = get_last_user_session_path(greeter.username.get()) { - // Set the selected menu option and the session source. - greeter.sessions.selected = greeter.sessions.options.iter().position(|Session { path, .. }| path.as_deref() == Some(session_path)).unwrap_or(0); - greeter.session_source = SessionSource::Session(greeter.sessions.selected); - } - // See if we have the last free-form command from the user. - if let Ok(command) = get_last_user_session(greeter.username.get()) { + if let Ok(command) = get_last_user_command(greeter.username.get()) { greeter.session_source = SessionSource::Command(command); } + + // If a session was saved, use it and its name. + if let Ok(ref session_path) = get_last_user_session(greeter.username.get()) { + // Set the selected menu option and the session source. + if let Some(index) = greeter.sessions.options.iter().position(|Session { path, .. }| path.as_deref() == Some(session_path)) { + greeter.sessions.selected = index; + greeter.session_source = SessionSource::Session(greeter.sessions.selected); + } + } } } } // Same thing, but not user specific. if greeter.remember_session { - if let Ok(ref session_path) = get_last_session_path() { - greeter.sessions.selected = greeter.sessions.options.iter().position(|Session { path, .. }| path.as_deref() == Some(session_path)).unwrap_or(0); - greeter.session_source = SessionSource::Session(greeter.sessions.selected); + if let Ok(command) = get_last_command() { + greeter.session_source = SessionSource::Command(command.trim().to_string()); } - if let Ok(command) = get_last_session() { - greeter.session_source = SessionSource::Command(command.trim().to_string()); + if let Ok(ref session_path) = get_last_session_path() { + if let Some(index) = greeter.sessions.options.iter().position(|Session { path, .. }| path.as_deref() == Some(session_path)) { + greeter.sessions.selected = index; + greeter.session_source = SessionSource::Session(greeter.sessions.selected); + } } } @@ -354,7 +365,7 @@ impl Greeter { match value.as_str() { "left" => GreetAlign::Left, "right" => GreetAlign::Right, - _ => GreetAlign::Center + _ => GreetAlign::Center, } } else { GreetAlign::default() @@ -405,7 +416,12 @@ impl Greeter { opts.optopt("", "window-padding", "padding inside the terminal area (default: 0)", "PADDING"); opts.optopt("", "container-padding", "padding inside the main prompt container (default: 1)", "PADDING"); opts.optopt("", "prompt-padding", "padding between prompt rows (default: 1)", "PADDING"); - opts.optopt("", "greet-align", "alignment of the greeting text in the main prompt container (default: 'center')", "[left|center|right]"); + opts.optopt( + "", + "greet-align", + "alignment of the greeting text in the main prompt container (default: 'center')", + "[left|center|right]", + ); opts.optopt("", "power-shutdown", "command to run to shut down the system", "'CMD [ARGS]...'"); opts.optopt("", "power-reboot", "command to run to reboot the system", "'CMD [ARGS]...'"); diff --git a/src/info.rs b/src/info.rs index 7ecda9e..12b75d0 100644 --- a/src/info.rs +++ b/src/info.rs @@ -23,8 +23,8 @@ use crate::{ const LAST_USER_USERNAME: &str = "/var/cache/tuigreet/lastuser"; const LAST_USER_NAME: &str = "/var/cache/tuigreet/lastuser-name"; -const LAST_SESSION: &str = "/var/cache/tuigreet/lastsession"; -const LAST_SESSION_PATH: &str = "/var/cache/tuigreet/lastsession-path"; +const LAST_COMMAND: &str = "/var/cache/tuigreet/lastsession"; +const LAST_SESSION: &str = "/var/cache/tuigreet/lastsession-path"; const DEFAULT_MIN_UID: u16 = 1000; const DEFAULT_MAX_UID: u16 = 60000; @@ -114,55 +114,59 @@ pub fn write_last_username(username: &MaskedString) { } pub fn get_last_session_path() -> Result { - Ok(PathBuf::from(fs::read_to_string(LAST_SESSION_PATH)?.trim())) + Ok(PathBuf::from(fs::read_to_string(LAST_SESSION)?.trim())) } -pub fn get_last_session() -> Result { - Ok(fs::read_to_string(LAST_SESSION)?.trim().to_string()) +pub fn get_last_command() -> Result { + Ok(fs::read_to_string(LAST_COMMAND)?.trim().to_string()) } pub fn write_last_session_path

(session: &P) where P: AsRef, { - let _ = fs::write(LAST_SESSION_PATH, session.as_ref().to_string_lossy().as_bytes()); + let _ = fs::write(LAST_SESSION, session.as_ref().to_string_lossy().as_bytes()); } -pub fn write_last_session(session: &str) { - let _ = fs::write(LAST_SESSION, session); +pub fn write_last_command(session: &str) { + let _ = fs::write(LAST_COMMAND, session); } -pub fn get_last_user_session_path(username: &str) -> Result { - Ok(PathBuf::from(fs::read_to_string(format!("{LAST_SESSION_PATH}-{username}"))?.trim())) +pub fn get_last_user_session(username: &str) -> Result { + Ok(PathBuf::from(fs::read_to_string(format!("{LAST_SESSION}-{username}"))?.trim())) } -pub fn get_last_user_session(username: &str) -> Result { - Ok(fs::read_to_string(format!("{LAST_SESSION}-{username}"))?.trim().to_string()) +pub fn get_last_user_command(username: &str) -> Result { + Ok(fs::read_to_string(format!("{LAST_COMMAND}-{username}"))?.trim().to_string()) } -pub fn write_last_user_session_path

(username: &str, session: P) +pub fn write_last_user_session

(username: &str, session: P) where P: AsRef, { - let _ = fs::write(format!("{LAST_SESSION_PATH}-{username}"), session.as_ref().to_string_lossy().as_bytes()); + let _ = fs::write(format!("{LAST_SESSION}-{username}"), session.as_ref().to_string_lossy().as_bytes()); } -pub fn delete_last_session_path() { - let _ = fs::remove_file(LAST_SESSION_PATH); +pub fn delete_last_session() { + let _ = fs::remove_file(LAST_SESSION); } -pub fn write_last_user_session(username: &str, session: &str) { - let _ = fs::write(format!("{LAST_SESSION}-{username}"), session); -} - -pub fn delete_last_user_session_path(username: &str) { - let _ = fs::remove_file(format!("{LAST_SESSION_PATH}-{username}")); +pub fn write_last_user_command(username: &str, session: &str) { + let _ = fs::write(format!("{LAST_COMMAND}-{username}"), session); } pub fn delete_last_user_session(username: &str) { let _ = fs::remove_file(format!("{LAST_SESSION}-{username}")); } +pub fn delete_last_command() { + let _ = fs::remove_file(LAST_COMMAND); +} + +pub fn delete_last_user_command(username: &str) { + let _ = fs::remove_file(format!("{LAST_COMMAND}-{username}")); +} + pub fn get_users(min_uid: u16, max_uid: u16) -> Vec { let users = unsafe { uzers::all_users() }; diff --git a/src/ipc.rs b/src/ipc.rs index d658b02..f0ae708 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -8,7 +8,7 @@ use tokio::sync::{ use crate::{ event::Event, - info::{delete_last_user_session, delete_last_user_session_path, write_last_user_session, write_last_user_session_path, write_last_username}, + info::{delete_last_user_command, delete_last_user_session, write_last_user_command, write_last_user_session, write_last_username}, macros::SafeDebug, ui::sessions::{Session, SessionSource, SessionType}, AuthStatus, Greeter, Mode, @@ -69,7 +69,11 @@ impl Ipc { } async fn parse_response(&mut self, greeter: &mut Greeter, response: Response) -> Result<(), Box> { - tracing::info!("received greetd message: {:?}", response); + // Do not display actual message from greetd, which may contain entered information, sometimes passwords. + match response { + Response::Error { ref error_type, .. } => tracing::info!("received greetd error message: {error_type:?}"), + ref response => tracing::info!("received greetd message: {:?}", response), + } match response { Response::AuthMessage { auth_message_type, auth_message } => match auth_message_type { @@ -124,16 +128,16 @@ impl Ipc { SessionSource::Command(ref command) => { tracing::info!("caching last user command: {command}"); - write_last_user_session(&greeter.username.value, command); - delete_last_user_session_path(&greeter.username.value); + write_last_user_command(&greeter.username.value, command); + delete_last_user_session(&greeter.username.value); } SessionSource::Session(index) => { if let Some(Session { path: Some(session_path), .. }) = greeter.sessions.options.get(index) { tracing::info!("caching last user session: {session_path:?}"); - write_last_user_session_path(&greeter.username.value, session_path); - delete_last_user_session(&greeter.username.value); + write_last_user_session(&greeter.username.value, session_path); + delete_last_user_command(&greeter.username.value); } } @@ -148,36 +152,51 @@ impl Ipc { } else { tracing::info!("authentication successful, starting session"); - let command = greeter.session_source.command(greeter).map(str::to_string); + match greeter.session_source.command(greeter).map(str::to_string) { + None => { + Ipc::cancel(greeter).await; + + greeter.message = Some(fl!("command_missing")); + greeter.reset(false).await; + } + + Some(command) if command.is_empty() => { + Ipc::cancel(greeter).await; + + greeter.message = Some(fl!("command_missing")); + greeter.reset(false).await; + } - if let Some(command) = command { - greeter.done = true; - greeter.mode = Mode::Processing; + Some(command) => { + greeter.done = true; + greeter.mode = Mode::Processing; - let session = Session::get_selected(greeter); - let (command, env) = wrap_session_command(greeter, session, &command); + let session = Session::get_selected(greeter); + let (command, env) = wrap_session_command(greeter, session, &command); - #[cfg(not(debug_assertions))] - self.send(Request::StartSession { cmd: vec![command.to_string()], env }).await; + #[cfg(not(debug_assertions))] + self.send(Request::StartSession { cmd: vec![command.to_string()], env }).await; - #[cfg(debug_assertions)] - { - let _ = command; - let _ = env; + #[cfg(debug_assertions)] + { + let _ = command; + let _ = env; - self - .send(Request::StartSession { - cmd: vec!["true".to_string()], - env: vec![], - }) - .await; + self + .send(Request::StartSession { + cmd: vec!["true".to_string()], + env: vec![], + }) + .await; + } } } } } - Response::Error { error_type, description } => { - tracing::info!("received an error from greetd: {error_type:?} - {description}"); + Response::Error { error_type, .. } => { + // Do not display actual message from greetd, which may contain entered information, sometimes passwords. + tracing::info!("received an error from greetd: {error_type:?}"); Ipc::cancel(greeter).await; @@ -193,7 +212,8 @@ impl Ipc { } ErrorType::Error => { - greeter.message = Some(description); + // Do not display actual message from greetd, which may contain entered information, sometimes passwords. + greeter.message = Some("An error was received from greetd".to_string()); greeter.reset(false).await; } } diff --git a/src/keyboard.rs b/src/keyboard.rs index bde94c3..e7b530a 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -5,7 +5,7 @@ use greetd_ipc::Request; use tokio::sync::RwLock; use crate::{ - info::{delete_last_session_path, get_last_user_session, get_last_user_session_path, write_last_session, write_last_session_path}, + info::{delete_last_command, delete_last_session, get_last_user_command, get_last_user_session, write_last_command, write_last_session_path}, ipc::Ipc, power::power, ui::{ @@ -225,8 +225,8 @@ pub async fn handle(greeter: Arc>, input: KeyEvent, ipc: Ipc) -> greeter.session_source = SessionSource::Command(greeter.buffer.clone()); if greeter.remember_session { - write_last_session(&greeter.buffer); - delete_last_session_path(); + write_last_command(&greeter.buffer); + delete_last_session(); } greeter.buffer = greeter.previous_buffer.take().unwrap_or_default(); @@ -248,13 +248,12 @@ pub async fn handle(greeter: Arc>, input: KeyEvent, ipc: Ipc) -> Mode::Sessions => { let session = greeter.sessions.options.get(greeter.sessions.selected).cloned(); - if let Some(Session { path, command, .. }) = session { + if let Some(Session { path, .. }) = session { if greeter.remember_session { if let Some(ref path) = path { write_last_session_path(path); + delete_last_command(); } - - write_last_session(&command); } greeter.session_source = SessionSource::Session(greeter.sessions.selected); @@ -365,7 +364,7 @@ async fn validate_username(greeter: &mut Greeter, ipc: &Ipc) { greeter.buffer = String::new(); if greeter.remember_user_session { - if let Ok(last_session) = get_last_user_session_path(&greeter.username.value) { + if let Ok(last_session) = get_last_user_session(&greeter.username.value) { if let Some(last_session) = Session::from_path(greeter, last_session).cloned() { tracing::info!("remembered user session is {}", last_session.name); @@ -374,7 +373,7 @@ async fn validate_username(greeter: &mut Greeter, ipc: &Ipc) { } } - if let Ok(command) = get_last_user_session(&greeter.username.value) { + if let Ok(command) = get_last_user_command(&greeter.username.value) { tracing::info!("remembered user command is {}", command); greeter.session_source = SessionSource::Command(command);