af057ff165
Keep current plans and release controls discoverable without losing historical evidence, and include the reviewed Windows start/stop workflow.
43 lines
3.0 KiB
PowerShell
43 lines
3.0 KiB
PowerShell
# Stop only recorded project processes whose creation time and identity still match.
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = $PSScriptRoot
|
|
$stateFile = Join-Path $root '.runtime\dev-logs\processes.json'
|
|
if (-not (Test-Path -LiteralPath $stateFile)) { Write-Host 'No recorded local services.'; exit 0 }
|
|
$state = Get-Content -Raw -LiteralPath $stateFile | ConvertFrom-Json
|
|
if ($state.Root -ne $root) { throw 'Process state belongs to another directory. Nothing stopped.' }
|
|
function Get-Identity([int]$ProcessId) {
|
|
$item = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId" -ErrorAction SilentlyContinue
|
|
if (-not $item) { return $null }
|
|
return [pscustomobject]@{ Pid = [int]$item.ProcessId; CreatedUtc = $item.CreationDate.ToUniversalTime().ToString('o'); ExecutablePath = [string]$item.ExecutablePath; CommandLine = [string]$item.CommandLine }
|
|
}
|
|
function Stop-MatchedProcess($Expected, [switch]$VerifiedPythonChild) {
|
|
$actual = Get-Identity $Expected.Pid
|
|
if (-not $actual) { return }
|
|
if ($actual.CreatedUtc -ne $Expected.CreatedUtc -or $actual.ExecutablePath -ne $Expected.ExecutablePath -or $actual.CommandLine -ne $Expected.CommandLine -or (-not $VerifiedPythonChild -and $actual.CommandLine.IndexOf($root, [StringComparison]::OrdinalIgnoreCase) -lt 0)) {
|
|
Write-Warning "PID $($Expected.Pid) no longer matches this project; skipped."
|
|
return
|
|
}
|
|
# Only matched project descendants; never kill by process name.
|
|
$moduleArguments = if ($actual.CommandLine -match '\s-m\s+(.+)$') { $matches[1] } else { '' }
|
|
$children = @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $($actual.Pid)" | Where-Object {
|
|
$_.CommandLine -and $_.CreationDate.ToUniversalTime() -ge [datetime]::Parse($actual.CreatedUtc).ToUniversalTime()
|
|
})
|
|
foreach ($child in $children) {
|
|
$identity = Get-Identity $child.ProcessId
|
|
if (-not $identity) { continue }
|
|
if ($identity.CreatedUtc -ne $child.CreationDate.ToUniversalTime().ToString('o') -or $identity.CommandLine -ne $child.CommandLine) { continue }
|
|
if ($identity.CommandLine.IndexOf($root, [StringComparison]::OrdinalIgnoreCase) -ge 0) { Stop-MatchedProcess $identity }
|
|
elseif ($moduleArguments -and $actual.ExecutablePath -match '\\python.exe$' -and $identity.ExecutablePath -match '\\python.exe$' -and $identity.CommandLine -match '\s-m\s+(.+)$' -and $matches[1] -eq $moduleArguments) {
|
|
# Same module and arguments, live parent link, and later creation time establish venv ownership.
|
|
Stop-MatchedProcess $identity -VerifiedPythonChild
|
|
}
|
|
}
|
|
$recheck = Get-Identity $actual.Pid
|
|
if ($recheck -and $recheck.CreatedUtc -eq $actual.CreatedUtc -and $recheck.CommandLine -eq $actual.CommandLine -and $recheck.ExecutablePath -eq $actual.ExecutablePath) {
|
|
Stop-Process -Id $actual.Pid -Force -ErrorAction Stop
|
|
Write-Host "Stopped PID $($actual.Pid)"
|
|
}
|
|
}
|
|
foreach ($entry in @($state.Services | Sort-Object { if ($_.Name -match 'worker') { 0 } else { 1 } })) { Stop-MatchedProcess $entry.Process }
|
|
Write-Host 'Recorded project services stopped; logs retained in .runtime\dev-logs.'
|