diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 7bd9cc0a8..24f2bb7c8 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "os" "strings" @@ -12,8 +11,16 @@ import ( "golang.org/x/crypto/ssh/terminal" ) +type CmdBackup struct{} + func init() { - commands["backup"] = commandBackup + _, err := parser.AddCommand("backup", + "save file/directory", + "The backup command creates a snapshot of a file or directory", + &CmdBackup{}) + if err != nil { + panic(err) + } } func format_bytes(c uint64) string { @@ -56,13 +63,21 @@ func print_tree2(indent int, t *restic.Tree) { } } -func commandBackup(be backend.Server, key *restic.Key, args []string) error { - if len(args) < 1 || len(args) > 2 { - return errors.New("usage: backup [dir|file] [snapshot-id]") +func (cmd CmdBackup) Usage() string { + return "DIR/FILE [snapshot-ID]" +} + +func (cmd CmdBackup) Execute(args []string) error { + if len(args) == 0 || len(args) > 2 { + return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } var parentSnapshotID backend.ID - var err error target := args[0] if len(args) > 1 { diff --git a/cmd/restic/cmd_cat.go b/cmd/restic/cmd_cat.go index 55cd43bc1..7bb6c3129 100644 --- a/cmd/restic/cmd_cat.go +++ b/cmd/restic/cmd_cat.go @@ -10,13 +10,30 @@ import ( "github.com/restic/restic/backend" ) +type CmdCat struct{} + func init() { - commands["cat"] = commandCat + _, err := parser.AddCommand("cat", + "dump something", + "The cat command dumps data structures or data from a repository", + &CmdCat{}) + if err != nil { + panic(err) + } } -func commandCat(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdCat) Usage() string { + return "[blob|tree|snapshot|key|lock] ID" +} + +func (cmd CmdCat) Execute(args []string) error { if len(args) != 2 { - return errors.New("usage: cat [blob|tree|snapshot|key|lock] ID") + return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } tpe := args[0] diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index c38ba54a9..951c8c6bd 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -1,16 +1,18 @@ package main import ( - "errors" "fmt" "path/filepath" + "time" "github.com/restic/restic" "github.com/restic/restic/backend" ) -func init() { - commands["find"] = commandFind +type findQuery struct { + name string + minModTime time.Time + maxModTime time.Time } type findResult struct { @@ -18,6 +20,21 @@ type findResult struct { path string } +type CmdFind struct { + Oldest time.Time `short:"o" long:"oldest" description:"Oldest modification date/time"` + Newest time.Time `short:"n" long:"newest" description:"Newest modification date/time"` +} + +func init() { + _, err := parser.AddCommand("find", + "find a file/directory", + "The find command searches for files or directories in snapshots", + &CmdFind{}) + if err != nil { + panic(err) + } +} + func findInTree(ch *restic.ContentHandler, id backend.ID, path, pattern string) ([]findResult, error) { debug("checking tree %v\n", id) @@ -83,9 +100,18 @@ func findInSnapshot(be backend.Server, key *restic.Key, id backend.ID, pattern s return nil } -func commandFind(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdFind) Usage() string { + return "[find-OPTIONS] PATTERN [snapshot-ID]" +} + +func (cmd CmdFind) Execute(args []string) error { if len(args) == 0 { - return errors.New("usage: find PATTERN [snapshot-id]") + return fmt.Errorf("no pattern given, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } pattern := args[0] diff --git a/cmd/restic/cmd_fsck.go b/cmd/restic/cmd_fsck.go index 0a37a31c1..4db9a6ff2 100644 --- a/cmd/restic/cmd_fsck.go +++ b/cmd/restic/cmd_fsck.go @@ -1,15 +1,22 @@ package main import ( - "errors" "fmt" "github.com/restic/restic" "github.com/restic/restic/backend" ) +type CmdFsck struct{} + func init() { - commands["fsck"] = commandFsck + _, err := parser.AddCommand("fsck", + "check the repository", + "The fsck command check the integrity and consistency of the repository", + &CmdFsck{}) + if err != nil { + panic(err) + } } func fsckFile(ch *restic.ContentHandler, IDs []backend.ID) error { @@ -92,9 +99,18 @@ func fsck_snapshot(be backend.Server, key *restic.Key, id backend.ID) error { return fsckTree(ch, sn.Tree) } -func commandFsck(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdFsck) Usage() string { + return "fsck [all|snapshot-ID]" +} + +func (cmd CmdFsck) Execute(args []string) error { if len(args) == 0 { - return errors.New("usage: fsck [all|snapshot-id]") + return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } if len(args) == 1 && args[0] != "all" { diff --git a/cmd/restic/cmd_key.go b/cmd/restic/cmd_key.go index bec18e3d7..c111696d1 100644 --- a/cmd/restic/cmd_key.go +++ b/cmd/restic/cmd_key.go @@ -10,8 +10,16 @@ import ( "github.com/restic/restic/backend" ) +type CmdKey struct{} + func init() { - commands["key"] = commandKey + _, err := parser.AddCommand("key", + "manage keys", + "The key command manages keys (passwords) of a repository", + &CmdKey{}) + if err != nil { + panic(err) + } } func list_keys(be backend.Server, key *restic.Key) error { @@ -103,9 +111,18 @@ func change_password(be backend.Server, key *restic.Key) error { return nil } -func commandKey(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdKey) Usage() string { + return "[list|add|rm|change] [ID]" +} + +func (cmd CmdKey) Execute(args []string) error { if len(args) < 1 || (args[0] == "rm" && len(args) != 2) { - return errors.New("usage: key [list|add|rm|change] [ID]") + return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } switch args[0] { diff --git a/cmd/restic/cmd_list.go b/cmd/restic/cmd_list.go index 3f0bb0e67..caca10556 100644 --- a/cmd/restic/cmd_list.go +++ b/cmd/restic/cmd_list.go @@ -3,18 +3,33 @@ package main import ( "errors" "fmt" - - "github.com/restic/restic" "github.com/restic/restic/backend" ) +type CmdList struct{} + func init() { - commands["list"] = commandList + _, err := parser.AddCommand("list", + "lists data", + "The list command lists structures or data of a repository", + &CmdList{}) + if err != nil { + panic(err) + } } -func commandList(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdList) Usage() string { + return "[data|trees|snapshots|keys|locks]" +} + +func (cmd CmdList) Execute(args []string) error { if len(args) != 1 { - return errors.New("usage: list [data|trees|snapshots|keys|locks]") + return fmt.Errorf("type not specified, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } var ( diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index ef3c4d1f6..a586a3835 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "os" "path/filepath" @@ -10,8 +9,16 @@ import ( "github.com/restic/restic/backend" ) +type CmdLs struct{} + func init() { - commands["ls"] = commandLs + _, err := parser.AddCommand("ls", + "list files", + "The ls command lists all files and directories in a snapshot", + &CmdLs{}) + if err != nil { + panic(err) + } } func print_node(prefix string, n *restic.Node) string { @@ -52,9 +59,18 @@ func print_tree(prefix string, ch *restic.ContentHandler, id backend.ID) error { return nil } -func commandLs(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdLs) Usage() string { + return "ls snapshot-ID [DIR]" +} + +func (cmd CmdLs) Execute(be backend.Server, key *restic.Key, args []string) error { if len(args) < 1 || len(args) > 2 { - return errors.New("usage: ls SNAPSHOT_ID [dir]") + return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } id, err := backend.FindSnapshot(be, args[0]) diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index bc0afe99e..2dbe9ea74 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "os" @@ -9,13 +8,30 @@ import ( "github.com/restic/restic/backend" ) +type CmdRestore struct{} + func init() { - commands["restore"] = commandRestore + _, err := parser.AddCommand("restore", + "restore a snapshot", + "The restore command restores a snapshot to a directory", + &CmdRestore{}) + if err != nil { + panic(err) + } } -func commandRestore(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdRestore) Usage() string { + return "snapshot-ID TARGETDIR" +} + +func (cmd CmdRestore) Execute(args []string) error { if len(args) != 2 { - return errors.New("usage: restore ID dir") + return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } id, err := backend.FindSnapshot(be, args[0]) diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go index b769ef9b5..84cfa532f 100644 --- a/cmd/restic/cmd_snapshots.go +++ b/cmd/restic/cmd_snapshots.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "io" "os" @@ -72,13 +71,30 @@ func reltime(t time.Time) string { } } +type CmdSnapshots struct{} + func init() { - commands["snapshots"] = commandSnapshots + _, err := parser.AddCommand("snapshots", + "show snapshots", + "The snapshots command lists all snapshots stored in a repository", + &CmdSnapshots{}) + if err != nil { + panic(err) + } } -func commandSnapshots(be backend.Server, key *restic.Key, args []string) error { +func (cmd CmdSnapshots) Usage() string { + return "" +} + +func (cmd CmdSnapshots) Execute(args []string) error { if len(args) != 0 { - return errors.New("usage: snapshots") + return fmt.Errorf("wrong number of arguments, usage: %s", cmd.Usage()) + } + + be, key, err := OpenRepo() + if err != nil { + return err } ch, err := restic.NewContentHandler(be, key) diff --git a/cmd/restic/main.go b/cmd/restic/main.go index 422b3a460..469034af7 100644 --- a/cmd/restic/main.go +++ b/cmd/restic/main.go @@ -1,13 +1,11 @@ package main import ( + "errors" "fmt" - "log" "net/url" "os" "runtime" - "sort" - "strings" "golang.org/x/crypto/ssh/terminal" @@ -22,6 +20,8 @@ var opts struct { Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"` } +var parser = flags.NewParser(&opts, flags.Default) + func errx(code int, format string, data ...interface{}) { if len(format) > 0 && format[len(format)-1] != '\n' { format += "\n" @@ -30,10 +30,6 @@ func errx(code int, format string, data ...interface{}) { os.Exit(code) } -type commandFunc func(backend.Server, *restic.Key, []string) error - -var commands = make(map[string]commandFunc) - func readPassword(env string, prompt string) string { if env != "" { @@ -54,7 +50,13 @@ func readPassword(env string, prompt string) string { return string(pw) } -func commandInit(repo string) error { +type CmdInit struct{} + +func (cmd CmdInit) Execute(args []string) error { + if opts.Repo == "" { + return errors.New("Please specify repository location (-r)") + } + pw := readPassword("RESTIC_PASSWORD", "enter password for new backend: ") pw2 := readPassword("RESTIC_PASSWORD", "enter password again: ") @@ -62,15 +64,15 @@ func commandInit(repo string) error { errx(1, "passwords do not match") } - be, err := create(repo) + be, err := create(opts.Repo) if err != nil { - fmt.Fprintf(os.Stderr, "creating backend at %s failed: %v\n", repo, err) + fmt.Fprintf(os.Stderr, "creating backend at %s failed: %v\n", opts.Repo, err) os.Exit(1) } _, err = restic.CreateKey(be, pw) if err != nil { - fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", repo, err) + fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err) os.Exit(1) } @@ -125,73 +127,99 @@ func create(u string) (backend.Server, error) { return backend.CreateSFTP(url.Path[1:], "ssh", args...) } +func OpenRepo() (backend.Server, *restic.Key, error) { + be, err := open(opts.Repo) + if err != nil { + return nil, nil, err + } + + key, err := restic.SearchKey(be, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: ")) + if err != nil { + return nil, nil, fmt.Errorf("unable to open repo: %v", err) + } + + return be, key, nil +} + func init() { // set GOMAXPROCS to number of CPUs runtime.GOMAXPROCS(runtime.NumCPU()) + + _, err := parser.AddCommand("init", + "create repository", + "The init command creates a new repository", + &CmdInit{}) + if err != nil { + panic(err) + } } func main() { // defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop() - - log.SetOutput(os.Stdout) - opts.Repo = os.Getenv("RESTIC_REPOSITORY") - args, err := flags.Parse(&opts) + _, err := parser.Parse() if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { os.Exit(0) } - if opts.Repo == "" { - fmt.Fprintf(os.Stderr, "no repository specified, use -r or RESTIC_REPOSITORY variable\n") + if err != nil { os.Exit(1) } - if len(args) == 0 { - cmds := []string{"init"} - for k := range commands { - cmds = append(cmds, k) - } - sort.Strings(cmds) - fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|")) - os.Exit(0) - } + // fmt.Printf("parser: %#v\n", parser) + // fmt.Printf("%#v\n", parser.Active.Name) - cmd := args[0] + // if opts.Repo == "" { + // fmt.Fprintf(os.Stderr, "no repository specified, use -r or RESTIC_REPOSITORY variable\n") + // os.Exit(1) + // } - switch cmd { - case "init": - err = commandInit(opts.Repo) - if err != nil { - errx(1, "error executing command %q: %v", cmd, err) - } - return + // if len(args) == 0 { + // cmds := []string{"init"} + // for k := range commands { + // cmds = append(cmds, k) + // } + // sort.Strings(cmds) + // fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|")) + // os.Exit(0) + // } - case "version": - fmt.Printf("%v\n", version) - return - } + // cmd := args[0] - f, ok := commands[cmd] - if !ok { - errx(1, "unknown command: %q\n", cmd) - } + // switch cmd { + // case "init": + // err = commandInit(opts.Repo) + // if err != nil { + // errx(1, "error executing command %q: %v", cmd, err) + // } + // return - // read_password("enter password: ") - repo, err := open(opts.Repo) - if err != nil { - errx(1, "unable to open repo: %v", err) - } + // case "version": + // fmt.Printf("%v\n", version) + // return + // } - key, err := restic.SearchKey(repo, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: ")) - if err != nil { - errx(2, "unable to open repo: %v", err) - } + // f, ok := commands[cmd] + // if !ok { + // errx(1, "unknown command: %q\n", cmd) + // } - err = f(repo, key, args[1:]) - if err != nil { - errx(1, "error executing command %q: %v", cmd, err) - } + // // read_password("enter password: ") + // repo, err := open(opts.Repo) + // if err != nil { + // errx(1, "unable to open repo: %v", err) + // } - restic.PoolAlloc() + // key, err := restic.SearchKey(repo, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: ")) + // if err != nil { + // errx(2, "unable to open repo: %v", err) + // } + + // err = f(repo, key, args[1:]) + // if err != nil { + // errx(1, "error executing command %q: %v", cmd, err) + // } + + // restic.PoolAlloc() }