diff --git a/apps/web/orb_trading_service.py b/apps/web/orb_trading_service.py index f250b34..0bcaa28 100644 --- a/apps/web/orb_trading_service.py +++ b/apps/web/orb_trading_service.py @@ -759,13 +759,30 @@ class ORBDaemonController: @property def running(self) -> bool: - """True if the daemon process is alive (PID file + kill-0 check).""" + """True if the daemon process is alive (PID file + kill-0 check, zombie-safe).""" pid_file = self._pid_file() if not pid_file.exists(): return False try: pid = int(pid_file.read_text().strip()) os.kill(pid, 0) # signal 0: probe without sending anything + # kill-0 succeeds for zombie processes too — check /proc or ps to exclude zombies + try: + import subprocess as _sp + result = _sp.run( + ["ps", "-p", str(pid), "-o", "stat="], + capture_output=True, text=True, timeout=2, + ) + stat = result.stdout.strip() + if stat.startswith("Z"): + # Zombie — treat as dead, clean up PID file + try: + pid_file.unlink() + except Exception: + pass + return False + except Exception: + pass # if ps fails, trust kill-0 result return True except (ValueError, ProcessLookupError, PermissionError, OSError): # Process dead or PID stale — remove the file