From 7b5123c04d500b6004c6c18f7abe980adeead43e Mon Sep 17 00:00:00 2001 From: Yanyun Liao Date: Thu, 16 Apr 2026 16:04:16 +0800 Subject: [PATCH 1/5] fix(uninstall): clean up orphaned openshell processes on uninstall (#1940) `nemoclaw uninstall` only killed `openshell forward` processes but left behind `openshell sandbox create`, `openshell ssh-proxy`, and their child `ssh` sessions. These orphaned processes accumulate across onboard/destroy cycles. Add `stop_orphaned_openshell_processes()` to the uninstall flow that finds and kills all openshell-related processes: - `openshell sandbox create` and `openshell ssh-proxy` via pgrep - `ssh` sessions spawned by openshell (verified via ps command line) Co-Authored-By: Claude Opus 4.6 Signed-off-by: Yanyun Liao --- uninstall.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/uninstall.sh b/uninstall.sh index 4b461362a18..1dcc5373e5e 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -281,6 +281,58 @@ stop_openshell_forward_processes() { done } +# Kill orphaned openshell processes left behind after uninstall — +# sandbox create, ssh-proxy, and related ssh sessions. (#1940) +stop_orphaned_openshell_processes() { + if ! command -v pgrep >/dev/null 2>&1; then + warn "pgrep not found; skipping orphaned openshell process cleanup." + return 0 + fi + + local -a pids=() + local pid + + # Collect openshell sandbox create, ssh-proxy, and related ssh processes. + while IFS= read -r pid; do + [ -n "$pid" ] || continue + pids+=("$pid") + done < <(pgrep -f "openshell (sandbox create|ssh-proxy)" 2>/dev/null || true) + + # Also collect ssh processes whose command line references openshell + # (these are the SSH sessions spawned by openshell sandbox create). + while IFS= read -r pid; do + [ -n "$pid" ] || continue + local cmd + cmd="$(ps -p "$pid" -o args= 2>/dev/null)" || continue + if [[ "$cmd" == *openshell* ]]; then + pids+=("$pid") + fi + done < <(pgrep -x ssh 2>/dev/null || true) + + # Deduplicate + local -A seen=() + local -a unique_pids=() + for pid in "${pids[@]}"; do + if [ -z "${seen[$pid]:-}" ]; then + seen[$pid]=1 + unique_pids+=("$pid") + fi + done + + if [ "${#unique_pids[@]}" -eq 0 ]; then + info "No orphaned openshell processes found" + return 0 + fi + + for pid in "${unique_pids[@]}"; do + if kill "$pid" >/dev/null 2>&1 || kill -9 "$pid" >/dev/null 2>&1; then + info "Stopped orphaned openshell process $pid" + else + warn "Failed to stop orphaned openshell process $pid" + fi + done +} + remove_openshell_resources() { if ! command -v openshell >/dev/null 2>&1; then warn "openshell not found; skipping gateway/provider/sandbox cleanup." @@ -629,6 +681,7 @@ main() { step 1 "Stopping services" stop_helper_services stop_openshell_forward_processes + stop_orphaned_openshell_processes step 2 "OpenShell resources" remove_openshell_resources From 0626164410d160e182af427a885aacdeefa9ed01 Mon Sep 17 00:00:00 2001 From: Yanyun Liao Date: Thu, 16 Apr 2026 16:12:43 +0800 Subject: [PATCH 2/5] fix(uninstall): scope PID discovery to invoking user (#1940) Add `-u $(id -un)` to pgrep calls to avoid killing openshell/ssh processes belonging to other users on shared systems when running uninstall via sudo. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Yanyun Liao --- uninstall.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/uninstall.sh b/uninstall.sh index 1dcc5373e5e..6c16a200223 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -292,11 +292,20 @@ stop_orphaned_openshell_processes() { local -a pids=() local pid + # Scope to the invoking user to avoid killing other users' processes + # on shared systems (e.g. when running via sudo). (#1940) + local _user + _user="$(id -un 2>/dev/null || echo "")" + local -a _pgrep_user=() + if [ -n "$_user" ]; then + _pgrep_user=(-u "$_user") + fi + # Collect openshell sandbox create, ssh-proxy, and related ssh processes. while IFS= read -r pid; do [ -n "$pid" ] || continue pids+=("$pid") - done < <(pgrep -f "openshell (sandbox create|ssh-proxy)" 2>/dev/null || true) + done < <(pgrep "${_pgrep_user[@]}" -f "openshell (sandbox create|ssh-proxy)" 2>/dev/null || true) # Also collect ssh processes whose command line references openshell # (these are the SSH sessions spawned by openshell sandbox create). @@ -307,7 +316,7 @@ stop_orphaned_openshell_processes() { if [[ "$cmd" == *openshell* ]]; then pids+=("$pid") fi - done < <(pgrep -x ssh 2>/dev/null || true) + done < <(pgrep "${_pgrep_user[@]}" -x ssh 2>/dev/null || true) # Deduplicate local -A seen=() From b9792091fa84697c1bd5debdec429f5d0c9b188e Mon Sep 17 00:00:00 2001 From: Yanyun Liao Date: Thu, 16 Apr 2026 16:24:47 +0800 Subject: [PATCH 3/5] docs(uninstall): add function comments for docstring coverage Co-Authored-By: Claude Opus 4.6 Signed-off-by: Yanyun Liao --- uninstall.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uninstall.sh b/uninstall.sh index 6c16a200223..1eaeb190e6c 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -243,6 +243,7 @@ remove_file_with_optional_sudo() { info "Removed $path" } +# Stop NemoClaw helper services (e.g. Telegram bridge, cloudflared). stop_helper_services() { if [ -x "$SCRIPT_DIR/scripts/start-services.sh" ]; then run_optional "Stopped NemoClaw helper services" "$SCRIPT_DIR/scripts/start-services.sh" --stop @@ -251,6 +252,7 @@ stop_helper_services() { remove_glob_paths "${TMP_ROOT}/nemoclaw-services-*" } +# Stop openshell port-forward processes on the dashboard port. stop_openshell_forward_processes() { if ! command -v pgrep >/dev/null 2>&1; then warn "pgrep not found; skipping local OpenShell forward process cleanup." @@ -342,6 +344,7 @@ stop_orphaned_openshell_processes() { done } +# Remove OpenShell sandboxes, providers, and gateway. remove_openshell_resources() { if ! command -v openshell >/dev/null 2>&1; then warn "openshell not found; skipping gateway/provider/sandbox cleanup." From 1fafefd2106ee4e212face34474de236aad2a64a Mon Sep 17 00:00:00 2001 From: Yanyun Liao Date: Thu, 16 Apr 2026 16:40:46 +0800 Subject: [PATCH 4/5] fix(uninstall): use original caller for pgrep and tighten ssh matching (#1940) - Use SUDO_USER/LOGNAME instead of id -un so pgrep targets the invoking user (not root) when running under sudo. - Tighten ssh process matching from broad "*openshell*" to specific "*openshell ssh-proxy*" or "*openshell-*" patterns to avoid false positives on unrelated ssh sessions. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Yanyun Liao --- uninstall.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/uninstall.sh b/uninstall.sh index 1eaeb190e6c..fb0bdee20bf 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -294,10 +294,11 @@ stop_orphaned_openshell_processes() { local -a pids=() local pid - # Scope to the invoking user to avoid killing other users' processes - # on shared systems (e.g. when running via sudo). (#1940) + # Scope to the original invoking user to avoid killing other users' processes + # on shared systems. Under sudo, SUDO_USER holds the real caller; fall back + # to LOGNAME, then id -un. (#1940) local _user - _user="$(id -un 2>/dev/null || echo "")" + _user="${SUDO_USER:-${LOGNAME:-$(id -un 2>/dev/null || echo "")}}" local -a _pgrep_user=() if [ -n "$_user" ]; then _pgrep_user=(-u "$_user") @@ -309,13 +310,15 @@ stop_orphaned_openshell_processes() { pids+=("$pid") done < <(pgrep "${_pgrep_user[@]}" -f "openshell (sandbox create|ssh-proxy)" 2>/dev/null || true) - # Also collect ssh processes whose command line references openshell - # (these are the SSH sessions spawned by openshell sandbox create). + # Also collect ssh processes whose command line references openshell. + # Match "openshell ssh-proxy" or "openshell-" (gateway name pattern) to + # avoid false positives on unrelated ssh sessions. User scoping via + # _pgrep_user provides an additional safety net. while IFS= read -r pid; do [ -n "$pid" ] || continue local cmd cmd="$(ps -p "$pid" -o args= 2>/dev/null)" || continue - if [[ "$cmd" == *openshell* ]]; then + if [[ "$cmd" == *"openshell ssh-proxy"* ]] || [[ "$cmd" == *"openshell-"* ]]; then pids+=("$pid") fi done < <(pgrep "${_pgrep_user[@]}" -x ssh 2>/dev/null || true) From af0d2fedb5d52e663ba6469f79ecd5038dbdd84c Mon Sep 17 00:00:00 2001 From: Yanyun Liao Date: Thu, 16 Apr 2026 16:48:35 +0800 Subject: [PATCH 5/5] style(uninstall): move local cmd declaration outside loop (#1940) Co-Authored-By: Claude Opus 4.6 Signed-off-by: Yanyun Liao --- uninstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uninstall.sh b/uninstall.sh index fb0bdee20bf..1300f5d2967 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -314,9 +314,9 @@ stop_orphaned_openshell_processes() { # Match "openshell ssh-proxy" or "openshell-" (gateway name pattern) to # avoid false positives on unrelated ssh sessions. User scoping via # _pgrep_user provides an additional safety net. + local cmd while IFS= read -r pid; do [ -n "$pid" ] || continue - local cmd cmd="$(ps -p "$pid" -o args= 2>/dev/null)" || continue if [[ "$cmd" == *"openshell ssh-proxy"* ]] || [[ "$cmd" == *"openshell-"* ]]; then pids+=("$pid")