all: Remove global events.Default (ref #4085) (#5886)

This commit is contained in:
Simon Frei 2019-08-15 16:29:37 +02:00 committed by GitHub
parent f6f696c6c5
commit b1c74860e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 467 additions and 374 deletions

View File

@ -22,6 +22,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -85,7 +86,7 @@ func main() {
myID := protocol.NewDeviceID(cert.Certificate[0]) myID := protocol.NewDeviceID(cert.Certificate[0])
// Load the config // Load the config
cfg, err := config.Load(locations.Get(locations.ConfigFile), myID) cfg, err := config.Load(locations.Get(locations.ConfigFile), myID, events.NoopLogger)
if err != nil { if err != nil {
log.Fatalln(errors.Wrap(err, "loading config")) log.Fatalln(errors.Wrap(err, "loading config"))
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/discover" "github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
) )
@ -82,7 +83,7 @@ func checkServers(deviceID protocol.DeviceID, servers ...string) {
} }
func checkServer(deviceID protocol.DeviceID, server string) checkResult { func checkServer(deviceID protocol.DeviceID, server string) checkResult {
disco, err := discover.NewGlobal(server, tls.Certificate{}, nil) disco, err := discover.NewGlobal(server, tls.Certificate{}, nil, events.NoopLogger)
if err != nil { if err != nil {
return checkResult{error: err} return checkResult{error: err}
} }

View File

@ -20,6 +20,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/relay/protocol" "github.com/syncthing/syncthing/lib/relay/protocol"
"github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/tlsutil"
@ -194,7 +195,7 @@ func main() {
log.Println("ID:", id) log.Println("ID:", id)
} }
wrapper := config.Wrap("config", config.New(id)) wrapper := config.Wrap("config", config.New(id), events.NoopLogger)
wrapper.SetOptions(config.OptionsConfiguration{ wrapper.SetOptions(config.OptionsConfiguration{
NATLeaseM: natLease, NATLeaseM: natLease,
NATRenewalM: natRenewal, NATRenewalM: natRenewal,

View File

@ -394,7 +394,7 @@ func main() {
} }
func openGUI(myID protocol.DeviceID) error { func openGUI(myID protocol.DeviceID) error {
cfg, err := loadOrDefaultConfig(myID) cfg, err := loadOrDefaultConfig(myID, events.NoopLogger)
if err != nil { if err != nil {
return err return err
} }
@ -437,7 +437,7 @@ func generate(generateDir string) error {
l.Warnln("Config exists; will not overwrite.") l.Warnln("Config exists; will not overwrite.")
return nil return nil
} }
cfg, err := syncthing.DefaultConfig(cfgFile, myID, noDefaultFolder) cfg, err := syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder)
if err != nil { if err != nil {
return err return err
} }
@ -471,7 +471,7 @@ func debugFacilities() string {
} }
func checkUpgrade() upgrade.Release { func checkUpgrade() upgrade.Release {
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID) cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
opts := cfg.Options() opts := cfg.Options()
release, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases) release, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
if err != nil { if err != nil {
@ -512,7 +512,7 @@ func performUpgrade(release upgrade.Release) {
} }
func upgradeViaRest() error { func upgradeViaRest() error {
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID) cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
u, err := url.Parse(cfg.GUI().URL()) u, err := url.Parse(cfg.GUI().URL())
if err != nil { if err != nil {
return err return err
@ -566,7 +566,11 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
os.Exit(1) os.Exit(1)
} }
cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, runtimeOptions.allowNewerConfig, noDefaultFolder) evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
if err != nil { if err != nil {
l.Warnln("Failed to initialize config:", err) l.Warnln("Failed to initialize config:", err)
os.Exit(exitError) os.Exit(exitError)
@ -594,7 +598,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
appOpts.DeadlockTimeoutS = secs appOpts.DeadlockTimeoutS = secs
} }
app := syncthing.New(cfg, ldb, cert, appOpts) app := syncthing.New(cfg, ldb, evLogger, cert, appOpts)
setupSignalHandling(app) setupSignalHandling(app)
@ -639,7 +643,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
if runtimeOptions.NoUpgrade { if runtimeOptions.NoUpgrade {
l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.") l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
} else { } else {
go autoUpgrade(cfg, app) go autoUpgrade(cfg, app, evLogger)
} }
} }
@ -684,12 +688,12 @@ func setupSignalHandling(app *syncthing.App) {
}() }()
} }
func loadOrDefaultConfig(myID protocol.DeviceID) (config.Wrapper, error) { func loadOrDefaultConfig(myID protocol.DeviceID, evLogger events.Logger) (config.Wrapper, error) {
cfgFile := locations.Get(locations.ConfigFile) cfgFile := locations.Get(locations.ConfigFile)
cfg, err := config.Load(cfgFile, myID) cfg, err := config.Load(cfgFile, myID, evLogger)
if err != nil { if err != nil {
cfg, err = syncthing.DefaultConfig(cfgFile, myID, noDefaultFolder) cfg, err = syncthing.DefaultConfig(cfgFile, myID, evLogger, noDefaultFolder)
} }
return cfg, err return cfg, err
@ -774,9 +778,9 @@ func standbyMonitor(app *syncthing.App) {
} }
} }
func autoUpgrade(cfg config.Wrapper, app *syncthing.App) { func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger) {
timer := time.NewTimer(0) timer := time.NewTimer(0)
sub := events.Default.Subscribe(events.DeviceConnected) sub := evLogger.Subscribe(events.DeviceConnected)
for { for {
select { select {
case event := <-sub.C(): case event := <-sub.C():
@ -798,7 +802,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App) {
rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases) rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
if err == upgrade.ErrUpgradeUnsupported { if err == upgrade.ErrUpgradeUnsupported {
events.Default.Unsubscribe(sub) sub.Unsubscribe()
return return
} }
if err != nil { if err != nil {
@ -822,7 +826,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App) {
timer.Reset(checkInterval) timer.Reset(checkInterval)
continue continue
} }
events.Default.Unsubscribe(sub) sub.Unsubscribe()
l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag) l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag)
time.Sleep(time.Minute) time.Sleep(time.Minute)
app.Stop(syncthing.ExitUpgrade) app.Stop(syncthing.ExitUpgrade)

View File

@ -18,6 +18,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
@ -450,7 +451,7 @@ func childEnv() []string {
// panicUploadMaxWait uploading panics... // panicUploadMaxWait uploading panics...
func maybeReportPanics() { func maybeReportPanics() {
// Try to get a config to see if/where panics should be reported. // Try to get a config to see if/where panics should be reported.
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID) cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
if err != nil { if err != nil {
l.Warnln("Couldn't load config; not reporting crash") l.Warnln("Couldn't load config; not reporting crash")
return return

View File

@ -71,6 +71,7 @@ type service struct {
model model.Model model model.Model
eventSubs map[events.EventType]events.BufferedSubscription eventSubs map[events.EventType]events.BufferedSubscription
eventSubsMut sync.Mutex eventSubsMut sync.Mutex
evLogger events.Logger
discoverer discover.CachingMux discoverer discover.CachingMux
connectionsService connections.Service connectionsService connections.Service
fss model.FolderSummaryService fss model.FolderSummaryService
@ -105,7 +106,7 @@ type Service interface {
WaitForStart() error WaitForStart() error
} }
func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, cpu Rater, contr Controller, noUpgrade bool) Service { func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.CachingMux, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, cpu Rater, contr Controller, noUpgrade bool) Service {
s := &service{ s := &service{
id: id, id: id,
cfg: cfg, cfg: cfg,
@ -116,6 +117,7 @@ func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonNam
DiskEventMask: diskSub, DiskEventMask: diskSub,
}, },
eventSubsMut: sync.NewMutex(), eventSubsMut: sync.NewMutex(),
evLogger: evLogger,
discoverer: discoverer, discoverer: discoverer,
connectionsService: connectionsService, connectionsService: connectionsService,
fss: fss, fss: fss,
@ -315,7 +317,7 @@ func (s *service) serve(stop chan struct{}) {
// Wrap everything in basic auth, if user/password is set. // Wrap everything in basic auth, if user/password is set.
if guiCfg.IsAuthEnabled() { if guiCfg.IsAuthEnabled() {
handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, s.cfg.LDAP(), handler) handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, s.cfg.LDAP(), handler, s.evLogger)
} }
// Redirect to HTTPS if we are supposed to // Redirect to HTTPS if we are supposed to
@ -1215,7 +1217,7 @@ func (s *service) getEventSub(mask events.EventType) events.BufferedSubscription
s.eventSubsMut.Lock() s.eventSubsMut.Lock()
bufsub, ok := s.eventSubs[mask] bufsub, ok := s.eventSubs[mask]
if !ok { if !ok {
evsub := events.Default.Subscribe(mask) evsub := s.evLogger.Subscribe(mask)
bufsub = events.NewBufferedSubscription(evsub, EventSubBufferSize) bufsub = events.NewBufferedSubscription(evsub, EventSubBufferSize)
s.eventSubs[mask] = bufsub s.eventSubs[mask] = bufsub
} }

View File

@ -28,14 +28,14 @@ var (
sessionsMut = sync.NewMutex() sessionsMut = sync.NewMutex()
) )
func emitLoginAttempt(success bool, username string) { func emitLoginAttempt(success bool, username string, evLogger events.Logger) {
events.Default.Log(events.LoginAttempt, map[string]interface{}{ evLogger.Log(events.LoginAttempt, map[string]interface{}{
"success": success, "success": success,
"username": username, "username": username,
}) })
} }
func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler) http.Handler { func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler, evLogger events.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if guiCfg.IsValidAPIKey(r.Header.Get("X-API-Key")) { if guiCfg.IsValidAPIKey(r.Header.Get("X-API-Key")) {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
@ -94,7 +94,7 @@ func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfigura
} }
if !authOk { if !authOk {
emitLoginAttempt(false, username) emitLoginAttempt(false, username, evLogger)
error() error()
return return
} }
@ -109,7 +109,7 @@ func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfigura
MaxAge: 0, MaxAge: 0,
}) })
emitLoginAttempt(true, username) emitLoginAttempt(true, username, evLogger)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }

View File

@ -100,9 +100,9 @@ func TestStopAfterBrokenConfig(t *testing.T) {
RawUseTLS: false, RawUseTLS: false,
}, },
} }
w := config.Wrap("/dev/null", cfg) w := config.Wrap("/dev/null", cfg, events.NoopLogger)
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service) srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token) defer os.Remove(token)
srv.started = make(chan string) srv.started = make(chan string)
@ -512,8 +512,8 @@ func startHTTP(cfg *mockedConfig) (string, error) {
// Instantiate the API service // Instantiate the API service
urService := ur.New(cfg, m, connections, false) urService := ur.New(cfg, m, connections, false)
summaryService := model.NewFolderSummaryService(cfg, m, protocol.LocalDeviceID) summaryService := model.NewFolderSummaryService(cfg, m, protocol.LocalDeviceID, events.NoopLogger)
svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, discoverer, connections, urService, summaryService, errorLog, systemLog, cpu, nil, false).(*service) svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, events.NoopLogger, discoverer, connections, urService, summaryService, errorLog, systemLog, cpu, nil, false).(*service)
defer os.Remove(token) defer os.Remove(token)
svc.started = addrChan svc.started = addrChan
@ -979,7 +979,7 @@ func TestEventMasks(t *testing.T) {
cfg := new(mockedConfig) cfg := new(mockedConfig)
defSub := new(mockedEventSub) defSub := new(mockedEventSub)
diskSub := new(mockedEventSub) diskSub := new(mockedEventSub)
svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service) svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, events.NoopLogger, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token) defer os.Remove(token)
if mask := svc.getEventMask(""); mask != DefaultEventMask { if mask := svc.getEventMask(""); mask != DefaultEventMask {

View File

@ -44,7 +44,7 @@ func (validationError) String() string {
func TestReplaceCommit(t *testing.T) { func TestReplaceCommit(t *testing.T) {
t.Skip("broken, fails randomly, #3834") t.Skip("broken, fails randomly, #3834")
w := Wrap("/dev/null", Configuration{Version: 0}) w := wrap("/dev/null", Configuration{Version: 0})
if w.RawCopy().Version != 0 { if w.RawCopy().Version != 0 {
t.Fatal("Config incorrect") t.Fatal("Config incorrect")
} }

View File

@ -20,6 +20,7 @@ import (
"testing" "testing"
"github.com/d4l3k/messagediff" "github.com/d4l3k/messagediff"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
@ -86,7 +87,7 @@ func TestDefaultValues(t *testing.T) {
func TestDeviceConfig(t *testing.T) { func TestDeviceConfig(t *testing.T) {
for i := OldestHandledVersion; i <= CurrentVersion; i++ { for i := OldestHandledVersion; i <= CurrentVersion; i++ {
os.RemoveAll(filepath.Join("testdata", DefaultMarkerName)) os.RemoveAll(filepath.Join("testdata", DefaultMarkerName))
wr, err := Load(fmt.Sprintf("testdata/v%d.xml", i), device1) wr, err := load(fmt.Sprintf("testdata/v%d.xml", i), device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -168,7 +169,7 @@ func TestDeviceConfig(t *testing.T) {
} }
func TestNoListenAddresses(t *testing.T) { func TestNoListenAddresses(t *testing.T) {
cfg, err := Load("testdata/nolistenaddress.xml", device1) cfg, err := load("testdata/nolistenaddress.xml", device1)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -225,7 +226,7 @@ func TestOverriddenValues(t *testing.T) {
} }
os.Unsetenv("STNOUPGRADE") os.Unsetenv("STNOUPGRADE")
cfg, err := Load("testdata/overridenvalues.xml", device1) cfg, err := load("testdata/overridenvalues.xml", device1)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -270,7 +271,7 @@ func TestDeviceAddressesDynamic(t *testing.T) {
}, },
} }
cfg, err := Load("testdata/deviceaddressesdynamic.xml", device4) cfg, err := load("testdata/deviceaddressesdynamic.xml", device4)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -319,7 +320,7 @@ func TestDeviceCompression(t *testing.T) {
}, },
} }
cfg, err := Load("testdata/devicecompression.xml", device4) cfg, err := load("testdata/devicecompression.xml", device4)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -365,7 +366,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
}, },
} }
cfg, err := Load("testdata/deviceaddressesstatic.xml", device4) cfg, err := load("testdata/deviceaddressesstatic.xml", device4)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -377,7 +378,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
} }
func TestVersioningConfig(t *testing.T) { func TestVersioningConfig(t *testing.T) {
cfg, err := Load("testdata/versioningconfig.xml", device4) cfg, err := load("testdata/versioningconfig.xml", device4)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -404,7 +405,7 @@ func TestIssue1262(t *testing.T) {
t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux") t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux")
} }
cfg, err := Load("testdata/issue-1262.xml", device4) cfg, err := load("testdata/issue-1262.xml", device4)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -418,7 +419,7 @@ func TestIssue1262(t *testing.T) {
} }
func TestIssue1750(t *testing.T) { func TestIssue1750(t *testing.T) {
cfg, err := Load("testdata/issue-1750.xml", device4) cfg, err := load("testdata/issue-1750.xml", device4)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -520,7 +521,7 @@ func TestNewSaveLoad(t *testing.T) {
} }
intCfg := New(device1) intCfg := New(device1)
cfg := Wrap(path, intCfg) cfg := wrap(path, intCfg)
// To make the equality pass later // To make the equality pass later
cfg.(*wrapper).cfg.XMLName.Local = "configuration" cfg.(*wrapper).cfg.XMLName.Local = "configuration"
@ -537,7 +538,7 @@ func TestNewSaveLoad(t *testing.T) {
t.Error(path, "does not exist") t.Error(path, "does not exist")
} }
cfg2, err := Load(path, device1) cfg2, err := load(path, device1)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -564,7 +565,7 @@ func TestPrepare(t *testing.T) {
} }
func TestCopy(t *testing.T) { func TestCopy(t *testing.T) {
wrapper, err := Load("testdata/example.xml", device1) wrapper, err := load("testdata/example.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -603,7 +604,7 @@ func TestCopy(t *testing.T) {
} }
func TestPullOrder(t *testing.T) { func TestPullOrder(t *testing.T) {
wrapper, err := Load("testdata/pullorder.xml", device1) wrapper, err := load("testdata/pullorder.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -643,7 +644,7 @@ func TestPullOrder(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wrapper = Wrap("testdata/pullorder.xml", cfg) wrapper = wrap("testdata/pullorder.xml", cfg)
folders = wrapper.Folders() folders = wrapper.Folders()
for _, tc := range expected { for _, tc := range expected {
@ -654,7 +655,7 @@ func TestPullOrder(t *testing.T) {
} }
func TestLargeRescanInterval(t *testing.T) { func TestLargeRescanInterval(t *testing.T) {
wrapper, err := Load("testdata/largeinterval.xml", device1) wrapper, err := load("testdata/largeinterval.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -692,7 +693,7 @@ func TestGUIConfigURL(t *testing.T) {
func TestDuplicateDevices(t *testing.T) { func TestDuplicateDevices(t *testing.T) {
// Duplicate devices should be removed // Duplicate devices should be removed
wrapper, err := Load("testdata/dupdevices.xml", device1) wrapper, err := load("testdata/dupdevices.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -710,7 +711,7 @@ func TestDuplicateDevices(t *testing.T) {
func TestDuplicateFolders(t *testing.T) { func TestDuplicateFolders(t *testing.T) {
// Duplicate folders are a loading error // Duplicate folders are a loading error
_, err := Load("testdata/dupfolders.xml", device1) _, err := load("testdata/dupfolders.xml", device1)
if err == nil || !strings.Contains(err.Error(), errFolderIDDuplicate.Error()) { if err == nil || !strings.Contains(err.Error(), errFolderIDDuplicate.Error()) {
t.Fatal(`Expected error to mention "duplicate folder ID":`, err) t.Fatal(`Expected error to mention "duplicate folder ID":`, err)
} }
@ -721,7 +722,7 @@ func TestEmptyFolderPaths(t *testing.T) {
// get messed up by the prepare steps (e.g., become the current dir or // get messed up by the prepare steps (e.g., become the current dir or
// get a slash added so that it becomes the root directory or similar). // get a slash added so that it becomes the root directory or similar).
_, err := Load("testdata/nopath.xml", device1) _, err := load("testdata/nopath.xml", device1)
if err == nil || !strings.Contains(err.Error(), errFolderPathEmpty.Error()) { if err == nil || !strings.Contains(err.Error(), errFolderPathEmpty.Error()) {
t.Fatal("Expected error due to empty folder path, got", err) t.Fatal("Expected error due to empty folder path, got", err)
} }
@ -790,7 +791,7 @@ func TestIgnoredDevices(t *testing.T) {
// Verify that ignored devices that are also present in the // Verify that ignored devices that are also present in the
// configuration are not in fact ignored. // configuration are not in fact ignored.
wrapper, err := Load("testdata/ignoreddevices.xml", device1) wrapper, err := load("testdata/ignoreddevices.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -808,7 +809,7 @@ func TestIgnoredFolders(t *testing.T) {
// configuration are not in fact ignored. // configuration are not in fact ignored.
// Also, verify that folders that are shared with a device are not ignored. // Also, verify that folders that are shared with a device are not ignored.
wrapper, err := Load("testdata/ignoredfolders.xml", device1) wrapper, err := load("testdata/ignoredfolders.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -844,7 +845,7 @@ func TestIgnoredFolders(t *testing.T) {
func TestGetDevice(t *testing.T) { func TestGetDevice(t *testing.T) {
// Verify that the Device() call does the right thing // Verify that the Device() call does the right thing
wrapper, err := Load("testdata/ignoreddevices.xml", device1) wrapper, err := load("testdata/ignoreddevices.xml", device1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -871,7 +872,7 @@ func TestGetDevice(t *testing.T) {
} }
func TestSharesRemovedOnDeviceRemoval(t *testing.T) { func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
wrapper, err := Load("testdata/example.xml", device1) wrapper, err := load("testdata/example.xml", device1)
if err != nil { if err != nil {
t.Errorf("Failed: %s", err) t.Errorf("Failed: %s", err)
} }
@ -956,7 +957,7 @@ func TestIssue4219(t *testing.T) {
t.Errorf("There should be three ignored folders, not %d", ignoredFolders) t.Errorf("There should be three ignored folders, not %d", ignoredFolders)
} }
w := Wrap("/tmp/cfg", cfg) w := wrap("/tmp/cfg", cfg)
if !w.IgnoredFolder(device2, "t1") { if !w.IgnoredFolder(device2, "t1") {
t.Error("Folder device2 t1 should be ignored") t.Error("Folder device2 t1 should be ignored")
} }
@ -1145,3 +1146,11 @@ func defaultConfigAsMap() map[string]interface{} {
} }
return tmp return tmp
} }
func load(path string, myID protocol.DeviceID) (Wrapper, error) {
return Load(path, myID, events.NoopLogger)
}
func wrap(path string, cfg Configuration) Wrapper {
return Wrap(path, cfg, events.NoopLogger)
}

View File

@ -98,6 +98,7 @@ type Wrapper interface {
type wrapper struct { type wrapper struct {
cfg Configuration cfg Configuration
path string path string
evLogger events.Logger
deviceMap map[protocol.DeviceID]DeviceConfiguration deviceMap map[protocol.DeviceID]DeviceConfiguration
folderMap map[string]FolderConfiguration folderMap map[string]FolderConfiguration
@ -133,10 +134,11 @@ func (w *wrapper) StunServers() []string {
// Wrap wraps an existing Configuration structure and ties it to a file on // Wrap wraps an existing Configuration structure and ties it to a file on
// disk. // disk.
func Wrap(path string, cfg Configuration) Wrapper { func Wrap(path string, cfg Configuration, evLogger events.Logger) Wrapper {
w := &wrapper{ w := &wrapper{
cfg: cfg, cfg: cfg,
path: path, path: path,
evLogger: evLogger,
mut: sync.NewMutex(), mut: sync.NewMutex(),
} }
return w return w
@ -144,7 +146,7 @@ func Wrap(path string, cfg Configuration) Wrapper {
// Load loads an existing file on disk and returns a new configuration // Load loads an existing file on disk and returns a new configuration
// wrapper. // wrapper.
func Load(path string, myID protocol.DeviceID) (Wrapper, error) { func Load(path string, myID protocol.DeviceID, evLogger events.Logger) (Wrapper, error) {
fd, err := os.Open(path) fd, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -156,7 +158,7 @@ func Load(path string, myID protocol.DeviceID) (Wrapper, error) {
return nil, err return nil, err
} }
return Wrap(path, cfg), nil return Wrap(path, cfg, evLogger), nil
} }
func (w *wrapper) ConfigPath() string { func (w *wrapper) ConfigPath() string {
@ -450,7 +452,7 @@ func (w *wrapper) Save() error {
return err return err
} }
events.Default.Log(events.ConfigSaved, w.cfg) w.evLogger.Log(events.ConfigSaved, w.cfg)
return nil return nil
} }

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
) )
func TestIsLANHost(t *testing.T) { func TestIsLANHost(t *testing.T) {
@ -35,7 +36,7 @@ func TestIsLANHost(t *testing.T) {
Options: config.OptionsConfiguration{ Options: config.OptionsConfiguration{
AlwaysLocalNets: []string{"10.20.30.0/24"}, AlwaysLocalNets: []string{"10.20.30.0/24"},
}, },
}) }, events.NoopLogger)
s := &service{cfg: cfg} s := &service{cfg: cfg}
for _, tc := range cases { for _, tc := range cases {

View File

@ -8,6 +8,7 @@ package connections
import ( import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/time/rate" "golang.org/x/time/rate"
"math/rand" "math/rand"
@ -25,7 +26,7 @@ func init() {
} }
func initConfig() config.Wrapper { func initConfig() config.Wrapper {
cfg := config.Wrap("/dev/null", config.New(device1)) cfg := config.Wrap("/dev/null", config.New(device1), events.NoopLogger)
dev1Conf = config.NewDeviceConfiguration(device1, "device1") dev1Conf = config.NewDeviceConfiguration(device1, "device1")
dev2Conf = config.NewDeviceConfiguration(device2, "device2") dev2Conf = config.NewDeviceConfiguration(device2, "device2")
dev3Conf = config.NewDeviceConfiguration(device3, "device3") dev3Conf = config.NewDeviceConfiguration(device3, "device3")

View File

@ -120,6 +120,7 @@ type service struct {
limiter *limiter limiter *limiter
natService *nat.Service natService *nat.Service
natServiceToken *suture.ServiceToken natServiceToken *suture.ServiceToken
evLogger events.Logger
listenersMut sync.RWMutex listenersMut sync.RWMutex
listeners map[string]genericListener listeners map[string]genericListener
@ -130,7 +131,7 @@ type service struct {
connectionStatus map[string]ConnectionStatusEntry // address -> latest error/status connectionStatus map[string]ConnectionStatusEntry // address -> latest error/status
} }
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string) Service { func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger) Service {
service := &service{ service := &service{
Supervisor: suture.New("connections.Service", suture.Spec{ Supervisor: suture.New("connections.Service", suture.Spec{
Log: func(line string) { Log: func(line string) {
@ -148,6 +149,7 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
tlsDefaultCommonName: tlsDefaultCommonName, tlsDefaultCommonName: tlsDefaultCommonName,
limiter: newLimiter(cfg), limiter: newLimiter(cfg),
natService: nat.NewService(myID, cfg), natService: nat.NewService(myID, cfg),
evLogger: evLogger,
listenersMut: sync.NewRWMutex(), listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener), listeners: make(map[string]genericListener),
@ -552,7 +554,7 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
} }
func (s *service) logListenAddressesChangedEvent(l genericListener) { func (s *service) logListenAddressesChangedEvent(l genericListener) {
events.Default.Log(events.ListenAddressesChanged, map[string]interface{}{ s.evLogger.Log(events.ListenAddressesChanged, map[string]interface{}{
"address": l.URI(), "address": l.URI(),
"lan": l.LANAddresses(), "lan": l.LANAddresses(),
"wan": l.WANAddresses(), "wan": l.WANAddresses(),
@ -579,7 +581,7 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
s.listenersMut.Lock() s.listenersMut.Lock()
seen := make(map[string]struct{}) seen := make(map[string]struct{})
for _, addr := range config.Wrap("", to).ListenAddresses() { for _, addr := range config.Wrap("", to, s.evLogger).ListenAddresses() {
if addr == "" { if addr == "" {
// We can get an empty address if there is an empty listener // We can get an empty address if there is an empty listener
// element in the config, indicating no listeners should be // element in the config, indicating no listeners should be

View File

@ -35,6 +35,7 @@ type globalClient struct {
queryClient httpClient queryClient httpClient
noAnnounce bool noAnnounce bool
noLookup bool noLookup bool
evLogger events.Logger
errorHolder errorHolder
} }
@ -70,7 +71,7 @@ func (e lookupError) CacheFor() time.Duration {
return e.cacheFor return e.cacheFor
} }
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister) (FinderService, error) { func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLogger events.Logger) (FinderService, error) {
server, opts, err := parseOptions(server) server, opts, err := parseOptions(server)
if err != nil { if err != nil {
return nil, err return nil, err
@ -125,6 +126,7 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister) (Fin
queryClient: queryClient, queryClient: queryClient,
noAnnounce: opts.noAnnounce, noAnnounce: opts.noAnnounce,
noLookup: opts.noLookup, noLookup: opts.noLookup,
evLogger: evLogger,
} }
cl.Service = util.AsService(cl.serve) cl.Service = util.AsService(cl.serve)
if !opts.noAnnounce { if !opts.noAnnounce {
@ -197,8 +199,8 @@ func (c *globalClient) serve(stop chan struct{}) {
timer := time.NewTimer(0) timer := time.NewTimer(0)
defer timer.Stop() defer timer.Stop()
eventSub := events.Default.Subscribe(events.ListenAddressesChanged) eventSub := c.evLogger.Subscribe(events.ListenAddressesChanged)
defer events.Default.Unsubscribe(eventSub) defer eventSub.Unsubscribe()
for { for {
select { select {

View File

@ -15,6 +15,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/tlsutil"
) )
@ -54,15 +55,15 @@ func TestGlobalOverHTTP(t *testing.T) {
// is only allowed in combination with the "insecure" and "noannounce" // is only allowed in combination with the "insecure" and "noannounce"
// parameters. // parameters.
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil); err == nil { if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without insecure and noannounce") t.Fatal("http is not allowed without insecure and noannounce")
} }
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil); err == nil { if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without noannounce") t.Fatal("http is not allowed without noannounce")
} }
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil); err == nil { if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without insecure") t.Fatal("http is not allowed without insecure")
} }
@ -193,7 +194,7 @@ func TestGlobalAnnounce(t *testing.T) {
go func() { _ = http.Serve(list, mux) }() go func() { _ = http.Serve(list, mux) }()
url := "https://" + list.Addr().String() + "?insecure" url := "https://" + list.Addr().String() + "?insecure"
disco, err := NewGlobal(url, cert, new(fakeAddressLister)) disco, err := NewGlobal(url, cert, new(fakeAddressLister), events.NoopLogger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -217,7 +218,7 @@ func TestGlobalAnnounce(t *testing.T) {
} }
func testLookup(url string) ([]string, error) { func testLookup(url string) ([]string, error) {
disco, err := NewGlobal(url, tls.Certificate{}, nil) disco, err := NewGlobal(url, tls.Certificate{}, nil, events.NoopLogger)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,6 +30,7 @@ type localClient struct {
myID protocol.DeviceID myID protocol.DeviceID
addrList AddressLister addrList AddressLister
name string name string
evLogger events.Logger
beacon beacon.Interface beacon beacon.Interface
localBcastStart time.Time localBcastStart time.Time
@ -46,13 +47,14 @@ const (
v13Magic = uint32(0x7D79BC40) // previous version v13Magic = uint32(0x7D79BC40) // previous version
) )
func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) { func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister, evLogger events.Logger) (FinderService, error) {
c := &localClient{ c := &localClient{
Supervisor: suture.New("local", suture.Spec{ Supervisor: suture.New("local", suture.Spec{
PassThroughPanics: true, PassThroughPanics: true,
}), }),
myID: id, myID: id,
addrList: addrList, addrList: addrList,
evLogger: evLogger,
localBcastTick: time.NewTicker(BroadcastInterval).C, localBcastTick: time.NewTicker(BroadcastInterval).C,
forcedBcastTick: make(chan time.Time), forcedBcastTick: make(chan time.Time),
localBcastStart: time.Now(), localBcastStart: time.Now(),
@ -272,7 +274,7 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
}) })
if isNewDevice { if isNewDevice {
events.Default.Log(events.DeviceDiscovered, map[string]interface{}{ c.evLogger.Log(events.DeviceDiscovered, map[string]interface{}{
"device": device.ID.String(), "device": device.ID.String(),
"addrs": validAddresses, "addrs": validAddresses,
}) })

View File

@ -11,11 +11,12 @@ import (
"net" "net"
"testing" "testing"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
) )
func TestLocalInstanceID(t *testing.T) { func TestLocalInstanceID(t *testing.T) {
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}) c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}, events.NoopLogger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -38,7 +39,7 @@ func TestLocalInstanceID(t *testing.T) {
} }
func TestLocalInstanceIDShouldTriggerNew(t *testing.T) { func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}) c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}, events.NoopLogger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -10,11 +10,11 @@ import (
"os" "os"
"strings" "strings"
"github.com/syncthing/syncthing/lib/logger" liblogger "github.com/syncthing/syncthing/lib/logger"
) )
var ( var (
dl = logger.DefaultLogger.NewFacility("events", "Event generation and logging") dl = liblogger.DefaultLogger.NewFacility("events", "Event generation and logging")
) )
func init() { func init() {

View File

@ -13,7 +13,10 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/thejerf/suture"
"github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
) )
type EventType int type EventType int
@ -51,7 +54,10 @@ const (
AllEvents = (1 << iota) - 1 AllEvents = (1 << iota) - 1
) )
var runningTests = false var (
runningTests = false
errNoop = errors.New("method of a noop object called")
)
const eventLogTimeout = 15 * time.Millisecond const eventLogTimeout = 15 * time.Millisecond
@ -199,13 +205,21 @@ func UnmarshalEventType(s string) EventType {
const BufferSize = 64 const BufferSize = 64
type Logger struct { type Logger interface {
subs []*Subscription suture.Service
Log(t EventType, data interface{})
Subscribe(mask EventType) Subscription
}
type logger struct {
suture.Service
subs []*subscription
nextSubscriptionIDs []int nextSubscriptionIDs []int
nextGlobalID int nextGlobalID int
timeout *time.Timer timeout *time.Timer
events chan Event events chan Event
funcs chan func() funcs chan func()
toUnsubscribe chan *subscription
stop chan struct{} stop chan struct{}
} }
@ -219,19 +233,17 @@ type Event struct {
Data interface{} `json:"data"` Data interface{} `json:"data"`
} }
type Subscription struct { type Subscription interface {
mask EventType C() <-chan Event
events chan Event Poll(timeout time.Duration) (Event, error)
timeout *time.Timer Unsubscribe()
} }
var Default = NewLogger() type subscription struct {
mask EventType
func init() { events chan Event
// The default logger never stops. To ensure this we nil out the stop toUnsubscribe chan *subscription
// channel so any attempt to stop it will panic. timeout *time.Timer
Default.stop = nil
go Default.Serve()
} }
var ( var (
@ -239,13 +251,14 @@ var (
ErrClosed = errors.New("closed") ErrClosed = errors.New("closed")
) )
func NewLogger() *Logger { func NewLogger() Logger {
l := &Logger{ l := &logger{
timeout: time.NewTimer(time.Second), timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize), events: make(chan Event, BufferSize),
funcs: make(chan func()), funcs: make(chan func()),
stop: make(chan struct{}), toUnsubscribe: make(chan *subscription),
} }
l.Service = util.AsService(l.serve)
// Make sure the timer is in the stopped state and hasn't fired anything // Make sure the timer is in the stopped state and hasn't fired anything
// into the channel. // into the channel.
if !l.timeout.Stop() { if !l.timeout.Stop() {
@ -254,7 +267,7 @@ func NewLogger() *Logger {
return l return l
} }
func (l *Logger) Serve() { func (l *logger) serve(stop chan struct{}) {
loop: loop:
for { for {
select { select {
@ -263,10 +276,13 @@ loop:
l.sendEvent(e) l.sendEvent(e)
case fn := <-l.funcs: case fn := <-l.funcs:
// Subscriptions etc are handled here. // Subscriptions are handled here.
fn() fn()
case <-l.stop: case s := <-l.toUnsubscribe:
l.unsubscribe(s)
case <-stop:
break loop break loop
} }
} }
@ -279,11 +295,7 @@ loop:
} }
} }
func (l *Logger) Stop() { func (l *logger) Log(t EventType, data interface{}) {
close(l.stop)
}
func (l *Logger) Log(t EventType, data interface{}) {
l.events <- Event{ l.events <- Event{
Time: time.Now(), Time: time.Now(),
Type: t, Type: t,
@ -292,7 +304,7 @@ func (l *Logger) Log(t EventType, data interface{}) {
} }
} }
func (l *Logger) sendEvent(e Event) { func (l *logger) sendEvent(e Event) {
l.nextGlobalID++ l.nextGlobalID++
dl.Debugln("log", l.nextGlobalID, e.Type, e.Data) dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)
@ -323,14 +335,15 @@ func (l *Logger) sendEvent(e Event) {
} }
} }
func (l *Logger) Subscribe(mask EventType) *Subscription { func (l *logger) Subscribe(mask EventType) Subscription {
res := make(chan *Subscription) res := make(chan Subscription)
l.funcs <- func() { l.funcs <- func() {
dl.Debugln("subscribe", mask) dl.Debugln("subscribe", mask)
s := &Subscription{ s := &subscription{
mask: mask, mask: mask,
events: make(chan Event, BufferSize), events: make(chan Event, BufferSize),
toUnsubscribe: l.toUnsubscribe,
timeout: time.NewTimer(0), timeout: time.NewTimer(0),
} }
@ -355,9 +368,8 @@ func (l *Logger) Subscribe(mask EventType) *Subscription {
return <-res return <-res
} }
func (l *Logger) Unsubscribe(s *Subscription) { func (l *logger) unsubscribe(s *subscription) {
l.funcs <- func() { dl.Debugln("unsubscribe", s.mask)
dl.Debugln("unsubscribe")
for i, ss := range l.subs { for i, ss := range l.subs {
if s == ss { if s == ss {
last := len(l.subs) - 1 last := len(l.subs) - 1
@ -375,12 +387,11 @@ func (l *Logger) Unsubscribe(s *Subscription) {
} }
close(s.events) close(s.events)
} }
}
// Poll returns an event from the subscription or an error if the poll times // Poll returns an event from the subscription or an error if the poll times
// out of the event channel is closed. Poll should not be called concurrently // out of the event channel is closed. Poll should not be called concurrently
// from multiple goroutines for a single subscription. // from multiple goroutines for a single subscription.
func (s *Subscription) Poll(timeout time.Duration) (Event, error) { func (s *subscription) Poll(timeout time.Duration) (Event, error) {
dl.Debugln("poll", timeout) dl.Debugln("poll", timeout)
s.timeout.Reset(timeout) s.timeout.Reset(timeout)
@ -409,12 +420,16 @@ func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
} }
} }
func (s *Subscription) C() <-chan Event { func (s *subscription) C() <-chan Event {
return s.events return s.events
} }
func (s *subscription) Unsubscribe() {
s.toUnsubscribe <- s
}
type bufferedSubscription struct { type bufferedSubscription struct {
sub *Subscription sub Subscription
buf []Event buf []Event
next int next int
cur int // Current SubscriptionID cur int // Current SubscriptionID
@ -426,7 +441,7 @@ type BufferedSubscription interface {
Since(id int, into []Event, timeout time.Duration) []Event Since(id int, into []Event, timeout time.Duration) []Event
} }
func NewBufferedSubscription(s *Subscription, size int) BufferedSubscription { func NewBufferedSubscription(s Subscription, size int) BufferedSubscription {
bs := &bufferedSubscription{ bs := &bufferedSubscription{
sub: s, sub: s,
buf: make([]Event, size), buf: make([]Event, size),
@ -489,3 +504,29 @@ func Error(err error) *string {
str := err.Error() str := err.Error()
return &str return &str
} }
type noopLogger struct{}
var NoopLogger Logger = &noopLogger{}
func (*noopLogger) Serve() {}
func (*noopLogger) Stop() {}
func (*noopLogger) Log(t EventType, data interface{}) {}
func (*noopLogger) Subscribe(mask EventType) Subscription {
return &noopSubscription{}
}
type noopSubscription struct{}
func (*noopSubscription) C() <-chan Event {
return nil
}
func (*noopSubscription) Poll(timeout time.Duration) (Event, error) {
return Event{}, errNoop
}
func (*noopSubscription) Unsubscribe() {}

View File

@ -33,7 +33,7 @@ func TestSubscriber(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer s.Unsubscribe()
if s == nil { if s == nil {
t.Fatal("Unexpected nil Subscription") t.Fatal("Unexpected nil Subscription")
} }
@ -45,7 +45,7 @@ func TestTimeout(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer s.Unsubscribe()
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != ErrTimeout { if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err) t.Fatal("Unexpected non-Timeout error:", err)
@ -59,7 +59,7 @@ func TestEventBeforeSubscribe(t *testing.T) {
l.Log(DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
s := l.Subscribe(0) s := l.Subscribe(0)
defer l.Unsubscribe(s) defer s.Unsubscribe()
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
if err != ErrTimeout { if err != ErrTimeout {
@ -73,7 +73,7 @@ func TestEventAfterSubscribe(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
l.Log(DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
ev, err := s.Poll(timeout) ev, err := s.Poll(timeout)
@ -100,7 +100,7 @@ func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(DeviceDisconnected) s := l.Subscribe(DeviceDisconnected)
defer l.Unsubscribe(s) defer s.Unsubscribe()
l.Log(DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_, err := s.Poll(timeout) _, err := s.Poll(timeout)
@ -115,7 +115,7 @@ func TestBufferOverflow(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
// The first BufferSize events will be logged pretty much // The first BufferSize events will be logged pretty much
// instantaneously. The next BufferSize events will each block for up to // instantaneously. The next BufferSize events will each block for up to
@ -147,7 +147,7 @@ func TestUnsubscribe(t *testing.T) {
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
l.Unsubscribe(s) s.Unsubscribe()
l.Log(DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
_, err = s.Poll(timeout) _, err = s.Poll(timeout)
@ -162,7 +162,7 @@ func TestGlobalIDs(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
l.Log(DeviceConnected, "foo") l.Log(DeviceConnected, "foo")
l.Subscribe(AllEvents) l.Subscribe(AllEvents)
l.Log(DeviceConnected, "bar") l.Log(DeviceConnected, "bar")
@ -194,7 +194,7 @@ func TestSubscriptionIDs(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(DeviceConnected) s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s) defer s.Unsubscribe()
l.Log(DeviceDisconnected, "a") l.Log(DeviceDisconnected, "a")
l.Log(DeviceConnected, "b") l.Log(DeviceConnected, "b")
@ -236,7 +236,7 @@ func TestBufferedSub(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize) bs := NewBufferedSubscription(s, 10*BufferSize)
go func() { go func() {
@ -267,7 +267,7 @@ func BenchmarkBufferedSub(b *testing.B) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
bufferSize := BufferSize bufferSize := BufferSize
bs := NewBufferedSubscription(s, bufferSize) bs := NewBufferedSubscription(s, bufferSize)
@ -323,7 +323,7 @@ func TestSinceUsesSubscriptionId(t *testing.T) {
go l.Serve() go l.Serve()
s := l.Subscribe(DeviceConnected) s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s) defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize) bs := NewBufferedSubscription(s, 10*BufferSize)
l.Log(DeviceConnected, "a") // SubscriptionID = 1 l.Log(DeviceConnected, "a") // SubscriptionID = 1
@ -390,7 +390,7 @@ func TestUnsubscribeContention(t *testing.T) {
defer listenerWg.Done() defer listenerWg.Done()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
for { for {
select { select {
@ -449,7 +449,7 @@ func BenchmarkLogEvent(b *testing.B) {
go l.Serve() go l.Serve()
s := l.Subscribe(AllEvents) s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s) defer s.Unsubscribe()
NewBufferedSubscription(s, 1) // runs in the background NewBufferedSubscription(s, 1) // runs in the background
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@ -77,11 +77,11 @@ type puller interface {
pull() bool // true when successfull and should not be retried pull() bool // true when successfull and should not be retried
} }
func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration) folder { func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger) folder {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return folder{ return folder{
stateTracker: newStateTracker(cfg.ID), stateTracker: newStateTracker(cfg.ID, evLogger),
FolderConfiguration: cfg, FolderConfiguration: cfg,
FolderStatisticsReference: stats.NewFolderStatisticsReference(model.db, cfg.ID), FolderStatisticsReference: stats.NewFolderStatisticsReference(model.db, cfg.ID),
@ -630,7 +630,7 @@ func (f *folder) monitorWatch(ctx context.Context) {
failTimer.Reset(time.Minute) failTimer.Reset(time.Minute)
continue continue
} }
watchaggregator.Aggregate(eventChan, f.watchChan, f.FolderConfiguration, f.model.cfg, aggrCtx) watchaggregator.Aggregate(eventChan, f.watchChan, f.FolderConfiguration, f.model.cfg, f.evLogger, aggrCtx)
l.Debugln("Started filesystem watcher for folder", f.Description()) l.Debugln("Started filesystem watcher for folder", f.Description())
case err = <-errChan: case err = <-errChan:
f.setWatchError(err) f.setWatchError(err)
@ -669,7 +669,7 @@ func (f *folder) setWatchError(err error) {
if err != nil { if err != nil {
data["to"] = err.Error() data["to"] = err.Error()
} }
events.Default.Log(events.FolderWatchStateChanged, data) f.evLogger.Log(events.FolderWatchStateChanged, data)
} }
if err == nil { if err == nil {
return return
@ -800,7 +800,7 @@ func (f *folder) updateLocals(fs []protocol.FileInfo) {
filenames[i] = file.Name filenames[i] = file.Name
} }
events.Default.Log(events.LocalIndexUpdated, map[string]interface{}{ f.evLogger.Log(events.LocalIndexUpdated, map[string]interface{}{
"folder": f.ID, "folder": f.ID,
"items": len(fs), "items": len(fs),
"filenames": filenames, "filenames": filenames,
@ -839,7 +839,7 @@ func (f *folder) emitDiskChangeEvents(fs []protocol.FileInfo, typeOfEvent events
} }
// Two different events can be fired here based on what EventType is passed into function // Two different events can be fired here based on what EventType is passed into function
events.Default.Log(typeOfEvent, map[string]string{ f.evLogger.Log(typeOfEvent, map[string]string{
"folder": f.ID, "folder": f.ID,
"folderID": f.ID, // incorrect, deprecated, kept for historical compliance "folderID": f.ID, // incorrect, deprecated, kept for historical compliance
"label": f.Label, "label": f.Label,

View File

@ -12,6 +12,7 @@ import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
@ -56,8 +57,8 @@ type receiveOnlyFolder struct {
*sendReceiveFolder *sendReceiveFolder
} }
func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger) service {
sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, fs).(*sendReceiveFolder) sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, fs, evLogger).(*sendReceiveFolder)
sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
return &receiveOnlyFolder{sr} return &receiveOnlyFolder{sr}
} }

View File

@ -321,6 +321,7 @@ func setupROFolder() (*model, *sendOnlyFolder) {
f := &sendOnlyFolder{ f := &sendOnlyFolder{
folder: folder{ folder: folder{
stateTracker: newStateTracker(fcfg.ID, m.evLogger),
fset: m.folderFiles[fcfg.ID], fset: m.folderFiles[fcfg.ID],
FolderConfiguration: fcfg, FolderConfiguration: fcfg,
}, },

View File

@ -9,6 +9,7 @@ package model
import ( import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
@ -24,9 +25,9 @@ type sendOnlyFolder struct {
folder folder
} }
func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service { func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem, evLogger events.Logger) service {
f := &sendOnlyFolder{ f := &sendOnlyFolder{
folder: newFolder(model, fset, ignores, cfg), folder: newFolder(model, fset, ignores, cfg, evLogger),
} }
f.folder.puller = f f.folder.puller = f
f.folder.Service = util.AsService(f.serve) f.folder.Service = util.AsService(f.serve)

View File

@ -108,9 +108,9 @@ type sendReceiveFolder struct {
pullErrorsMut sync.Mutex pullErrorsMut sync.Mutex
} }
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger) service {
f := &sendReceiveFolder{ f := &sendReceiveFolder{
folder: newFolder(model, fset, ignores, cfg), folder: newFolder(model, fset, ignores, cfg, evLogger),
fs: fs, fs: fs,
versioner: ver, versioner: ver,
queue: newJobQueue(), queue: newJobQueue(),
@ -211,7 +211,7 @@ func (f *sendReceiveFolder) pull() bool {
// errors preventing us. Flag this with a warning and // errors preventing us. Flag this with a warning and
// wait a bit longer before retrying. // wait a bit longer before retrying.
if errors := f.Errors(); len(errors) > 0 { if errors := f.Errors(); len(errors) > 0 {
events.Default.Log(events.FolderErrors, map[string]interface{}{ f.evLogger.Log(events.FolderErrors, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"errors": errors, "errors": errors,
}) })
@ -544,7 +544,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
f.resetPullError(file.Name) f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "dir", "type": "dir",
@ -552,7 +552,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
}) })
defer func() { defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"error": events.Error(err), "error": events.Error(err),
@ -700,7 +700,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan c
f.resetPullError(file.Name) f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "symlink", "type": "symlink",
@ -708,7 +708,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan c
}) })
defer func() { defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"error": events.Error(err), "error": events.Error(err),
@ -782,7 +782,7 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<
// care not declare another err. // care not declare another err.
var err error var err error
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "dir", "type": "dir",
@ -790,7 +790,7 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<
}) })
defer func() { defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"error": events.Error(err), "error": events.Error(err),
@ -822,7 +822,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
f.resetPullError(file.Name) f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "file", "type": "file",
@ -833,7 +833,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
if err != nil { if err != nil {
f.newPullError(file.Name, errors.Wrap(err, "delete file")) f.newPullError(file.Name, errors.Wrap(err, "delete file"))
} }
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"error": events.Error(err), "error": events.Error(err),
@ -897,13 +897,13 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, db
// care not declare another err. // care not declare another err.
var err error var err error
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": source.Name, "item": source.Name,
"type": "file", "type": "file",
"action": "delete", "action": "delete",
}) })
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": target.Name, "item": target.Name,
"type": "file", "type": "file",
@ -911,14 +911,14 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, db
}) })
defer func() { defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": source.Name, "item": source.Name,
"error": events.Error(err), "error": events.Error(err),
"type": "file", "type": "file",
"action": "delete", "action": "delete",
}) })
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": target.Name, "item": target.Name,
"error": events.Error(err), "error": events.Error(err),
@ -1095,7 +1095,7 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
// Shuffle the blocks // Shuffle the blocks
rand.Shuffle(blocks) rand.Shuffle(blocks)
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "file", "type": "file",
@ -1178,7 +1178,7 @@ func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpda
f.resetPullError(file.Name) f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"type": "file", "type": "file",
@ -1186,7 +1186,7 @@ func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpda
}) })
var err error var err error
defer events.Default.Log(events.ItemFinished, map[string]interface{}{ defer f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
"error": events.Error(err), "error": events.Error(err),
@ -1575,7 +1575,7 @@ func (f *sendReceiveFolder) finisherRoutine(in <-chan *sharedPullerState, dbUpda
f.model.progressEmitter.Deregister(state) f.model.progressEmitter.Deregister(state)
events.Default.Log(events.ItemFinished, map[string]interface{}{ f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": state.file.Name, "item": state.file.Name,
"error": events.Error(err), "error": events.Error(err),

View File

@ -96,7 +96,7 @@ func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFol
f := &sendReceiveFolder{ f := &sendReceiveFolder{
folder: folder{ folder: folder{
stateTracker: newStateTracker("default"), stateTracker: newStateTracker("default", model.evLogger),
model: model, model: model,
fset: model.folderFiles[fcfg.ID], fset: model.folderFiles[fcfg.ID],
initialScanFinished: make(chan struct{}), initialScanFinished: make(chan struct{}),
@ -121,6 +121,12 @@ func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFol
return model, f return model, f
} }
func cleanupSRFolder(f *sendReceiveFolder, m *model) {
m.evLogger.Stop()
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}
// Layout of the files: (indexes from the above array) // Layout of the files: (indexes from the above array)
// 12345678 - Required file // 12345678 - Required file
// 02005008 - Existing file (currently in the index) // 02005008 - Existing file (currently in the index)
@ -137,10 +143,7 @@ func TestHandleFile(t *testing.T) {
requiredFile.Blocks = blocks[1:] requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(existingFile) m, f := setupSendReceiveFolder(existingFile)
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
copyChan := make(chan copyBlocksState, 1) copyChan := make(chan copyBlocksState, 1)
dbUpdateChan := make(chan dbUpdateJob, 1) dbUpdateChan := make(chan dbUpdateJob, 1)
@ -183,10 +186,7 @@ func TestHandleFileWithTemp(t *testing.T) {
requiredFile.Blocks = blocks[1:] requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(existingFile) m, f := setupSendReceiveFolder(existingFile)
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
if _, err := prepareTmpFile(f.Filesystem()); err != nil { if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err) t.Fatal(err)
@ -236,10 +236,7 @@ func TestCopierFinder(t *testing.T) {
requiredFile.Name = "file2" requiredFile.Name = "file2"
m, f := setupSendReceiveFolder(existingFile) m, f := setupSendReceiveFolder(existingFile)
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
if _, err := prepareTmpFile(f.Filesystem()); err != nil { if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err) t.Fatal(err)
@ -302,11 +299,8 @@ func TestCopierFinder(t *testing.T) {
func TestWeakHash(t *testing.T) { func TestWeakHash(t *testing.T) {
// Setup the model/pull environment // Setup the model/pull environment
model, fo := setupSendReceiveFolder() model, fo := setupSendReceiveFolder()
defer cleanupSRFolder(fo, model)
ffs := fo.Filesystem() ffs := fo.Filesystem()
defer func() {
os.Remove(model.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
tempFile := fs.TempName("weakhash") tempFile := fs.TempName("weakhash")
var shift int64 = 10 var shift int64 = 10
@ -432,10 +426,7 @@ func TestCopierCleanup(t *testing.T) {
// Create a file // Create a file
file := setupFile("test", []int{0}) file := setupFile("test", []int{0})
m, f := setupSendReceiveFolder(file) m, f := setupSendReceiveFolder(file)
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
file.Blocks = []protocol.BlockInfo{blocks[1]} file.Blocks = []protocol.BlockInfo{blocks[1]}
file.Version = file.Version.Update(myID.Short()) file.Version = file.Version.Update(myID.Short())
@ -468,13 +459,10 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8}) file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
// Set up our evet subscription early // Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished) s := m.evLogger.Subscribe(events.ItemFinished)
// queue.Done should be called by the finisher routine // queue.Done should be called by the finisher routine
f.queue.Push("filex", 0, time.Time{}) f.queue.Push("filex", 0, time.Time{})
@ -558,13 +546,10 @@ func TestDeregisterOnFailInPull(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8}) file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer func() { defer cleanupSRFolder(f, m)
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
// Set up our evet subscription early // Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished) s := m.evLogger.Subscribe(events.ItemFinished)
// queue.Done should be called by the finisher routine // queue.Done should be called by the finisher routine
f.queue.Push("filex", 0, time.Time{}) f.queue.Push("filex", 0, time.Time{})
@ -636,12 +621,9 @@ func TestDeregisterOnFailInPull(t *testing.T) {
func TestIssue3164(t *testing.T) { func TestIssue3164(t *testing.T) {
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem() ffs := f.Filesystem()
tmpDir := ffs.URI() tmpDir := ffs.URI()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(tmpDir)
}()
ignDir := filepath.Join("issue3164", "oktodelete") ignDir := filepath.Join("issue3164", "oktodelete")
subDir := filepath.Join(ignDir, "foobar") subDir := filepath.Join(ignDir, "foobar")
@ -728,11 +710,8 @@ func TestDiffEmpty(t *testing.T) {
// in the db. // in the db.
func TestDeleteIgnorePerms(t *testing.T) { func TestDeleteIgnorePerms(t *testing.T) {
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem() ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
f.IgnorePerms = true f.IgnorePerms = true
name := "deleteIgnorePerms" name := "deleteIgnorePerms"
@ -778,7 +757,7 @@ func TestCopyOwner(t *testing.T) {
// filesystem. // filesystem.
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer os.Remove(m.cfg.ConfigPath()) defer cleanupSRFolder(f, m)
f.folder.FolderConfiguration = config.NewFolderConfiguration(m.id, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner") f.folder.FolderConfiguration = config.NewFolderConfiguration(m.id, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner")
f.folder.FolderConfiguration.CopyOwnershipFromParent = true f.folder.FolderConfiguration.CopyOwnershipFromParent = true
@ -867,11 +846,8 @@ func TestCopyOwner(t *testing.T) {
// is replaced with a directory and versions are conflicting // is replaced with a directory and versions are conflicting
func TestSRConflictReplaceFileByDir(t *testing.T) { func TestSRConflictReplaceFileByDir(t *testing.T) {
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem() ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
name := "foo" name := "foo"
@ -902,11 +878,8 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
// is replaced with a link and versions are conflicting // is replaced with a link and versions are conflicting
func TestSRConflictReplaceFileByLink(t *testing.T) { func TestSRConflictReplaceFileByLink(t *testing.T) {
m, f := setupSendReceiveFolder() m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem() ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
name := "foo" name := "foo"

View File

@ -36,6 +36,7 @@ type folderSummaryService struct {
cfg config.Wrapper cfg config.Wrapper
model Model model Model
id protocol.DeviceID id protocol.DeviceID
evLogger events.Logger
immediate chan string immediate chan string
// For keeping track of folders to recalculate for // For keeping track of folders to recalculate for
@ -47,7 +48,7 @@ type folderSummaryService struct {
lastEventReqMut sync.Mutex lastEventReqMut sync.Mutex
} }
func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID) FolderSummaryService { func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID, evLogger events.Logger) FolderSummaryService {
service := &folderSummaryService{ service := &folderSummaryService{
Supervisor: suture.New("folderSummaryService", suture.Spec{ Supervisor: suture.New("folderSummaryService", suture.Spec{
PassThroughPanics: true, PassThroughPanics: true,
@ -55,6 +56,7 @@ func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID)
cfg: cfg, cfg: cfg,
model: m, model: m,
id: id, id: id,
evLogger: evLogger,
immediate: make(chan string), immediate: make(chan string),
folders: make(map[string]struct{}), folders: make(map[string]struct{}),
foldersMut: sync.NewMutex(), foldersMut: sync.NewMutex(),
@ -144,8 +146,8 @@ func (c *folderSummaryService) OnEventRequest() {
// listenForUpdates subscribes to the event bus and makes note of folders that // listenForUpdates subscribes to the event bus and makes note of folders that
// need their data recalculated. // need their data recalculated.
func (c *folderSummaryService) listenForUpdates(stop chan struct{}) { func (c *folderSummaryService) listenForUpdates(stop chan struct{}) {
sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged | events.DownloadProgress) sub := c.evLogger.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged | events.DownloadProgress)
defer events.Default.Unsubscribe(sub) defer sub.Unsubscribe()
for { for {
// This loop needs to be fast so we don't miss too many events. // This loop needs to be fast so we don't miss too many events.
@ -291,7 +293,7 @@ func (c *folderSummaryService) sendSummary(folder string) {
if err != nil { if err != nil {
return return
} }
events.Default.Log(events.FolderSummary, map[string]interface{}{ c.evLogger.Log(events.FolderSummary, map[string]interface{}{
"folder": folder, "folder": folder,
"summary": data, "summary": data,
}) })
@ -311,6 +313,6 @@ func (c *folderSummaryService) sendSummary(folder string) {
comp := c.model.Completion(devCfg.DeviceID, folder).Map() comp := c.model.Completion(devCfg.DeviceID, folder).Map()
comp["folder"] = folder comp["folder"] = folder
comp["device"] = devCfg.DeviceID.String() comp["device"] = devCfg.DeviceID.String()
events.Default.Log(events.FolderCompletion, comp) c.evLogger.Log(events.FolderCompletion, comp)
} }
} }

View File

@ -42,6 +42,7 @@ func (s folderState) String() string {
type stateTracker struct { type stateTracker struct {
folderID string folderID string
evLogger events.Logger
mut sync.Mutex mut sync.Mutex
current folderState current folderState
@ -49,9 +50,10 @@ type stateTracker struct {
changed time.Time changed time.Time
} }
func newStateTracker(id string) stateTracker { func newStateTracker(id string, evLogger events.Logger) stateTracker {
return stateTracker{ return stateTracker{
folderID: id, folderID: id,
evLogger: evLogger,
mut: sync.NewMutex(), mut: sync.NewMutex(),
} }
} }
@ -83,7 +85,7 @@ func (s *stateTracker) setState(newState folderState) {
s.current = newState s.current = newState
s.changed = time.Now() s.changed = time.Now()
events.Default.Log(events.StateChanged, eventData) s.evLogger.Log(events.StateChanged, eventData)
} }
s.mut.Unlock() s.mut.Unlock()
} }
@ -124,5 +126,5 @@ func (s *stateTracker) setError(err error) {
s.err = err s.err = err
s.changed = time.Now() s.changed = time.Now()
events.Default.Log(events.StateChanged, eventData) s.evLogger.Log(events.StateChanged, eventData)
} }

View File

@ -128,6 +128,7 @@ type model struct {
shortID protocol.ShortID shortID protocol.ShortID
cacheIgnoredFiles bool cacheIgnoredFiles bool
protectedFiles []string protectedFiles []string
evLogger events.Logger
clientName string clientName string
clientVersion string clientVersion string
@ -152,7 +153,7 @@ type model struct {
foldersRunning int32 // for testing only foldersRunning int32 // for testing only
} }
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, fs.Filesystem, events.Logger) service
var ( var (
folderFactories = make(map[config.FolderType]folderFactory) folderFactories = make(map[config.FolderType]folderFactory)
@ -175,7 +176,7 @@ var (
// NewModel creates and starts a new model. The model starts in read-only mode, // 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 // where it sends index information to connected peers and responds to requests
// for file data without altering the local folder in any way. // for file data without altering the local folder in any way.
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) Model { func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger) Model {
m := &model{ m := &model{
Supervisor: suture.New("model", suture.Spec{ Supervisor: suture.New("model", suture.Spec{
Log: func(line string) { Log: func(line string) {
@ -186,11 +187,12 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
cfg: cfg, cfg: cfg,
db: ldb, db: ldb,
finder: db.NewBlockFinder(ldb), finder: db.NewBlockFinder(ldb),
progressEmitter: NewProgressEmitter(cfg), progressEmitter: NewProgressEmitter(cfg, evLogger),
id: id, id: id,
shortID: id.Short(), shortID: id.Short(),
cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles, cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles,
protectedFiles: protectedFiles, protectedFiles: protectedFiles,
evLogger: evLogger,
clientName: clientName, clientName: clientName,
clientVersion: clientVersion, clientVersion: clientVersion,
folderCfgs: make(map[string]config.FolderConfiguration), folderCfgs: make(map[string]config.FolderConfiguration),
@ -310,7 +312,7 @@ func (m *model) startFolderLocked(cfg config.FolderConfiguration) {
ffs.Hide(".stversions") ffs.Hide(".stversions")
ffs.Hide(".stignore") ffs.Hide(".stignore")
p := folderFactory(m, fset, m.folderIgnores[folder], cfg, ver, ffs) p := folderFactory(m, fset, m.folderIgnores[folder], cfg, ver, ffs, m.evLogger)
m.folderRunners[folder] = p m.folderRunners[folder] = p
@ -1023,7 +1025,7 @@ func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
} }
files.Update(deviceID, fs) files.Update(deviceID, fs)
events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{ m.evLogger.Log(events.RemoteIndexUpdated, map[string]interface{}{
"device": deviceID.String(), "device": deviceID.String(),
"folder": folder, "folder": folder,
"items": len(fs), "items": len(fs),
@ -1077,7 +1079,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
} }
m.cfg.AddOrUpdatePendingFolder(folder.ID, folder.Label, deviceID) m.cfg.AddOrUpdatePendingFolder(folder.ID, folder.Label, deviceID)
changed = true changed = true
events.Default.Log(events.FolderRejected, map[string]string{ m.evLogger.Log(events.FolderRejected, map[string]string{
"folder": folder.ID, "folder": folder.ID,
"folderLabel": folder.Label, "folderLabel": folder.Label,
"device": deviceID.String(), "device": deviceID.String(),
@ -1180,6 +1182,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
fset: fs, fset: fs,
prevSequence: startSequence, prevSequence: startSequence,
dropSymlinks: dropSymlinks, dropSymlinks: dropSymlinks,
evLogger: m.evLogger,
} }
is.Service = util.AsService(is.serve) is.Service = util.AsService(is.serve)
// The token isn't tracked as the service stops when the connection // The token isn't tracked as the service stops when the connection
@ -1432,7 +1435,7 @@ func (m *model) Closed(conn protocol.Connection, err error) {
delete(m.closed, device) delete(m.closed, device)
l.Infof("Connection to %s at %s closed: %v", device, conn.Name(), err) l.Infof("Connection to %s at %s closed: %v", device, conn.Name(), err)
events.Default.Log(events.DeviceDisconnected, map[string]string{ m.evLogger.Log(events.DeviceDisconnected, map[string]string{
"id": device.String(), "id": device.String(),
"error": err.Error(), "error": err.Error(),
}) })
@ -1773,7 +1776,7 @@ func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
if !ok { if !ok {
m.cfg.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String()) m.cfg.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String())
_ = m.cfg.Save() // best effort _ = m.cfg.Save() // best effort
events.Default.Log(events.DeviceRejected, map[string]string{ m.evLogger.Log(events.DeviceRejected, map[string]string{
"name": hello.DeviceName, "name": hello.DeviceName,
"device": remoteID.String(), "device": remoteID.String(),
"address": addr.String(), "address": addr.String(),
@ -1859,7 +1862,7 @@ func (m *model) AddConnection(conn connections.Connection, hello protocol.HelloR
event["addr"] = addr.String() event["addr"] = addr.String()
} }
events.Default.Log(events.DeviceConnected, event) m.evLogger.Log(events.DeviceConnected, event)
l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName, conn) l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName, conn)
@ -1894,7 +1897,7 @@ func (m *model) DownloadProgress(device protocol.DeviceID, folder string, update
downloads.Update(folder, updates) downloads.Update(folder, updates)
state := downloads.GetBlockCounts(folder) state := downloads.GetBlockCounts(folder)
events.Default.Log(events.RemoteDownloadProgress, map[string]interface{}{ m.evLogger.Log(events.RemoteDownloadProgress, map[string]interface{}{
"device": device.String(), "device": device.String(),
"folder": folder, "folder": folder,
"state": state, "state": state,
@ -1926,6 +1929,7 @@ type indexSender struct {
fset *db.FileSet fset *db.FileSet
prevSequence int64 prevSequence int64
dropSymlinks bool dropSymlinks bool
evLogger events.Logger
connClosed chan struct{} connClosed chan struct{}
} }
@ -1941,8 +1945,8 @@ func (s *indexSender) serve(stop chan struct{}) {
// Subscribe to LocalIndexUpdated (we have new information to send) and // Subscribe to LocalIndexUpdated (we have new information to send) and
// DeviceDisconnected (it might be us who disconnected, so we should // DeviceDisconnected (it might be us who disconnected, so we should
// exit). // exit).
sub := events.Default.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected) sub := s.evLogger.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected)
defer events.Default.Unsubscribe(sub) defer sub.Unsubscribe()
evChan := sub.C() evChan := sub.C()
ticker := time.NewTicker(time.Minute) ticker := time.NewTicker(time.Minute)
@ -2531,7 +2535,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
if toCfg.Paused { if toCfg.Paused {
eventType = events.FolderPaused eventType = events.FolderPaused
} }
events.Default.Log(eventType, map[string]string{"id": toCfg.ID, "label": toCfg.Label}) m.evLogger.Log(eventType, map[string]string{"id": toCfg.ID, "label": toCfg.Label})
} }
} }
@ -2559,9 +2563,9 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
if toCfg.Paused { if toCfg.Paused {
l.Infoln("Pausing", deviceID) l.Infoln("Pausing", deviceID)
m.closeConn(deviceID, errDevicePaused) m.closeConn(deviceID, errDevicePaused)
events.Default.Log(events.DevicePaused, map[string]string{"device": deviceID.String()}) m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
} else { } else {
events.Default.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()}) m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
} }
} }

View File

@ -110,7 +110,7 @@ func createTmpWrapper(cfg config.Configuration) config.Wrapper {
if err != nil { if err != nil {
panic(err) panic(err)
} }
wrapper := config.Wrap(tmpFile.Name(), cfg) wrapper := config.Wrap(tmpFile.Name(), cfg, events.NoopLogger)
tmpFile.Close() tmpFile.Close()
return wrapper return wrapper
} }
@ -303,7 +303,7 @@ func TestDeviceRename(t *testing.T) {
DeviceID: device1, DeviceID: device1,
}, },
} }
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg) cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg, events.NoopLogger)
db := db.OpenMemory() db := db.OpenMemory()
m := newModel(cfg, myID, "syncthing", "dev", db, nil) m := newModel(cfg, myID, "syncthing", "dev", db, nil)
@ -339,7 +339,7 @@ func TestDeviceRename(t *testing.T) {
t.Errorf("Device name got overwritten") t.Errorf("Device name got overwritten")
} }
cfgw, err := config.Load("testdata/tmpconfig.xml", myID) cfgw, err := config.Load("testdata/tmpconfig.xml", myID, events.NoopLogger)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -3358,12 +3358,12 @@ func TestModTimeWindow(t *testing.T) {
} }
func TestDevicePause(t *testing.T) { func TestDevicePause(t *testing.T) {
sub := events.Default.Subscribe(events.DevicePaused)
defer events.Default.Unsubscribe(sub)
m, _, fcfg := setupModelWithConnection() m, _, fcfg := setupModelWithConnection()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
sub := m.evLogger.Subscribe(events.DevicePaused)
defer sub.Unsubscribe()
m.pmut.RLock() m.pmut.RLock()
closed := m.closed[device1] closed := m.closed[device1]
m.pmut.RUnlock() m.pmut.RUnlock()

View File

@ -29,6 +29,7 @@ type ProgressEmitter struct {
connections map[protocol.DeviceID]protocol.Connection connections map[protocol.DeviceID]protocol.Connection
foldersByConns map[protocol.DeviceID][]string foldersByConns map[protocol.DeviceID][]string
disabled bool disabled bool
evLogger events.Logger
mut sync.Mutex mut sync.Mutex
timer *time.Timer timer *time.Timer
@ -36,13 +37,14 @@ type ProgressEmitter struct {
// NewProgressEmitter creates a new progress emitter which emits // NewProgressEmitter creates a new progress emitter which emits
// DownloadProgress events every interval. // DownloadProgress events every interval.
func NewProgressEmitter(cfg config.Wrapper) *ProgressEmitter { func NewProgressEmitter(cfg config.Wrapper, evLogger events.Logger) *ProgressEmitter {
t := &ProgressEmitter{ t := &ProgressEmitter{
registry: make(map[string]map[string]*sharedPullerState), registry: make(map[string]map[string]*sharedPullerState),
timer: time.NewTimer(time.Millisecond), timer: time.NewTimer(time.Millisecond),
sentDownloadStates: make(map[protocol.DeviceID]*sentDownloadState), sentDownloadStates: make(map[protocol.DeviceID]*sentDownloadState),
connections: make(map[protocol.DeviceID]protocol.Connection), connections: make(map[protocol.DeviceID]protocol.Connection),
foldersByConns: make(map[protocol.DeviceID][]string), foldersByConns: make(map[protocol.DeviceID][]string),
evLogger: evLogger,
mut: sync.NewMutex(), mut: sync.NewMutex(),
} }
t.Service = util.AsService(t.serve) t.Service = util.AsService(t.serve)
@ -107,7 +109,7 @@ func (t *ProgressEmitter) sendDownloadProgressEventLocked() {
output[folder][name] = puller.Progress() output[folder][name] = puller.Progress()
} }
} }
events.Default.Log(events.DownloadProgress, output) t.evLogger.Log(events.DownloadProgress, output)
l.Debugf("progress emitter: emitting %#v", output) l.Debugf("progress emitter: emitting %#v", output)
} }

View File

@ -30,7 +30,7 @@ func caller(skip int) string {
return fmt.Sprintf("%s:%d", filepath.Base(file), line) return fmt.Sprintf("%s:%d", filepath.Base(file), line)
} }
func expectEvent(w *events.Subscription, t *testing.T, size int) { func expectEvent(w events.Subscription, t *testing.T, size int) {
event, err := w.Poll(timeout) event, err := w.Poll(timeout)
if err != nil { if err != nil {
t.Fatal("Unexpected error:", err, "at", caller(1)) t.Fatal("Unexpected error:", err, "at", caller(1))
@ -44,7 +44,7 @@ func expectEvent(w *events.Subscription, t *testing.T, size int) {
} }
} }
func expectTimeout(w *events.Subscription, t *testing.T) { func expectTimeout(w events.Subscription, t *testing.T) {
_, err := w.Poll(timeout) _, err := w.Poll(timeout)
if err != events.ErrTimeout { if err != events.ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err, "at", caller(1)) t.Fatal("Unexpected non-Timeout error:", err, "at", caller(1))
@ -52,7 +52,11 @@ func expectTimeout(w *events.Subscription, t *testing.T) {
} }
func TestProgressEmitter(t *testing.T) { func TestProgressEmitter(t *testing.T) {
w := events.Default.Subscribe(events.DownloadProgress) evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
w := evLogger.Subscribe(events.DownloadProgress)
c := createTmpWrapper(config.Configuration{}) c := createTmpWrapper(config.Configuration{})
defer os.Remove(c.ConfigPath()) defer os.Remove(c.ConfigPath())
@ -60,7 +64,7 @@ func TestProgressEmitter(t *testing.T) {
ProgressUpdateIntervalS: 0, ProgressUpdateIntervalS: 0,
}) })
p := NewProgressEmitter(c) p := NewProgressEmitter(c, evLogger)
go p.Serve() go p.Serve()
p.interval = 0 p.interval = 0
@ -112,7 +116,11 @@ func TestSendDownloadProgressMessages(t *testing.T) {
fc := &fakeConnection{} fc := &fakeConnection{}
p := NewProgressEmitter(c) evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
p := NewProgressEmitter(c, evLogger)
p.temporaryIndexSubscribe(fc, []string{"folder", "folder2"}) p.temporaryIndexSubscribe(fc, []string{"folder", "folder2"})
p.registry["folder"] = make(map[string]*sharedPullerState) p.registry["folder"] = make(map[string]*sharedPullerState)
p.registry["folder2"] = make(map[string]*sharedPullerState) p.registry["folder2"] = make(map[string]*sharedPullerState)

View File

@ -350,8 +350,8 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
} }
fc.mut.Unlock() fc.mut.Unlock()
sub := events.Default.Subscribe(events.FolderErrors) sub := m.evLogger.Subscribe(events.FolderErrors)
defer events.Default.Unsubscribe(sub) defer sub.Unsubscribe()
fc.sendIndexUpdate() fc.sendIndexUpdate()
@ -640,8 +640,8 @@ func TestRequestSymlinkWindows(t *testing.T) {
t.Fatalf("timed out before pull was finished") t.Fatalf("timed out before pull was finished")
} }
sub := events.Default.Subscribe(events.StateChanged | events.LocalIndexUpdated) sub := m.evLogger.Subscribe(events.StateChanged | events.LocalIndexUpdated)
defer events.Default.Unsubscribe(sub) defer sub.Unsubscribe()
m.ScanFolder("default") m.ScanFolder("default")
@ -978,8 +978,8 @@ func TestNeedFolderFiles(t *testing.T) {
tmpDir := tfs.URI() tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir) defer cleanupModelAndRemoveDir(m, tmpDir)
sub := events.Default.Subscribe(events.RemoteIndexUpdated) sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
defer events.Default.Unsubscribe(sub) defer sub.Unsubscribe()
errPreventSync := errors.New("you aren't getting any of this") errPreventSync := errors.New("you aren't getting any of this")
fc.mut.Lock() fc.mut.Lock()

View File

@ -13,6 +13,7 @@ import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
) )
@ -117,12 +118,16 @@ func setupModel(w config.Wrapper) *model {
} }
func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model { func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model {
return NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles).(*model) evLogger := events.NewLogger()
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger).(*model)
go evLogger.Serve()
return m
} }
func cleanupModel(m *model) { func cleanupModel(m *model) {
m.Stop() m.Stop()
m.db.Close() m.db.Close()
m.evLogger.Stop()
os.Remove(m.cfg.ConfigPath()) os.Remove(m.cfg.ConfigPath())
} }

View File

@ -26,6 +26,7 @@ import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/sync"
) )
@ -455,7 +456,7 @@ func (p *Process) eventLoop() {
default: default:
} }
events, err := p.Events(since) evs, err := p.Events(since)
if err != nil { if err != nil {
if time.Since(start) < 5*time.Second { if time.Since(start) < 5*time.Second {
// The API has probably not started yet, lets give it some time. // The API has probably not started yet, lets give it some time.
@ -473,7 +474,7 @@ func (p *Process) eventLoop() {
continue continue
} }
for _, ev := range events { for _, ev := range evs {
if ev.ID != since+1 { if ev.ID != since+1 {
l.Warnln("Event ID jumped", since, "to", ev.ID) l.Warnln("Event ID jumped", since, "to", ev.ID)
} }
@ -493,7 +494,7 @@ func (p *Process) eventLoop() {
p.id = id p.id = id
home := data["home"].(string) home := data["home"].(string)
w, err := config.Load(filepath.Join(home, "config.xml"), protocol.LocalDeviceID) w, err := config.Load(filepath.Join(home, "config.xml"), protocol.LocalDeviceID, events.NoopLogger)
if err != nil { if err != nil {
log.Println("eventLoop: Starting:", err) log.Println("eventLoop: Starting:", err)
continue continue

View File

@ -56,6 +56,8 @@ type Config struct {
LocalFlags uint32 LocalFlags uint32
// Modification time is to be considered unchanged if the difference is lower. // Modification time is to be considered unchanged if the difference is lower.
ModTimeWindow time.Duration ModTimeWindow time.Duration
// Event logger to which the scan progress events are sent
EvLogger events.Logger
} }
type CurrentFiler interface { type CurrentFiler interface {
@ -168,7 +170,7 @@ func (w *walker) walk(ctx context.Context) chan ScanResult {
current := progress.Total() current := progress.Total()
rate := progress.Rate() rate := progress.Rate()
l.Debugf("Walk %s %s current progress %d/%d at %.01f MiB/s (%d%%)", w.Folder, w.Subs, current, total, rate/1024/1024, current*100/total) l.Debugf("Walk %s %s current progress %d/%d at %.01f MiB/s (%d%%)", w.Folder, w.Subs, current, total, rate/1024/1024, current*100/total)
events.Default.Log(events.FolderScanProgress, map[string]interface{}{ w.EvLogger.Log(events.FolderScanProgress, map[string]interface{}{
"folder": w.Folder, "folder": w.Folder,
"current": current, "current": current,
"total": total, "total": total,

View File

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/d4l3k/messagediff" "github.com/d4l3k/messagediff"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/osutil"
@ -66,12 +67,10 @@ func TestWalkSub(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
fchan := Walk(context.TODO(), Config{ cfg := testConfig()
Filesystem: testFs, cfg.Subs = []string{"dir2"}
Subs: []string{"dir2"}, cfg.Matcher = ignores
Matcher: ignores, fchan := Walk(context.TODO(), cfg)
Hashers: 2,
})
var files []protocol.FileInfo var files []protocol.FileInfo
for f := range fchan { for f := range fchan {
if f.Err != nil { if f.Err != nil {
@ -102,11 +101,9 @@ func TestWalk(t *testing.T) {
} }
t.Log(ignores) t.Log(ignores)
fchan := Walk(context.TODO(), Config{ cfg := testConfig()
Filesystem: testFs, cfg.Matcher = ignores
Matcher: ignores, fchan := Walk(context.TODO(), cfg)
Hashers: 2,
})
var tmp []protocol.FileInfo var tmp []protocol.FileInfo
for f := range fchan { for f := range fchan {
@ -466,15 +463,14 @@ func TestWalkReceiveOnly(t *testing.T) {
} }
func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore.Matcher, localFlags uint32) []protocol.FileInfo { func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore.Matcher, localFlags uint32) []protocol.FileInfo {
fchan := Walk(context.TODO(), Config{ cfg := testConfig()
Filesystem: fs, cfg.Filesystem = fs
Subs: []string{dir}, cfg.Subs = []string{dir}
AutoNormalize: true, cfg.AutoNormalize = true
Hashers: 2, cfg.CurrentFiler = cfiler
CurrentFiler: cfiler, cfg.Matcher = matcher
Matcher: matcher, cfg.LocalFlags = localFlags
LocalFlags: localFlags, fchan := Walk(context.TODO(), cfg)
})
var tmp []protocol.FileInfo var tmp []protocol.FileInfo
for f := range fchan { for f := range fchan {
@ -576,11 +572,11 @@ func TestStopWalk(t *testing.T) {
const numHashers = 4 const numHashers = 4
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
fchan := Walk(ctx, Config{ cfg := testConfig()
Filesystem: fs, cfg.Filesystem = fs
Hashers: numHashers, cfg.Hashers = numHashers
ProgressTickIntervalS: -1, // Don't attempt to build the full list of files before starting to scan... cfg.ProgressTickIntervalS = -1 // Don't attempt to build the full list of files before starting to scan...
}) fchan := Walk(ctx, cfg)
// Receive a few entries to make sure the walker is up and running, // Receive a few entries to make sure the walker is up and running,
// scanning both files and dirs. Do some quick sanity tests on the // scanning both files and dirs. Do some quick sanity tests on the
@ -705,21 +701,17 @@ func TestIssue4841(t *testing.T) {
} }
fd.Close() fd.Close()
fchan := Walk(context.TODO(), Config{ cfg := testConfig()
Filesystem: fs, cfg.Filesystem = fs
Subs: nil, cfg.AutoNormalize = true
AutoNormalize: true, cfg.CurrentFiler = fakeCurrentFiler{"foo": {
Hashers: 2,
CurrentFiler: fakeCurrentFiler{
"foo": {
Name: "foo", Name: "foo",
Type: protocol.FileInfoTypeFile, Type: protocol.FileInfoTypeFile,
LocalFlags: protocol.FlagLocalIgnored, LocalFlags: protocol.FlagLocalIgnored,
Version: protocol.Vector{}.Update(1), Version: protocol.Vector{}.Update(1),
}, }}
}, cfg.ShortID = protocol.LocalDeviceID.Short()
ShortID: protocol.LocalDeviceID.Short(), fchan := Walk(context.TODO(), cfg)
})
var files []protocol.FileInfo var files []protocol.FileInfo
for f := range fchan { for f := range fchan {
@ -745,11 +737,9 @@ func TestNotExistingError(t *testing.T) {
t.Fatalf("Lstat returned error %v, while nothing should exist there.", err) t.Fatalf("Lstat returned error %v, while nothing should exist there.", err)
} }
fchan := Walk(context.TODO(), Config{ cfg := testConfig()
Filesystem: testFs, cfg.Subs = []string{sub}
Subs: []string{sub}, fchan := Walk(context.TODO(), cfg)
Hashers: 2,
})
for f := range fchan { for f := range fchan {
t.Fatalf("Expected no result from scan, got %v", f) t.Fatalf("Expected no result from scan, got %v", f)
} }
@ -793,3 +783,13 @@ func (fcf fakeCurrentFiler) CurrentFile(name string) (protocol.FileInfo, bool) {
f, ok := fcf[name] f, ok := fcf[name]
return f, ok return f, ok
} }
func testConfig() Config {
evLogger := events.NewLogger()
go evLogger.Serve()
return Config{
Filesystem: testFs,
Hashers: 2,
EvLogger: evLogger,
}
}

View File

@ -21,13 +21,13 @@ import (
type auditService struct { type auditService struct {
suture.Service suture.Service
w io.Writer // audit destination w io.Writer // audit destination
sub *events.Subscription sub events.Subscription
} }
func newAuditService(w io.Writer) *auditService { func newAuditService(w io.Writer, evLogger events.Logger) *auditService {
s := &auditService{ s := &auditService{
w: w, w: w,
sub: events.Default.Subscribe(events.AllEvents), sub: evLogger.Subscribe(events.AllEvents),
} }
s.Service = util.AsService(s.serve) s.Service = util.AsService(s.serve)
return s return s
@ -50,5 +50,5 @@ func (s *auditService) serve(stop chan struct{}) {
// Stop stops the audit service. // Stop stops the audit service.
func (s *auditService) Stop() { func (s *auditService) Stop() {
s.Service.Stop() s.Service.Stop()
events.Default.Unsubscribe(s.sub) s.sub.Unsubscribe()
} }

View File

@ -17,15 +17,22 @@ import (
func TestAuditService(t *testing.T) { func TestAuditService(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
sub := evLogger.Subscribe(events.AllEvents)
defer sub.Unsubscribe()
// Event sent before construction, will not be logged // Event sent before start, will not be logged
events.Default.Log(events.ConfigSaved, "the first event") evLogger.Log(events.ConfigSaved, "the first event")
// Make sure the event goes through before creating the service
<-sub.C()
service := newAuditService(buf) service := newAuditService(buf, evLogger)
go service.Serve() go service.Serve()
// Event that should end up in the audit log // Event that should end up in the audit log
events.Default.Log(events.ConfigSaved, "the second event") evLogger.Log(events.ConfigSaved, "the second event")
// We need to give the events time to arrive, since the channels are buffered etc. // We need to give the events time to arrive, since the channels are buffered etc.
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
@ -33,7 +40,7 @@ func TestAuditService(t *testing.T) {
service.Stop() service.Stop()
// This event should not be logged, since we have stopped. // This event should not be logged, since we have stopped.
events.Default.Log(events.ConfigSaved, "the third event") evLogger.Log(events.ConfigSaved, "the third event")
result := buf.String() result := buf.String()
t.Log(result) t.Log(result)

View File

@ -68,6 +68,7 @@ type App struct {
mainService *suture.Supervisor mainService *suture.Supervisor
cfg config.Wrapper cfg config.Wrapper
ll *db.Lowlevel ll *db.Lowlevel
evLogger events.Logger
cert tls.Certificate cert tls.Certificate
opts Options opts Options
exitStatus ExitStatus exitStatus ExitStatus
@ -78,10 +79,11 @@ type App struct {
stopped chan struct{} stopped chan struct{}
} }
func New(cfg config.Wrapper, ll *db.Lowlevel, cert tls.Certificate, opts Options) *App { func New(cfg config.Wrapper, ll *db.Lowlevel, evLogger events.Logger, cert tls.Certificate, opts Options) *App {
return &App{ return &App{
cfg: cfg, cfg: cfg,
ll: ll, ll: ll,
evLogger: evLogger,
opts: opts, opts: opts,
cert: cert, cert: cert,
stop: make(chan struct{}), stop: make(chan struct{}),
@ -120,11 +122,11 @@ func (a *App) startup() error {
a.mainService.ServeBackground() a.mainService.ServeBackground()
if a.opts.AuditWriter != nil { if a.opts.AuditWriter != nil {
a.mainService.Add(newAuditService(a.opts.AuditWriter)) a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger))
} }
if a.opts.Verbose { if a.opts.Verbose {
a.mainService.Add(newVerboseService()) a.mainService.Add(newVerboseService(a.evLogger))
} }
errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0) errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0)
@ -133,8 +135,8 @@ func (a *App) startup() error {
// Event subscription for the API; must start early to catch the early // Event subscription for the API; must start early to catch the early
// events. The LocalChangeDetected event might overwhelm the event // events. The LocalChangeDetected event might overwhelm the event
// receiver in some situations so we will not subscribe to it here. // receiver in some situations so we will not subscribe to it here.
defaultSub := events.NewBufferedSubscription(events.Default.Subscribe(api.DefaultEventMask), api.EventSubBufferSize) defaultSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DefaultEventMask), api.EventSubBufferSize)
diskSub := events.NewBufferedSubscription(events.Default.Subscribe(api.DiskEventMask), api.EventSubBufferSize) diskSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DiskEventMask), api.EventSubBufferSize)
// Attempt to increase the limit on number of open files to the maximum // 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 // allowed, in case we have many peers. We don't really care enough to
@ -153,7 +155,7 @@ func (a *App) startup() error {
// Emit the Starting event, now that we know who we are. // Emit the Starting event, now that we know who we are.
events.Default.Log(events.Starting, map[string]string{ a.evLogger.Log(events.Starting, map[string]string{
"home": locations.GetBaseDir(locations.ConfigBaseDir), "home": locations.GetBaseDir(locations.ConfigBaseDir),
"myID": a.myID.String(), "myID": a.myID.String(),
}) })
@ -228,7 +230,7 @@ func (a *App) startup() error {
miscDB.PutString("prevVersion", build.Version) miscDB.PutString("prevVersion", build.Version)
} }
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles) m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger)
if a.opts.DeadlockTimeoutS > 0 { if a.opts.DeadlockTimeoutS > 0 {
m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second) m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second)
@ -265,13 +267,13 @@ func (a *App) startup() error {
// Start connection management // Start connection management
connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName) connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName, a.evLogger)
a.mainService.Add(connectionsService) a.mainService.Add(connectionsService)
if a.cfg.Options().GlobalAnnEnabled { if a.cfg.Options().GlobalAnnEnabled {
for _, srv := range a.cfg.GlobalDiscoveryServers() { for _, srv := range a.cfg.GlobalDiscoveryServers() {
l.Infoln("Using discovery server", srv) l.Infoln("Using discovery server", srv)
gd, err := discover.NewGlobal(srv, a.cert, connectionsService) gd, err := discover.NewGlobal(srv, a.cert, connectionsService, a.evLogger)
if err != nil { if err != nil {
l.Warnln("Global discovery:", err) l.Warnln("Global discovery:", err)
continue continue
@ -286,14 +288,14 @@ func (a *App) startup() error {
if a.cfg.Options().LocalAnnEnabled { if a.cfg.Options().LocalAnnEnabled {
// v4 broadcasts // v4 broadcasts
bcd, err := discover.NewLocal(a.myID, fmt.Sprintf(":%d", a.cfg.Options().LocalAnnPort), connectionsService) bcd, err := discover.NewLocal(a.myID, fmt.Sprintf(":%d", a.cfg.Options().LocalAnnPort), connectionsService, a.evLogger)
if err != nil { if err != nil {
l.Warnln("IPv4 local discovery:", err) l.Warnln("IPv4 local discovery:", err)
} else { } else {
cachedDiscovery.Add(bcd, 0, 0) cachedDiscovery.Add(bcd, 0, 0)
} }
// v6 multicasts // v6 multicasts
mcd, err := discover.NewLocal(a.myID, a.cfg.Options().LocalAnnMCAddr, connectionsService) mcd, err := discover.NewLocal(a.myID, a.cfg.Options().LocalAnnMCAddr, connectionsService, a.evLogger)
if err != nil { if err != nil {
l.Warnln("IPv6 local discovery:", err) l.Warnln("IPv6 local discovery:", err)
} else { } else {
@ -342,7 +344,7 @@ func (a *App) startup() error {
l.Warnln("Syncthing should not run as a privileged or system user. Please consider using a normal user account.") l.Warnln("Syncthing should not run as a privileged or system user. Please consider using a normal user account.")
} }
events.Default.Log(events.StartupComplete, map[string]string{ a.evLogger.Log(events.StartupComplete, map[string]string{
"myID": a.myID.String(), "myID": a.myID.String(),
}) })
@ -426,10 +428,10 @@ func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscri
cpu := newCPUService() cpu := newCPUService()
a.mainService.Add(cpu) a.mainService.Add(cpu)
summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID) summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID, a.evLogger)
a.mainService.Add(summaryService) a.mainService.Add(summaryService)
apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, discoverer, connectionsService, urService, summaryService, errors, systemLog, cpu, &controller{a}, a.opts.NoUpgrade) apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, a.evLogger, discoverer, connectionsService, urService, summaryService, errors, systemLog, cpu, &controller{a}, a.opts.NoUpgrade)
a.mainService.Add(apiSvc) a.mainService.Add(apiSvc)
if err := apiSvc.WaitForStart(); err != nil { if err := apiSvc.WaitForStart(); err != nil {

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
) )
@ -19,7 +20,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 0, 0}}, {DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 0, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 1, 1}}, // first 56 bits same, differ in the first 64 bits {DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 1, 1}}, // first 56 bits same, differ in the first 64 bits
}, },
}) }, events.NoopLogger)
if err := checkShortIDs(cfg); err != nil { if err := checkShortIDs(cfg); err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
@ -30,7 +31,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 0}}, {DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 1}}, // first 64 bits same {DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 1}}, // first 64 bits same
}, },
}) }, events.NoopLogger)
if err := checkShortIDs(cfg); err == nil { if err := checkShortIDs(cfg); err == nil {
t.Error("Should have gotten an error") t.Error("Should have gotten an error")

View File

@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
@ -39,7 +40,7 @@ func LoadOrGenerateCertificate(certFile, keyFile string) (tls.Certificate, error
return cert, nil return cert, nil
} }
func DefaultConfig(path string, myID protocol.DeviceID, noDefaultFolder bool) (config.Wrapper, error) { func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger, noDefaultFolder bool) (config.Wrapper, error) {
newCfg, err := config.NewWithFreePorts(myID) newCfg, err := config.NewWithFreePorts(myID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -47,23 +48,23 @@ func DefaultConfig(path string, myID protocol.DeviceID, noDefaultFolder bool) (c
if noDefaultFolder { if noDefaultFolder {
l.Infoln("We will skip creation of a default folder on first start") l.Infoln("We will skip creation of a default folder on first start")
return config.Wrap(path, newCfg), nil return config.Wrap(path, newCfg, evLogger), nil
} }
newCfg.Folders = append(newCfg.Folders, config.NewFolderConfiguration(myID, "default", "Default Folder", fs.FilesystemTypeBasic, locations.Get(locations.DefFolder))) newCfg.Folders = append(newCfg.Folders, config.NewFolderConfiguration(myID, "default", "Default Folder", fs.FilesystemTypeBasic, locations.Get(locations.DefFolder)))
l.Infoln("Default folder created and/or linked to new config") l.Infoln("Default folder created and/or linked to new config")
return config.Wrap(path, newCfg), nil return config.Wrap(path, newCfg, evLogger), nil
} }
// LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it // LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it
// creates a default one, without the default folder if noDefaultFolder is ture. // creates a default one, without the default folder if noDefaultFolder is ture.
// Otherwise it checks the version, and archives and upgrades the config if // Otherwise it checks the version, and archives and upgrades the config if
// necessary or returns an error, if the version isn't compatible. // necessary or returns an error, if the version isn't compatible.
func LoadConfigAtStartup(path string, cert tls.Certificate, allowNewerConfig, noDefaultFolder bool) (config.Wrapper, error) { func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logger, allowNewerConfig, noDefaultFolder bool) (config.Wrapper, error) {
myID := protocol.NewDeviceID(cert.Certificate[0]) myID := protocol.NewDeviceID(cert.Certificate[0])
cfg, err := config.Load(path, myID) cfg, err := config.Load(path, myID, evLogger)
if fs.IsNotExist(err) { if fs.IsNotExist(err) {
cfg, err = DefaultConfig(path, myID, noDefaultFolder) cfg, err = DefaultConfig(path, myID, evLogger, noDefaultFolder)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to generate default config") return nil, errors.Wrap(err, "failed to generate default config")
} }

View File

@ -19,12 +19,12 @@ import (
// verbose format to the console using INFO level. // verbose format to the console using INFO level.
type verboseService struct { type verboseService struct {
suture.Service suture.Service
sub *events.Subscription sub events.Subscription
} }
func newVerboseService() *verboseService { func newVerboseService(evLogger events.Logger) *verboseService {
s := &verboseService{ s := &verboseService{
sub: events.Default.Subscribe(events.AllEvents), sub: evLogger.Subscribe(events.AllEvents),
} }
s.Service = util.AsService(s.serve) s.Service = util.AsService(s.serve)
return s return s
@ -48,7 +48,7 @@ func (s *verboseService) serve(stop chan struct{}) {
// Stop stops the verbose logging service. // Stop stops the verbose logging service.
func (s *verboseService) Stop() { func (s *verboseService) Stop() {
s.Service.Stop() s.Service.Stop()
events.Default.Unsubscribe(s.sub) s.sub.Unsubscribe()
} }

View File

@ -125,19 +125,19 @@ func newAggregator(folderCfg config.FolderConfiguration, ctx context.Context) *a
return a return a
} }
func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, ctx context.Context) { func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, evLogger events.Logger, ctx context.Context) {
a := newAggregator(folderCfg, ctx) a := newAggregator(folderCfg, ctx)
// Necessary for unit tests where the backend is mocked // Necessary for unit tests where the backend is mocked
go a.mainLoop(in, out, cfg) go a.mainLoop(in, out, cfg, evLogger)
} }
func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper) { func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper, evLogger events.Logger) {
a.notifyTimer = time.NewTimer(a.notifyDelay) a.notifyTimer = time.NewTimer(a.notifyDelay)
defer a.notifyTimer.Stop() defer a.notifyTimer.Stop()
inProgressItemSubscription := events.Default.Subscribe(events.ItemStarted | events.ItemFinished) inProgressItemSubscription := evLogger.Subscribe(events.ItemStarted | events.ItemFinished)
defer events.Default.Unsubscribe(inProgressItemSubscription) defer inProgressItemSubscription.Unsubscribe()
cfg.Subscribe(a) cfg.Subscribe(a)
defer cfg.Unsubscribe(a) defer cfg.Unsubscribe(a)

View File

@ -47,7 +47,7 @@ var (
} }
defaultCfg = config.Wrap("", config.Configuration{ defaultCfg = config.Wrap("", config.Configuration{
Folders: []config.FolderConfiguration{defaultFolderCfg}, Folders: []config.FolderConfiguration{defaultFolderCfg},
}) }, events.NoopLogger)
) )
// Represents possibly multiple (different event types) expected paths from // Represents possibly multiple (different event types) expected paths from
@ -151,14 +151,17 @@ func TestAggregate(t *testing.T) {
// TestInProgress checks that ignoring files currently edited by Syncthing works // TestInProgress checks that ignoring files currently edited by Syncthing works
func TestInProgress(t *testing.T) { func TestInProgress(t *testing.T) {
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
testCase := func(c chan<- fs.Event) { testCase := func(c chan<- fs.Event) {
events.Default.Log(events.ItemStarted, map[string]string{ evLogger.Log(events.ItemStarted, map[string]string{
"item": "inprogress", "item": "inprogress",
}) })
sleepMs(100) sleepMs(100)
c <- fs.Event{Name: "inprogress", Type: fs.NonRemove} c <- fs.Event{Name: "inprogress", Type: fs.NonRemove}
sleepMs(1000) sleepMs(1000)
events.Default.Log(events.ItemFinished, map[string]interface{}{ evLogger.Log(events.ItemFinished, map[string]interface{}{
"item": "inprogress", "item": "inprogress",
}) })
sleepMs(100) sleepMs(100)
@ -170,7 +173,7 @@ func TestInProgress(t *testing.T) {
{[][]string{{"notinprogress"}}, 2000, 3500}, {[][]string{{"notinprogress"}}, 2000, 3500},
} }
testScenario(t, "InProgress", testCase, expectedBatches) testScenario(t, "InProgress", testCase, expectedBatches, evLogger)
} }
// TestDelay checks that recurring changes to the same path are delayed // TestDelay checks that recurring changes to the same path are delayed
@ -208,7 +211,7 @@ func TestDelay(t *testing.T) {
{[][]string{{delayed}, {delAfter}}, 3600, 7000}, {[][]string{{delayed}, {delAfter}}, 3600, 7000},
} }
testScenario(t, "Delay", testCase, expectedBatches) testScenario(t, "Delay", testCase, expectedBatches, nil)
} }
// TestNoDelay checks that no delay occurs if there are no non-remove events // TestNoDelay checks that no delay occurs if there are no non-remove events
@ -225,7 +228,7 @@ func TestNoDelay(t *testing.T) {
{[][]string{{mixed}, {del}}, 500, 2000}, {[][]string{{mixed}, {del}}, 500, 2000},
} }
testScenario(t, "NoDelay", testCase, expectedBatches) testScenario(t, "NoDelay", testCase, expectedBatches, nil)
} }
func getEventPaths(dir *eventDir, dirPath string, a *aggregator) []string { func getEventPaths(dir *eventDir, dirPath string, a *aggregator) []string {
@ -277,8 +280,13 @@ func compareBatchToExpectedDirect(t *testing.T, batch []string, expectedPaths []
} }
} }
func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), expectedBatches []expectedBatch) { func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), expectedBatches []expectedBatch, evLogger events.Logger) {
t.Helper() t.Helper()
if evLogger == nil {
evLogger = events.NoopLogger
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
eventChan := make(chan fs.Event) eventChan := make(chan fs.Event)
watchChan := make(chan []string) watchChan := make(chan []string)
@ -289,7 +297,7 @@ func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), e
a.notifyTimeout = testNotifyTimeout a.notifyTimeout = testNotifyTimeout
startTime := time.Now() startTime := time.Now()
go a.mainLoop(eventChan, watchChan, defaultCfg) go a.mainLoop(eventChan, watchChan, defaultCfg, evLogger)
sleepMs(20) sleepMs(20)