72 lines
2.0 KiB
Go
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...)
|
|
}
|