mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 06:40:45 +08:00
Some checks failed
Release Windows / build (push) Has been cancelled
API logs go under ProgramData; first-run verifies CREATE ROLE/DATABASE and login. Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
5.3 KiB
PowerShell
158 lines
5.3 KiB
PowerShell
param(
|
|
[int]$Port = 8890,
|
|
[int]$WebPort = 5173,
|
|
# Windows often ignores graceful Stop-Process on python; default hard-kill.
|
|
[switch]$Force = $true,
|
|
[switch]$NoForce = $false
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
$useForce = if ($NoForce) { $false } else { [bool]$Force }
|
|
|
|
if ($env:NETX_RUN_DIR) {
|
|
$runDir = $env:NETX_RUN_DIR
|
|
} elseif ($env:NETX_SCHEDULER_HEARTBEAT_PATH) {
|
|
$runDir = Split-Path -Parent ($env:NETX_SCHEDULER_HEARTBEAT_PATH -replace '/', '\')
|
|
} else {
|
|
$runDir = Join-Path $PSScriptRoot ".run"
|
|
}
|
|
$pidFile = Join-Path $runDir "netx.pid"
|
|
$workerPidFile = Join-Path $runDir "worker.pid"
|
|
$webPidFile = Join-Path $runDir "web.pid"
|
|
$bizStateWorkerPidDir = Join-Path $runDir "biz_state_workers"
|
|
|
|
function Stop-OnePid {
|
|
param([int]$ProcId, [string]$Label)
|
|
if ($ProcId -le 4) { return }
|
|
if ($ProcId -eq $PID) { return }
|
|
try {
|
|
Stop-Process -Id $ProcId -Force:$useForce -ErrorAction Stop
|
|
Write-Host "Stopped $Label PID=$ProcId"
|
|
} catch {
|
|
$msg = $_.Exception.Message
|
|
Write-Host "[WARN] Failed to stop $Label PID=$ProcId : $msg"
|
|
if ($msg -match 'Access|Denied|拒绝|拒绝访问') {
|
|
Write-Host " Run elevated PowerShell, then: taskkill /F /PID $ProcId" -ForegroundColor Yellow
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-ListenPids {
|
|
param([int]$LocalPort)
|
|
$ids = [System.Collections.Generic.HashSet[int]]::new()
|
|
$job = $null
|
|
try {
|
|
$job = Start-Job -ScriptBlock {
|
|
param($pt)
|
|
Get-NetTCPConnection -LocalPort $pt -State Listen -ErrorAction SilentlyContinue |
|
|
Select-Object -ExpandProperty OwningProcess -Unique
|
|
} -ArgumentList $LocalPort
|
|
$null = Wait-Job $job -Timeout 5
|
|
foreach ($x in @(Receive-Job $job -ErrorAction SilentlyContinue)) {
|
|
try {
|
|
$n = [int]$x
|
|
if ($n -gt 4) { [void]$ids.Add($n) }
|
|
} catch {}
|
|
}
|
|
} catch {
|
|
Write-Host "[WARN] port query failed for $LocalPort : $($_.Exception.Message)"
|
|
} finally {
|
|
if ($job) {
|
|
# Windows PowerShell 5.1 does not support -Force on Stop-Job/Remove-Job.
|
|
Stop-Job $job -ErrorAction SilentlyContinue
|
|
Remove-Job $job -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
@($ids)
|
|
}
|
|
|
|
function Stop-NetxByCommandLine {
|
|
# Orphan workers often have no worker.pid but still hold worker.out.log.
|
|
$hits = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.CommandLine -match 'netx_api\.(main|worker|biz_state_worker)' })
|
|
if ($hits.Count -eq 0) {
|
|
Write-Host "[INFO] No netx_api.main/worker/biz_state_worker process by command line"
|
|
return
|
|
}
|
|
foreach ($p in $hits) {
|
|
$kind = if ($p.CommandLine -match 'biz_state_worker') {
|
|
"biz_state_worker(cmd)"
|
|
} elseif ($p.CommandLine -match 'netx_api\.worker') {
|
|
"worker(cmd)"
|
|
} else {
|
|
"api(cmd)"
|
|
}
|
|
Stop-OnePid -ProcId ([int]$p.ProcessId) -Label $kind
|
|
}
|
|
}
|
|
|
|
Write-Host "==> Stopping netx (Force=$useForce)"
|
|
|
|
if (Test-Path $pidFile) {
|
|
$pidText = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
$procId = 0
|
|
[void][int]::TryParse("$pidText", [ref]$procId)
|
|
if ($procId -gt 0) {
|
|
Stop-OnePid -ProcId $procId -Label "API"
|
|
}
|
|
Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue
|
|
} else {
|
|
Write-Host "==> No API PID file, try by port $Port"
|
|
}
|
|
|
|
if (Test-Path $workerPidFile) {
|
|
$workerPidText = (Get-Content -Path $workerPidFile -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
$workerProcId = 0
|
|
[void][int]::TryParse("$workerPidText", [ref]$workerProcId)
|
|
if ($workerProcId -gt 0) {
|
|
Stop-OnePid -ProcId $workerProcId -Label "worker"
|
|
}
|
|
Remove-Item -Path $workerPidFile -Force -ErrorAction SilentlyContinue
|
|
} else {
|
|
Write-Host "[INFO] No worker PID file"
|
|
}
|
|
|
|
if (Test-Path $bizStateWorkerPidDir) {
|
|
Get-ChildItem -Path $bizStateWorkerPidDir -Filter "*.pid" -ErrorAction SilentlyContinue | ForEach-Object {
|
|
$t = (Get-Content -Path $_.FullName -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
$id = 0
|
|
[void][int]::TryParse("$t", [ref]$id)
|
|
if ($id -gt 0) {
|
|
Stop-OnePid -ProcId $id -Label "biz_state_worker"
|
|
}
|
|
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
if (Test-Path $webPidFile) {
|
|
$webPidText = (Get-Content -Path $webPidFile -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
$webProcId = 0
|
|
[void][int]::TryParse("$webPidText", [ref]$webProcId)
|
|
if ($webProcId -gt 0) {
|
|
Stop-OnePid -ProcId $webProcId -Label "web"
|
|
}
|
|
Remove-Item -Path $webPidFile -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
Stop-NetxByCommandLine
|
|
|
|
$owningApi = @(Get-ListenPids -LocalPort $Port)
|
|
if ($owningApi.Count -gt 0) {
|
|
foreach ($procId in $owningApi) {
|
|
Stop-OnePid -ProcId $procId -Label "port:$Port"
|
|
}
|
|
} else {
|
|
Write-Host "[INFO] No listener found on port $Port"
|
|
}
|
|
|
|
$owningWeb = @(Get-ListenPids -LocalPort $WebPort)
|
|
if ($owningWeb.Count -gt 0) {
|
|
foreach ($procId in $owningWeb) {
|
|
Stop-OnePid -ProcId $procId -Label "port:$WebPort"
|
|
}
|
|
} else {
|
|
Write-Host "[INFO] No listener found on web port $WebPort"
|
|
}
|
|
|
|
Write-Host "==> netx stop finished" -ForegroundColor Green
|
|
exit 0
|