diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index ed45c7d9e..d5bd24710 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -139,12 +139,10 @@ The following are valid values for the STTRACE variable: %s` ) +// Environment options var ( - // Environment options innerProcess = os.Getenv("STNORESTART") != "" || os.Getenv("STMONITORED") != "" noDefaultFolder = os.Getenv("STNODEFAULTFOLDER") != "" - - errConcurrentUpgrade = errors.New("upgrade prevented by other running Syncthing instance") ) type RuntimeOptions struct { @@ -361,24 +359,14 @@ func main() { } if options.doUpgradeCheck { - if _, err := checkUpgrade(); err != nil { - l.Warnln("Checking for upgrade:", err) - os.Exit(exitCodeForUpgrade(err)) - } + checkUpgrade() return } if options.doUpgrade { - release, err := checkUpgrade() - if err == nil { - err = performUpgrade(release) - } - if err != nil { - l.Warnln("Upgrade:", err) - os.Exit(exitCodeForUpgrade(err)) - } - l.Infof("Upgraded to %q", release.Tag) - os.Exit(syncthing.ExitUpgrade.AsInt()) + release := checkUpgrade() + performUpgrade(release) + return } if options.resetDatabase { @@ -474,47 +462,45 @@ func debugFacilities() string { return b.String() } -type errNoUpgrade struct { - current, latest string -} - -func (e errNoUpgrade) Error() string { - return fmt.Sprintf("no upgrade available (current %q >= latest %q).", e.current, e.latest) -} - -func checkUpgrade() (upgrade.Release, error) { +func checkUpgrade() upgrade.Release { cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger) opts := cfg.Options() release, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases) if err != nil { - return upgrade.Release{}, err + l.Warnln("Upgrade:", err) + os.Exit(syncthing.ExitError.AsInt()) } if upgrade.CompareVersions(release.Tag, build.Version) <= 0 { - return upgrade.Release{}, errNoUpgrade{build.Version, release.Tag} + noUpgradeMessage := "No upgrade available (current %q >= latest %q)." + l.Infof(noUpgradeMessage, build.Version, release.Tag) + os.Exit(syncthing.ExitNoUpgradeAvailable.AsInt()) } l.Infof("Upgrade available (current %q < latest %q)", build.Version, release.Tag) - return release, nil + return release } -func performUpgradeDirect(release upgrade.Release) error { +func performUpgrade(release upgrade.Release) { // Use leveldb database locks to protect against concurrent upgrades - if _, err := syncthing.OpenDBBackend(locations.Get(locations.Database), config.TuningAuto); err != nil { - return errConcurrentUpgrade - } - return upgrade.To(release) -} - -func performUpgrade(release upgrade.Release) error { - if err := performUpgradeDirect(release); err != nil { - if err != errConcurrentUpgrade { - return err + _, err := syncthing.OpenDBBackend(locations.Get(locations.Database), config.TuningAuto) + if err == nil { + err = upgrade.To(release) + if err != nil { + l.Warnln("Upgrade:", err) + os.Exit(syncthing.ExitError.AsInt()) } + l.Infof("Upgraded to %q", release.Tag) + } else { l.Infoln("Attempting upgrade through running Syncthing...") - return upgradeViaRest() + err = upgradeViaRest() + if err != nil { + l.Warnln("Upgrade:", err) + os.Exit(syncthing.ExitError.AsInt()) + } + l.Infoln("Syncthing upgrading") + os.Exit(syncthing.ExitUpgrade.AsInt()) } - return nil } func upgradeViaRest() error { @@ -582,45 +568,6 @@ func syncthingMain(runtimeOptions RuntimeOptions) { os.Exit(syncthing.ExitError.AsInt()) } - // Candidate builds should auto upgrade. Make sure the option is set, - // unless we are in a build where it's disabled or the STNOUPGRADE - // environment variable is set. - - if build.IsCandidate && !upgrade.DisabledByCompilation && !runtimeOptions.NoUpgrade { - l.Infoln("Automatic upgrade is always enabled for candidate releases.") - if opts := cfg.Options(); opts.AutoUpgradeIntervalH == 0 || opts.AutoUpgradeIntervalH > 24 { - opts.AutoUpgradeIntervalH = 12 - // Set the option into the config as well, as the auto upgrade - // loop expects to read a valid interval from there. - cfg.SetOptions(opts) - cfg.Save() - } - // We don't tweak the user's choice of upgrading to pre-releases or - // not, as otherwise they cannot step off the candidate channel. - } - - // Check if auto-upgrades should be done and if yes, do an initial - // upgrade immedately. The auto-upgrade routine can only be started - // later after App is initialised. - - shouldAutoUpgrade := shouldUpgrade(cfg, runtimeOptions) - if shouldAutoUpgrade { - // Try to do upgrade directly - release, err := checkUpgrade() - if err == nil { - if err = performUpgradeDirect(release); err == nil { - l.Infof("Upgraded to %q, exiting now.", release.Tag) - os.Exit(syncthing.ExitUpgrade.AsInt()) - } - } - // Log the error if relevant. - if err != nil { - if _, ok := err.(errNoUpgrade); !ok { - l.Infoln("Initial automatic upgrade:", err) - } - } - } - if runtimeOptions.unpaused { setPauseState(cfg, false) } else if runtimeOptions.paused { @@ -645,10 +592,6 @@ func syncthingMain(runtimeOptions RuntimeOptions) { app := syncthing.New(cfg, ldb, evLogger, cert, appOpts) - if shouldAutoUpgrade { - go autoUpgrade(cfg, app, evLogger) - } - setupSignalHandling(app) if len(os.Getenv("GOMAXPROCS")) == 0 { @@ -671,6 +614,31 @@ func syncthingMain(runtimeOptions RuntimeOptions) { go standbyMonitor(app) } + // Candidate builds should auto upgrade. Make sure the option is set, + // unless we are in a build where it's disabled or the STNOUPGRADE + // environment variable is set. + + if build.IsCandidate && !upgrade.DisabledByCompilation && !runtimeOptions.NoUpgrade { + l.Infoln("Automatic upgrade is always enabled for candidate releases.") + if opts := cfg.Options(); opts.AutoUpgradeIntervalH == 0 || opts.AutoUpgradeIntervalH > 24 { + opts.AutoUpgradeIntervalH = 12 + // Set the option into the config as well, as the auto upgrade + // loop expects to read a valid interval from there. + cfg.SetOptions(opts) + cfg.Save() + } + // We don't tweak the user's choice of upgrading to pre-releases or + // not, as otherwise they cannot step off the candidate channel. + } + + if opts := cfg.Options(); opts.AutoUpgradeIntervalH > 0 { + if runtimeOptions.NoUpgrade { + l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.") + } else { + go autoUpgrade(cfg, app, evLogger) + } + } + if err := app.Start(); err != nil { os.Exit(syncthing.ExitError.AsInt()) } @@ -803,20 +771,6 @@ func standbyMonitor(app *syncthing.App) { } } -func shouldUpgrade(cfg config.Wrapper, runtimeOptions RuntimeOptions) bool { - if upgrade.DisabledByCompilation { - return false - } - if opts := cfg.Options(); opts.AutoUpgradeIntervalH < 0 { - return false - } - if runtimeOptions.NoUpgrade { - l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.") - return false - } - return true -} - func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger) { timer := time.NewTimer(0) sub := evLogger.Subscribe(events.DeviceConnected) @@ -939,10 +893,3 @@ func setPauseState(cfg config.Wrapper, paused bool) { os.Exit(syncthing.ExitError.AsInt()) } } - -func exitCodeForUpgrade(err error) int { - if _, ok := err.(errNoUpgrade); ok { - return syncthing.ExitNoUpgradeAvailable.AsInt() - } - return syncthing.ExitError.AsInt() -}