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, collections::VecDeque,
io::{self, Read, Write}, io::{self, Read, Write},
process::{Command, ExitStatus, Output, Stdio}, process::{Command, ExitStatus, Output, Stdio},
time::{Duration, Instant},
}; };
use crate::error::{EhError, Result}; use crate::error::{EhError, Result};
@ -27,6 +28,9 @@ impl LogInterceptor for StdIoInterceptor {
/// Default buffer size for reading command output /// Default buffer size for reading command output
const DEFAULT_BUFFER_SIZE: usize = 4096; 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. /// Builder and executor for Nix commands.
pub struct NixCommand { pub struct NixCommand {
subcommand: String, subcommand: String,
@ -147,10 +151,19 @@ impl NixCommand {
let mut out_queue = VecDeque::new(); let mut out_queue = VecDeque::new();
let mut err_queue = VecDeque::new(); let mut err_queue = VecDeque::new();
let start_time = Instant::now();
loop { loop {
let mut did_something = false; 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) { match stdout.read(&mut out_buf) {
Ok(0) => {}, Ok(0) => {},
Ok(n) => { Ok(n) => {
@ -176,6 +189,11 @@ impl NixCommand {
if !did_something && child.try_wait()?.is_some() { if !did_something && child.try_wait()?.is_some() {
break; break;
} }
// Prevent busy waiting when no data is available
if !did_something {
std::thread::sleep(Duration::from_millis(10));
}
} }
let status = child.wait()?; let status = child.wait()?;

View file

@ -40,9 +40,15 @@ impl EhError {
pub const fn exit_code(&self) -> i32 { pub const fn exit_code(&self) -> i32 {
match self { match self {
Self::ProcessExit { code } => *code, Self::ProcessExit { code } => *code,
Self::NixCommandFailed(_) => 1, Self::NixCommandFailed(_) => 2,
Self::CommandFailed { .. } => 1, Self::CommandFailed { .. } => 3,
_ => 1, Self::HashExtractionFailed => 4,
Self::NoNixFilesFound => 5,
Self::HashFixFailed { .. } => 6,
Self::InvalidInput { .. } => 7,
Self::Io(_) => 8,
Self::Regex(_) => 9,
Self::Utf8(_) => 10,
} }
} }
} }