use crate::config::{
record_system_inventory_refresh_failure, record_system_inventory_refresh_started,
record_system_inventory_refresh_success, reserve_system_inventory_refresh_slot,
sync_system_inventory,
};
use chrono::Duration as ChronoDuration;
use std::env;
use std::process::{Command as ProcessCommand, Stdio};
const BACKGROUND_CHILD_ENV: &str = "XBP_SYSTEM_INVENTORY_BACKGROUND_CHILD";
const BACKGROUND_TRIGGER_ENV: &str = "XBP_SYSTEM_INVENTORY_BACKGROUND_TRIGGER";
const BACKGROUND_MODE: &str = "background";
const MANUAL_MODE: &str = "manual";
const BACKGROUND_MIN_INTERVAL_MINUTES: i64 = 30;
#[cfg(windows)]
const CREATE_NO_WINDOW: u32 = 0x08000000;
pub fn maybe_start_background_system_inventory_refresh(trigger: &str) -> Result<bool, String> {
if is_background_system_inventory_child() {
return Ok(false);
}
let reserved = reserve_system_inventory_refresh_slot(
trigger,
BACKGROUND_MODE,
false,
ChronoDuration::minutes(BACKGROUND_MIN_INTERVAL_MINUTES),
)?;
if !reserved {
return Ok(false);
}
if let Err(error) = spawn_background_system_inventory_refresh_process(trigger, false) {
let _ = record_system_inventory_refresh_failure(trigger, BACKGROUND_MODE, false, &error);
return Err(error);
}
Ok(true)
}
pub fn run_system_inventory_refresh(force: bool, include_cursor: bool) -> Result<(), String> {
let trigger = current_system_inventory_refresh_trigger();
let mode = current_system_inventory_refresh_mode();
let _ = record_system_inventory_refresh_started(&trigger, mode, include_cursor);
let current_dir = env::current_dir().ok();
match sync_system_inventory(force, include_cursor, current_dir.as_deref()) {
Ok(result) => {
let _ = record_system_inventory_refresh_success(
&trigger,
mode,
include_cursor,
result.refreshed,
);
Ok(())
}
Err(error) => {
let _ = record_system_inventory_refresh_failure(&trigger, mode, include_cursor, &error);
Err(error)
}
}
}
fn is_background_system_inventory_child() -> bool {
env::var(BACKGROUND_CHILD_ENV)
.ok()
.map(|value| value == "1")
.unwrap_or(false)
}
fn current_system_inventory_refresh_trigger() -> String {
env::var(BACKGROUND_TRIGGER_ENV)
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| "manual-system-inventory-refresh".to_string())
}
fn current_system_inventory_refresh_mode() -> &'static str {
if is_background_system_inventory_child() {
BACKGROUND_MODE
} else {
MANUAL_MODE
}
}
fn spawn_background_system_inventory_refresh_process(
trigger: &str,
include_cursor: bool,
) -> Result<(), String> {
let executable = env::current_exe()
.map_err(|error| format!("Failed to resolve current XBP executable: {}", error))?;
let mut command = ProcessCommand::new(executable);
command
.arg("diag")
.arg("--refresh-system-inventory")
.env(BACKGROUND_CHILD_ENV, "1")
.env(BACKGROUND_TRIGGER_ENV, trigger)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
if include_cursor {
command.arg("--cursor");
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
command.creation_flags(CREATE_NO_WINDOW);
}
command.spawn().map(|_| ()).map_err(|error| {
format!(
"Failed to spawn background system inventory refresh: {}",
error
)
})
}