e4c469c149
By default (i.e., without profile.NoShutdownHook), profile.Start listens for SIGINT and will stop the profile and call os.Exit(0). restic already listens for SIGINT and runs its own cleanup handlers before calling os.Exit(0). As is, these handlers are racing when an interrupt occurs, and in my experience, restic tends to win the race, resulting in an unusable profile. Eliminate the race and properly stop profiles on interrupt by disabling package profile's signal handler and instead stop the profile in a restic cleanup handler.
94 lines
2.1 KiB
Go
94 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
|
|
"github.com/restic/restic/internal/debug"
|
|
"github.com/restic/restic/internal/options"
|
|
"github.com/restic/restic/internal/restic"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/restic/restic/internal/errors"
|
|
)
|
|
|
|
// cmdRoot is the base command when no other command has been specified.
|
|
var cmdRoot = &cobra.Command{
|
|
Use: "restic",
|
|
Short: "backup and restore files",
|
|
Long: `
|
|
restic is a backup program which allows saving multiple revisions of files and
|
|
directories in an encrypted repository stored on different backends.
|
|
`,
|
|
SilenceErrors: true,
|
|
SilenceUsage: true,
|
|
DisableAutoGenTag: true,
|
|
|
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
|
// parse extended options
|
|
opts, err := options.Parse(globalOptions.Options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
globalOptions.extended = opts
|
|
|
|
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD")
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
|
|
Exit(1)
|
|
}
|
|
globalOptions.password = pwd
|
|
|
|
// run the debug functions for all subcommands (if build tag "debug" is
|
|
// enabled)
|
|
if err := runDebug(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var logBuffer = bytes.NewBuffer(nil)
|
|
|
|
func init() {
|
|
// install custom global logger into a buffer, if an error occurs
|
|
// we can show the logs
|
|
log.SetOutput(logBuffer)
|
|
}
|
|
|
|
func main() {
|
|
debug.Log("main %#v", os.Args)
|
|
debug.Log("restic %s, compiled with %v on %v/%v",
|
|
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
|
err := cmdRoot.Execute()
|
|
|
|
switch {
|
|
case restic.IsAlreadyLocked(errors.Cause(err)):
|
|
fmt.Fprintf(os.Stderr, "%v\nthe `unlock` command can be used to remove stale locks\n", err)
|
|
case errors.IsFatal(errors.Cause(err)):
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
case err != nil:
|
|
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
|
|
|
if logBuffer.Len() > 0 {
|
|
fmt.Fprintf(os.Stderr, "also, the following messages were logged by a library:\n")
|
|
sc := bufio.NewScanner(logBuffer)
|
|
for sc.Scan() {
|
|
fmt.Fprintln(os.Stderr, sc.Text())
|
|
}
|
|
}
|
|
}
|
|
|
|
var exitCode int
|
|
if err != nil {
|
|
exitCode = 1
|
|
}
|
|
|
|
Exit(exitCode)
|
|
}
|