Skip to content

Commit

Permalink
cmd/main: Prevent to load syncthing along with new config file (fixes s…
Browse files Browse the repository at this point in the history
  • Loading branch information
Bahadir Yilmaz committed Oct 9, 2018
1 parent f12ca95 commit c5eb4c5
Showing 1 changed file with 127 additions and 31 deletions.
158 changes: 127 additions & 31 deletions cmd/syncthing/main.go
Expand Up @@ -9,6 +9,7 @@ package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -225,34 +226,35 @@ var (
)

type RuntimeOptions struct {
confDir string
resetDatabase bool
resetDeltaIdxs bool
showVersion bool
showPaths bool
showDeviceId bool
doUpgrade bool
doUpgradeCheck bool
upgradeTo string
noBrowser bool
browserOnly bool
hideConsole bool
logFile string
auditEnabled bool
auditFile string
verbose bool
paused bool
unpaused bool
guiAddress string
guiAPIKey string
generateDir string
noRestart bool
profiler string
assetDir string
cpuProfile bool
stRestarting bool
logFlags int
showHelp bool
confDir string
resetDatabase bool
resetDeltaIdxs bool
showVersion bool
showPaths bool
showDeviceId bool
doUpgrade bool
doUpgradeCheck bool
upgradeTo string
noBrowser bool
browserOnly bool
hideConsole bool
logFile string
auditEnabled bool
auditFile string
verbose bool
paused bool
unpaused bool
guiAddress string
guiAPIKey string
generateDir string
noRestart bool
profiler string
assetDir string
cpuProfile bool
stRestarting bool
logFlags int
showHelp bool
allowConfigurationDowngrade bool
}

func defaultRuntimeOptions() RuntimeOptions {
Expand Down Expand Up @@ -306,6 +308,7 @@ func parseCommandLineOptions() RuntimeOptions {
flag.BoolVar(&options.unpaused, "unpaused", false, "Start with all devices and folders unpaused")
flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (still always logs to stdout). Cannot be used together with -no-restart/STNORESTART environment variable.")
flag.StringVar(&options.auditFile, "auditfile", options.auditFile, "Specify audit file (use \"-\" for stdout, \"--\" for stderr)")
flag.BoolVar(&options.allowConfigurationDowngrade, "allow-configuration-downgrade", false, "Downgrade configuration to current Syncthing configuration version")
if runtime.GOOS == "windows" {
// Allow user to hide the console window
flag.BoolVar(&options.hideConsole, "no-console", false, "Hide console window")
Expand Down Expand Up @@ -663,7 +666,12 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
"myID": myID.String(),
})

cfg := loadConfigAtStartup()
var cfg *config.Wrapper
if runtimeOptions.allowConfigurationDowngrade {
cfg = downgradeConfig()
} else {
cfg = loadConfigAtStartup()
}

if err := checkShortIDs(cfg); err != nil {
l.Fatalln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err)
Expand Down Expand Up @@ -748,7 +756,13 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
curParts := strings.Split(Version, "-")
if prevParts[0] != curParts[0] {
if prevVersion != "" {
l.Infoln("Detected upgrade from", prevVersion, "to", Version)
// Check Syncthing version
checkRelation := upgrade.CompareVersions(curParts[0], prevParts[0])
if checkRelation == upgrade.Older || checkRelation == upgrade.MajorOlder {
l.Warnf("Downgrade detected: %s to %s", prevVersion, Version)
} else {
l.Infoln("Detected upgrade from", prevVersion, "to", Version)
}
}

// Drop delta indexes in case we've changed random stuff we
Expand Down Expand Up @@ -972,15 +986,97 @@ func loadConfigAtStartup() *config.Wrapper {
}

if cfg.RawCopy().OriginalVersion != config.CurrentVersion {
// Check config version
if cfg.RawCopy().OriginalVersion > config.CurrentVersion {
// Config version is not compatible with installed version
if findOldConfig(cfg) {
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", config.CurrentVersion)
l.Infof("Compatible version of config was found in path %s, can be used manually.", archivePath)
}
l.Infof("Use `-allow-downgrade-config' flag to downgrade your config.")
l.Fatalf("Current Configuration Version: %d, Loading Configuration Version: %d (Configuration Version Mismatch)", cfg.RawCopy().OriginalVersion, config.CurrentVersion)
}
err = archiveAndSaveConfig(cfg)
if err != nil {
l.Fatalln("Config archive:", err)
}
}
return cfg
}

func downgradeConfig() *config.Wrapper {
cfgFile := locations[locConfigFile]
cfg, err := config.Load(cfgFile, myID)
if os.IsNotExist(err) {
cfg = defaultConfig(cfgFile)
cfg.Save()
l.Infof("Default config saved. Edit %s to taste or use the GUI\n", cfg.ConfigPath())
} else if err == io.EOF {
l.Fatalln("Failed to load config: unexpected end of file. Truncated or empty configuration?")
} else if err != nil {
l.Fatalln("Failed to load config:", err)
}

configVersion := cfg.RawCopy().OriginalVersion

// Check config version
if configVersion > config.CurrentVersion {
// Config version is not compatible with installed version
l.Infof("Current Configuration Version: %d, Loading Configuration Version: %d (Configuration Version Mismatch)", configVersion, config.CurrentVersion)
l.Infof("Wait for downgrading...")

fd, err := os.Open(cfgFile)
if err != nil {
l.Fatalln("Config not opened")
}
defer fd.Close()

// Read XML and set defaults (Check-1)
// In most cases everything should correct on here except Version.
cfgXML, err := config.ReadXML(fd, myID)
if err != nil {
l.Fatalln("Config not read")
}

err = archiveAndSaveConfig(cfg)
if err != nil {
l.Fatalln("Config archive:", err)
}

// Read configuration second time and Unmarshal to configuration struct (Check-2)
var cfgConfig config.Configuration
var buf bytes.Buffer
err = json.NewEncoder(&buf).Encode(cfgXML)
if err != nil {
l.Fatalln("Config didn't downgrade: ", err)
}

cfgConfig, err = config.ReadJSON(&buf, myID)
if err != nil {
l.Fatalln("Config didn't downgrade: ", err)
}

// Change version
cfgConfig.Version = config.CurrentVersion
cfgConfig.OriginalVersion = config.CurrentVersion
cfgConfigWrapper := config.Wrap(cfgFile, cfgConfig)

cfgConfigWrapper.Save()
return cfgConfigWrapper
}
l.Infoln("No need to use `-allow-downgrade-config` flag")
return cfg
}

func findOldConfig(cfg *config.Wrapper) bool {
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", config.CurrentVersion)
// Check if file already exists
if _, err := os.Stat(archivePath); err == nil {
return true
}
return false
}

func archiveAndSaveConfig(cfg *config.Wrapper) error {
// Copy the existing config to an archive copy
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", cfg.RawCopy().OriginalVersion)
Expand All @@ -989,7 +1085,7 @@ func archiveAndSaveConfig(cfg *config.Wrapper) error {
return err
}

// Do a regular atomic config sve
// Do a regular atomic config save
return cfg.Save()
}

Expand Down

0 comments on commit c5eb4c5

Please sign in to comment.