Skip to content

fix: argoexec kill command in Windows to use osspecific.Kill #14352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions cmd/argoexec/commands/kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package commands

import (
"fmt"
"os"
osspecific "github.com/argoproj/argo-workflows/v3/workflow/executor/os-specific"
"strconv"
"syscall"

Expand All @@ -23,12 +23,8 @@ func NewKillCommand() *cobra.Command {
return err
}
sig := syscall.Signal(signum)
p, err := os.FindProcess(pid)
if err != nil {
return err
}
fmt.Printf("killing %d with %v\n", pid, sig)
if err := p.Signal(sig); err != nil {
if err := osspecific.Kill(pid, sig); err != nil {
return err
}
return nil
Expand Down
100 changes: 95 additions & 5 deletions workflow/executor/os-specific/signal_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package os_specific
import (
"fmt"
"os"
"strings"
"syscall"
"unsafe"

Expand All @@ -13,18 +14,105 @@ import (
var (
Term = os.Interrupt

modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread")
procCtrlRoutine = modkernel32.NewProc("CtrlRoutine")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread")
procCtrlRoutine = modkernel32.NewProc("CtrlRoutine")
procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
procProcess32First = modkernel32.NewProc("Process32FirstW")
procProcess32Next = modkernel32.NewProc("Process32NextW")
)

const (
TH32CS_SNAPPROCESS = 0x00000002
MAX_PATH = 260
)

type ProcessEntry32 struct {
Size uint32
Usage uint32
ProcessID uint32
DefaultHeapID uintptr
ModuleID uint32
Threads uint32
ParentProcessID uint32
PriClassBase int32
Flags uint32
ExeFile [MAX_PATH]uint16
}

func CanIgnoreSignal(s os.Signal) bool {
return false
}

func createToolhelp32Snapshot(flags uint32, processID uint32) (handle windows.Handle, err error) {
r0, _, e1 := syscall.SyscallN(procCreateToolhelp32Snapshot.Addr(), uintptr(flags), uintptr(processID))
handle = windows.Handle(r0)
if handle == 0 {
err = errnoErr(e1)
}
return
}

func process32First(snapshot windows.Handle, procEntry *ProcessEntry32) (err error) {
r0, _, e1 := syscall.SyscallN(procProcess32First.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)))
if r0 == 0 {
err = errnoErr(e1)
}
return
}

func process32Next(snapshot windows.Handle, procEntry *ProcessEntry32) (err error) {
r0, _, e1 := syscall.SyscallN(procProcess32Next.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)))
if r0 == 0 {
err = errnoErr(e1)
}
return
}

func findProcessByName(name string) (int, error) {
snapshot, err := createToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
if err != nil {
return -1, fmt.Errorf("failed to create process snapshot: %w", err)
}
defer windows.CloseHandle(snapshot)

var pe ProcessEntry32
pe.Size = uint32(unsafe.Sizeof(pe))

err = process32First(snapshot, &pe)
if err != nil {
return -1, fmt.Errorf("failed to get first process: %w", err)
}

nameLower := strings.ToLower(name)

for {
exeFile := windows.UTF16ToString(pe.ExeFile[:])
exeFileLower := strings.ToLower(exeFile)

if exeFileLower == nameLower || exeFileLower == nameLower+".exe" {
return int(pe.ProcessID), nil
}

err = process32Next(snapshot, &pe)
if err != nil {
break
}
}

return -1, fmt.Errorf("process %s not found", name)
}

func Kill(pid int, s syscall.Signal) error {
if pid < 0 {
pid = -pid // // we cannot kill a negative process on windows
// Special case: if pid is 1, find the process named "argoexec"
if pid == 1 {
execPid, err := findProcessByName("argoexec")
if err != nil {
return fmt.Errorf("failed to find argoexec process: %w", err)
}
pid = execPid
} else if pid < 0 {
pid = -pid // we cannot kill a negative process on windows
}

winSignal := -1
Expand All @@ -33,6 +121,8 @@ func Kill(pid int, s syscall.Signal) error {
winSignal = windows.CTRL_SHUTDOWN_EVENT
case syscall.SIGINT:
winSignal = windows.CTRL_C_EVENT
case syscall.SIGKILL:
winSignal = windows.CTRL_SHUTDOWN_EVENT
}

if winSignal == -1 {
Expand Down
Loading