func BenchmarkUpdateOneFile(b *testing.B) { local0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, // A longer name is more realistic and causes more allocations protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)}, } ldb, err := db.Open("testdata/benchmarkupdate.db") if err != nil { b.Fatal(err) } defer func() { ldb.Close() os.RemoveAll("testdata/benchmarkupdate.db") }() m := db.NewFileSet("test", ldb) m.Replace(protocol.LocalDeviceID, local0) l := local0[4:5] for i := 0; i < b.N; i++ { l[0].Version = l[0].Version.Update(myID) m.Update(protocol.LocalDeviceID, local0) } b.ReportAllocs() }
func main() { var mode string log.SetFlags(0) log.SetOutput(os.Stdout) flag.StringVar(&mode, "mode", "dump", "Mode of operation: dump, dumpsize") flag.Parse() path := flag.Arg(0) if path == "" { path = filepath.Join(defaultConfigDir(), "index-v0.14.0.db") } fmt.Println("Path:", path) ldb, err := db.Open(path) if err != nil { log.Fatal(err) } if mode == "dump" { dump(ldb) } else if mode == "dumpsize" { dumpsize(ldb) } else { fmt.Println("Unknown mode") } }
func tempDB() (*db.Instance, string) { dir, err := ioutil.TempDir("", "syncthing") if err != nil { panic(err) } dbi, err := db.Open(filepath.Join(dir, "db")) if err != nil { panic(err) } return dbi, dir }
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 syncthingMain(runtimeOptions RuntimeOptions) { setupSignalHandling() // Create a main service manager. We'll add things to this as we go along. // We want any logging it does to go through our log system. mainService := suture.New("main", suture.Spec{ Log: func(line string) { l.Debugln(line) }, }) mainService.ServeBackground() // Set a log prefix similar to the ID we will have later on, or early log // lines look ugly. l.SetPrefix("[start] ") if runtimeOptions.auditEnabled { startAuditing(mainService) } if runtimeOptions.verbose { mainService.Add(newVerboseService()) } errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0) systemLog := logger.NewRecorder(l, logger.LevelDebug, maxSystemLog, initialSystemLog) // Event subscription for the API; must start early to catch the early events. apiSub := events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents), 1000) if len(os.Getenv("GOMAXPROCS")) == 0 { runtime.GOMAXPROCS(runtime.NumCPU()) } // Attempt to increase the limit on number of open files to the maximum // allowed, in case we have many peers. We don't really care enough to // report the error if there is one. osutil.MaximizeOpenFileLimit() // Ensure that that we have a certificate and key. cert, err := tls.LoadX509KeyPair(locations[locCertFile], locations[locKeyFile]) if err != nil { l.Infof("Generating ECDSA key and certificate for %s...", tlsDefaultCommonName) cert, err = tlsutil.NewCertificate(locations[locCertFile], locations[locKeyFile], tlsDefaultCommonName, bepRSABits) if err != nil { l.Fatalln(err) } } myID = protocol.NewDeviceID(cert.Certificate[0]) l.SetPrefix(fmt.Sprintf("[%s] ", myID.String()[:5])) l.Infoln(LongVersion) l.Infoln("My ID:", myID) printHashRate() // Emit the Starting event, now that we know who we are. events.Default.Log(events.Starting, map[string]string{ "home": baseDirs["config"], "myID": myID.String(), }) cfg := loadOrCreateConfig() 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) } if len(runtimeOptions.profiler) > 0 { go func() { l.Debugln("Starting profiler on", runtimeOptions.profiler) runtime.SetBlockProfileRate(1) err := http.ListenAndServe(runtimeOptions.profiler, nil) if err != nil { l.Fatalln(err) } }() } // The TLS configuration is used for both the listening socket and outgoing // connections. tlsCfg := &tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{bepProtocolName}, ClientAuth: tls.RequestClientCert, SessionTicketsDisabled: true, InsecureSkipVerify: true, MinVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, }, } // If the read or write rate should be limited, set up a rate limiter for it. // This will be used on connections created in the connect and listen routines. opts := cfg.Options() if !opts.SymlinksEnabled { symlinks.Supported = false } if (opts.MaxRecvKbps > 0 || opts.MaxSendKbps > 0) && !opts.LimitBandwidthInLan { lans, _ = osutil.GetLans() for _, lan := range opts.AlwaysLocalNets { _, ipnet, err := net.ParseCIDR(lan) if err != nil { l.Infoln("Network", lan, "is malformed:", err) continue } lans = append(lans, ipnet) } networks := make([]string, len(lans)) for i, lan := range lans { networks[i] = lan.String() } l.Infoln("Local networks:", strings.Join(networks, ", ")) } dbFile := locations[locDatabase] ldb, err := db.Open(dbFile) if err != nil { l.Fatalln("Cannot open database:", err, "- Is another copy of Syncthing already running?") } protectedFiles := []string{ locations[locDatabase], locations[locConfigFile], locations[locCertFile], locations[locKeyFile], } // Remove database entries for folders that no longer exist in the config folders := cfg.Folders() for _, folder := range ldb.ListFolders() { if _, ok := folders[folder]; !ok { l.Infof("Cleaning data for dropped folder %q", folder) db.DropFolder(ldb, folder) } } m := model.NewModel(cfg, myID, myDeviceName(cfg), "syncthing", Version, ldb, protectedFiles) cfg.Subscribe(m) if t := os.Getenv("STDEADLOCKTIMEOUT"); len(t) > 0 { it, err := strconv.Atoi(t) if err == nil { m.StartDeadlockDetector(time.Duration(it) * time.Second) } } else if !IsRelease || IsBeta { m.StartDeadlockDetector(20 * time.Minute) } if runtimeOptions.paused { for device := range cfg.Devices() { m.PauseDevice(device) } } // Clear out old indexes for other devices. Otherwise we'll start up and // start needing a bunch of files which are nowhere to be found. This // needs to be changed when we correctly do persistent indexes. for _, folderCfg := range cfg.Folders() { m.AddFolder(folderCfg) for _, device := range folderCfg.DeviceIDs() { if device == myID { continue } m.Index(device, folderCfg.ID, nil, 0, nil) } m.StartFolder(folderCfg.ID) } mainService.Add(m) // Start discovery cachedDiscovery := discover.NewCachingMux() mainService.Add(cachedDiscovery) // Start connection management connectionsService := connections.NewService(cfg, myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName, lans) mainService.Add(connectionsService) if cfg.Options().GlobalAnnEnabled { for _, srv := range cfg.GlobalDiscoveryServers() { l.Infoln("Using discovery server", srv) gd, err := discover.NewGlobal(srv, cert, connectionsService) if err != nil { l.Warnln("Global discovery:", err) continue } // Each global discovery server gets its results cached for five // minutes, and is not asked again for a minute when it's returned // unsuccessfully. cachedDiscovery.Add(gd, 5*time.Minute, time.Minute, globalDiscoveryPriority) } } if cfg.Options().LocalAnnEnabled { // v4 broadcasts bcd, err := discover.NewLocal(myID, fmt.Sprintf(":%d", cfg.Options().LocalAnnPort), connectionsService) if err != nil { l.Warnln("IPv4 local discovery:", err) } else { cachedDiscovery.Add(bcd, 0, 0, ipv4LocalDiscoveryPriority) } // v6 multicasts mcd, err := discover.NewLocal(myID, cfg.Options().LocalAnnMCAddr, connectionsService) if err != nil { l.Warnln("IPv6 local discovery:", err) } else { cachedDiscovery.Add(mcd, 0, 0, ipv6LocalDiscoveryPriority) } } // GUI setupGUI(mainService, cfg, m, apiSub, cachedDiscovery, connectionsService, errors, systemLog, runtimeOptions) if runtimeOptions.cpuProfile { f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid())) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) } for _, device := range cfg.Devices() { if len(device.Name) > 0 { l.Infof("Device %s is %q at %v", device.DeviceID, device.Name, device.Addresses) } } if opts.URAccepted > 0 && opts.URAccepted < usageReportVersion { l.Infoln("Anonymous usage report has changed; revoking acceptance") opts.URAccepted = 0 opts.URUniqueID = "" cfg.SetOptions(opts) } if opts.URAccepted >= usageReportVersion { if opts.URUniqueID == "" { // Previously the ID was generated from the node ID. We now need // to generate a new one. opts.URUniqueID = util.RandomString(8) cfg.SetOptions(opts) cfg.Save() } } // The usageReportingManager registers itself to listen to configuration // changes, and there's nothing more we need to tell it from the outside. // Hence we don't keep the returned pointer. newUsageReportingManager(cfg, m) if opts.RestartOnWakeup { go standbyMonitor() } if opts.AutoUpgradeIntervalH > 0 { if noUpgrade { l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.") } else if IsRelease { go autoUpgrade(cfg) } else { l.Infof("No automatic upgrades; %s is not a release version.", Version) } } events.Default.Log(events.StartupComplete, map[string]string{ "myID": myID.String(), }) go generatePingEvents() cleanConfigDirectory() code := <-stop mainService.Stop() l.Infoln("Exiting") if runtimeOptions.cpuProfile { pprof.StopCPUProfile() } os.Exit(code) }
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 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 != "" { 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 = 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() } }