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 5f48488 commit 27f490b
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 32 deletions.
152 changes: 121 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,8 @@ 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 +667,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 +757,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.Infof("Current Syncthing Version: %s, Loading Syncthing Version: %s: (Syncthing Version Mismatch)", 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 +987,90 @@ 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 config version was found in path %s, can be usable 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 {
var cfgConfig config.Configuration
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)
}

// Reading configuration and Unmarshal to configuration struct (Check-2)
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(cfgXML)
l.Infoln("BUFFER:", buf.String())
json.Unmarshal(buf.Bytes(), &cfgConfig)

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 +1079,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
2 changes: 1 addition & 1 deletion lib/config/folderconfiguration.go
Expand Up @@ -96,7 +96,7 @@ func (f FolderConfiguration) Filesystem() fs.Filesystem {
// This is intentionally not a pointer method, because things like
// cfg.Folders["default"].Filesystem() should be valid.
if f.cachedFilesystem == nil && f.Path != "" {
l.Infoln("bug: uncached filesystem call (should only happen in tests)")
l.Infoln("bug: uncached filesystem call (should only happen in tests and `-allow-configuration-downgrade` flag)")
return fs.NewFilesystem(f.FilesystemType, f.Path)
}
return f.cachedFilesystem
Expand Down

0 comments on commit 27f490b

Please sign in to comment.