// 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
}
Beispiel #2
0
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
}
Beispiel #3
0
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
		}
	}
}
Beispiel #4
0
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)
			}
		}
	}
}
Beispiel #5
0
// 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
}