| //go:build windows | |
| package orchestrator | |
| import ( | |
| "os" | |
| "os/exec" | |
| "syscall" | |
| "unsafe" | |
| ) | |
| func setProcGroup(cmd *exec.Cmd) {} | |
| func killProcessGroup(pid int, _ syscall.Signal) error { | |
| p, err := os.FindProcess(pid) | |
| if err != nil { | |
| return err | |
| } | |
| return p.Kill() | |
| } | |
| var ( | |
| modkernel32 = syscall.NewLazyDLL("kernel32.dll") | |
| procOpenProcess = modkernel32.NewProc("OpenProcess") | |
| procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess") | |
| ) | |
| const ( | |
| processQueryLimitedInfo = 0x1000 | |
| stillActive = 259 // STATUS_PENDING — process has not exited | |
| ) | |
| func processAlive(pid int) bool { | |
| // On Windows, os.Process.Signal(0) always fails because Go does not | |
| // support signals on Windows (only os.Kill via TerminateProcess). | |
| // Instead, use OpenProcess + GetExitCodeProcess to check liveness. | |
| handle, _, err := procOpenProcess.Call( | |
| uintptr(processQueryLimitedInfo), | |
| 0, // bInheritHandle = false | |
| uintptr(pid), | |
| ) | |
| if handle == 0 { | |
| // Cannot open process — either it doesn't exist or access denied. | |
| // If access denied the process is alive, but we can't tell; assume dead | |
| // to match the previous (broken) behaviour conservatively. | |
| _ = err | |
| return false | |
| } | |
| defer func() { _ = syscall.CloseHandle(syscall.Handle(handle)) }() | |
| var exitCode uint32 | |
| ret, _, _ := procGetExitCodeProcess.Call(handle, uintptr(unsafe.Pointer(&exitCode))) | |
| if ret == 0 { | |
| return false // API call failed | |
| } | |
| return exitCode == stillActive | |
| } | |
| const sigTERM = syscall.Signal(0xf) | |
| const sigKILL = syscall.Signal(0x9) | |