github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/util/process/process_windows.go (about) 1 package process 2 3 import ( 4 "fmt" 5 "os" 6 "sync" 7 "syscall" 8 9 "golang.org/x/sys/windows" 10 ) 11 12 var ( 13 kernel32 = syscall.NewLazyDLL("kernel32.dll") 14 procAttachConsole = kernel32.NewProc("AttachConsole") 15 procFreeConsole = kernel32.NewProc("FreeConsole") 16 procSetConsoleCtrlHandler = kernel32.NewProc("SetConsoleCtrlHandler") 17 ) 18 19 func Exists(pid int) bool { 20 const da = syscall.STANDARD_RIGHTS_READ | 21 syscall.PROCESS_QUERY_INFORMATION | 22 syscall.SYNCHRONIZE 23 h, err := syscall.OpenProcess(da, false, uint32(pid)) 24 if err != nil { 25 return false 26 } 27 defer func() { 28 _ = windows.Close(windows.Handle(h)) 29 }() 30 // Refer to Microsoft documentation: 31 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess 32 var exitCode uint32 33 err = windows.GetExitCodeProcess(windows.Handle(h), &exitCode) 34 if err != nil { 35 return false 36 } 37 const STILL_ACTIVE = 259 38 return exitCode == STILL_ACTIVE 39 } 40 41 // SendSignal sends a specified signal to a console process group that shares 42 // the console associated with the calling process. 43 // 44 // Any signal except Interrupt simply terminates the given process p. SIGINT is 45 // sent as a CTRL+C event to the console. It is important to note that the call 46 // disables event handling for the current process. 47 func SendSignal(p *os.Process, s os.Signal) error { 48 if p == nil || p.Pid == 0 { 49 return nil 50 } 51 // On Windows Go runtime does not handle signals (events) other than SIGINT. 52 if s == syscall.SIGINT { 53 if err := sendCtrlC(p.Pid); err == nil { 54 return nil 55 } 56 } 57 return p.Kill() 58 } 59 60 // A process can be attached to at most one console. 61 // Refer to https://docs.microsoft.com/en-us/windows/console/attachconsole. 62 var console sync.Mutex 63 64 // Interrupt sends CTRL+C signal to the console. 65 // Refer to https://github.com/golang/go/issues/6720 for details. 66 func sendCtrlC(pid int) (err error) { 67 console.Lock() 68 defer console.Unlock() 69 ret, _, err := procAttachConsole.Call(uintptr(pid)) 70 if ret == 0 && err != windows.ERROR_ACCESS_DENIED { 71 // A process can be attached to at most one console. If the calling 72 // process is already attached to a console, the error code returned is 73 // ERROR_ACCESS_DENIED (5). If the specified process does not have a 74 // console, the error code returned is ERROR_INVALID_HANDLE (6). If the 75 // specified process does not exist, the error code returned is 76 // ERROR_INVALID_PARAMETER (87). 77 return fmt.Errorf("AttachConsole: %w", err) 78 } 79 // Disable events handling for the current process 80 ret, _, err = procSetConsoleCtrlHandler.Call(0, 1) 81 if ret == 0 { 82 return fmt.Errorf("SetConsoleCtrlHandler: %w", err) 83 } 84 // Note on CTRL_C_EVENT: This signal cannot be generated for process 85 // groups. If dwProcessGroupId is nonzero, this function will succeed, but 86 // the CTRL+C signal will not be received by processes within the specified 87 // process group. 88 if err = windows.GenerateConsoleCtrlEvent(windows.CTRL_C_EVENT, 0); err != nil { 89 return fmt.Errorf("GenerateConsoleCtrlEvent: %w", err) 90 } 91 // A process can use the FreeConsole function to detach itself from its 92 // console. If other processes share the console, the console is not 93 // destroyed, but the process that called FreeConsole cannot refer to it. 94 // If the calling process is not already attached to a console, 95 // the error code returned is ERROR_INVALID_PARAMETER (87). 96 // The console must be released immediately after GenerateConsoleCtrlEvent. 97 _, _, _ = procFreeConsole.Call() 98 return nil 99 }