func main() { _, err := flags.Parse(&opts) if err != nil { os.Exit(0) } if opts.Debug.TraceFile || opts.Debug.TraceIdx || opts.Debug.TraceNet || opts.Debug.LogSource { logger = log.New(os.Stderr, "", log.Lshortfile|log.Ldate|log.Ltime|log.Lmicroseconds) } if strings.HasPrefix(opts.ConfDir, "~/") { opts.ConfDir = strings.Replace(opts.ConfDir, "~", getHomeDir(), 1) } infoln("Version", Version) // Ensure that our home directory exists and that we have a certificate and key. ensureDir(opts.ConfDir, 0700) cert, err := loadCert(opts.ConfDir) if err != nil { newCertificate(opts.ConfDir) cert, err = loadCert(opts.ConfDir) fatalErr(err) } myID := string(certId(cert.Certificate[0])) infoln("My ID:", myID) if opts.Debug.Profiler != "" { go func() { err := http.ListenAndServe(opts.Debug.Profiler, nil) if err != nil { warnln(err) } }() } // The TLS configuration is used for both the listening socket and outgoing // connections. cfg := &tls.Config{ ClientAuth: tls.RequestClientCert, ServerName: "syncthing", NextProtos: []string{"bep/1.0"}, InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}, } // Load the configuration file, if it exists. cf, err := os.Open(path.Join(opts.ConfDir, confFileName)) if err != nil { fatalln("No config file") config = ini.Config{} } config = ini.Parse(cf) cf.Close() var dir = config.Get("repository", "dir") // Create a map of desired node connections based on the configuration file // directives. for nodeID, addrs := range config.OptionMap("nodes") { addrs := strings.Fields(addrs) nodeAddrs[nodeID] = addrs } ensureDir(dir, -1) m := NewModel(dir) // Walk the repository and update the local model before establishing any // connections to other nodes. if !opts.Rehash { infoln("Loading index cache") loadIndex(m) } infoln("Populating repository index") updateLocalModel(m) // Routine to listen for incoming connections infoln("Listening for incoming connections") go listen(myID, opts.Listen, m, cfg) // Routine to connect out to configured nodes infoln("Attempting to connect to other nodes") go connect(myID, opts.Listen, nodeAddrs, m, cfg) // Routine to pull blocks from other nodes to synchronize the local // repository. Does not run when we are in read only (publish only) mode. if !opts.ReadOnly { infoln("Cleaning out incomplete synchronizations") CleanTempFiles(dir) okln("Ready to synchronize") m.Start() } // Periodically scan the repository and update the local model. // XXX: Should use some fsnotify mechanism. go func() { for { time.Sleep(opts.Advanced.ScanInterval) updateLocalModel(m) } }() select {} }
func main() { _, err := flags.Parse(&opts) if err != nil { if err, ok := err.(*flags.Error); ok { if err.Type == flags.ErrHelp { os.Exit(0) } } fatalln(err) } if opts.ShowVersion { fmt.Println(Version) os.Exit(0) } if len(os.Getenv("GOGC")) == 0 { debug.SetGCPercent(25) } if len(os.Getenv("GOMAXPROCS")) == 0 { runtime.GOMAXPROCS(runtime.NumCPU()) } log.SetOutput(os.Stderr) logger = log.New(os.Stderr, "", log.Flags()) if len(opts.Debug.TraceModel) > 0 || opts.Debug.LogSource { log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds) logger.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds) } opts.ConfDir = expandTilde(opts.ConfDir) infoln("Version", Version) // Ensure that our home directory exists and that we have a certificate and key. ensureDir(opts.ConfDir, 0700) cert, err := loadCert(opts.ConfDir) if err != nil { newCertificate(opts.ConfDir) cert, err = loadCert(opts.ConfDir) fatalErr(err) } myID = string(certId(cert.Certificate[0])) infoln("My ID:", myID) log.SetPrefix("[" + myID[0:5] + "] ") logger.SetPrefix("[" + myID[0:5] + "] ") if opts.Debug.Profiler != "" { go func() { err := http.ListenAndServe(opts.Debug.Profiler, nil) if err != nil { warnln(err) } }() } // The TLS configuration is used for both the listening socket and outgoing // connections. cfg := &tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{"bep/1.0"}, ServerName: myID, ClientAuth: tls.RequestClientCert, SessionTicketsDisabled: true, InsecureSkipVerify: true, MinVersion: tls.VersionTLS12, } // Load the configuration file, if it exists. cf, err := os.Open(path.Join(opts.ConfDir, confFileName)) if err != nil { fatalln("No config file") config = ini.Config{} } config = ini.Parse(cf) cf.Close() var dir = expandTilde(config.Get("repository", "dir")) // Create a map of desired node connections based on the configuration file // directives. for nodeID, addrs := range config.OptionMap("nodes") { addrs := strings.Fields(addrs) nodeAddrs[nodeID] = addrs } ensureDir(dir, -1) m := model.NewModel(dir) for _, t := range opts.Debug.TraceModel { m.Trace(t) } if opts.Advanced.LimitRate > 0 { m.LimitRate(opts.Advanced.LimitRate) } // GUI if !opts.NoGUI && opts.GUIAddr != "" { host, port, err := net.SplitHostPort(opts.GUIAddr) if err != nil { warnf("Cannot start GUI on %q: %v", opts.GUIAddr, err) } else { if len(host) > 0 { infof("Starting web GUI on http://%s", opts.GUIAddr) } else { infof("Starting web GUI on port %s", port) } startGUI(opts.GUIAddr, m) } } // Walk the repository and update the local model before establishing any // connections to other nodes. if !opts.Rehash { infoln("Loading index cache") loadIndex(m) } infoln("Populating repository index") updateLocalModel(m) // Routine to listen for incoming connections infoln("Listening for incoming connections") go listen(myID, opts.Listen, m, cfg) // Routine to connect out to configured nodes infoln("Attempting to connect to other nodes") go connect(myID, opts.Listen, nodeAddrs, m, cfg) // Routine to pull blocks from other nodes to synchronize the local // repository. Does not run when we are in read only (publish only) mode. if !opts.ReadOnly { if opts.NoDelete { infoln("Deletes from peer nodes will be ignored") } else { infoln("Deletes from peer nodes are allowed") } okln("Ready to synchronize (read-write)") m.StartRW(!opts.NoDelete, opts.Advanced.RequestsInFlight) } else { okln("Ready to synchronize (read only; no external updates accepted)") } // Periodically scan the repository and update the local model. // XXX: Should use some fsnotify mechanism. go func() { for { time.Sleep(opts.Advanced.ScanInterval) if m.LocalAge() > opts.Advanced.ScanInterval.Seconds()/2 { updateLocalModel(m) } } }() if !opts.NoStats { // Periodically print statistics go printStatsLoop(m) } select {} }