// Run the function with stats and retries if required func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) { var err error var stopStats chan struct{} if !showStats && ShowStats() { showStats = true } if showStats { stopStats = StartStats() } for try := 1; try <= *retries; try++ { err = f() if !Retry || (err == nil && !fs.Stats.Errored()) { if try > 1 { fs.ErrorLog(nil, "Attempt %d/%d succeeded", try, *retries) } break } if fs.IsFatalError(err) { fs.ErrorLog(nil, "Fatal error received - not attempting retries") break } if fs.IsNoRetryError(err) { fs.ErrorLog(nil, "Can't retry this error - not attempting retries") break } if err != nil { fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors and: %v", try, *retries, fs.Stats.GetErrors(), err) } else { fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors", try, *retries, fs.Stats.GetErrors()) } if try < *retries { fs.Stats.ResetErrors() } } if showStats { close(stopStats) } if err != nil { log.Fatalf("Failed to %s: %v", cmd.Name(), err) } if showStats && (!fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0) { fs.Log(nil, "%s", fs.Stats) } if fs.Config.Verbose { fs.Debug(nil, "Go routines at exit %d\n", runtime.NumGoroutine()) } if fs.Stats.Errored() { os.Exit(1) } }
func main() { ParseFlags() if *version { fmt.Printf("rclone %s\n", fs.Version) os.Exit(0) } command, args := ParseCommand() // Log file output if *logFile != "" { f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) if err != nil { log.Fatalf("Failed to open log file: %v", err) } _, err = f.Seek(0, os.SEEK_END) if err != nil { fs.ErrorLog(nil, "Failed to seek log file to end: %v", err) } log.SetOutput(f) fs.DebugLogger.SetOutput(f) redirectStderr(f) } // Load the rest of the config now we have started the logger fs.LoadConfig() // Write the args for debug purposes fs.Debug("rclone", "Version %q starting with parameters %q", fs.Version, os.Args) // Setup CPU profiling if desired if *cpuProfile != "" { fs.Log(nil, "Creating CPU profile %q\n", *cpuProfile) f, err := os.Create(*cpuProfile) if err != nil { fs.Stats.Error() log.Fatal(err) } err = pprof.StartCPUProfile(f) if err != nil { fs.Stats.Error() log.Fatal(err) } defer pprof.StopCPUProfile() } // Setup memory profiling if desired if *memProfile != "" { defer func() { fs.Log(nil, "Saving Memory profile %q\n", *memProfile) f, err := os.Create(*memProfile) if err != nil { fs.Stats.Error() log.Fatal(err) } err = pprof.WriteHeapProfile(f) if err != nil { fs.Stats.Error() log.Fatal(err) } err = f.Close() if err != nil { fs.Stats.Error() log.Fatal(err) } }() } // Make source and destination fs var fdst, fsrc fs.Fs if len(args) >= 1 { fdst = NewFsSrc(args[0]) } if len(args) >= 2 { fsrc = fdst fdst = NewFs(args[1]) } fs.CalculateModifyWindow(fdst, fsrc) if !command.NoStats { StartStats() } // Exit if no command to run if command.Run == nil { return } // Run the actual command var err error for try := 1; try <= *retries; try++ { err = command.Run(fdst, fsrc) if !command.Retry || (err == nil && !fs.Stats.Errored()) { break } if fs.IsFatalError(err) { fs.Log(nil, "Fatal error received - not attempting retries") break } if fs.IsNoRetryError(err) { fs.Log(nil, "Can't retry this error - not attempting retries") break } if err != nil { fs.Log(nil, "Attempt %d/%d failed with %d errors and: %v", try, *retries, fs.Stats.GetErrors(), err) } else { fs.Log(nil, "Attempt %d/%d failed with %d errors", try, *retries, fs.Stats.GetErrors()) } if try < *retries { fs.Stats.ResetErrors() } } if err != nil { log.Fatalf("Failed to %s: %v", command.Name, err) } if !command.NoStats && (!fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0) { fs.Log(nil, "%s", fs.Stats) } if fs.Config.Verbose { fs.Debug(nil, "Go routines at exit %d\n", runtime.NumGoroutine()) } if fs.Stats.Errored() { os.Exit(1) } }