An open browser on the box buys it four times the idle time, and an open noVNC viewer keeps it awake: a bot that opened the browser left the person a frozen screen

This commit is contained in:
王性驊 2026-11-05 16:11:25 +08:00
parent cd3f7b5c72
commit 1a80bfdcc2
1 changed files with 29 additions and 1 deletions

View File

@ -709,7 +709,13 @@ pub fn start_background() {
let s = shared().lock().unwrap();
(s.last_used.elapsed().as_secs(), s.run.is_running(), s.rebuilding)
};
if running && !rebuilding && !is_busy() && !HELD_BY_PERSON.load(Ordering::SeqCst) && idle >= IDLE_SLEEP_SECS && !viewer_open() {
let awake = running && !rebuilding && !is_busy() && !HELD_BY_PERSON.load(Ordering::SeqCst) && !viewer_open();
// A browser on the desktop was left open on purpose (a login page the person walks
// back to): it buys the box four times the idle time before parking. Freezing the
// container froze Chromium and websockify together, so a bot that "opened the
// browser" left the person a viewer that never showed anything.
let limit = if awake && browser_running() { IDLE_SLEEP_SECS * 4 } else { IDLE_SLEEP_SECS };
if awake && idle >= limit && vnc_clients() == 0 {
let _ = sleep_now();
}
if !rebuilding {
@ -731,6 +737,20 @@ fn viewer_open() -> bool {
VIEWER_OPEN.load(Ordering::SeqCst)
}
/// Viewer tabs open on the box's desktop: every noVNC page keeps a websockify connection to
/// x11vnc on :5900 (:170C hex), so a live connection means the person is looking at it now.
fn vnc_clients() -> usize {
dk(&["exec", CONTAINER, "sh", "-c",
"awk '$4==\"01\" && ($2 ~ /:170C$/ || $3 ~ /:170C$/) {n++} END {print n+0}' /proc/net/tcp /proc/net/tcp6 2>/dev/null"])
.ok()
.and_then(|s| parse_count(&s))
.unwrap_or(0)
}
fn parse_count(s: &str) -> Option<usize> {
s.trim().lines().last()?.trim().parse().ok()
}
// ---------- snapshots (the durable store) ----------
/// Save the bot's home folder to this Mac. Returns its size in bytes.
@ -1461,6 +1481,14 @@ pub fn shutdown() {
mod tests {
use super::*;
#[test]
fn vnc_probe_parses_only_a_count() {
assert_eq!(parse_count("3"), Some(3));
assert_eq!(parse_count(" 2 \n"), Some(2));
assert_eq!(parse_count(""), None);
assert_eq!(parse_count("Error response from daemon: container paused"), None);
}
#[test]
fn secret_names_follow_the_original_rule() {
assert!(valid_secret_name("CRM_API_TOKEN"));