check / check (push) Waiting to run
A SIGINT, SIGTERM or SIGHUP at the mnemonic prompt killed the tool with echo still off. The prompt now catches them with signals.Notify from before echo goes off, and reads the line in a goroutine. Once the line is read or a signal arrives, it puts the terminal settings back and stops catching; if a signal has arrived by then, it sends the tool the same signal again, so the tool is still ended by it (130 for SIGINT in a shell). A test sends SIGINT at the prompt on a pseudo-terminal. The signals package moves from internal/cli to internal/signals, since the mnemonic prompt now uses it as well. Unchanged: the Windows prompt, which uses term.ReadPassword. Model: opus-5-5
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
// Package signals catches SIGINT, SIGTERM and SIGHUP, the signals that
|
|
// end the tool, for the commands that clean up before they end and for
|
|
// the mnemonic prompt, which puts the terminal back before the signal
|
|
// ends the tool. Go keeps an inherited ignore only for SIGHUP and
|
|
// SIGINT, so a SIGTERM the tool was started with set to be ignored is
|
|
// not left ignored.
|
|
package signals
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
)
|
|
|
|
// Context is signal.NotifyContext for SIGINT, SIGTERM and SIGHUP: the
|
|
// context it returns is cancelled when one of them arrives, and stop
|
|
// stops catching them. It leaves out SIGHUP or SIGINT if the tool was
|
|
// started with it set to be ignored, as nohup does with SIGHUP, because
|
|
// catching a signal turns an ignored one back on and would end a run
|
|
// that was meant to survive it.
|
|
func Context(parent context.Context) (context.Context, context.CancelFunc) {
|
|
endings := caught()
|
|
|
|
// Given no signals at all, NotifyContext would catch every one.
|
|
if len(endings) == 0 {
|
|
return context.WithCancel(parent)
|
|
}
|
|
|
|
return signal.NotifyContext(parent, endings...)
|
|
}
|
|
|
|
// Notify is signal.Notify for the signals Context catches: each one
|
|
// that arrives is sent to c, until signal.Stop(c).
|
|
func Notify(c chan<- os.Signal) {
|
|
endings := caught()
|
|
|
|
// Given no signals at all, Notify would catch every one.
|
|
if len(endings) > 0 {
|
|
signal.Notify(c, endings...)
|
|
}
|
|
}
|
|
|
|
// caught returns SIGINT, SIGTERM and SIGHUP, less any that
|
|
// signal.Ignored reports as ignored.
|
|
func caught() []os.Signal {
|
|
endings := []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP}
|
|
kept := make([]os.Signal, 0, len(endings))
|
|
|
|
for _, ending := range endings {
|
|
if !signal.Ignored(ending) {
|
|
kept = append(kept, ending)
|
|
}
|
|
}
|
|
|
|
return kept
|
|
}
|