There are quite a few things to be teased apart here.
First, there's what the OS itself does. Then, once we know what the OS is and what it does, there's what the program does.
What the OS does is, obviously, OS-dependent. POSIX-flavored OSes have two kinds of kill
though: plain kill
and process-group-based kill
, or killpg
. The killpg variety of this function is the only one that sends a signal to an entire process group; plain kill
just sends a signal to a single process.
When a program is run from a controlling terminal, keyboard signals (^C, ^Z, etc) get sent to the foreground process group of that control terminal (see the linked page for a reasonably good description of these and note that BSD/macOS has ^T and SIGINFO
as well). But if the signals are being sent from some other program, rather than from a controlling terminal, it is up to that program whether to call killpg
or kill
, and what signal(s) to send.
Some signals cannot be caught. This is the case for SIGKILL and SIGSTOP. These signals should not be sent willy-nilly; they should be reserved to a last resort. Instead, programs that want another program to stop should generally send one of SIGINT, SIGTERM, SIGHUP, or (rarely) SIGQUIT. Go tends to tie SIGQUIT to debug (in that the runtime on POSIX systems makes ^\ dump the stacks of the various goroutines) so that one is not a good choice. However, it's not up to the Go program you write here, which can only try to catch the signal. The choice of what to send is up to the sender.
The "Go way" to catch the signal is to use a goroutine and a channel. The signal.Notify
function turns an OS-level signal into an event on the channel. What you do not (and cannot) know is whether the signal reached your process through kill
or killpg
(though if it came from a controlling terminal interaction, the POSIX-y kernel sent it via the equivalent of killpg
). If you want to propagate that signal on your own, simply use the notification event to invoke code that makes an OS-level kill
call. When using the os/exec
package, use cmd.Process.Signal
: note that this invokes the POSIX kill
, not its killpg
, but you would not want to use killpg
here since we're assuming a non-process-group signal in the first place (a pgroup-based signal presumably needs no propagation).
There is no fully portable way to send a signal to a POSIX process group (which is not surprising, since this isn't portable to non-POSIX systems). Sadly, there's no direct Unix or POSIX specific way to do that either, it seems, in Go.
On non-POSIX systems, everything is quite different. See the discussion near the front of the os/signal
package.