use crate::constants::{RESCUE_MIN_COUNT, RESCUE_MUST_HAVE};
use crate::types::{RescueError, RescueStatus};
use std::env;
use std::ffi::OsString;
#[must_use]
pub fn verify_rescue_tools() -> bool {
verify_rescue(false).is_ok()
}
pub fn verify_rescue_min_with_override(
exec_check: bool,
min_count: usize,
force_ok: Option<bool>,
) -> Result<RescueStatus, RescueError> {
match force_ok {
Some(true) => Ok(RescueStatus::GNU {
found: min_count,
min: min_count,
}),
Some(false) => Err(RescueError::Unavailable),
None => verify_rescue_min(exec_check, min_count),
}
}
#[must_use]
pub fn verify_rescue_tools_with_exec(exec_check: bool) -> bool {
verify_rescue(exec_check).is_ok()
}
#[must_use]
pub fn verify_rescue_tools_with_exec_min(exec_check: bool, min_count: usize) -> bool {
verify_rescue_min(exec_check, min_count).is_ok()
}
pub fn verify_rescue(exec_check: bool) -> Result<RescueStatus, RescueError> {
verify_rescue_min(exec_check, RESCUE_MIN_COUNT)
}
fn verify_rescue_min(exec_check: bool, min_count: usize) -> Result<RescueStatus, RescueError> {
let allow_env_overrides = cfg!(test)
|| env::var_os("SWITCHYARD_TEST_ALLOW_ENV_OVERRIDES") == Some(OsString::from("1"));
if allow_env_overrides {
if let Ok(v) = env::var("SWITCHYARD_FORCE_RESCUE_OK") {
let v = v.trim();
if v == "1" {
return Ok(RescueStatus::GNU {
found: min_count,
min: min_count,
});
}
if v == "0" {
return Err(RescueError::Unavailable);
}
}
}
if !exec_check && min_count == 0 {
return Ok(RescueStatus::GNU { found: 0, min: 0 });
}
if let Some(p) = which_on_path("busybox") {
if !exec_check || is_executable(&p) {
return Ok(RescueStatus::BusyBox { path: p });
}
}
let must_have = RESCUE_MUST_HAVE;
let mut found = 0usize;
for bin in must_have {
if let Some(p) = which_on_path(bin) {
if !exec_check || is_executable(&p) {
found += 1;
}
}
}
if found >= min_count {
Ok(RescueStatus::GNU {
found,
min: min_count,
})
} else {
Err(RescueError::Unavailable)
}
}
fn which_on_path(bin: &str) -> Option<String> {
let path = env::var_os("PATH")?;
for dir in env::split_paths(&path) {
let cand = dir.join(bin);
if cand.exists() {
return Some(cand.display().to_string());
}
}
None
}
#[cfg(unix)]
fn is_executable(path: &str) -> bool {
use std::os::unix::fs::PermissionsExt;
if let Ok(md) = std::fs::metadata(path) {
let mode = md.permissions().mode();
return (mode & 0o111) != 0;
}
false
}
#[cfg(not(unix))]
fn is_executable(_path: &str) -> bool {
true
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn forced_ok_env_yields_ok() {
env::set_var("SWITCHYARD_FORCE_RESCUE_OK", "1");
let r = verify_rescue(false);
env::remove_var("SWITCHYARD_FORCE_RESCUE_OK");
assert!(r.is_ok());
}
#[test]
#[serial]
fn forced_fail_env_yields_err() {
env::set_var("SWITCHYARD_FORCE_RESCUE_OK", "0");
let r = verify_rescue(false);
env::remove_var("SWITCHYARD_FORCE_RESCUE_OK");
assert!(r.is_err());
}
}