use anyhow::Result;
#[cfg(unix)]
use std::fs::{OpenOptions, create_dir_all};
#[cfg(unix)]
use std::os::fd::AsRawFd;
use std::path::Path;
#[cfg(unix)]
pub(crate) struct StdIoRedirectGuard {
saved_stdout_fd: i32,
saved_stderr_fd: i32,
}
#[cfg(not(unix))]
pub(crate) struct StdIoRedirectGuard;
impl StdIoRedirectGuard {
#[cfg(unix)]
pub(crate) fn redirect_to_file(log_path: &Path) -> Result<Self> {
if let Some(parent) = log_path.parent() {
create_dir_all(parent)?;
}
let log_file = OpenOptions::new().create(true).append(true).open(log_path)?;
let log_fd = log_file.as_raw_fd();
let saved_stdout_fd = unsafe { libc::dup(libc::STDOUT_FILENO) };
if saved_stdout_fd < 0 {
return Err(std::io::Error::last_os_error().into());
}
let saved_stderr_fd = unsafe { libc::dup(libc::STDERR_FILENO) };
if saved_stderr_fd < 0 {
unsafe {
libc::close(saved_stdout_fd);
}
return Err(std::io::Error::last_os_error().into());
}
if unsafe { libc::dup2(log_fd, libc::STDOUT_FILENO) } < 0 {
unsafe {
libc::close(saved_stdout_fd);
libc::close(saved_stderr_fd);
}
return Err(std::io::Error::last_os_error().into());
}
if unsafe { libc::dup2(log_fd, libc::STDERR_FILENO) } < 0 {
unsafe {
libc::dup2(saved_stdout_fd, libc::STDOUT_FILENO);
libc::close(saved_stdout_fd);
libc::close(saved_stderr_fd);
}
return Err(std::io::Error::last_os_error().into());
}
Ok(Self { saved_stdout_fd, saved_stderr_fd })
}
#[cfg(not(unix))]
pub(crate) fn redirect_to_file(_log_path: &Path) -> Result<Self> {
Ok(Self)
}
}
#[cfg(unix)]
impl Drop for StdIoRedirectGuard {
fn drop(&mut self) {
unsafe {
let _ = libc::dup2(self.saved_stdout_fd, libc::STDOUT_FILENO);
let _ = libc::dup2(self.saved_stderr_fd, libc::STDERR_FILENO);
let _ = libc::close(self.saved_stdout_fd);
let _ = libc::close(self.saved_stderr_fd);
}
}
}