Files
keyfunc/internal/signals/signals.go
T
sneak c3cc26c721
check / check (push) Waiting to run
Put the terminal back when a signal ends the mnemonic prompt (closes #91)
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
2026-10-14 23:05:41 +00:00

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
}