github.com/mckael/restic@v0.8.3/cmd/restic/cmd_debug.go (about) 1 // +build debug 2 3 package main 4 5 import ( 6 "context" 7 "encoding/json" 8 "fmt" 9 "io" 10 "os" 11 12 "github.com/spf13/cobra" 13 14 "github.com/restic/restic/internal/errors" 15 "github.com/restic/restic/internal/pack" 16 "github.com/restic/restic/internal/repository" 17 "github.com/restic/restic/internal/restic" 18 ) 19 20 var cmdDebug = &cobra.Command{ 21 Use: "debug", 22 Short: "Debug commands", 23 } 24 25 var cmdDebugDump = &cobra.Command{ 26 Use: "dump [indexes|snapshots|all|packs]", 27 Short: "Dump data structures", 28 Long: ` 29 The "dump" command dumps data structures from the repository as JSON objects. It 30 is used for debugging purposes only.`, 31 DisableAutoGenTag: true, 32 RunE: func(cmd *cobra.Command, args []string) error { 33 return runDebugDump(globalOptions, args) 34 }, 35 } 36 37 func init() { 38 cmdRoot.AddCommand(cmdDebug) 39 cmdDebug.AddCommand(cmdDebugDump) 40 } 41 42 func prettyPrintJSON(wr io.Writer, item interface{}) error { 43 buf, err := json.MarshalIndent(item, "", " ") 44 if err != nil { 45 return err 46 } 47 48 _, err = wr.Write(append(buf, '\n')) 49 return err 50 } 51 52 func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error { 53 return repo.List(context.TODO(), restic.SnapshotFile, func(id restic.ID, size int64) error { 54 snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id) 55 if err != nil { 56 return err 57 } 58 59 fmt.Fprintf(wr, "snapshot_id: %v\n", id) 60 61 return prettyPrintJSON(wr, snapshot) 62 }) 63 } 64 65 // Pack is the struct used in printPacks. 66 type Pack struct { 67 Name string `json:"name"` 68 69 Blobs []Blob `json:"blobs"` 70 } 71 72 // Blob is the struct used in printPacks. 73 type Blob struct { 74 Type restic.BlobType `json:"type"` 75 Length uint `json:"length"` 76 ID restic.ID `json:"id"` 77 Offset uint `json:"offset"` 78 } 79 80 func printPacks(repo *repository.Repository, wr io.Writer) error { 81 82 return repo.List(context.TODO(), restic.DataFile, func(id restic.ID, size int64) error { 83 h := restic.Handle{Type: restic.DataFile, Name: id.String()} 84 85 blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), size) 86 if err != nil { 87 fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", id.Str(), err) 88 return nil 89 } 90 91 p := Pack{ 92 Name: id.String(), 93 Blobs: make([]Blob, len(blobs)), 94 } 95 for i, blob := range blobs { 96 p.Blobs[i] = Blob{ 97 Type: blob.Type, 98 Length: blob.Length, 99 ID: blob.ID, 100 Offset: blob.Offset, 101 } 102 } 103 104 return prettyPrintJSON(os.Stdout, p) 105 }) 106 107 return nil 108 } 109 110 func dumpIndexes(repo restic.Repository) error { 111 return repo.List(context.TODO(), restic.IndexFile, func(id restic.ID, size int64) error { 112 fmt.Printf("index_id: %v\n", id) 113 114 idx, err := repository.LoadIndex(context.TODO(), repo, id) 115 if err != nil { 116 return err 117 } 118 119 return idx.Dump(os.Stdout) 120 }) 121 } 122 123 func runDebugDump(gopts GlobalOptions, args []string) error { 124 if len(args) != 1 { 125 return errors.Fatal("type not specified") 126 } 127 128 repo, err := OpenRepository(gopts) 129 if err != nil { 130 return err 131 } 132 133 if !gopts.NoLock { 134 lock, err := lockRepo(repo) 135 defer unlockRepo(lock) 136 if err != nil { 137 return err 138 } 139 } 140 141 err = repo.LoadIndex(gopts.ctx) 142 if err != nil { 143 return err 144 } 145 146 tpe := args[0] 147 148 switch tpe { 149 case "indexes": 150 return dumpIndexes(repo) 151 case "snapshots": 152 return debugPrintSnapshots(repo, os.Stdout) 153 case "packs": 154 return printPacks(repo, os.Stdout) 155 case "all": 156 fmt.Printf("snapshots:\n") 157 err := debugPrintSnapshots(repo, os.Stdout) 158 if err != nil { 159 return err 160 } 161 162 fmt.Printf("\nindexes:\n") 163 err = dumpIndexes(repo) 164 if err != nil { 165 return err 166 } 167 168 return nil 169 default: 170 return errors.Fatalf("no such type %q", tpe) 171 } 172 }