# ================================================================ # PowerShell-Helfer fuer run_web.bat (start/stop/status) # Aufruf: powershell -NoProfile -ExecutionPolicy Bypass # -File run_web_helper.ps1 # # "check" liefert nur den Exitcode (0 = Server lauscht, 1 = nicht) # und keine Ausgabe - fuer die Pruefung nach dem Start. # # Verwaiste Socket-Eintraege (Listener, deren Prozess nicht mehr # existiert - passiert nach abgestuerzten Reload-Kindprozessen) # werden ueberall ignoriert. # ================================================================ param( [Parameter(Mandatory = $true)][ValidateSet('status', 'stop', 'check')] [string]$Aktion, [int]$Port = 8000 ) function Lebende-Listener([int]$p) { try { Get-NetTCPConnection -LocalPort $p -State Listen -ErrorAction Stop | Where-Object { Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue } } catch { @() } } function Uvicorn-Prozesse { Get-CimInstance Win32_Process -Filter "Name like 'python%'" | Where-Object { $_.CommandLine -match 'uvicorn main:app' } } switch ($Aktion) { 'check' { if (Lebende-Listener $Port) { exit 0 } else { exit 1 } } 'status' { $l = Lebende-Listener $Port | Select-Object -First 1 if ($l) { "LAEUFT: http://localhost:$Port (Listener-PID $($l.OwningProcess))" foreach ($e in Uvicorn-Prozesse) { " uvicorn-Prozess: PID $($e.ProcessId)" } exit 0 } else { "GESTOPPT: kein Server auf Port $Port" exit 1 } } 'stop' { $gefunden = $false $alle = Get-CimInstance Win32_Process -Filter "Name like 'python%'" $reloaderPids = @() foreach ($e in ($alle | Where-Object { $_.CommandLine -match 'uvicorn main:app' })) { $gefunden = $true $reloaderPids += $e.ProcessId # erst die Kindprozesse (Reload-Worker), dann den Reloader selbst $alle | Where-Object { $_.ParentProcessId -eq $e.ProcessId } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -Confirm:$false -ErrorAction SilentlyContinue } Stop-Process -Id $e.ProcessId -Force -Confirm:$false -ErrorAction SilentlyContinue } # Zombie-Jagd: uvicorn-Reload-Worker sind multiprocessing-Spawn-Kinder # ("spawn_main(parent_pid=N, ...)"). Stirbt ihr Reloader unsanft, # ueberleben sie MIT geerbtem Server-Socket und bedienen den Port # unsichtbar weiter (weder "uvicorn" in der Kommandozeile noch # Eintrag als Socket-Besitzer!). Erkennung: spawn_main-Prozess, # dessen parent_pid tot oder ein eben beendeter Reloader ist. foreach ($k in ($alle | Where-Object { $_.CommandLine -match 'multiprocessing.spawn.*spawn_main' })) { if ($k.CommandLine -match 'parent_pid=(\d+)') { $eltern = [int]$Matches[1] $elternTot = -not (Get-Process -Id $eltern -ErrorAction SilentlyContinue) if ($elternTot -or ($reloaderPids -contains $eltern)) { $gefunden = $true Stop-Process -Id $k.ProcessId -Force -Confirm:$false -ErrorAction SilentlyContinue } } } # Nachzuegler: alles Lebende, das den Port noch haelt Lebende-Listener $Port | ForEach-Object { $gefunden = $true Stop-Process -Id $_.OwningProcess -Force -Confirm:$false -ErrorAction SilentlyContinue } if ($gefunden) { 'Beendet.' } else { 'Es lief nichts.' } exit 0 } }