//go:build windows package cli import ( "fmt" "os/exec" "sync" "unsafe" "golang.org/x/sys/windows") var commandJobs sync.Map func configureCommandGroup(cmd *exec.Cmd) { job, err := windows.CreateJobObject(nil, nil) if err != nil { return } info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{} info.BasicLimitInformation.LimitFlags = windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE if _, err := windows.SetInformationJobObject( job, windows.JobObjectExtendedLimitInformation, uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), ); err != nil { _ = windows.CloseHandle(job) return } commandJobs.Store(cmd, job)} func attachCommandGroup(cmd *exec.Cmd) error { if cmd == nil || cmd.Process == nil { return nil } value, ok := commandJobs.Load(cmd) if !ok { return nil } job, ok := value.(windows.Handle) if !ok || job == 0 { return nil } process, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(cmd.Process.Pid)) if err != nil { cleanupCommandGroup(cmd) return nil } defer windows.CloseHandle(process) if err := windows.AssignProcessToJobObject(job, process); err != nil { cleanupCommandGroup(cmd) return nil } return nil} func killCommandGroup(cmd *exec.Cmd) { if cmd == nil || cmd.Process == nil { return } if value, ok := commandJobs.Load(cmd); ok { if job, ok := value.(windows.Handle); ok && job != 0 { _ = windows.TerminateJobObject(job, 1) } } _ = exec.Command("taskkill", "/T", "/F", "/PID", fmt.Sprint(cmd.Process.Pid)).Run() _ = cmd.Process.Kill()} func cleanupCommandGroup(cmd *exec.Cmd) { value, ok := commandJobs.LoadAndDelete(cmd) if !ok { return } if job, ok := value.(windows.Handle); ok && job != 0 { _ = windows.CloseHandle(job) }}
//go:build windows
package cli
import (
"fmt"
"os/exec"
"sync"
"unsafe"
"golang.org/x/sys/windows"
)
var commandJobs sync.Map
func configureCommandGroup(cmd *exec.Cmd) {
job, err := windows.CreateJobObject(nil, nil)
if err != nil {
return
}
info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{}
info.BasicLimitInformation.LimitFlags = windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
if _, err := windows.SetInformationJobObject(
job,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
); err != nil {
_ = windows.CloseHandle(job)
return
}
commandJobs.Store(cmd, job)
}
func attachCommandGroup(cmd *exec.Cmd) error {
if cmd == nil || cmd.Process == nil {
return nil
}
value, ok := commandJobs.Load(cmd)
if !ok {
return nil
}
job, ok := value.(windows.Handle)
if !ok || job == 0 {
return nil
}
process, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(cmd.Process.Pid))
if err != nil {
cleanupCommandGroup(cmd)
return nil
}
defer windows.CloseHandle(process)
if err := windows.AssignProcessToJobObject(job, process); err != nil {
cleanupCommandGroup(cmd)
return nil
}
return nil
}
func killCommandGroup(cmd *exec.Cmd) {
if cmd == nil || cmd.Process == nil {
return
}
if value, ok := commandJobs.Load(cmd); ok {
if job, ok := value.(windows.Handle); ok && job != 0 {
_ = windows.TerminateJobObject(job, 1)
}
}
_ = exec.Command("taskkill", "/T", "/F", "/PID", fmt.Sprint(cmd.Process.Pid)).Run()
_ = cmd.Process.Kill()
}
func cleanupCommandGroup(cmd *exec.Cmd) {
value, ok := commandJobs.LoadAndDelete(cmd)
if !ok {
return
}
if job, ok := value.(windows.Handle); ok && job != 0 {
_ = windows.CloseHandle(job)
}
}