package cli import ( "os" "git.eeqj.de/sneak/secret/internal/secret" "github.com/awnumar/memguard" "github.com/spf13/cobra" "golang.org/x/sys/unix" "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 a signal there would leave echo off. // Only a process in the terminal's foreground process group may reset // it: one in the background that tries is stopped instead of exiting. terminalState, terminalErr := term.GetState(unix.Stdin) memguard.CatchSignal(func(os.Signal) { foreground, err := unix.IoctlGetInt(unix.Stdin, unix.TIOCGPGRP) if terminalErr == nil && err == nil && foreground == unix.Getpgrp() { _ = term.Restore(unix.Stdin, terminalState) } }, os.Interrupt, unix.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 }