129 lines
2.9 KiB
Go
129 lines
2.9 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const windowsCreateNewProcessGroupForAIServer = 0x00000200
|
|
|
|
func prepareAIServerCommandForLifecycle(cmd *exec.Cmd) {
|
|
if cmd == nil {
|
|
return
|
|
}
|
|
if cmd.SysProcAttr == nil {
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
}
|
|
cmd.SysProcAttr.CreationFlags |= windowsCreateNewProcessGroupForAIServer
|
|
}
|
|
|
|
func terminateAIServerProcessTree(cmd *exec.Cmd) {
|
|
if cmd == nil || cmd.Process == nil || cmd.Process.Pid <= 0 {
|
|
return
|
|
}
|
|
|
|
terminateWindowsPIDTree(cmd.Process.Pid)
|
|
_ = cmd.Process.Kill()
|
|
}
|
|
|
|
func stopStaleAIServerOnPort(port string) error {
|
|
portNumber, err := strconv.Atoi(strings.TrimSpace(port))
|
|
if err != nil || portNumber <= 0 || portNumber > 65535 {
|
|
return nil
|
|
}
|
|
|
|
pids, err := windowsPIDsListeningOnPort(portNumber)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
currentPID := os.Getpid()
|
|
for _, pid := range pids {
|
|
if pid <= 0 || pid == currentPID {
|
|
continue
|
|
}
|
|
if !windowsPIDLooksLikeAIServer(pid) {
|
|
appLogf("⚠️ Listener auf AI-Port %d übersprungen, Prozess sieht nicht nach AI Server aus: pid=%d", portNumber, pid)
|
|
continue
|
|
}
|
|
appLogf("🧹 Alter AI Server auf Port %d wird beendet: pid=%d", portNumber, pid)
|
|
terminateWindowsPIDTree(pid)
|
|
}
|
|
|
|
if len(pids) > 0 {
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func windowsPIDsListeningOnPort(port int) ([]int, error) {
|
|
ps := "Get-NetTCPConnection -LocalPort " + strconv.Itoa(port) + " -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique"
|
|
cmd := exec.Command(
|
|
"powershell",
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-ExecutionPolicy", "Bypass",
|
|
"-Command", ps,
|
|
)
|
|
hideCommandWindow(cmd)
|
|
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pidSet := map[int]struct{}{}
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
pid, err := strconv.Atoi(strings.TrimSpace(line))
|
|
if err != nil || pid <= 0 {
|
|
continue
|
|
}
|
|
pidSet[pid] = struct{}{}
|
|
}
|
|
|
|
pids := make([]int, 0, len(pidSet))
|
|
for pid := range pidSet {
|
|
pids = append(pids, pid)
|
|
}
|
|
return pids, nil
|
|
}
|
|
|
|
func terminateWindowsPIDTree(pid int) {
|
|
if pid <= 0 {
|
|
return
|
|
}
|
|
|
|
cmd := exec.Command("taskkill", "/PID", strconv.Itoa(pid), "/T", "/F")
|
|
hideCommandWindow(cmd)
|
|
_ = cmd.Run()
|
|
}
|
|
|
|
func windowsPIDLooksLikeAIServer(pid int) bool {
|
|
ps := "(Get-CimInstance Win32_Process -Filter \"ProcessId = " + strconv.Itoa(pid) + "\" -ErrorAction SilentlyContinue).CommandLine"
|
|
cmd := exec.Command(
|
|
"powershell",
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-ExecutionPolicy", "Bypass",
|
|
"-Command", ps,
|
|
)
|
|
hideCommandWindow(cmd)
|
|
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
cmdline := strings.ToLower(strings.TrimSpace(string(out)))
|
|
return strings.Contains(cmdline, "ai_server:app") ||
|
|
strings.Contains(cmdline, "ai_server.py") ||
|
|
(strings.Contains(cmdline, "uvicorn") && strings.Contains(cmdline, "ai_server"))
|
|
}
|