Files
secret/internal/cli/root.go
T
sneak 6f2538cfd2
check / check (push) Successful in 1m4s
Wipe memguard buffers on every exit, restore echo on Ctrl-C (closes #35)
Entry() now returns the exit code and only main calls os.Exit, so the
deferred memguard.Purge() in Entry() runs on success and on error;
before, os.Exit(1) skipped every deferred Destroy().

SIGINT and SIGTERM go through memguard's handler, which wipes every
buffer and exits with status 1. The passphrase prompt turns terminal
echo off until its read returns, and the handler exits before that, so
on Ctrl-C the handler first restores the terminal settings saved at
startup. It leaves the terminal alone on SIGTERM, which can reach a
background process, and changing the terminal from the background would
stop the process.

Model: opus-5-5
2026-10-03 12:15:47 +00:00

75 lines
2.0 KiB
Go

package cli
import (
"os"
"syscall"
"git.eeqj.de/sneak/secret/internal/secret"
"github.com/awnumar/memguard"
"github.com/spf13/cobra"
"golang.org/x/term"
)
// Entry runs the secret CLI and returns the process exit code. It wipes
// every memguard buffer before it returns, so the caller must do nothing
// but exit with the code.
func Entry() int {
// On SIGINT or SIGTERM memguard runs this function, wipes every buffer
// and exits with status 1. The passphrase prompt turns terminal echo
// off until the read finishes, so Ctrl-C there would leave echo off.
// Ctrl-C means this process is in the terminal's foreground and may
// reset it; doing that from the background would stop the process.
terminalState, terminalErr := term.GetState(syscall.Stdin)
memguard.CatchSignal(func(sig os.Signal) {
if sig == os.Interrupt && terminalErr == nil {
_ = term.Restore(syscall.Stdin, terminalState)
}
}, os.Interrupt, syscall.SIGTERM)
defer memguard.Purge()
err := newRootCmd().Execute()
if err != nil {
return 1
}
return 0
}
func newRootCmd() *cobra.Command {
secret.Debug("newRootCmd starting")
cmd := &cobra.Command{
Use: "secret",
Short: "A simple secrets manager",
Long: `A simple secrets manager to store and retrieve sensitive ` +
`information securely.`,
// Ensure usage is shown after errors
SilenceUsage: false,
SilenceErrors: false,
}
secret.Debug("Adding subcommands to root command")
// Add subcommands
cmd.AddCommand(NewInitCmd())
cmd.AddCommand(newGenerateCmd())
cmd.AddCommand(newVaultCmd())
cmd.AddCommand(newAddCmd())
cmd.AddCommand(newGetCmd())
cmd.AddCommand(newListCmd())
cmd.AddCommand(newRemoveCmd())
cmd.AddCommand(newMoveCmd())
cmd.AddCommand(newUnlockerCmd())
cmd.AddCommand(newImportCmd())
cmd.AddCommand(newEncryptCmd())
cmd.AddCommand(newDecryptCmd())
cmd.AddCommand(newVersionCmd())
cmd.AddCommand(newInfoCmd())
cmd.AddCommand(newCompletionCmd())
secret.Debug("newRootCmd completed")
return cmd
}