forked from TrueCloudLab/restic
The backup command used to return a zero exit code as long as a snapshot could be created successfully, even if some of the source files could not be read (in which case the snapshot would contain the rest of the files). This made it hard for automation/scripts to detect failures/incomplete backups by looking at the exit code. Restic now returns the following exit codes for the backup command: - 0 when the command was successful - 1 when there was a fatal error (no snapshot created) - 3 when some source data could not be read (incomplete snapshot created)
115 lines
2.6 KiB
Go
115 lines
2.6 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(c *cobra.Command, args []string) error {
|
|
// set verbosity, default is one
|
|
globalOptions.verbosity = 1
|
|
if globalOptions.Quiet && (globalOptions.Verbose > 1) {
|
|
return errors.Fatal("--quiet and --verbose cannot be specified at the same time")
|
|
}
|
|
|
|
switch {
|
|
case globalOptions.Verbose >= 2:
|
|
globalOptions.verbosity = 3
|
|
case globalOptions.Verbose > 0:
|
|
globalOptions.verbosity = 2
|
|
case globalOptions.Quiet:
|
|
globalOptions.verbosity = 0
|
|
}
|
|
|
|
// parse extended options
|
|
opts, err := options.Parse(globalOptions.Options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
globalOptions.extended = opts
|
|
if c.Name() == "version" {
|
|
return nil
|
|
}
|
|
pwd, err := resolvePassword(globalOptions)
|
|
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
|
|
switch err {
|
|
case nil:
|
|
exitCode = 0
|
|
case InvalidSourceData:
|
|
exitCode = 3
|
|
default:
|
|
exitCode = 1
|
|
}
|
|
Exit(exitCode)
|
|
}
|