func autoUpgrade(cfg *config.Wrapper) { 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(cfg.Options().ReleasesURL, 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 performUpgrade(release upgrade.Release) { // Use leveldb database locks to protect against concurrent upgrades _, err := db.Open(locations[locDatabase]) if err == nil { err = upgrade.To(release) if err != nil { l.Fatalln("Upgrade:", err) } l.Infof("Upgraded to %q", release.Tag) } else { l.Infoln("Attempting upgrade through running Syncthing...") err = upgradeViaRest() if err != nil { l.Fatalln("Upgrade:", err) } l.Infoln("Syncthing upgrading") os.Exit(exitUpgrading) } }
func (s *apiService) postSystemUpgrade(w http.ResponseWriter, r *http.Request) { rel, err := upgrade.LatestRelease(s.cfg.Options().ReleasesURL, 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") } else { flag.StringVar(&logFile, "logfile", "-", "Log file name (use \"-\" for stdout)") } flag.StringVar(&generateDir, "generate", "", "Generate key and config in specified dir, then exit") flag.StringVar(&guiAddress, "gui-address", guiAddress, "Override GUI address") 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.BoolVar(&paused, "paused", false, "Start with all devices paused") longUsage := fmt.Sprintf(extraUsage, baseDirs["config"], debugFacilities()) flag.Usage = usageFor(flag.CommandLine, usage, longUsage) 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 logFile == "" { // Use the default log file location logFile = locations[locLogFile] } 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 = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, tlsRSABits) if err != nil { l.Fatalln("Create certificate:", err) } myID = protocol.NewDeviceID(cert.Certificate[0]) if err != nil { l.Fatalln("Load certificate:", 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 { releasesURL := "https://api.github.com/repos/syncthing/syncthing/releases?per_page=30" if cfg, _, err := loadConfig(locations[locConfigFile]); err == nil { releasesURL = cfg.Options().ReleasesURL } rel, err := upgrade.LatestRelease(releasesURL, 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() { 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") } else { flag.StringVar(&logFile, "logfile", "-", "Log file name (use \"-\" for stdout)") } var guiAddress, guiAPIKey string var generateDir string flag.StringVar(&generateDir, "generate", "", "Generate key and config in specified dir, then exit") flag.StringVar(&guiAddress, "gui-address", guiAddress, "Override GUI address") 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.BoolVar(&paused, "paused", false, "Start with all devices paused") longUsage := fmt.Sprintf(extraUsage, baseDirs["config"], debugFacilities()) flag.Usage = usageFor(flag.CommandLine, usage, longUsage) flag.Parse() if guiAddress != "" { // The config picks this up from the environment. os.Setenv("STGUIADDRESS", guiAddress) } if guiAPIKey != "" { // The config picks this up from the environment. os.Setenv("STGUIAPIKEY", guiAPIKey) } 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 logFile == "" { // Use the default log file location logFile = locations[locLogFile] } if showVersion { fmt.Println(LongVersion) return } l.SetFlags(logFlags) if generateDir != "" { generate(generateDir) 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 { releasesURL := "https://api.github.com/repos/syncthing/syncthing/releases?per_page=30" if cfg, _, err := loadConfig(locations[locConfigFile]); err == nil { releasesURL = cfg.Options().ReleasesURL } rel, err := upgrade.LatestRelease(releasesURL, 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 = db.Open(locations[locDatabase]) 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() } }