xbp 10.30.1

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
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
        )
    })
}