Files
kandev/apps/backend/internal/agentctl/server/process/procattr_windows.go
T
2026-06-26 10:09:23 +01:00

72 lines
2.0 KiB
Go

//go:build windows
package process
import (
"errors"
"fmt"
"os/exec"
"syscall"
"github.com/kandev/kandev/internal/agentctl/server/winproc"
"golang.org/x/sys/windows"
)
// setProcGroup configures the command to run in its own process group.
func setProcGroup(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
}
// setAgentProcGroup configures long-running agent processes for process-tree
// cleanup. On Windows, agents start suspended so installProcessLifecycle can
// bind them to a kill-on-close Job Object before they can spawn descendants.
func setAgentProcGroup(cmd *exec.Cmd) {
setProcGroup(cmd)
cmd.SysProcAttr.CreationFlags |= windows.CREATE_SUSPENDED
}
type processLifecycleHandle struct {
job winproc.KillOnCloseJob
}
func installProcessLifecycle(cmd *exec.Cmd) (processLifecycleHandle, error) {
job, err := winproc.InstallKillOnCloseJobForSuspendedCommand(cmd)
if err != nil {
return processLifecycleHandle{}, err
}
return processLifecycleHandle{job: job}, nil
}
func releaseProcessLifecycle(lifecycle processLifecycleHandle) {
_ = lifecycle.job.Close()
}
// killProcessGroup kills the entire process tree for the given PID.
// On Windows, we use taskkill with /T flag to kill the process tree.
func killProcessGroup(pid int) error {
return runProcessTaskkill("/F", "/T", "/PID", fmt.Sprintf("%d", pid))
}
// terminateProcessGroup sends a graceful shutdown signal to the process tree.
// Without /F, taskkill sends WM_CLOSE to console and windowed apps, which is
// the closest Windows equivalent of SIGTERM.
func terminateProcessGroup(pid int) error {
return runProcessTaskkill("/T", "/PID", fmt.Sprintf("%d", pid))
}
func processGroupAlive(_ int) bool {
// taskkill /T is the authoritative process-tree operation on Windows.
// There is no Unix-style process group to poll here.
return false
}
func isProcessGroupMissing(err error) bool {
return errors.Is(err, syscall.ESRCH)
}
func runProcessTaskkill(args ...string) error {
return winproc.RunTaskkill(args...)
}