github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/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 "github.com/restic/restic/internal/worker" 20 ) 21 22 var cmdDebug = &cobra.Command{ 23 Use: "debug", 24 Short: "Debug commands", 25 } 26 27 var cmdDebugDump = &cobra.Command{ 28 Use: "dump [indexes|snapshots|all|packs]", 29 Short: "Dump data structures", 30 Long: ` 31 The "dump" command dumps data structures from the repository as JSON objects. It 32 is used for debugging purposes only.`, 33 DisableAutoGenTag: true, 34 RunE: func(cmd *cobra.Command, args []string) error { 35 return runDebugDump(globalOptions, args) 36 }, 37 } 38 39 func init() { 40 cmdRoot.AddCommand(cmdDebug) 41 cmdDebug.AddCommand(cmdDebugDump) 42 } 43 44 func prettyPrintJSON(wr io.Writer, item interface{}) error { 45 buf, err := json.MarshalIndent(item, "", " ") 46 if err != nil { 47 return err 48 } 49 50 _, err = wr.Write(append(buf, '\n')) 51 return err 52 } 53 54 func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error { 55 for id := range repo.List(context.TODO(), restic.SnapshotFile) { 56 snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id) 57 if err != nil { 58 fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err) 59 continue 60 } 61 62 fmt.Fprintf(wr, "snapshot_id: %v\n", id) 63 64 err = prettyPrintJSON(wr, snapshot) 65 if err != nil { 66 return err 67 } 68 } 69 70 return nil 71 } 72 73 const dumpPackWorkers = 10 74 75 // Pack is the struct used in printPacks. 76 type Pack struct { 77 Name string `json:"name"` 78 79 Blobs []Blob `json:"blobs"` 80 } 81 82 // Blob is the struct used in printPacks. 83 type Blob struct { 84 Type restic.BlobType `json:"type"` 85 Length uint `json:"length"` 86 ID restic.ID `json:"id"` 87 Offset uint `json:"offset"` 88 } 89 90 func printPacks(repo *repository.Repository, wr io.Writer) error { 91 f := func(ctx context.Context, job worker.Job) (interface{}, error) { 92 name := job.Data.(string) 93 94 h := restic.Handle{Type: restic.DataFile, Name: name} 95 96 blobInfo, err := repo.Backend().Stat(ctx, h) 97 if err != nil { 98 return nil, err 99 } 100 101 blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), blobInfo.Size) 102 if err != nil { 103 return nil, err 104 } 105 106 return blobs, nil 107 } 108 109 jobCh := make(chan worker.Job) 110 resCh := make(chan worker.Job) 111 wp := worker.New(context.TODO(), dumpPackWorkers, f, jobCh, resCh) 112 113 go func() { 114 for name := range repo.Backend().List(context.TODO(), restic.DataFile) { 115 jobCh <- worker.Job{Data: name} 116 } 117 close(jobCh) 118 }() 119 120 for job := range resCh { 121 name := job.Data.(string) 122 123 if job.Error != nil { 124 fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", name, job.Error) 125 continue 126 } 127 128 entries := job.Result.([]restic.Blob) 129 p := Pack{ 130 Name: name, 131 Blobs: make([]Blob, len(entries)), 132 } 133 for i, blob := range entries { 134 p.Blobs[i] = Blob{ 135 Type: blob.Type, 136 Length: blob.Length, 137 ID: blob.ID, 138 Offset: blob.Offset, 139 } 140 } 141 142 prettyPrintJSON(os.Stdout, p) 143 } 144 145 wp.Wait() 146 147 return nil 148 } 149 150 func dumpIndexes(repo restic.Repository) error { 151 for id := range repo.List(context.TODO(), restic.IndexFile) { 152 fmt.Printf("index_id: %v\n", id) 153 154 idx, err := repository.LoadIndex(context.TODO(), repo, id) 155 if err != nil { 156 return err 157 } 158 159 err = idx.Dump(os.Stdout) 160 if err != nil { 161 return err 162 } 163 } 164 165 return nil 166 } 167 168 func runDebugDump(gopts GlobalOptions, args []string) error { 169 if len(args) != 1 { 170 return errors.Fatal("type not specified") 171 } 172 173 repo, err := OpenRepository(gopts) 174 if err != nil { 175 return err 176 } 177 178 if !gopts.NoLock { 179 lock, err := lockRepo(repo) 180 defer unlockRepo(lock) 181 if err != nil { 182 return err 183 } 184 } 185 186 err = repo.LoadIndex(gopts.ctx) 187 if err != nil { 188 return err 189 } 190 191 tpe := args[0] 192 193 switch tpe { 194 case "indexes": 195 return dumpIndexes(repo) 196 case "snapshots": 197 return debugPrintSnapshots(repo, os.Stdout) 198 case "packs": 199 return printPacks(repo, os.Stdout) 200 case "all": 201 fmt.Printf("snapshots:\n") 202 err := debugPrintSnapshots(repo, os.Stdout) 203 if err != nil { 204 return err 205 } 206 207 fmt.Printf("\nindexes:\n") 208 err = dumpIndexes(repo) 209 if err != nil { 210 return err 211 } 212 213 return nil 214 default: 215 return errors.Fatalf("no such type %q", tpe) 216 } 217 }