mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-19 11:15:24 -04:00
fix(cookbook): stop Windows process trees (#4283)
This commit is contained in:
@@ -784,40 +784,47 @@ function _winSessionCmd(task, tmuxArgs) {
|
||||
const ps = host
|
||||
? `Get-Content '${sd}\\${sid}.log' -Tail ${lines} -ErrorAction SilentlyContinue`
|
||||
: `Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.log') -Tail ${lines} -ErrorAction SilentlyContinue`;
|
||||
return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`;
|
||||
return _winPowerShellCmd(task, ps);
|
||||
}
|
||||
if (tmuxArgs.includes('has-session')) {
|
||||
const ps = host
|
||||
? `$p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p) { Get-Process -Id $p -ErrorAction SilentlyContinue | Out-Null; if ($?) { exit 0 } else { exit 1 } } else { exit 1 }`
|
||||
: `$p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p) { Get-Process -Id $p -ErrorAction SilentlyContinue | Out-Null; if ($?) { exit 0 } else { exit 1 } } else { exit 1 }`;
|
||||
return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`;
|
||||
return _winPowerShellCmd(task, ps);
|
||||
}
|
||||
if (tmuxArgs.includes('kill-session')) {
|
||||
const stopTree = `function Stop-Tree([int]$Id) { Get-CimInstance Win32_Process -Filter "ParentProcessId = $Id" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Tree ([int]$_.ProcessId) }; Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue }`;
|
||||
const ps = host
|
||||
? `${stopTree}; $p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item '${sd}\\${sid}.*' -Force -ErrorAction SilentlyContinue`
|
||||
: `${stopTree}; $p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.*') -Force -ErrorAction SilentlyContinue`;
|
||||
return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`;
|
||||
const ps = _winSessionStopTreePs(task);
|
||||
return _winPowerShellCmd(task, ps);
|
||||
}
|
||||
if (tmuxArgs.includes('send-keys') && tmuxArgs.includes('C-c')) {
|
||||
const ps = host
|
||||
? `$p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -ErrorAction SilentlyContinue }`
|
||||
: `$p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -ErrorAction SilentlyContinue }`;
|
||||
return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`;
|
||||
return _winPowerShellCmd(task, ps);
|
||||
}
|
||||
return host ? `ssh ${pf}${host} 'tmux ${tmuxArgs}' 2>/dev/null` : `tmux ${tmuxArgs} 2>/dev/null`;
|
||||
}
|
||||
|
||||
function _winPowerShellCmd(task, ps) {
|
||||
const command = `powershell -Command "${ps}"`;
|
||||
if (!task.remoteHost) return command;
|
||||
return `ssh ${_sshPrefix(_getPort(task))}${task.remoteHost} ${_shQuote(command)}`;
|
||||
}
|
||||
|
||||
function _winSessionStopTreePs(task) {
|
||||
const host = task.remoteHost;
|
||||
const sd = host ? '$env:TEMP\\odysseus-sessions' : '$env:TEMP\\odysseus-tmux';
|
||||
const sid = task.sessionId;
|
||||
const stopTree = `function Stop-Tree([int]$Id) { Get-CimInstance Win32_Process -Filter ('ParentProcessId = ' + $Id) -ErrorAction SilentlyContinue | ForEach-Object { Stop-Tree ([int]$_.ProcessId) }; Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue }`;
|
||||
return host
|
||||
? `${stopTree}; $p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item '${sd}\\${sid}.*' -Force -ErrorAction SilentlyContinue`
|
||||
: `${stopTree}; $p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.*') -Force -ErrorAction SilentlyContinue`;
|
||||
}
|
||||
|
||||
export function _tmuxGracefulKill(task) {
|
||||
if (_isWindows(task)) {
|
||||
const host = task.remoteHost;
|
||||
const sd = host ? '$env:TEMP\\odysseus-sessions' : '$env:TEMP\\odysseus-tmux';
|
||||
const sid = task.sessionId;
|
||||
const pf = _sshPrefix(_getPort(task));
|
||||
const ps = host
|
||||
? `$p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -Force -ErrorAction SilentlyContinue }; Remove-Item '${sd}\\${sid}.*' -Force -ErrorAction SilentlyContinue`
|
||||
: `$p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -Force -ErrorAction SilentlyContinue }; Remove-Item (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.*') -Force -ErrorAction SilentlyContinue`;
|
||||
return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`;
|
||||
const ps = _winSessionStopTreePs(task);
|
||||
return _winPowerShellCmd(task, ps);
|
||||
}
|
||||
if (task.remoteHost) {
|
||||
return `ssh ${_sshPrefix(_getPort(task))}${task.remoteHost} 'tmux send-keys -t ${task.sessionId} C-c 2>/dev/null; sleep 2; tmux kill-session -t ${task.sessionId} 2>/dev/null'`;
|
||||
|
||||
@@ -28,13 +28,15 @@ def test_background_status_poll_reconciles_into_local_tasks():
|
||||
assert "completedDeps.forEach(t => _refreshDepsAfterInstall(t));" in source
|
||||
|
||||
|
||||
def test_local_windows_session_commands_use_local_powershell_log_dir():
|
||||
def test_windows_session_commands_use_shared_powershell_wrapper_and_local_log_dir():
|
||||
source = _read("static/js/cookbookRunning.js")
|
||||
|
||||
assert "const host = task.remoteHost;" in source
|
||||
assert "host ? '$env:TEMP\\\\odysseus-sessions' : '$env:TEMP\\\\odysseus-tmux'" in source
|
||||
assert "return host ? `ssh ${pf}${host}" in source
|
||||
assert ": `powershell -Command \"${ps}\"`;" in source
|
||||
assert "function _winPowerShellCmd(task, ps)" in source
|
||||
assert "const command = `powershell -Command \"${ps}\"`;" in source
|
||||
assert "if (!task.remoteHost) return command;" in source
|
||||
assert "return `ssh ${_sshPrefix(_getPort(task))}${task.remoteHost} ${_shQuote(command)}`;" in source
|
||||
|
||||
|
||||
def test_dep_install_success_recognized_from_exit_sentinel():
|
||||
|
||||
@@ -718,7 +718,7 @@ def test_local_windows_download_pid_tracks_inner_bash_and_stop_kills_tree():
|
||||
|
||||
assert 'printf \'%s\\\\n\' \\"$$\\" > {pp}' in routes_src
|
||||
assert "function Stop-Tree([int]$Id)" in running_src
|
||||
assert "ParentProcessId = $Id" in running_src
|
||||
assert "('ParentProcessId = ' + $Id)" in running_src
|
||||
assert "Stop-Tree ([int]$p)" in running_src
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RUNNING_JS = ROOT / "static" / "js" / "cookbookRunning.js"
|
||||
|
||||
|
||||
def _between(source, start, end):
|
||||
start_idx = source.index(start)
|
||||
end_idx = source.index(end, start_idx)
|
||||
return source[start_idx:end_idx]
|
||||
|
||||
|
||||
def test_windows_graceful_kill_reuses_recursive_stop_tree_helper():
|
||||
source = RUNNING_JS.read_text(encoding="utf-8")
|
||||
wrapper = _between(source, "function _winPowerShellCmd(task, ps)", "function _winSessionStopTreePs(task)")
|
||||
helper = _between(source, "function _winSessionStopTreePs(task)", "function _tmuxGracefulKill(task)")
|
||||
graceful = _between(source, "function _tmuxGracefulKill(task)", "function _shQuote(value)")
|
||||
win_session = _between(source, "function _winSessionCmd(task, tmuxArgs)", "function _winPowerShellCmd(task, ps)")
|
||||
|
||||
assert "function Stop-Tree([int]$Id)" in helper
|
||||
assert "('ParentProcessId = ' + $Id)" in helper
|
||||
assert "Stop-Tree ([int]$p)" in helper
|
||||
assert "${_shQuote(command)}" in wrapper
|
||||
assert "_winSessionStopTreePs(task)" in win_session
|
||||
assert "_winPowerShellCmd(task, ps)" in win_session
|
||||
assert "_winSessionStopTreePs(task)" in graceful
|
||||
assert "_winPowerShellCmd(task, ps)" in graceful
|
||||
assert "Stop-Process -Id $p -Force" not in graceful
|
||||
assert '-Filter "ParentProcessId = $Id"' not in helper
|
||||
assert 'powershell -Command \\\\"${ps}\\\\"' not in source
|
||||
|
||||
|
||||
def _posix_quote(value):
|
||||
return "'" + value.replace("'", "'\\''") + "'"
|
||||
|
||||
|
||||
def test_remote_windows_stop_tree_payload_survives_shell_parsing():
|
||||
ps = (
|
||||
"function Stop-Tree([int]$Id) { "
|
||||
"Get-CimInstance Win32_Process -Filter ('ParentProcessId = ' + $Id) "
|
||||
"-ErrorAction SilentlyContinue | ForEach-Object { Stop-Tree ([int]$_.ProcessId) }; "
|
||||
"Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue }; "
|
||||
"$p = Get-Content '$env:TEMP\\odysseus-sessions\\serve_abc.pid' "
|
||||
"-ErrorAction SilentlyContinue; "
|
||||
"if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }"
|
||||
)
|
||||
remote_command = f'powershell -Command "{ps}"'
|
||||
shell_command = f"ssh -p 2222 winbox {_posix_quote(remote_command)}"
|
||||
|
||||
argv = shlex.split(shell_command)
|
||||
|
||||
assert argv == ["ssh", "-p", "2222", "winbox", remote_command]
|
||||
assert "$Id" in argv[-1]
|
||||
assert "$_.ProcessId" in argv[-1]
|
||||
assert "$env:TEMP" in argv[-1]
|
||||
assert "$p" in argv[-1]
|
||||
Reference in New Issue
Block a user