Skip to content
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

os/signal: check int type of signal using reflection #70368

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 19 additions & 1 deletion src/os/signal/signal_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package signal

import (
"os"
"reflect"
"syscall"
)

Expand Down Expand Up @@ -41,7 +42,24 @@ func signum(sig os.Signal) int {
}
return i
default:
return -1
// Use reflection to determine if sig has an underlying integer type.
// In some systems like Windows, os.Signal may have implementations
// with underlying integer types that are not directly accessible,
// as they might be defined in external packages like golang.org/x/sys.
// Since we cannot import those platform-specific signal types,
WineChord marked this conversation as resolved.
Show resolved Hide resolved
// reflection allows us to handle them in a generic way.
v := reflect.ValueOf(sig)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// Extract the integer value from sig and validate it.
i := int(v.Int())
if i < 0 || i >= numSig {
return -1
}
return i
default:
return -1
}
}
}

Expand Down
100 changes: 100 additions & 0 deletions src/os/signal/signal_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,103 @@ func main() {
t.Fatalf("Program exited with error: %v\n%v", err, buf.String())
}
}

func TestCtrlBreakForCustomizedSignal(t *testing.T) {
// Create source file.
const source = `
package main

import (
"log"
"os"
"os/signal"
"time"
)

const SIGINT = Signal(0x2)

// Signal is copied from x/sys.
type Signal int

func (s Signal) Signal() {}

func (s Signal) String() string {
if 0 <= s && int(s) < len(signals) {
str := signals[s]
if str != "" {
return str
}
}
return "signal " + itoa(int(s))
}

var signals = [...]string{
1: "hangup",
2: "interrupt",
3: "quit",
4: "illegal instruction",
5: "trace/breakpoint trap",
6: "aborted",
7: "bus error",
8: "floating point exception",
9: "killed",
10: "user defined signal 1",
11: "segmentation fault",
12: "user defined signal 2",
13: "broken pipe",
14: "alarm clock",
15: "terminated",
}

func main() {
c := make(chan os.Signal, 10)
signal.Notify(c, SIGINT)
select {
case s := <-c:
if s != os.Interrupt {
log.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt)
}
case <-time.After(3 * time.Second):
log.Fatalf("Timeout waiting for Ctrl+Break\n")
}
}
`
tmp := t.TempDir()

// Write ctrlbreak.go.
name := filepath.Join(tmp, "ctlbreak_customized_signal")
src := name + ".go"
f, err := os.Create(src)
if err != nil {
t.Fatalf("Failed to create %v: %v", src, err)
}
defer f.Close()
f.Write([]byte(source))

// Compile it.
exe := name + ".exe"
defer os.Remove(exe)
o, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
if err != nil {
t.Fatalf("Failed to compile: %v\n%v", err, string(o))
}

// Run it.
cmd := testenv.Command(t, exe)
var buf strings.Builder
cmd.Stdout = &buf
cmd.Stderr = &buf
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
if err := cmd.Start(); err != nil {
t.Fatalf("Start failed: %v", err)
}
go func() {
time.Sleep(1 * time.Second)
sendCtrlBreak(t, cmd.Process.Pid)
}()
if err := cmd.Wait(); err != nil {
t.Fatalf("Program exited with error: %v\n%v", err, buf.String())
}
}