Let's say I'm implementing the kill
program in Go. I can accept numeric signals and PIDs from the commandline and send them to syscall.Kill
no problem.
However, I don't know how to implement the "string" form of signal dispatch, e.g. kill -INT 12345
.
The real use case is a part of a larger program that prompts the user to send kill signals; not a replacement for kill
.
Question:
How can I convert valid signal names to signal numbers on any supported platform, at runtime (or at least without writing per-platform code to be run at compile time)?
What I've tried:
kill -l
on Mac OSX versus a modern Linux versus an older Linux, for example). The only way to make this solution work in general would be to make maps for every OS, which would require me to know the behavior of every OS, and keep up to date as they add new signal support.kill
tool and capture the signal lists from it. This is inelegant and kind of a paradox, and also requires a) being able to find kill
, b) having the ability/permission to exec subprocesses, and c) being able to predict/parse the output of kill
-the-binary.Signal
types' String
method. This just returns strings containing the signal number, e.g. os.Signal(4).String() == "signal 4"
, which is not useful.runtime.signame
, which does exactly what I want. go://linkname
hacks will work, but I'm assuming that this sort of thing is frowned-upon for a reason.Ideas/Things I Haven't Tried:
syscall
that start with SIG
somehow. I am told that this is not possible because names are compiled away; is it possible that, for something as fundamental as signal names, there's someplace they're not compiled away?Commit d455e41 added this feature in March 2019 as sys/unix.SignalNum()
and is thus available at least since Go 1.13. More details in GitHub issue #28027.
From the documentation of the golang.org/x/sys/unix
package:
func SignalNum(s string) syscall.Signal
SignalNum returns the syscall.Signal for signal named s, or 0 if a signal with such name is not found. The signal name should start with "SIG".
To answer a similar question, "how can I list the names of all available signals (on a given Unix-like platform)", we can use the inverse function sys/unix.SignalName()
:
import "golang.org/x/sys/unix"
// See https://github.com/golang/go/issues/28027#issuecomment-427377759
// for why looping in range 0,255 is enough.
for i := syscall.Signal(0); i < syscall.Signal(255); i++ {
name := unix.SignalName(i)
// Signal numbers are not guaranteed to be contiguous.
if name != "" {
fmt.Println(name)
}
}