From cc5fd676c9e8e24c3e6aca1ef4e076d046f01be8 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sat, 15 Nov 2025 22:59:07 +0300 Subject: [PATCH] eh: attempt to prevent resource leaks Signed-off-by: NotAShelf Change-Id: I28d716bd37d17dd96731c7863b3383416a6a6964 --- eh/src/command.rs | 18 ++++++++++++++++++ eh/src/error.rs | 12 +++++++++--- eh/src/main.rs | 4 ++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/eh/src/command.rs b/eh/src/command.rs index 74ca3f1..7eace98 100644 --- a/eh/src/command.rs +++ b/eh/src/command.rs @@ -2,6 +2,7 @@ use std::{ collections::VecDeque, io::{self, Read, Write}, process::{Command, ExitStatus, Output, Stdio}, + time::{Duration, Instant}, }; use crate::error::{EhError, Result}; @@ -27,6 +28,9 @@ impl LogInterceptor for StdIoInterceptor { /// Default buffer size for reading command output const DEFAULT_BUFFER_SIZE: usize = 4096; +/// Default timeout for command execution +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes + /// Builder and executor for Nix commands. pub struct NixCommand { subcommand: String, @@ -147,10 +151,19 @@ impl NixCommand { let mut out_queue = VecDeque::new(); let mut err_queue = VecDeque::new(); + let start_time = Instant::now(); loop { let mut did_something = false; + // Check for timeout + if start_time.elapsed() > DEFAULT_TIMEOUT { + let _ = child.kill(); + return Err(EhError::CommandFailed { + command: format!("nix {} timed out after 5 minutes", self.subcommand), + }); + } + match stdout.read(&mut out_buf) { Ok(0) => {}, Ok(n) => { @@ -176,6 +189,11 @@ impl NixCommand { if !did_something && child.try_wait()?.is_some() { break; } + + // Prevent busy waiting when no data is available + if !did_something { + std::thread::sleep(Duration::from_millis(10)); + } } let status = child.wait()?; diff --git a/eh/src/error.rs b/eh/src/error.rs index b9c2bfb..173f047 100644 --- a/eh/src/error.rs +++ b/eh/src/error.rs @@ -40,9 +40,15 @@ impl EhError { pub const fn exit_code(&self) -> i32 { match self { Self::ProcessExit { code } => *code, - Self::NixCommandFailed(_) => 1, - Self::CommandFailed { .. } => 1, - _ => 1, + Self::NixCommandFailed(_) => 2, + Self::CommandFailed { .. } => 3, + Self::HashExtractionFailed => 4, + Self::NoNixFilesFound => 5, + Self::HashFixFailed { .. } => 6, + Self::InvalidInput { .. } => 7, + Self::Io(_) => 8, + Self::Regex(_) => 9, + Self::Utf8(_) => 10, } } } diff --git a/eh/src/main.rs b/eh/src/main.rs index 13c29f4..370049f 100644 --- a/eh/src/main.rs +++ b/eh/src/main.rs @@ -36,12 +36,12 @@ fn dispatch_multicall( args: std::env::Args, ) -> Option> { let rest: Vec = args.collect(); - + // Validate arguments before processing if let Err(e) = util::validate_nix_args(&rest) { return Some(Err(e)); } - + let hash_extractor = util::RegexHashExtractor; let fixer = util::DefaultNixFileFixer; let classifier = util::DefaultNixErrorClassifier;