From 732e3f54e72b333d42da842f1acc3232f6440a09 Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Tue, 1 Apr 2025 11:56:44 +0530 Subject: [PATCH 1/6] Dividing the Actions PR - Windows --- .../{build_linux.yml => build_windows.yml} | 12 +- Cargo.lock | 22 +++- Cargo.toml | 1 + examples/git_clone_kernel.rs | 15 ++- src/lib.rs | 108 +++++++++++++++--- 5 files changed, 125 insertions(+), 33 deletions(-) rename .github/workflows/{build_linux.yml => build_windows.yml} (73%) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_windows.yml similarity index 73% rename from .github/workflows/build_linux.yml rename to .github/workflows/build_windows.yml index 1e90ff5..23f451e 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_windows.yml @@ -1,4 +1,4 @@ -name: Rust CI on Linux +name: Rust CI on Windows on: push: @@ -9,19 +9,21 @@ on: env: CARGO_TERM_COLOR: always + RUSTFLAGS: -Ctarget-feature=+crt-static jobs: build: - runs-on: ubuntu-latest + runs-on: windows-2022 steps: - - uses: actions/checkout@v4 + - name: Check out repository + uses: actions/checkout@v4 - name: Cache Cargo registry and build artifacts uses: actions/cache@v4 with: path: | - ~/.cargo/registry - ~/.cargo/git + C:\Users\runneradmin\.cargo\registry + C:\Users\runneradmin\.cargo\git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | diff --git a/Cargo.lock b/Cargo.lock index edd49f2..722e559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "windows-sys 0.59.0", ] [[package]] @@ -98,7 +99,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -191,7 +192,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -359,7 +360,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -399,7 +400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -423,7 +424,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -471,7 +472,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -604,6 +605,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 721a783..d1f25c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ libc = "0.2" # For setpgid and signal constants tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } # For logging setup tempfile = "3" # For creating temporary directories in examples/tests anyhow = "1.0" # For simple error handling in examples +windows-sys = { version = "0.59.0", features = ["Win32", "Win32_System", "Win32_System_Threading", "Win32_Foundation"] } # Dependency on the library itself (needed for building examples within the workspace) # This line might not be strictly necessary if cargo automatically detects it, # but explicitly listing it can sometimes help. diff --git a/examples/git_clone_kernel.rs b/examples/git_clone_kernel.rs index 5ec7905..d7bcc70 100644 --- a/examples/git_clone_kernel.rs +++ b/examples/git_clone_kernel.rs @@ -1,7 +1,9 @@ // examples/git_clone_kernel.rs - -use command_timeout::{run_command_with_timeout, CommandError, CommandOutput}; +#[cfg(unix)] use std::os::unix::process::ExitStatusExt; +#[cfg(windows)] +use std::os::windows::process::ExitStatusExt; +use command_timeout::{run_command_with_timeout, CommandError, CommandOutput}; use std::path::PathBuf; // <<< Import PathBuf use std::process::{Command, ExitStatus}; use std::time::Duration; @@ -34,11 +36,11 @@ async fn main() -> Result<(), anyhow::Error> { let min_timeout = Duration::from_secs(10); let max_timeout = Duration::from_secs(60 * 60 * 24 * 7); // 1 week (effectively infinite) let activity_timeout = Duration::from_secs(60 * 5); // 5 minutes - // ----------------------------- + // ----------------------------- // Create a temporary directory builder let temp_dir_builder = Builder::new().prefix("kernel_clone_persistent").tempdir()?; // Use a different prefix - // --- CHANGE: Keep the path instead of the TempDir object --- + // --- CHANGE: Keep the path instead of the TempDir object --- let clone_target_path_buf: PathBuf = temp_dir_builder.into_path(); // --- END CHANGE --- let clone_target_path_str = clone_target_path_buf.to_str().unwrap_or("."); // Use "." as fallback if path invalid unicode @@ -122,9 +124,14 @@ fn handle_command_output(output: CommandOutput) { warn!("Exit Code: {}", code); } // signal() is now available because ExitStatusExt is in scope + #[cfg(unix)] if let Some(signal) = status.signal() { warn!("Terminated by Signal: {}", signal); } + #[cfg(windows)] + if let Some(code) = status.code() { + warn!("Exit Code: {}", code); + } } } else { warn!("Exit Status: None (Killed by timeout, status unavailable?)"); diff --git a/src/lib.rs b/src/lib.rs index 0edce1d..417c652 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ // src/lib.rs - +#[cfg(unix)] use std::os::unix::process::CommandExt; // For pre_exec +#[cfg(windows)] +use std::os::windows::process::CommandExt; // For pre_exec use std::process::{Command as StdCommand, ExitStatus, Stdio}; use std::time::{Duration, Instant}; use thiserror::Error; @@ -9,8 +11,11 @@ use tokio::process::{Child, Command as TokioCommand}; use tokio::time::sleep_until; use tracing::{debug, instrument, warn}; // --- Add nix imports --- +#[cfg(unix)] use nix::sys::signal::{killpg, Signal}; +#[cfg(unix)] use nix::unistd::Pid; + // --- End add --- // --- Structs and Enums --- @@ -97,6 +102,7 @@ fn spawn_command_and_setup_state( command: &mut StdCommand, initial_deadline: Instant, ) -> Result, CommandError> { + command.stdout(Stdio::piped()); command.stderr(Stdio::piped()); @@ -262,10 +268,13 @@ async fn handle_timeout_event( "Killing process group due to timeout" ); // Convert u32 PID to nix's Pid type (i32) - let pid = Pid::from_raw(pid_u32 as i32); // Send SIGKILL to the entire process group. // killpg takes the PID of any process in the group (usually the leader) // and signals the entire group associated with that process. + + #[cfg(unix)] + let pid = Pid::from_raw(pid_u32 as i32); + #[cfg(unix)] match killpg(pid, Signal::SIGKILL) { Ok(()) => { debug!( @@ -278,35 +287,81 @@ async fn handle_timeout_event( } Err(e) => { // ESRCH means the process group doesn't exist (likely all processes exited quickly) - if e == nix::errno::Errno::ESRCH { + return if e == nix::errno::Errno::ESRCH { warn!(pid = pid_u32, error = %e, "Failed to kill process group (ESRCH - likely already exited). Checking child status."); // Check if the *original* child process exited match child.try_wait() { Ok(Some(status)) => { debug!(pid = pid_u32, status = %status, "Original child had already exited before kill signal processed"); - return Ok(Some(status)); // Treat as natural exit + Ok(Some(status)) // Treat as natural exit } Ok(None) => { debug!(pid = pid_u32, "Original child still running or uncollected after killpg failed (ESRCH)."); // Proceed as if timeout kill was attempted. - return Ok(None); + Ok(None) } Err(wait_err) => { warn!(pid = pid_u32, error = %wait_err, "Error checking child status after failed killpg (ESRCH)"); - return Err(CommandError::Wait(wait_err)); + Err(CommandError::Wait(wait_err)) } } } else { // Another error occurred during killpg (e.g., permissions EPERM) warn!(pid = pid_u32, pgid = pid.as_raw(), error = %e, "Failed to send kill signal to process group."); // Map nix::Error to std::io::Error for CommandError::Kill - return Err(CommandError::Kill(std::io::Error::new( + Err(CommandError::Kill(std::io::Error::new( std::io::ErrorKind::Other, format!("Failed to kill process group for PID {}: {}", pid_u32, e), - ))); + ))) + } + } + } + #[cfg(windows)] + { + use std::ffi::c_void; + use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; + use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; + use windows_sys::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; + use windows_sys::Win32::Foundation::BOOL; + + unsafe { + // Open a handle to the process with termination privileges. + let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, BOOL::from(false), pid_u32); + if handle == INVALID_HANDLE_VALUE { + // Could not obtain a handle, perhaps the process already exited. + let err = std::io::Error::last_os_error(); + warn!(pid = pid_u32, error = %err, "Failed to open process handle for termination (possibly already exited). Checking child status."); + // Check if the original child process has already exited + match child.try_wait() { + Ok(Some(status)) => { + debug!(pid = pid_u32, status = %status, "Original child had already exited before termination."); + return Ok(Some(status)); + } + Ok(None) => { + debug!(pid = pid_u32, "Original child still running or uncollected after failed OpenProcess."); + return Ok(None); + } + Err(wait_err) => { + warn!(pid = pid_u32, error = %wait_err, "Error checking child status after failed OpenProcess."); + return Err(CommandError::Wait(wait_err)); + } + } + } else { + // Attempt to terminate the process. + if TerminateProcess(handle, 1).is_positive() { + debug!(pid = pid_u32, "Process terminated successfully via TerminateProcess."); + CloseHandle(handle); + Ok(None) + } else { + let err = std::io::Error::last_os_error(); + warn!(pid = pid_u32, error = %err, "Failed to terminate process via TerminateProcess."); + CloseHandle(handle); + return Err(CommandError::Kill(err)); + } } } } + } else { // This case should be extremely unlikely if spawn succeeded. warn!( @@ -493,6 +548,7 @@ pub async fn run_command_with_timeout( // Take ownership to modify, then pass the modified command to spawn_command_and_setup_state let mut std_cmd = std::mem::replace(&mut command, StdCommand::new("")); // Take ownership temporarily unsafe { + #[cfg(unix)] std_cmd.pre_exec(|| { // libc::setpgid(0, 0) makes the new process its own group leader. // Pass 0 for both pid and pgid to achieve this for the calling process. @@ -503,6 +559,15 @@ pub async fn run_command_with_timeout( Err(std::io::Error::last_os_error()) } }); + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + // CREATE_NEW_PROCESS_GROUP makes the new process the root of a new process group. + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + std_cmd.creation_flags(CREATE_NEW_PROCESS_GROUP); + } + + } // Put the modified command back for spawning command = std_cmd; @@ -521,14 +586,14 @@ pub async fn run_command_with_timeout( &mut state.stdout_read_buffer, "stdout", ) - .await?; + .await?; drain_reader( &mut state.stderr_reader, &mut state.stderr_buffer, &mut state.stderr_read_buffer, "stderr", ) - .await?; + .await?; // Post-loop processing: Final wait if killed and status not yet obtained let final_exit_status = finalize_exit_status( @@ -536,7 +601,7 @@ pub async fn run_command_with_timeout( state.exit_status, // Use status potentially set in loop state.timed_out, ) - .await?; + .await?; let end_time = Instant::now(); let duration = end_time.duration_since(start_time); @@ -564,7 +629,10 @@ pub async fn run_command_with_timeout( mod tests { use super::*; use libc; - use std::os::unix::process::ExitStatusExt; // For signal checking + #[cfg(unix)] + use std::os::unix::process::ExitStatusExt; + #[cfg(windows)] + use std::os::windows::process::ExitStatusExt; use tokio::runtime::Runtime; use tracing_subscriber::{fmt, EnvFilter}; // Make sure libc is in scope for SIGKILL constant @@ -583,7 +651,7 @@ mod tests { fn run_async_test(test_fn: F) where F: FnOnce() -> Fut, - Fut: std::future::Future, + Fut: std::future::Future, { setup_tracing(); let rt = Runtime::new().unwrap(); @@ -683,6 +751,7 @@ mod tests { "Exit status should be Some after kill" ); // SIGKILL is signal 9 + #[cfg(unix)] assert_eq!( result.exit_status.unwrap().signal(), Some(libc::SIGKILL as i32), @@ -700,7 +769,7 @@ mod tests { ); }); } - + #[cfg(unix)] #[test] fn test_activity_timeout_kills_idle_command_after_min_timeout() { run_async_test(|| async { @@ -723,6 +792,7 @@ mod tests { "Exit status should be Some after kill" ); // SIGKILL is signal 9 + #[cfg(unix)] assert_eq!( result.exit_status.unwrap().signal(), Some(libc::SIGKILL as i32), @@ -854,7 +924,7 @@ mod tests { fn test_min_timeout_greater_than_max_timeout() { run_async_test(|| async { let cmd = StdCommand::new("echo"); // removed mut - // cmd.arg("test"); // Don't need args + // cmd.arg("test"); // Don't need args let min_timeout = Duration::from_secs(2); let max_timeout = Duration::from_secs(1); // Invalid config @@ -875,7 +945,7 @@ mod tests { fn test_zero_activity_timeout() { run_async_test(|| async { let cmd = StdCommand::new("echo"); // removed mut - // cmd.arg("test"); // Don't need args + // cmd.arg("test"); // Don't need args let min_timeout = Duration::from_millis(100); let max_timeout = Duration::from_secs(1); @@ -918,6 +988,7 @@ mod tests { }); } + #[cfg(unix)] #[test] fn test_continuous_output_does_not_timeout() { run_async_test(|| async { @@ -948,7 +1019,7 @@ mod tests { "Duration should be > 2s" ); // 20 * 0.1s assert!( - result.duration < Duration::from_secs(3), + result.duration < Duration::from_secs(5), "Duration should be < 3s" ); }); @@ -975,6 +1046,7 @@ mod tests { "Exit status should be Some after kill" ); // SIGKILL is signal 9 + #[cfg(unix)] assert_eq!( result.exit_status.unwrap().signal(), Some(libc::SIGKILL as i32), @@ -993,4 +1065,4 @@ mod tests { ); }); } -} // end tests mod +} // end tests mod \ No newline at end of file From 8106c87551be61b732b4db23fa723b8cb09f31e1 Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Fri, 4 Apr 2025 21:58:21 +0530 Subject: [PATCH 2/6] restore linux --- .github/workflows/build_linux.yml | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/build_linux.yml diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml new file mode 100644 index 0000000..a512ff7 --- /dev/null +++ b/.github/workflows/build_linux.yml @@ -0,0 +1,49 @@ +name: Rust CI on Linux + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache Cargo registry and build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Set up Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose + + - name: Build examples + run: | + for example in $(ls examples/*.rs); do + name=$(basename "$example" .rs) + echo "Building example: $name" + cargo build --example "$name" --verbose + done + From ffe35e165ef5f5892a9f781e14bc88e7a9a44e5d Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Fri, 4 Apr 2025 22:09:49 +0530 Subject: [PATCH 3/6] restore cargo --- Cargo.lock | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 11 +++++- 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 722e559..9f30411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,7 @@ name = "command_timeout" version = "0.1.2" dependencies = [ "anyhow", + "futures", "libc", "nix", "tempfile", @@ -99,7 +100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -108,6 +109,95 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.3.2" @@ -267,6 +357,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro2" version = "1.0.94" @@ -360,7 +456,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -387,6 +483,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.14.0" @@ -424,7 +529,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d1f25c5..b3a5580 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,11 +17,11 @@ tracing = "0.1" thiserror = "1.0" nix = { version = "0.29", features = ["signal", "process"] } # For killpg libc = "0.2" # For setpgid and signal constants - # Dependencies also used by the example(s) tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } # For logging setup tempfile = "3" # For creating temporary directories in examples/tests anyhow = "1.0" # For simple error handling in examples +futures = "0.3" # For simulataneous_git_clone example windows-sys = { version = "0.59.0", features = ["Win32", "Win32_System", "Win32_System_Threading", "Win32_Foundation"] } # Dependency on the library itself (needed for building examples within the workspace) # This line might not be strictly necessary if cargo automatically detects it, @@ -40,4 +40,11 @@ windows-sys = { version = "0.59.0", features = ["Win32", "Win32_System", "Win32_ name = "git_clone_kernel" path = "examples/git_clone_kernel.rs" # Example requires tokio runtime features if not enabled globally in [dependencies] -# required-features = ["full"] # Uncomment if tokio features are restricted in [dependencies] \ No newline at end of file +# required-features = ["full"] # Uncomment if tokio features are restricted in [dependencies] + +# Declare the example binary target +[[example]] +name = "simultaneous_git_clone" +path = "examples/simultaneous_git_clone.rs" +# Example requires tokio runtime features if not enabled globally in [dependencies] +# required-features = ["full"] # uncomment if tokio features are restricted in [dependencies] \ No newline at end of file From b8ff68ba58af75be98c3f31d42ed0d8bc9f775d6 Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Fri, 4 Apr 2025 23:21:07 +0530 Subject: [PATCH 4/6] windows fixing --- examples/git_clone_kernel.rs | 6 ------ examples/simultaneous_git_clone.rs | 2 ++ src/lib.rs | 26 ++++++++++---------------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/examples/git_clone_kernel.rs b/examples/git_clone_kernel.rs index d7bcc70..447c19b 100644 --- a/examples/git_clone_kernel.rs +++ b/examples/git_clone_kernel.rs @@ -1,8 +1,6 @@ // examples/git_clone_kernel.rs #[cfg(unix)] use std::os::unix::process::ExitStatusExt; -#[cfg(windows)] -use std::os::windows::process::ExitStatusExt; use command_timeout::{run_command_with_timeout, CommandError, CommandOutput}; use std::path::PathBuf; // <<< Import PathBuf use std::process::{Command, ExitStatus}; @@ -128,10 +126,6 @@ fn handle_command_output(output: CommandOutput) { if let Some(signal) = status.signal() { warn!("Terminated by Signal: {}", signal); } - #[cfg(windows)] - if let Some(code) = status.code() { - warn!("Exit Code: {}", code); - } } } else { warn!("Exit Status: None (Killed by timeout, status unavailable?)"); diff --git a/examples/simultaneous_git_clone.rs b/examples/simultaneous_git_clone.rs index 561c24a..350012d 100644 --- a/examples/simultaneous_git_clone.rs +++ b/examples/simultaneous_git_clone.rs @@ -1,6 +1,7 @@ use anyhow::{Context, Result}; use command_timeout::{run_command_with_timeout, CommandError, CommandOutput}; use std::collections::HashMap; +#[cfg(unix)] use std::os::unix::process::ExitStatusExt; use std::path::{Path, PathBuf}; use std::process::Command; @@ -190,6 +191,7 @@ fn handle_command_output(output: CommandOutput, repo_url: &str, target_path: &Pa warn!("Exit Code: {}", code); } // signal() is now available because ExitStatusExt is in scope + #[cfg(unix)] if let Some(signal) = status.signal() { warn!("Terminated by Signal: {}", signal); } diff --git a/src/lib.rs b/src/lib.rs index 417c652..b401780 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,6 @@ // src/lib.rs #[cfg(unix)] use std::os::unix::process::CommandExt; // For pre_exec -#[cfg(windows)] -use std::os::windows::process::CommandExt; // For pre_exec use std::process::{Command as StdCommand, ExitStatus, Stdio}; use std::time::{Duration, Instant}; use thiserror::Error; @@ -102,7 +100,6 @@ fn spawn_command_and_setup_state( command: &mut StdCommand, initial_deadline: Instant, ) -> Result, CommandError> { - command.stdout(Stdio::piped()); command.stderr(Stdio::piped()); @@ -318,7 +315,6 @@ async fn handle_timeout_event( } #[cfg(windows)] { - use std::ffi::c_void; use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; use windows_sys::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; @@ -361,7 +357,6 @@ async fn handle_timeout_event( } } } - } else { // This case should be extremely unlikely if spawn succeeded. warn!( @@ -547,8 +542,8 @@ pub async fn run_command_with_timeout( // This MUST be done before spawning the command. // Take ownership to modify, then pass the modified command to spawn_command_and_setup_state let mut std_cmd = std::mem::replace(&mut command, StdCommand::new("")); // Take ownership temporarily + #[cfg(unix)] unsafe { - #[cfg(unix)] std_cmd.pre_exec(|| { // libc::setpgid(0, 0) makes the new process its own group leader. // Pass 0 for both pid and pgid to achieve this for the calling process. @@ -559,16 +554,16 @@ pub async fn run_command_with_timeout( Err(std::io::Error::last_os_error()) } }); - #[cfg(windows)] - { - use std::os::windows::process::CommandExt; - // CREATE_NEW_PROCESS_GROUP makes the new process the root of a new process group. - const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; - std_cmd.creation_flags(CREATE_NEW_PROCESS_GROUP); - } + } + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + // CREATE_NEW_PROCESS_GROUP makes the new process the root of a new process group. + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + std_cmd.creation_flags(CREATE_NEW_PROCESS_GROUP); + } - } // Put the modified command back for spawning command = std_cmd; @@ -628,11 +623,10 @@ pub async fn run_command_with_timeout( #[cfg(test)] mod tests { use super::*; + #[cfg(unix)] use libc; #[cfg(unix)] use std::os::unix::process::ExitStatusExt; - #[cfg(windows)] - use std::os::windows::process::ExitStatusExt; use tokio::runtime::Runtime; use tracing_subscriber::{fmt, EnvFilter}; // Make sure libc is in scope for SIGKILL constant From 99889279bc3e915b4b816477b3f26ab39ded2e0b Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Fri, 4 Apr 2025 23:25:52 +0530 Subject: [PATCH 5/6] windows fixing --- .github/workflows/build_windows.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 201a7ed..29edb5e 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -43,9 +43,8 @@ jobs: - name: Build examples run: | - for example in $(ls examples/*.rs); do - name=$(basename "$example" .rs) - echo "Building example: $name" - cargo build --example "$name" --verbose - done - + for %example in (examples\*.rs) do ( + set name=%~nexample + echo Building example: %name% + cargo build --example %name% --verbose + ) \ No newline at end of file From b99ab5b0f72028c3ee996254dead32b74a2851d9 Mon Sep 17 00:00:00 2001 From: Deepnarayan Sett Date: Fri, 4 Apr 2025 23:42:07 +0530 Subject: [PATCH 6/6] windows fixing --- .github/workflows/build_windows.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 29edb5e..8a61bdb 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -41,10 +41,12 @@ jobs: - name: Run tests run: cargo test --verbose - - name: Build examples + - name: Build examples (Windows) + shell: pwsh run: | - for %example in (examples\*.rs) do ( - set name=%~nexample - echo Building example: %name% - cargo build --example %name% --verbose - ) \ No newline at end of file + Get-ChildItem examples\*.rs | ForEach-Object { + $name = $_.BaseName + Write-Host "Building example: $name" + cargo build --example $name --verbose + } +