From 410413dc5765d1af1411c864810713943b25a1b9 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 15 Mar 2026 06:05:17 -0700 Subject: [PATCH] Watchdog respects keep-server-running setting via /watchdog/disable endpoint --- backend/main.py | 8 ++++++++ backend/server.py | 12 ++++++++++++ tauri/src-tauri/src/main.rs | 33 ++++++++++++++++++++++----------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/backend/main.py b/backend/main.py index b2c2d06b..dfb942be 100644 --- a/backend/main.py +++ b/backend/main.py @@ -143,6 +143,14 @@ async def shutdown(): return {"message": "Shutting down..."} +@app.post("/watchdog/disable") +async def watchdog_disable(): + """Disable the parent process watchdog so the server keeps running.""" + from backend.server import disable_watchdog + disable_watchdog() + return {"message": "Watchdog disabled"} + + @app.get("/health", response_model=models.HealthResponse) async def health(): """Health check endpoint.""" diff --git a/backend/server.py b/backend/server.py index af0d4f90..f3348225 100644 --- a/backend/server.py +++ b/backend/server.py @@ -51,6 +51,15 @@ except Exception as e: logger.error(f"Failed to import required modules: {e}", exc_info=True) sys.exit(1) +_watchdog_disabled = False + + +def disable_watchdog(): + """Disable the parent watchdog so the server keeps running after parent exits.""" + global _watchdog_disabled + _watchdog_disabled = True + + def _start_parent_watchdog(parent_pid, data_dir=None): """Monitor parent process and exit if it dies. @@ -117,6 +126,9 @@ def _start_parent_watchdog(parent_pid, data_dir=None): watchdog_logger.warning(f"Parent PID {parent_pid} not found on first check — disabling watchdog") return while True: + if _watchdog_disabled: + watchdog_logger.info("Watchdog disabled (keep server running), stopping monitor") + return if not _is_pid_alive(parent_pid): watchdog_logger.info(f"Parent process {parent_pid} gone, shutting down server...") if sys.platform == "win32": diff --git a/tauri/src-tauri/src/main.rs b/tauri/src-tauri/src/main.rs index 939bb962..f6993cfa 100644 --- a/tauri/src-tauri/src/main.rs +++ b/tauri/src-tauri/src/main.rs @@ -760,17 +760,28 @@ pub fn run() { let _ = &app; // used on unix match &event { RunEvent::Exit => { - println!("RunEvent::Exit received - server will self-terminate via parent watchdog"); - // The server monitors this process's PID via --parent-pid. - // When this process exits, the server detects it and shuts itself down. - // No need for taskkill/wmic/process tree enumeration. - - // On Unix, send SIGTERM to the process group for immediate cleanup. - #[cfg(unix)] - { - let state = app.state::(); - let keep_running = *state.keep_running_on_close.lock().unwrap(); - if !keep_running { + let state = app.state::(); + let keep_running = *state.keep_running_on_close.lock().unwrap(); + + if keep_running { + // Tell the server to disable its watchdog so it survives + // after this process exits. + println!("Keep server running: disabling watchdog..."); + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(2)) + .build() + .unwrap(); + let _ = client + .post(&format!("http://127.0.0.1:{}/watchdog/disable", SERVER_PORT)) + .send(); + } else { + // Server will self-terminate via parent-pid watchdog when + // this process exits. On Unix, also send SIGTERM for + // immediate cleanup. + println!("RunEvent::Exit - server will self-terminate via watchdog"); + + #[cfg(unix)] + { if let Some(pid) = state.server_pid.lock().unwrap().take() { use std::process::Command; let _ = Command::new("kill")