// Creates a new progress emitter which emits DownloadProgress events every // interval. func NewProgressEmitter(cfg *config.ConfigWrapper) *ProgressEmitter { t := &ProgressEmitter{ stop: make(chan struct{}), registry: make(map[string]*sharedPullerState), last: make(map[string]map[string]*pullerProgress), timer: time.NewTimer(time.Millisecond), } t.Changed(cfg.Raw()) cfg.Subscribe(t) return t }
func NewBlockFinder(db *leveldb.DB, cfg *config.ConfigWrapper) *BlockFinder { if blockFinder != nil { return blockFinder } f := &BlockFinder{ db: db, } f.Changed(cfg.Raw()) cfg.Subscribe(f) return f }
func sanityCheckFolders(cfg *config.ConfigWrapper, m *model.Model) { nextFolder: for id, folder := range cfg.Folders() { if folder.Invalid != "" { continue } m.AddFolder(folder) fi, err := os.Stat(folder.Path) if m.CurrentLocalVersion(id) > 0 { // Safety check. If the cached index contains files but the // folder doesn't exist, we have a problem. We would assume // that all files have been deleted which might not be the case, // so mark it as invalid instead. if err != nil || !fi.IsDir() { l.Warnf("Stopping folder %q - path does not exist, but has files in index", folder.ID) cfg.InvalidateFolder(id, "folder path missing") continue nextFolder } else if !folder.HasMarker() { l.Warnf("Stopping folder %q - path exists, but folder marker missing, check for mount issues", folder.ID) cfg.InvalidateFolder(id, "folder marker missing") continue nextFolder } } else if os.IsNotExist(err) { // If we don't have any files in the index, and the directory // doesn't exist, try creating it. err = os.MkdirAll(folder.Path, 0700) if err != nil { l.Warnf("Stopping folder %q - %v", folder.ID, err) cfg.InvalidateFolder(id, err.Error()) continue nextFolder } err = folder.CreateMarker() } else if !folder.HasMarker() { // If we don't have any files in the index, and the path does exist // but the marker is not there, create it. err = folder.CreateMarker() } if err != nil { // If there was another error or we could not create the // path, the folder is invalid. l.Warnf("Stopping folder %q - %v", folder.ID, err) cfg.InvalidateFolder(id, err.Error()) continue nextFolder } } }
func setupGUI(cfg *config.ConfigWrapper, m *model.Model) { opts := cfg.Options() guiCfg := overrideGUIConfig(cfg.GUI(), guiAddress, guiAuthentication, guiAPIKey) if guiCfg.Enabled && guiCfg.Address != "" { addr, err := net.ResolveTCPAddr("tcp", guiCfg.Address) if err != nil { l.Fatalf("Cannot start GUI on %q: %v", guiCfg.Address, err) } else { var hostOpen, hostShow string switch { case addr.IP == nil: hostOpen = "localhost" hostShow = "0.0.0.0" case addr.IP.IsUnspecified(): hostOpen = "localhost" hostShow = addr.IP.String() default: hostOpen = addr.IP.String() hostShow = hostOpen } var proto = "http" if guiCfg.UseTLS { proto = "https" } urlShow := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostShow, strconv.Itoa(addr.Port))) l.Infoln("Starting web GUI on", urlShow) err := startGUI(guiCfg, guiAssets, m) if err != nil { l.Fatalln("Cannot start GUI:", err) } if opts.StartBrowser && !noBrowser && !stRestarting { urlOpen := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostOpen, strconv.Itoa(addr.Port))) openURL(urlOpen) } } } }
// NewModel creates and starts a new model. The model starts in read-only mode, // where it sends index information to connected peers and responds to requests // for file data without altering the local folder in any way. func NewModel(cfg *config.ConfigWrapper, deviceName, clientName, clientVersion string, db *leveldb.DB) *Model { m := &Model{ cfg: cfg, db: db, deviceName: deviceName, clientName: clientName, clientVersion: clientVersion, folderCfgs: make(map[string]config.FolderConfiguration), folderFiles: make(map[string]*files.Set), folderDevices: make(map[string][]protocol.DeviceID), deviceFolders: make(map[protocol.DeviceID][]string), deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference), folderIgnores: make(map[string]*ignore.Matcher), folderRunners: make(map[string]service), folderState: make(map[string]folderState), folderStateChanged: make(map[string]time.Time), protoConn: make(map[protocol.DeviceID]protocol.Connection), rawConn: make(map[protocol.DeviceID]io.Closer), deviceVer: make(map[protocol.DeviceID]string), finder: files.NewBlockFinder(db, cfg), progressEmitter: NewProgressEmitter(cfg), } if cfg.Options().ProgressUpdateIntervalS > -1 { go m.progressEmitter.Serve() } var timeout = 20 * 60 // seconds if t := os.Getenv("STDEADLOCKTIMEOUT"); len(t) > 0 { it, err := strconv.Atoi(t) if err == nil { timeout = it } } deadlockDetect(&m.fmut, time.Duration(timeout)*time.Second) deadlockDetect(&m.smut, time.Duration(timeout)*time.Second) deadlockDetect(&m.pmut, time.Duration(timeout)*time.Second) return m }