eh: attempt to prevent resource leaks

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I28d716bd37d17dd96731c7863b3383416a6a6964
This commit is contained in:
raf 2025-11-15 22:59:07 +03:00
commit cc5fd676c9
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
3 changed files with 29 additions and 5 deletions

View file

@ -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()?;

View file

@ -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,
}
}
}

View file

@ -36,12 +36,12 @@ fn dispatch_multicall(
args: std::env::Args,
) -> Option<Result<i32>> {
let rest: Vec<String> = 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;