From 48cd7a3c7d89bcd0fa1a3687286c22bfa12068f5 Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Sat, 1 Feb 2020 02:37:09 +0100 Subject: [PATCH] Better support for windows --- Cargo.lock | 11 --------- Cargo.toml | 8 ++++--- daemon/task_handler.rs | 53 ++++++++++++++++++++++++++++-------------- shared/log.rs | 30 ++++++++++++++++++++++-- shared/settings.rs | 29 +++++++++++++++++++++++ 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f4b6f5..6e71e31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1082,7 +1082,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1208,15 +1207,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "term" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -1500,7 +1490,6 @@ dependencies = [ "checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" diff --git a/Cargo.toml b/Cargo.toml index 7957ea9..bdd5dea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,10 +34,8 @@ path = "shared/lib.rs" anyhow = "1" async-std = { version = "1", features = ["attributes", "unstable"] } dirs = "2" -users = "^0.9" chrono = { version = "^0.4", features = ["serde"] } rand = "^0.7" -nix = "0.16" strum = "0.17" strum_macros = "0.17" @@ -50,8 +48,12 @@ serde_derive = "^1.0" log = "0.4" config = "^0.10" -simplelog = "0.7" +simplelog = { version = "0.7", default-features = false } structopt = "0.3" crossterm = "^0.15" comfy-table= "0.0.6" tempfile = "^3" + +[target.'cfg(not(windows))'.dependencies] +users = "^0.9" +nix = "0.16" diff --git a/daemon/task_handler.rs b/daemon/task_handler.rs index fae5393..13baf31 100644 --- a/daemon/task_handler.rs +++ b/daemon/task_handler.rs @@ -8,9 +8,11 @@ use ::std::time::Duration; use ::anyhow::Result; use ::chrono::prelude::*; use ::log::{debug, error, info, warn}; -use ::nix::sys::signal; -use ::nix::sys::signal::Signal; -use ::nix::unistd::Pid; +#[cfg(not(windows))] +use ::nix::{ + sys::signal::{self, Signal}, + unistd::Pid, +}; use ::pueue::log::*; use ::pueue::message::*; @@ -119,8 +121,10 @@ impl TaskHandler { }; // Spawn the actual subprocess - let spawn_result = Command::new("sh") - .arg("-c") + let spawn_result = Command::new(if cfg!(windows) { "cmd" } else { "sh" }) + // Windows: Tell cmd to output everything using unicode. + .args(if cfg!(windows) { Some("/U") } else { None }) + .arg(if cfg!(windows) { "/C" } else { "-c" }) .arg(&task.command) .current_dir(&task.path) .stdin(Stdio::piped()) @@ -258,6 +262,7 @@ impl TaskHandler { } /// Send a signal to a unix process + #[cfg(not(windows))] fn send_signal(&mut self, id: usize, signal: Signal) -> Result { if let Some(child) = self.children.get(&id) { debug!("Sending signal {} to {}", signal, id); @@ -319,12 +324,19 @@ impl TaskHandler { return; } } - match self.send_signal(id, Signal::SIGCONT) { - Err(err) => warn!("Failed starting task {}: {:?}", id, err), - Ok(success) => { - if success { - let mut state = self.state.lock().unwrap(); - state.change_status(id, TaskStatus::Running); + #[cfg(windows)] + { + warn!("Failed starting task {}: not supported on windows.", id); + } + #[cfg(not(windows))] + { + match self.send_signal(id, Signal::SIGCONT) { + Err(err) => warn!("Failed starting task {}: {:?}", id, err), + Ok(success) => { + if success { + let mut state = self.state.lock().unwrap(); + state.change_status(id, TaskStatus::Running); + } } } } @@ -359,12 +371,19 @@ impl TaskHandler { if !self.children.contains_key(&id) { return; } - match self.send_signal(id, Signal::SIGSTOP) { - Err(err) => info!("Failed pausing task {}: {:?}", id, err), - Ok(success) => { - if success { - let mut state = self.state.lock().unwrap(); - state.change_status(id, TaskStatus::Paused); + #[cfg(windows)] + { + info!("Failed pausing task {}: not supported on windows.", id); + } + #[cfg(not(windows))] + { + match self.send_signal(id, Signal::SIGSTOP) { + Err(err) => info!("Failed pausing task {}: {:?}", id, err), + Ok(success) => { + if success { + let mut state = self.state.lock().unwrap(); + state.change_status(id, TaskStatus::Paused); + } } } } diff --git a/shared/log.rs b/shared/log.rs index beef6bb..58eb21e 100644 --- a/shared/log.rs +++ b/shared/log.rs @@ -1,5 +1,7 @@ use ::anyhow::Result; +use ::byteorder::{LittleEndian, ReadBytesExt}; use ::log::error; +use ::std::borrow::Cow; use ::std::fs::{remove_file, File}; use ::std::io::prelude::*; use ::std::path::{Path, PathBuf}; @@ -32,6 +34,30 @@ pub fn get_log_file_handles(task_id: usize, settings: &Settings) -> Result<(File Ok((stdout, stderr)) } +fn command_data_to_text(mut data: &[u8]) -> Cow { + if cfg!(windows) { + // On windows we run the command using "cmd" with a flag that makes it output Unicode (UTF16). + + let is_odd = data.len() % 2 == 1; + let mut buffer = Vec::with_capacity(data.len() / 2 + if is_odd { 1 } else { 0 }); + + type CmdUtf16Endian = LittleEndian; + + while data.len() > 1 { + buffer.push(data.read_u16::().unwrap()); + } + if is_odd { + let extra = [*data.last().unwrap(), 0]; + let mut extra = &extra as &[u8]; + buffer.push(extra.read_u16::().unwrap()); + } + + Cow::from(String::from_utf16_lossy(&buffer)) + } else { + String::from_utf8_lossy(data) + } +} + /// Return the content of temporary stdout and stderr files for a task pub fn read_log_files(task_id: usize, settings: &Settings) -> Result<(String, String)> { let (mut stdout_handle, mut stderr_handle) = get_log_file_handles(task_id, settings)?; @@ -41,8 +67,8 @@ pub fn read_log_files(task_id: usize, settings: &Settings) -> Result<(String, St stdout_handle.read_to_end(&mut stdout_buffer)?; stderr_handle.read_to_end(&mut stderr_buffer)?; - let stdout = String::from_utf8_lossy(&stdout_buffer); - let stderr = String::from_utf8_lossy(&stderr_buffer); + let stdout = command_data_to_text(&stdout_buffer); + let stderr = command_data_to_text(&stderr_buffer); Ok((stdout.to_string(), stderr.to_string())) } diff --git a/shared/settings.rs b/shared/settings.rs index 697ac57..7189553 100644 --- a/shared/settings.rs +++ b/shared/settings.rs @@ -153,3 +153,32 @@ fn default_pueue_path() -> Result { |v| Ok(v.to_string()), ) } + +#[cfg(target_os = "windows")] +fn default_config_path() -> Result { + Ok(get_home_dir()?.join("pueue.yml")) +} + +#[cfg(target_os = "windows")] +fn get_config_paths() -> Result> { + Ok(vec![ + // Windows Terminal stores its config file in the "AppData/Local" directory. + dirs::data_local_dir() + .ok_or(anyhow!("Couldn't resolve app data directory"))? + .join("pueue/pueue.yml"), + default_config_path()?, + Path::new("./pueue.yml").to_path_buf(), + ]) +} + +#[cfg(target_os = "windows")] +fn default_pueue_path() -> Result { + // Use local data directory since this data doesn't need to be synced. + let path = dirs::data_local_dir() + .ok_or(anyhow!("Couldn't resolve app data directory"))? + .join("pueue"); + path.to_str().map_or_else( + || Err(anyhow!("Failed to parse log path (Weird characters?)")), + |v| Ok(v.to_string()), + ) +}