Put the terminal back when a signal ends the mnemonic prompt (closes #91)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
A SIGINT, SIGTERM or SIGHUP at the mnemonic prompt killed the tool with echo still off. The prompt now catches them 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. Untested: a signal that comes just as the line is read. Model: opus-5-5
This commit was merged in pull request #92.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user