mirror of
https://github.com/golang/go.git
synced 2025-05-31 23:25:39 +00:00
os: add handling of os.Interrupt for windows
Add GenerateConsoleCtrlEvent call to internal syscall package. Define ErrProcessDone while reviewing handling of os.Signal(). Update test to run for windows using the added call. Fixes #42311 Fixes #46354 Change-Id: I460955efc76c4febe04b612ac9a0670e62ba5ff3 Reviewed-on: https://go-review.googlesource.com/c/go/+/367495 Trust: Patrik Nyblom <pnyb@google.com> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
85b5f86584
commit
345184496c
@ -344,3 +344,4 @@ func LoadGetFinalPathNameByHandle() error {
|
||||
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
|
||||
|
||||
//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
|
||||
//sys GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) = kernel32.GenerateConsoleCtrlEvent
|
||||
|
@ -54,6 +54,7 @@ var (
|
||||
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
|
||||
procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||
@ -161,6 +162,14 @@ func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapter
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGenerateConsoleCtrlEvent.Addr(), 2, uintptr(ctrlEvent), uintptr(processGroupID), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetACP() (acp uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetACP.Addr(), 0, 0, 0, 0)
|
||||
acp = uint32(r0)
|
||||
|
@ -7,6 +7,7 @@
|
||||
package exec_test
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -54,3 +55,20 @@ func TestNoInheritHandles(t *testing.T) {
|
||||
t.Fatalf("got exit code %d; want 88", exitError.ExitCode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrProcessDone(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
// On Windows, ProcAttr cannot be empty
|
||||
p, err := os.StartProcess(testenv.GoToolPath(t), []string{""},
|
||||
&os.ProcAttr{Dir: "", Env: nil, Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, Sys: nil})
|
||||
if err != nil {
|
||||
t.Errorf("starting test process: %v", err)
|
||||
}
|
||||
_, err = p.Wait()
|
||||
if err != nil {
|
||||
t.Errorf("Wait: %v", err)
|
||||
}
|
||||
if got := p.Signal(os.Kill); got != os.ErrProcessDone {
|
||||
t.Fatalf("got %v want %v", got, os.ErrProcessDone)
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,7 @@ import (
|
||||
|
||||
// The only signal values guaranteed to be present in the os package on all
|
||||
// systems are os.Interrupt (send the process an interrupt) and os.Kill (force
|
||||
// the process to exit). On Windows, sending os.Interrupt to a process with
|
||||
// os.Process.Signal is not implemented; it will return an error instead of
|
||||
// sending a signal.
|
||||
// the process to exit).
|
||||
var (
|
||||
Interrupt Signal = syscall.SIGINT
|
||||
Kill Signal = syscall.SIGKILL
|
||||
|
@ -47,13 +47,14 @@ func (p *Process) wait() (ps *ProcessState, err error) {
|
||||
|
||||
func (p *Process) signal(sig Signal) error {
|
||||
handle := atomic.LoadUintptr(&p.handle)
|
||||
if handle == uintptr(syscall.InvalidHandle) {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
if p.done() {
|
||||
return ErrProcessDone
|
||||
}
|
||||
if sig == Kill {
|
||||
s, ok := sig.(syscall.Signal)
|
||||
if !ok {
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
if s == syscall.SIGKILL {
|
||||
var terminationHandle syscall.Handle
|
||||
e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0)
|
||||
if e != nil {
|
||||
@ -61,11 +62,17 @@ func (p *Process) signal(sig Signal) error {
|
||||
}
|
||||
runtime.KeepAlive(p)
|
||||
defer syscall.CloseHandle(terminationHandle)
|
||||
e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1)
|
||||
e = syscall.TerminateProcess(terminationHandle, 1)
|
||||
return NewSyscallError("TerminateProcess", e)
|
||||
}
|
||||
// TODO(rsc): Handle Interrupt too?
|
||||
return syscall.Errno(syscall.EWINDOWS)
|
||||
if s == syscall.SIGINT {
|
||||
e := windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, uint32(p.Pid))
|
||||
if e != nil {
|
||||
return NewSyscallError("GenerateConsoleCtrlEvent", e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func (p *Process) release() error {
|
||||
|
@ -15,21 +15,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func sendCtrlBreak(t *testing.T, pid int) {
|
||||
d, e := syscall.LoadDLL("kernel32.dll")
|
||||
if e != nil {
|
||||
t.Fatalf("LoadDLL: %v\n", e)
|
||||
}
|
||||
p, e := d.FindProc("GenerateConsoleCtrlEvent")
|
||||
if e != nil {
|
||||
t.Fatalf("FindProc: %v\n", e)
|
||||
}
|
||||
r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
|
||||
if r == 0 {
|
||||
t.Fatalf("GenerateConsoleCtrlEvent: %v\n", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCtrlBreak(t *testing.T) {
|
||||
// create source file
|
||||
const source = `
|
||||
@ -90,7 +75,7 @@ func main() {
|
||||
}
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
sendCtrlBreak(t, cmd.Process.Pid)
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
}()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
|
@ -59,22 +59,6 @@ func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func sendCtrlBreak(pid int) error {
|
||||
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
||||
if err != nil {
|
||||
return fmt.Errorf("LoadDLL: %v\n", err)
|
||||
}
|
||||
generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent")
|
||||
if err != nil {
|
||||
return fmt.Errorf("FindProc: %v\n", err)
|
||||
}
|
||||
result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
|
||||
if result == 0 {
|
||||
return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestCtrlHandler tests that Go can gracefully handle closing the console window.
|
||||
// See https://golang.org/issues/41884.
|
||||
func TestCtrlHandler(t *testing.T) {
|
||||
@ -183,7 +167,7 @@ func TestLibraryCtrlHandler(t *testing.T) {
|
||||
} else if strings.TrimSpace(line) != "ready" {
|
||||
errCh <- fmt.Errorf("unexpected message: %v", line)
|
||||
} else {
|
||||
errCh <- sendCtrlBreak(cmd.Process.Pid)
|
||||
errCh <- cmd.Process.Signal(syscall.SIGINT)
|
||||
}
|
||||
}()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user