From e8cdcf532882edb575f3dca4b0e53adf5c416b69 Mon Sep 17 00:00:00 2001 From: Byrn Tong <26782336+cixuuz@users.noreply.github.com> Date: Sun, 3 May 2026 09:08:01 +0000 Subject: [PATCH] fix: exclude ancestor PIDs from gateway process scan (#13242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _scan_gateway_pids() uses ps-based pattern matching to find running gateways. When invoked from the CLI (e.g. `hermes gateway status`), the calling process itself matches gateway patterns, causing false positives — the CLI is mistakenly counted as a running gateway. Add _get_ancestor_pids() that walks the process tree from the current PID up to init (PID 1). Merge this set into exclude_pids at the top of _scan_gateway_pids() so the entire ancestor chain is filtered out. This complements the existing os.getpid() exclusion in _append_unique_pid() by also covering parent/grandparent processes (e.g. when hermes is invoked via a wrapper script or shell). Closes #13242 --- hermes_cli/gateway.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index 39fc7476bc..c7abea5bad 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -237,6 +237,26 @@ def _graceful_restart_via_sigusr1(pid: int, drain_timeout: float) -> bool: return False +def _get_ancestor_pids() -> set[int]: + """Return the set of PIDs in the current process's ancestor chain. + + Walks from the current PID up to PID 1 (init) so that process-table scans + never match the calling CLI process or any of its parents. This prevents + ``hermes gateway status`` from falsely counting the ``hermes`` CLI that + invoked it as a running gateway instance (see #13242). + """ + ancestors: set[int] = set() + pid = os.getpid() + # Cap iterations to avoid infinite loops on exotic platforms. + for _ in range(64): + ancestors.add(pid) + parent = _get_parent_pid(pid) + if parent is None or parent <= 0 or parent in ancestors: + break + pid = parent + return ancestors + + def _append_unique_pid(pids: list[int], pid: int | None, exclude_pids: set[int]) -> None: if pid is None or pid <= 0: return @@ -252,6 +272,10 @@ def _scan_gateway_pids(exclude_pids: set[int], all_profiles: bool = False) -> li a live gateway when the PID file is stale/missing, and ``--all`` sweeps can discover gateways outside the current profile. """ + # Exclude the entire ancestor chain so the CLI process that invoked this + # scan (e.g. ``hermes gateway status``) is never mistaken for a running + # gateway. See #13242. + exclude_pids = exclude_pids | _get_ancestor_pids() pids: list[int] = [] patterns = [ "hermes_cli.main gateway",