@@ -5,19 +5,14 @@ import (
55 "fmt"
66 "math/rand"
77 "os"
8- "os/signal"
9- "path/filepath"
108 "runtime"
119 "strings"
12- "syscall"
1310 "text/tabwriter"
1411
1512 "github.com/creativeprojects/clog"
1613 "github.com/creativeprojects/resticprofile/config"
1714 "github.com/creativeprojects/resticprofile/constants"
1815 "github.com/creativeprojects/resticprofile/filesearch"
19- "github.com/creativeprojects/resticprofile/monitor/prom"
20- "github.com/creativeprojects/resticprofile/monitor/status"
2116 "github.com/creativeprojects/resticprofile/preventsleep"
2217 "github.com/creativeprojects/resticprofile/priority"
2318 "github.com/creativeprojects/resticprofile/remote"
@@ -261,7 +256,7 @@ func main() {
261256 ctx = ctx .WithCommand (ctx .request .command )
262257
263258 // it wasn't an internal command so we run a profile
264- err = startProfileOrGroup (ctx )
259+ err = startProfileOrGroup (ctx , runProfile )
265260 if err != nil {
266261 clog .Error (err )
267262 if errors .Is (err , ErrProfileNotFound ) {
@@ -367,186 +362,6 @@ func detectResticBinary(global *config.Global) (string, error) {
367362 return resticBinary , nil
368363}
369364
370- func startProfileOrGroup (ctx * Context ) error {
371- if ctx .config .HasProfile (ctx .request .profile ) {
372- // if running as a systemd timer
373- notifyStart ()
374- defer notifyStop ()
375-
376- // Single profile run
377- err := runProfile (ctx )
378- if err != nil {
379- return err
380- }
381-
382- } else if ctx .config .HasProfileGroup (ctx .request .profile ) {
383- // Group run
384- group , err := ctx .config .GetProfileGroup (ctx .request .profile )
385- if err != nil {
386- clog .Errorf ("cannot load group '%s': %v" , ctx .request .profile , err )
387- }
388- if group != nil && len (group .Profiles ) > 0 {
389- // if running as a systemd timer
390- notifyStart ()
391- defer notifyStop ()
392-
393- // profile name is the group name
394- groupName := ctx .request .profile
395-
396- for i , profileName := range group .Profiles {
397- clog .Debugf ("[%d/%d] starting profile '%s' from group '%s'" , i + 1 , len (group .Profiles ), profileName , groupName )
398- ctx = ctx .WithProfile (profileName ).WithGroup (groupName )
399- err = runProfile (ctx )
400- if err != nil {
401- if ctx .global .GroupContinueOnError && group .ContinueOnError .IsTrueOrUndefined () {
402- // keep going to the next profile
403- clog .Error (err )
404- continue
405- }
406- // fail otherwise
407- return err
408- }
409- }
410- }
411-
412- } else {
413- return fmt .Errorf ("%w: %q" , ErrProfileNotFound , ctx .request .profile )
414- }
415- return nil
416- }
417-
418- func openProfile (c * config.Config , profileName string ) (profile * config.Profile , cleanup func (), err error ) {
419- done := false
420- for attempts := 3 ; attempts > 0 && ! done ; attempts -- {
421- profile , err = c .GetProfile (profileName )
422- if err != nil || profile == nil {
423- err = fmt .Errorf ("cannot load profile '%s': %w" , profileName , err )
424- break
425- }
426-
427- done = true
428-
429- // Adjust baseDir if needed
430- if len (profile .BaseDir ) > 0 {
431- var currentDir string
432- currentDir , err = os .Getwd ()
433- if err != nil {
434- err = fmt .Errorf ("changing base directory not allowed as current directory is unknown in profile %q: %w" , profileName , err )
435- break
436- }
437-
438- if baseDir , _ := filepath .Abs (profile .BaseDir ); filepath .ToSlash (baseDir ) != filepath .ToSlash (currentDir ) {
439- if cleanup == nil {
440- cleanup = func () {
441- if e := os .Chdir (currentDir ); e != nil {
442- panic (fmt .Errorf (`fatal: failed restoring working directory "%s": %w` , currentDir , e ))
443- }
444- }
445- }
446-
447- if err = os .Chdir (baseDir ); err == nil {
448- clog .Infof ("profile '%s': base directory is %q" , profileName , baseDir )
449- done = false // reload the profile as .CurrentDir & .Env has changed
450- } else {
451- err = fmt .Errorf (`cannot change to base directory "%s" in profile %q: %w` , baseDir , profileName , err )
452- break
453- }
454- }
455- }
456- }
457-
458- if cleanup == nil {
459- cleanup = func () {
460- // nothing to do
461- }
462- }
463- return
464- }
465-
466- func runProfile (ctx * Context ) error {
467- profile , cleanup , err := openProfile (ctx .config , ctx .request .profile )
468- defer cleanup ()
469- if err != nil {
470- return err
471- }
472- ctx .profile = profile
473-
474- displayProfileDeprecationNotices (profile )
475- ctx .config .DisplayConfigurationIssues ()
476-
477- // Send the quiet/verbose down to restic as well (override profile configuration)
478- if ctx .flags .quiet {
479- profile .Quiet = true
480- profile .Verbose = constants .VerbosityNone
481- }
482- if ctx .flags .verbose {
483- profile .Verbose = constants .VerbosityLevel1
484- profile .Quiet = false
485- }
486- if ctx .flags .veryVerbose {
487- profile .Verbose = constants .VerbosityLevel3
488- profile .Quiet = false
489- }
490-
491- // change log filter according to profile settings
492- if profile .Quiet {
493- changeLevelFilter (clog .LevelWarning )
494- } else if profile .Verbose > constants .VerbosityNone && ! ctx .flags .veryVerbose {
495- changeLevelFilter (clog .LevelDebug )
496- }
497-
498- // tell the profile what version of restic is in use
499- if e := profile .SetResticVersion (ctx .global .ResticVersion ); e != nil {
500- clog .Warningf ("restic version %q is no valid semver: %s" , ctx .global .ResticVersion , e .Error ())
501- }
502-
503- // Specific case for the "host" flag where an empty value should be replaced by the hostname
504- hostname := "none"
505- currentHost , err := os .Hostname ()
506- if err == nil {
507- hostname = currentHost
508- }
509- profile .SetHost (hostname )
510-
511- if ctx .request .schedule != "" {
512- // this is a scheduled profile
513- loadScheduledProfile (ctx )
514- }
515-
516- // Catch CTR-C keypress, or other signal sent by a service manager (systemd)
517- sigChan := make (chan os.Signal , 1 )
518- signal .Notify (sigChan , os .Interrupt , syscall .SIGTERM , syscall .SIGABRT )
519- // remove signal catch before leaving
520- defer signal .Stop (sigChan )
521-
522- ctx .sigChan = sigChan
523- wrapper := newResticWrapper (ctx )
524-
525- if ctx .noLock {
526- wrapper .ignoreLock ()
527- } else if ctx .lockWait > 0 {
528- wrapper .maxWaitOnLock (ctx .lockWait )
529- }
530-
531- // add progress receivers if necessary
532- if profile .StatusFile != "" {
533- wrapper .addProgress (status .NewProgress (profile , status .NewStatus (profile .StatusFile )))
534- }
535- if profile .PrometheusPush != "" || profile .PrometheusSaveToFile != "" {
536- wrapper .addProgress (prom .NewProgress (profile , prom .NewMetrics (profile .Name , ctx .request .group , version , profile .PrometheusLabels )))
537- }
538-
539- err = wrapper .runProfile ()
540- if err != nil {
541- return err
542- }
543- return nil
544- }
545-
546- func loadScheduledProfile (ctx * Context ) {
547- ctx .schedule = ctx .profile .Schedules ()[ctx .command ]
548- }
549-
550365// randomBool returns true for Heads and false for Tails
551366func randomBool () bool {
552367 return rand .Int31n (10000 ) < 5000
0 commit comments