diff --git a/src/inventory.rs b/src/inventory.rs index 3e7cc97..ba839c5 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -12,8 +12,12 @@ //! Get-CimInstance and emits compact JSON. One subprocess for the whole //! inventory is cheaper than per-field WMI queries and avoids pulling a //! `wmi`/COM crate into the dep tree. Inventory is collected once at -//! startup; the sysinfo loop's hash-compare suppresses re-uploads of -//! unchanged data, so this isn't repeated on every 120 s tick. +//! startup. Collection routinely outruns the first sysinfo tick (TIME_CONN +//! = 3 s) — `Invoke-RestMethod 'api.ipify.org' -TimeoutSec 5` alone can +//! burn that budget on hosts with blocked egress — so the sysinfo loop in +//! `hbbs_http::sync` watches for INVENTORY transitioning empty → populated +//! and forces a re-upload at that point. Subsequent ticks are suppressed +//! by the loop's `had_inventory` / `uploaded` bookkeeping. //! //! Non-Windows builds return an empty JSON object — hello-agent v0 only //! ships on Windows, but keeping the cross-platform surface compiling diff --git a/vendor/rustdesk/src/hbbs_http/sync.rs b/vendor/rustdesk/src/hbbs_http/sync.rs index 7cc5072..8243a76 100644 --- a/vendor/rustdesk/src/hbbs_http/sync.rs +++ b/vendor/rustdesk/src/hbbs_http/sync.rs @@ -57,6 +57,13 @@ struct InfoUploaded { last_uploaded: Option, id: String, username: Option, + // hello-agent local patch: tracks whether the most recent successful + // sysinfo upload carried the `inventory` key. The CMDB collector + // (hello-agent's `inventory.rs`) runs on a background thread and can + // finish *after* the first sysinfo tick has already fired; without + // this flag the loop would never re-upload because `uploaded == true` + // and `username` is unchanged. Re-apply on vendor resync. + had_inventory: bool, } impl Default for InfoUploaded { @@ -67,18 +74,20 @@ impl Default for InfoUploaded { last_uploaded: None, id: "".to_owned(), username: None, + had_inventory: false, } } } impl InfoUploaded { - fn uploaded(url: String, id: String, username: String) -> Self { + fn uploaded(url: String, id: String, username: String, had_inventory: bool) -> Self { Self { uploaded: true, url, last_uploaded: None, id, username: Some(username), + had_inventory, } } } @@ -126,9 +135,18 @@ async fn start_hbbs_sync_async() { // we may not be able to get the username before login after the next restart. let mut v = crate::get_sysinfo(); let sys_username = v["username"].as_str().unwrap_or_default().to_string(); + // hello-agent local patch: the inventory collector is async + // (PowerShell + WMI + ipify, see `inventory::collect_inventory`) + // and routinely finishes after the first sysinfo tick. When + // INVENTORY transitions from empty → populated we need to + // re-upload, even though `uploaded == true` and the username + // hasn't changed. Re-apply on vendor resync. + let inventory_now_available = !config::INVENTORY.read().unwrap().is_empty(); // Though the username comparison is only necessary on Windows, // we still keep the comparison on other platforms for consistency. - let need_upload = (!info_uploaded.uploaded || info_uploaded.username.as_ref() != Some(&sys_username)) && + let need_upload = (!info_uploaded.uploaded + || info_uploaded.username.as_ref() != Some(&sys_username) + || (inventory_now_available && !info_uploaded.had_inventory)) && info_uploaded.last_uploaded.map(|x| x.elapsed() >= UPLOAD_SYSINFO_TIMEOUT).unwrap_or(true); if need_upload { v["version"] = json!(crate::VERSION); @@ -158,9 +176,11 @@ async fn start_hbbs_sync_async() { // the `inventory` key, identical to a vanilla rustdesk // install. let inventory = config::INVENTORY.read().unwrap().clone(); + let mut had_inventory = false; if !inventory.is_empty() { if let Ok(inv_v) = serde_json::from_str::(&inventory) { v["inventory"] = inv_v; + had_inventory = true; } } let ab_name = Config::get_option(keys::OPTION_PRESET_ADDRESS_BOOK_NAME); @@ -232,7 +252,7 @@ async fn start_hbbs_sync_async() { } }; if samever { - info_uploaded = InfoUploaded::uploaded(url.clone(), id.clone(), sys_username); + info_uploaded = InfoUploaded::uploaded(url.clone(), id.clone(), sys_username, had_inventory); log::info!("sysinfo not changed, skip upload"); continue; } @@ -241,7 +261,7 @@ async fn start_hbbs_sync_async() { match crate::post_request(url.replace("heartbeat", "sysinfo"), v, "").await { Ok(x) => { if x == "SYSINFO_UPDATED" { - info_uploaded = InfoUploaded::uploaded(url.clone(), id.clone(), sys_username); + info_uploaded = InfoUploaded::uploaded(url.clone(), id.clone(), sys_username, had_inventory); log::info!("sysinfo updated"); if !hash.is_empty() { config::Status::set("sysinfo_hash", hash);