mirror of
https://github.com/gopasspw/gopass.git
synced 2020-06-03 16:52:30 +00:00
0cfa536d28
This commit adds a new debug package to gopass. It is heavily inspired by github.com/restic/restic/internal/debug and adapted for the gopass use case. This change allows to further trim down the source code since the new package doesn't propagate the debug flag in the context anymore. As such we can now omit passing ctx in most places. In order to ensure we don't accidentially keep passing ununsed parameters we also introduce unparam to check for extra arguments. RELEASE_NOTES=[ENHANCEMENT] New Debug package Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
300 lines
7.3 KiB
Go
300 lines
7.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gopasspw/gopass/internal/action/binary"
|
|
"github.com/gopasspw/gopass/internal/action/create"
|
|
"github.com/gopasspw/gopass/internal/action/pwgen"
|
|
"github.com/gopasspw/gopass/internal/action/xc"
|
|
_ "github.com/gopasspw/gopass/internal/backend/crypto"
|
|
"github.com/gopasspw/gopass/internal/backend/crypto/gpg"
|
|
_ "github.com/gopasspw/gopass/internal/backend/rcs"
|
|
_ "github.com/gopasspw/gopass/internal/backend/storage"
|
|
"github.com/gopasspw/gopass/pkg/ctxutil"
|
|
"github.com/gopasspw/gopass/pkg/protect"
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
|
|
"github.com/blang/semver"
|
|
"github.com/fatih/color"
|
|
colorable "github.com/mattn/go-colorable"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
ap "github.com/gopasspw/gopass/internal/action"
|
|
"github.com/gopasspw/gopass/internal/config"
|
|
"github.com/gopasspw/gopass/internal/out"
|
|
"github.com/gopasspw/gopass/internal/store/leaf"
|
|
"github.com/gopasspw/gopass/internal/termio"
|
|
)
|
|
|
|
const (
|
|
name = "gopass"
|
|
)
|
|
|
|
var (
|
|
// Version is the released version of gopass
|
|
version string
|
|
// BuildTime is the time the binary was built
|
|
date string
|
|
// Commit is the git hash the binary was built from
|
|
commit string
|
|
)
|
|
|
|
func main() {
|
|
if err := protect.Pledge("stdio rpath wpath cpath tty proc exec"); err != nil {
|
|
panic(err)
|
|
}
|
|
ctx := context.Background()
|
|
|
|
// trap Ctrl+C and call cancel on the context
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, os.Interrupt)
|
|
defer func() {
|
|
signal.Stop(sigChan)
|
|
cancel()
|
|
}()
|
|
go func() {
|
|
select {
|
|
case <-sigChan:
|
|
cancel()
|
|
case <-ctx.Done():
|
|
}
|
|
}()
|
|
|
|
cli.ErrWriter = errorWriter{
|
|
out: colorable.NewColorableStderr(),
|
|
}
|
|
sv := getVersion()
|
|
cli.VersionPrinter = makeVersionPrinter(os.Stdout, sv)
|
|
|
|
ctx, app := setupApp(ctx, sv)
|
|
if err := app.RunContext(ctx, os.Args); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func setupApp(ctx context.Context, sv semver.Version) (context.Context, *cli.App) {
|
|
// try to read config (if it exists)
|
|
cfg := config.LoadWithFallback()
|
|
|
|
// set config values
|
|
ctx = initContext(ctx, cfg)
|
|
|
|
// initialize action handlers
|
|
action, err := ap.New(cfg, sv)
|
|
if err != nil {
|
|
out.Error(ctx, "No gpg binary found: %s", err)
|
|
os.Exit(ap.ExitGPG)
|
|
}
|
|
|
|
// set some action callbacks
|
|
if !cfg.AutoImport {
|
|
ctx = ctxutil.WithImportFunc(ctx, termio.AskForKeyImport)
|
|
}
|
|
if cfg.ConfirmRecipients {
|
|
ctx = leaf.WithRecipientFunc(ctx, action.ConfirmRecipients)
|
|
}
|
|
ctx = leaf.WithFsckFunc(ctx, termio.AskForConfirmation)
|
|
|
|
app := cli.NewApp()
|
|
|
|
app.Name = name
|
|
app.Version = sv.String()
|
|
app.Usage = "The standard unix password manager - rewritten in Go"
|
|
app.EnableBashCompletion = true
|
|
app.BashComplete = func(c *cli.Context) {
|
|
cli.DefaultAppComplete(c)
|
|
action.Complete(c)
|
|
}
|
|
|
|
app.Action = func(c *cli.Context) error {
|
|
if err := action.Initialized(c); err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.Args().Present() {
|
|
return action.Show(c)
|
|
}
|
|
return action.List(c)
|
|
}
|
|
|
|
app.Flags = []cli.Flag{
|
|
&cli.BoolFlag{
|
|
Name: "yes",
|
|
Usage: "Assume yes on all yes/no questions or use the default on all others",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "clip",
|
|
Aliases: []string{"c"},
|
|
Usage: "Copy the first line of the secret into the clipboard",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "alsoclip",
|
|
Aliases: []string{"C"},
|
|
Usage: "Copy the first line of the secret into the clipboard and show everything",
|
|
},
|
|
}
|
|
|
|
app.Commands = getCommands(action, app)
|
|
return ctx, app
|
|
}
|
|
|
|
func getCommands(action *ap.Action, app *cli.App) []*cli.Command {
|
|
cmds := []*cli.Command{
|
|
{
|
|
Name: "completion",
|
|
Usage: "Bash and ZSH completion",
|
|
Description: "" +
|
|
"Source the output of this command with bash or zsh to get auto completion",
|
|
Subcommands: []*cli.Command{{
|
|
Name: "bash",
|
|
Usage: "Source for auto completion in bash",
|
|
Action: action.CompletionBash,
|
|
}, {
|
|
Name: "zsh",
|
|
Usage: "Source for auto completion in zsh",
|
|
Action: func(c *cli.Context) error {
|
|
return action.CompletionZSH(app)
|
|
},
|
|
}, {
|
|
Name: "fish",
|
|
Usage: "Source for auto completion in fish",
|
|
Action: func(c *cli.Context) error {
|
|
return action.CompletionFish(app)
|
|
},
|
|
}, {
|
|
Name: "openbsdksh",
|
|
Usage: "Source for auto completion in OpenBSD's ksh",
|
|
Action: func(c *cli.Context) error {
|
|
return action.CompletionOpenBSDKsh(app)
|
|
},
|
|
}},
|
|
},
|
|
}
|
|
cmds = append(cmds, action.GetCommands()...)
|
|
cmds = append(cmds, xc.GetCommands()...)
|
|
cmds = append(cmds, create.GetCommands(action, action.Store)...)
|
|
cmds = append(cmds, binary.GetCommands(action, action.Store)...)
|
|
cmds = append(cmds, pwgen.GetCommands()...)
|
|
sort.Slice(cmds, func(i, j int) bool { return cmds[i].Name < cmds[j].Name })
|
|
return cmds
|
|
}
|
|
func makeVersionPrinter(out io.Writer, sv semver.Version) func(c *cli.Context) {
|
|
return func(c *cli.Context) {
|
|
buildtime := ""
|
|
if bt, err := time.Parse("2006-01-02T15:04:05-0700", date); err == nil {
|
|
buildtime = bt.Format("2006-01-02 15:04:05")
|
|
}
|
|
buildInfo := ""
|
|
if commit != "" {
|
|
buildInfo = commit
|
|
}
|
|
if buildtime != "" {
|
|
if buildInfo != "" {
|
|
buildInfo += " "
|
|
}
|
|
buildInfo += buildtime
|
|
}
|
|
if buildInfo != "" {
|
|
buildInfo = "(" + buildInfo + ") "
|
|
}
|
|
fmt.Fprintf(
|
|
out,
|
|
"%s %s %s%s %s %s\n",
|
|
name,
|
|
sv.String(),
|
|
buildInfo,
|
|
runtime.Version(),
|
|
runtime.GOOS,
|
|
runtime.GOARCH,
|
|
)
|
|
}
|
|
}
|
|
|
|
type errorWriter struct {
|
|
out io.Writer
|
|
}
|
|
|
|
func (e errorWriter) Write(p []byte) (int, error) {
|
|
return e.out.Write([]byte("\n" + color.RedString("Error: %s", p)))
|
|
}
|
|
|
|
func getVersion() semver.Version {
|
|
sv, err := semver.Parse(strings.TrimPrefix(version, "v"))
|
|
if err == nil {
|
|
if commit != "" {
|
|
sv.Build = []string{commit}
|
|
}
|
|
return sv
|
|
}
|
|
return semver.Version{
|
|
Major: 1,
|
|
Minor: 9,
|
|
Patch: 2,
|
|
Pre: []semver.PRVersion{
|
|
{VersionStr: "git"},
|
|
},
|
|
Build: []string{"HEAD"},
|
|
}
|
|
}
|
|
|
|
func initContext(ctx context.Context, cfg *config.Config) context.Context {
|
|
// initialize from config, may be overridden by env vars
|
|
ctx = cfg.WithContext(ctx)
|
|
|
|
// always trust
|
|
ctx = gpg.WithAlwaysTrust(ctx, true)
|
|
|
|
// check recipients conflicts with always trust, make sure it's not enabled
|
|
// when always trust is
|
|
if gpg.IsAlwaysTrust(ctx) {
|
|
ctx = leaf.WithCheckRecipients(ctx, false)
|
|
}
|
|
|
|
// need this override for our integration tests
|
|
if nc := os.Getenv("GOPASS_NOCOLOR"); nc == "true" || ctxutil.IsNoColor(ctx) {
|
|
color.NoColor = true
|
|
ctx = ctxutil.WithColor(ctx, false)
|
|
}
|
|
|
|
// support for no-color.org
|
|
if nc := os.Getenv("NO_COLOR"); nc != "" {
|
|
color.NoColor = true
|
|
ctx = ctxutil.WithColor(ctx, false)
|
|
}
|
|
|
|
// only emit color codes when stdout is a terminal
|
|
if !terminal.IsTerminal(int(os.Stdout.Fd())) {
|
|
color.NoColor = true
|
|
ctx = ctxutil.WithColor(ctx, false)
|
|
ctx = ctxutil.WithTerminal(ctx, false)
|
|
ctx = ctxutil.WithInteractive(ctx, false)
|
|
}
|
|
|
|
// reading from stdin?
|
|
if info, err := os.Stdin.Stat(); err == nil && info.Mode()&os.ModeCharDevice == 0 {
|
|
ctx = ctxutil.WithInteractive(ctx, false)
|
|
ctx = ctxutil.WithStdin(ctx, true)
|
|
}
|
|
|
|
// disable colored output on windows since cmd.exe doesn't support ANSI color
|
|
// codes. Other terminal may do, but until we can figure that out better
|
|
// disable this for all terms on this platform
|
|
if runtime.GOOS == "windows" {
|
|
color.NoColor = true
|
|
ctx = ctxutil.WithColor(ctx, false)
|
|
}
|
|
|
|
return ctx
|
|
}
|