#[allow(clippy::needless_bool)]
pub fn restore_posix_stdio_fds() -> bool {
unsafe {
if imp::bound() {
return true;
}
#[cfg(all(
esp_idf_comp_vfs_enabled,
esp_idf_vfs_support_io,
esp_idf_version_at_least_5_3_0
))]
{
imp::restore()
}
#[cfg(not(all(
esp_idf_comp_vfs_enabled,
esp_idf_vfs_support_io,
esp_idf_version_at_least_5_3_0
)))]
{
false
}
}
}
mod imp {
use crate::*;
pub(super) unsafe fn bound() -> bool {
matches!(
streams(),
Some((si, so, se)) if fileno(si) == 0 && fileno(so) == 1 && fileno(se) == 2
)
}
#[cfg(not(esp_idf_libc_picolibc))]
pub(super) unsafe fn streams() -> Option<(*mut FILE, *mut FILE, *mut FILE)> {
let g = _global_impure_ptr;
if g.is_null() {
return None;
}
let (si, so, se) = ((*g)._stdin, (*g)._stdout, (*g)._stderr);
(!si.is_null() && !so.is_null() && !se.is_null()).then_some((si, so, se))
}
#[cfg(esp_idf_libc_picolibc)]
pub(super) unsafe fn streams() -> Option<(*mut FILE, *mut FILE, *mut FILE)> {
(!stdin.is_null() && !stdout.is_null() && !stderr.is_null())
.then_some((stdin, stdout, stderr))
}
#[cfg(all(
esp_idf_comp_vfs_enabled,
esp_idf_vfs_support_io,
esp_idf_version_at_least_5_3_0
))]
pub(super) use restore::restore;
#[cfg(all(
esp_idf_comp_vfs_enabled,
esp_idf_vfs_support_io,
esp_idf_version_at_least_5_3_0
))]
mod restore {
use core::ffi::c_int;
use core::ptr;
use crate::*;
use super::streams;
pub(in crate::stdio) const CONSOLE: &core::ffi::CStr = c"/dev/console";
pub(in crate::stdio) unsafe fn claim_low_fds() -> Option<esp_vfs_id_t> {
let mut vfs_id: esp_vfs_id_t = -1;
let vfs = core::mem::zeroed::<esp_vfs_t>();
if esp_vfs_register_with_id(&vfs, ptr::null_mut(), &mut vfs_id) != ESP_OK {
return None;
}
for _ in 0..3 {
let mut placeholder: c_int = -1;
if esp_vfs_register_fd(vfs_id, &mut placeholder) != ESP_OK {
break;
}
}
Some(vfs_id)
}
pub(in crate::stdio) unsafe fn release_low_fds(vfs_id: esp_vfs_id_t) {
esp_vfs_unregister_with_id(vfs_id);
}
#[cfg(esp_idf_version_at_least_5_5_0)]
pub(in crate::stdio) unsafe fn init_global_stdio() {
esp_libc_init_global_stdio(CONSOLE.as_ptr());
}
#[cfg(not(esp_idf_version_at_least_5_5_0))]
pub(in crate::stdio) unsafe fn init_global_stdio() {
esp_newlib_init_global_stdio(CONSOLE.as_ptr());
}
#[cfg(not(esp_idf_libc_picolibc))]
pub(in crate::stdio) unsafe fn restore() -> bool {
let Some((si, so, se)) = streams() else {
return false;
};
fclose(si);
fclose(so);
if se != so {
fclose(se);
}
let placeholders = claim_low_fds();
let probe = open(CONSOLE.as_ptr(), O_WRONLY as c_int);
if let Some(vfs_id) = placeholders {
release_low_fds(vfs_id);
}
init_global_stdio();
if probe >= 0 {
close(probe);
}
let g = _global_impure_ptr;
let r = __getreent();
if !g.is_null() && !r.is_null() && r != g {
(*r)._stdin = (*g)._stdin;
(*r)._stdout = (*g)._stdout;
(*r)._stderr = (*g)._stderr;
}
matches!(streams(), Some((_, so, _)) if fileno(so) == 1)
}
#[cfg(esp_idf_libc_picolibc)]
pub(in crate::stdio) unsafe fn restore() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
const UNTRIED: u8 = 0;
const FAILED: u8 = 1;
const RESTORED: u8 = 2;
static STATE: AtomicU8 = AtomicU8::new(UNTRIED);
match STATE.load(Ordering::Relaxed) {
FAILED => return false,
RESTORED => return true,
_ => (),
}
let Some((si, so, _)) = streams() else {
STATE.store(FAILED, Ordering::Relaxed);
return false;
};
let (sifd, sofd) = (fileno(si), fileno(so));
fflush(so);
if sifd >= 0 {
close(sifd);
}
if sofd >= 0 && sofd != sifd {
close(sofd);
}
let placeholders = claim_low_fds();
let probe = open(CONSOLE.as_ptr(), O_WRONLY as c_int);
init_global_stdio();
if let Some(vfs_id) = placeholders {
release_low_fds(vfs_id);
}
let i = open(CONSOLE.as_ptr(), O_RDONLY as c_int);
let o = open(CONSOLE.as_ptr(), O_WRONLY as c_int);
let e = open(CONSOLE.as_ptr(), O_WRONLY as c_int);
if probe >= 0 {
close(probe);
}
let restored = i == 0 && o == 1 && e == 2;
if !restored {
for fd in [i, o, e] {
if fd >= 3 {
close(fd);
}
}
}
clearerr(si);
clearerr(so);
STATE.store(if restored { RESTORED } else { FAILED }, Ordering::Relaxed);
restored
}
}
}