func autoUpgrade() { timer := time.NewTimer(0) sub := events.Default.Subscribe(events.DeviceConnected) for { select { case event := <-sub.C(): data, ok := event.Data.(map[string]string) if !ok || data["clientName"] != "syncthing" || upgrade.CompareVersions(data["clientVersion"], Version) != upgrade.Newer { continue } l.Infof("Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades.", data["id"], Version, data["clientVersion"]) case <-timer.C: } rel, err := upgrade.LatestRelease(Version) if err == upgrade.ErrUpgradeUnsupported { events.Default.Unsubscribe(sub) return } if err != nil { // Don't complain too loudly here; we might simply not have // internet connectivity, or the upgrade server might be down. l.Infoln("Automatic upgrade:", err) timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour) continue } if upgrade.CompareVersions(rel.Tag, Version) != upgrade.Newer { // Skip equal, older or majorly newer (incompatible) versions timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour) continue } l.Infof("Automatic upgrade (current %q < latest %q)", Version, rel.Tag) err = upgrade.To(rel) if err != nil { l.Warnln("Automatic upgrade:", err) timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour) continue } events.Default.Unsubscribe(sub) l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag) time.Sleep(time.Minute) stop <- exitUpgrading return } }
func restPostUpgrade(w http.ResponseWriter, r *http.Request) { rel, err := upgrade.LatestRelease(strings.Contains(Version, "-beta")) if err != nil { l.Warnln("getting latest release:", err) http.Error(w, err.Error(), 500) return } if upgrade.CompareVersions(rel.Tag, Version) == 1 { err = upgrade.To(rel) if err != nil { l.Warnln("upgrading:", err) http.Error(w, err.Error(), 500) return } flushResponse(`{"ok": "restarting"}`, w) l.Infoln("Upgrading") stop <- exitUpgrading } }
func (s *apiSvc) postSystemUpgrade(w http.ResponseWriter, r *http.Request) { rel, err := upgrade.LatestRelease(Version) if err != nil { l.Warnln("getting latest release:", err) http.Error(w, err.Error(), 500) return } if upgrade.CompareVersions(rel.Tag, Version) > upgrade.Equal { err = upgrade.To(rel) if err != nil { l.Warnln("upgrading:", err) http.Error(w, err.Error(), 500) return } s.flushResponse(`{"ok": "restarting"}`, w) l.Infoln("Upgrading") stop <- exitUpgrading } }
func main() { if runtime.GOOS == "windows" { // On Windows, we use a log file by default. Setting the -logfile flag // to "-" disables this behavior. flag.StringVar(&logFile, "logfile", "", "Log file name (use \"-\" for stdout)") // We also add an option to hide the console window flag.BoolVar(&noConsole, "no-console", false, "Hide console window") } flag.StringVar(&generateDir, "generate", "", "Generate key and config in specified dir, then exit") flag.StringVar(&guiAddress, "gui-address", guiAddress, "Override GUI address") flag.StringVar(&guiAuthentication, "gui-authentication", guiAuthentication, "Override GUI authentication; username:password") flag.StringVar(&guiAPIKey, "gui-apikey", guiAPIKey, "Override GUI API key") flag.StringVar(&confDir, "home", "", "Set configuration directory") flag.IntVar(&logFlags, "logflags", logFlags, "Select information in log line prefix") flag.BoolVar(&noBrowser, "no-browser", false, "Do not start browser") flag.BoolVar(&noRestart, "no-restart", noRestart, "Do not restart; just exit") flag.BoolVar(&reset, "reset", false, "Reset the database") flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade") flag.BoolVar(&doUpgradeCheck, "upgrade-check", false, "Check for available upgrade") flag.BoolVar(&showVersion, "version", false, "Show version") flag.StringVar(&upgradeTo, "upgrade-to", upgradeTo, "Force upgrade directly from specified URL") flag.BoolVar(&auditEnabled, "audit", false, "Write events to audit file") flag.BoolVar(&verbose, "verbose", false, "Print verbose log output") flag.Usage = usageFor(flag.CommandLine, usage, fmt.Sprintf(extraUsage, baseDirs["config"])) flag.Parse() if noConsole { osutil.HideConsole() } if confDir != "" { // Not set as default above because the string can be really long. baseDirs["config"] = confDir } if err := expandLocations(); err != nil { l.Fatalln(err) } if guiAssets == "" { guiAssets = locations[locGUIAssets] } if runtime.GOOS == "windows" { if logFile == "" { // Use the default log file location logFile = locations[locLogFile] } else if logFile == "-" { // Don't use a logFile logFile = "" } } if showVersion { fmt.Println(LongVersion) return } l.SetFlags(logFlags) if generateDir != "" { dir, err := osutil.ExpandTilde(generateDir) if err != nil { l.Fatalln("generate:", err) } info, err := os.Stat(dir) if err == nil && !info.IsDir() { l.Fatalln(dir, "is not a directory") } if err != nil && os.IsNotExist(err) { err = osutil.MkdirAll(dir, 0700) if err != nil { l.Fatalln("generate:", err) } } certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem") cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err == nil { l.Warnln("Key exists; will not overwrite.") l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } else { cert, err = newCertificate(certFile, keyFile, tlsDefaultCommonName) myID = protocol.NewDeviceID(cert.Certificate[0]) if err != nil { l.Fatalln("load cert:", err) } if err == nil { l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } } cfgFile := filepath.Join(dir, "config.xml") if _, err := os.Stat(cfgFile); err == nil { l.Warnln("Config exists; will not overwrite.") return } var myName, _ = os.Hostname() var newCfg = defaultConfig(myName) var cfg = config.Wrap(cfgFile, newCfg) err = cfg.Save() if err != nil { l.Warnln("Failed to save config", err) } return } if info, err := os.Stat(baseDirs["config"]); err == nil && !info.IsDir() { l.Fatalln("Config directory", baseDirs["config"], "is not a directory") } // Ensure that our home directory exists. ensureDir(baseDirs["config"], 0700) if upgradeTo != "" { err := upgrade.ToURL(upgradeTo) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okln("Upgraded from", upgradeTo) return } if doUpgrade || doUpgradeCheck { rel, err := upgrade.LatestRelease(Version) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } if upgrade.CompareVersions(rel.Tag, Version) <= 0 { l.Infof("No upgrade available (current %q >= latest %q).", Version, rel.Tag) os.Exit(exitNoUpgradeAvailable) } l.Infof("Upgrade available (current %q < latest %q)", Version, rel.Tag) if doUpgrade { // Use leveldb database locks to protect against concurrent upgrades _, err = leveldb.OpenFile(locations[locDatabase], &opt.Options{OpenFilesCacheCapacity: 100}) if err != nil { l.Infoln("Attempting upgrade through running Syncthing...") err = upgradeViaRest() if err != nil { l.Fatalln("Upgrade:", err) } l.Okln("Syncthing upgrading") return } err = upgrade.To(rel) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okf("Upgraded to %q", rel.Tag) } return } if reset { resetDB() return } if noRestart { syncthingMain() } else { monitorMain() } }
func main() { defConfDir, err := getDefaultConfDir() if err != nil { l.Fatalln("home:", err) } if runtime.GOOS == "windows" { // On Windows, we use a log file by default. Setting the -logfile flag // to the empty string disables this behavior. logFile = filepath.Join(defConfDir, "syncthing.log") flag.StringVar(&logFile, "logfile", logFile, "Log file name (blank for stdout)") // We also add an option to hide the console window flag.BoolVar(&noConsole, "no-console", false, "Hide console window") } flag.StringVar(&generateDir, "generate", "", "Generate key and config in specified dir, then exit") flag.StringVar(&guiAddress, "gui-address", guiAddress, "Override GUI address") flag.StringVar(&guiAuthentication, "gui-authentication", guiAuthentication, "Override GUI authentication; username:password") flag.StringVar(&guiAPIKey, "gui-apikey", guiAPIKey, "Override GUI API key") flag.StringVar(&confDir, "home", "", "Set configuration directory") flag.IntVar(&logFlags, "logflags", logFlags, "Select information in log line prefix") flag.BoolVar(&noBrowser, "no-browser", false, "Do not start browser") flag.BoolVar(&noRestart, "no-restart", noRestart, "Do not restart; just exit") flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster") flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade") flag.BoolVar(&doUpgradeCheck, "upgrade-check", false, "Check for available upgrade") flag.BoolVar(&showVersion, "version", false, "Show version") flag.StringVar(&upgradeTo, "upgrade-to", upgradeTo, "Force upgrade directly from specified URL") flag.Usage = usageFor(flag.CommandLine, usage, fmt.Sprintf(extraUsage, defConfDir)) flag.Parse() if noConsole { osutil.HideConsole() } if confDir == "" { // Not set as default above because the string can be really long. confDir = defConfDir } if confDir != defConfDir && filepath.Dir(logFile) == defConfDir { // The user changed the config dir with -home, but not the log file // location. In this case we assume they meant for the logfile to // still live in it's default location *relative to the config dir*. logFile = filepath.Join(confDir, "syncthing.log") } if showVersion { fmt.Println(LongVersion) return } l.SetFlags(logFlags) if generateDir != "" { dir, err := osutil.ExpandTilde(generateDir) if err != nil { l.Fatalln("generate:", err) } info, err := os.Stat(dir) if err == nil && !info.IsDir() { l.Fatalln(dir, "is not a directory") } if err != nil && os.IsNotExist(err) { err = os.MkdirAll(dir, 0700) if err != nil { l.Fatalln("generate:", err) } } cert, err := loadCert(dir, "") if err == nil { l.Warnln("Key exists; will not overwrite.") l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } else { newCertificate(dir, "", tlsDefaultCommonName) cert, err = loadCert(dir, "") myID = protocol.NewDeviceID(cert.Certificate[0]) if err != nil { l.Fatalln("load cert:", err) } if err == nil { l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } } cfgFile := filepath.Join(dir, "config.xml") if _, err := os.Stat(cfgFile); err == nil { l.Warnln("Config exists; will not overwrite.") return } var myName, _ = os.Hostname() var newCfg = defaultConfig(myName) var cfg = config.Wrap(cfgFile, newCfg) err = cfg.Save() if err != nil { l.Warnln("Failed to save config", err) } return } confDir, err := osutil.ExpandTilde(confDir) if err != nil { l.Fatalln("home:", err) } if info, err := os.Stat(confDir); err == nil && !info.IsDir() { l.Fatalln("Config directory", confDir, "is not a directory") } // Ensure that our home directory exists. ensureDir(confDir, 0700) if upgradeTo != "" { err := upgrade.ToURL(upgradeTo) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okln("Upgraded from", upgradeTo) return } if doUpgrade || doUpgradeCheck { rel, err := upgrade.LatestRelease(IsBeta) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } if upgrade.CompareVersions(rel.Tag, Version) <= 0 { l.Infof("No upgrade available (current %q >= latest %q).", Version, rel.Tag) os.Exit(exitNoUpgradeAvailable) } l.Infof("Upgrade available (current %q < latest %q)", Version, rel.Tag) if doUpgrade { // Use leveldb database locks to protect against concurrent upgrades _, err = leveldb.OpenFile(filepath.Join(confDir, "index"), &opt.Options{OpenFilesCacheCapacity: 100}) if err != nil { l.Fatalln("Cannot upgrade, database seems to be locked. Is another copy of Syncthing already running?") } err = upgrade.To(rel) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okf("Upgraded to %q", rel.Tag) } return } if reset { resetFolders() return } if noRestart { syncthingMain() } else { monitorMain() } }