1// Copyright (C) 2014 The Syncthing Authors. 2// 3// This Source Code Form is subject to the terms of the Mozilla Public 4// License, v. 2.0. If a copy of the MPL was not distributed with this file, 5// You can obtain one at https://mozilla.org/MPL/2.0/. 6 7package syncthing 8 9import ( 10 "context" 11 "crypto/tls" 12 "errors" 13 "fmt" 14 "io" 15 "net/http" 16 "os" 17 "runtime" 18 "sort" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/thejerf/suture/v4" 24 25 "github.com/syncthing/syncthing/lib/api" 26 "github.com/syncthing/syncthing/lib/build" 27 "github.com/syncthing/syncthing/lib/config" 28 "github.com/syncthing/syncthing/lib/connections" 29 "github.com/syncthing/syncthing/lib/db" 30 "github.com/syncthing/syncthing/lib/db/backend" 31 "github.com/syncthing/syncthing/lib/discover" 32 "github.com/syncthing/syncthing/lib/events" 33 "github.com/syncthing/syncthing/lib/locations" 34 "github.com/syncthing/syncthing/lib/logger" 35 "github.com/syncthing/syncthing/lib/model" 36 "github.com/syncthing/syncthing/lib/osutil" 37 "github.com/syncthing/syncthing/lib/protocol" 38 "github.com/syncthing/syncthing/lib/rand" 39 "github.com/syncthing/syncthing/lib/sha256" 40 "github.com/syncthing/syncthing/lib/svcutil" 41 "github.com/syncthing/syncthing/lib/tlsutil" 42 "github.com/syncthing/syncthing/lib/upgrade" 43 "github.com/syncthing/syncthing/lib/ur" 44) 45 46const ( 47 bepProtocolName = "bep/1.0" 48 tlsDefaultCommonName = "syncthing" 49 maxSystemErrors = 5 50 initialSystemLog = 10 51 maxSystemLog = 250 52 deviceCertLifetimeDays = 20 * 365 53) 54 55type Options struct { 56 AssetDir string 57 AuditWriter io.Writer 58 DeadlockTimeoutS int 59 NoUpgrade bool 60 ProfilerAddr string 61 ResetDeltaIdxs bool 62 Verbose bool 63 // null duration means use default value 64 DBRecheckInterval time.Duration 65 DBIndirectGCInterval time.Duration 66} 67 68type App struct { 69 myID protocol.DeviceID 70 mainService *suture.Supervisor 71 cfg config.Wrapper 72 ll *db.Lowlevel 73 evLogger events.Logger 74 cert tls.Certificate 75 opts Options 76 exitStatus svcutil.ExitStatus 77 err error 78 stopOnce sync.Once 79 mainServiceCancel context.CancelFunc 80 stopped chan struct{} 81} 82 83func New(cfg config.Wrapper, dbBackend backend.Backend, evLogger events.Logger, cert tls.Certificate, opts Options) (*App, error) { 84 ll, err := db.NewLowlevel(dbBackend, evLogger, db.WithRecheckInterval(opts.DBRecheckInterval), db.WithIndirectGCInterval(opts.DBIndirectGCInterval)) 85 if err != nil { 86 return nil, err 87 } 88 a := &App{ 89 cfg: cfg, 90 ll: ll, 91 evLogger: evLogger, 92 opts: opts, 93 cert: cert, 94 stopped: make(chan struct{}), 95 } 96 close(a.stopped) // Hasn't been started, so shouldn't block on Wait. 97 return a, nil 98} 99 100// Start executes the app and returns once all the startup operations are done, 101// e.g. the API is ready for use. 102// Must be called once only. 103func (a *App) Start() error { 104 // Create a main service manager. We'll add things to this as we go along. 105 // We want any logging it does to go through our log system. 106 spec := svcutil.SpecWithDebugLogger(l) 107 a.mainService = suture.New("main", spec) 108 109 // Start the supervisor and wait for it to stop to handle cleanup. 110 a.stopped = make(chan struct{}) 111 ctx, cancel := context.WithCancel(context.Background()) 112 a.mainServiceCancel = cancel 113 errChan := a.mainService.ServeBackground(ctx) 114 go a.wait(errChan) 115 116 if err := a.startup(); err != nil { 117 a.stopWithErr(svcutil.ExitError, err) 118 return err 119 } 120 121 return nil 122} 123 124func (a *App) startup() error { 125 a.mainService.Add(ur.NewFailureHandler(a.cfg, a.evLogger)) 126 127 a.mainService.Add(a.ll) 128 129 if a.opts.AuditWriter != nil { 130 a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger)) 131 } 132 133 if a.opts.Verbose { 134 a.mainService.Add(newVerboseService(a.evLogger)) 135 } 136 137 errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0) 138 systemLog := logger.NewRecorder(l, logger.LevelDebug, maxSystemLog, initialSystemLog) 139 140 // Event subscription for the API; must start early to catch the early 141 // events. The LocalChangeDetected event might overwhelm the event 142 // receiver in some situations so we will not subscribe to it here. 143 defaultSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DefaultEventMask), api.EventSubBufferSize) 144 diskSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DiskEventMask), api.EventSubBufferSize) 145 146 // Attempt to increase the limit on number of open files to the maximum 147 // allowed, in case we have many peers. We don't really care enough to 148 // report the error if there is one. 149 osutil.MaximizeOpenFileLimit() 150 151 // Figure out our device ID, set it as the log prefix and log it. 152 a.myID = protocol.NewDeviceID(a.cert.Certificate[0]) 153 l.SetPrefix(fmt.Sprintf("[%s] ", a.myID.String()[:5])) 154 l.Infoln("My ID:", a.myID) 155 156 // Select SHA256 implementation and report. Affected by the 157 // STHASHING environment variable. 158 sha256.SelectAlgo() 159 sha256.Report() 160 161 // Emit the Starting event, now that we know who we are. 162 163 a.evLogger.Log(events.Starting, map[string]string{ 164 "home": locations.GetBaseDir(locations.ConfigBaseDir), 165 "myID": a.myID.String(), 166 }) 167 168 if err := checkShortIDs(a.cfg); err != nil { 169 l.Warnln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err) 170 return err 171 } 172 173 if len(a.opts.ProfilerAddr) > 0 { 174 go func() { 175 l.Debugln("Starting profiler on", a.opts.ProfilerAddr) 176 runtime.SetBlockProfileRate(1) 177 err := http.ListenAndServe(a.opts.ProfilerAddr, nil) 178 if err != nil { 179 l.Warnln(err) 180 return 181 } 182 }() 183 } 184 185 perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond, true) 186 l.Infof("Hashing performance is %.02f MB/s", perf) 187 188 if err := db.UpdateSchema(a.ll); err != nil { 189 l.Warnln("Database schema:", err) 190 return err 191 } 192 193 if a.opts.ResetDeltaIdxs { 194 l.Infoln("Reinitializing delta index IDs") 195 db.DropDeltaIndexIDs(a.ll) 196 } 197 198 protectedFiles := []string{ 199 locations.Get(locations.Database), 200 locations.Get(locations.ConfigFile), 201 locations.Get(locations.CertFile), 202 locations.Get(locations.KeyFile), 203 } 204 205 // Remove database entries for folders that no longer exist in the config 206 folders := a.cfg.Folders() 207 for _, folder := range a.ll.ListFolders() { 208 if _, ok := folders[folder]; !ok { 209 l.Infof("Cleaning data for dropped folder %q", folder) 210 db.DropFolder(a.ll, folder) 211 } 212 } 213 214 // Grab the previously running version string from the database. 215 216 miscDB := db.NewMiscDataNamespace(a.ll) 217 prevVersion, _, err := miscDB.String("prevVersion") 218 if err != nil { 219 l.Warnln("Database:", err) 220 return err 221 } 222 223 // Strip away prerelease/beta stuff and just compare the release 224 // numbers. 0.14.44 to 0.14.45-banana is an upgrade, 0.14.45-banana to 225 // 0.14.45-pineapple is not. 226 227 prevParts := strings.Split(prevVersion, "-") 228 curParts := strings.Split(build.Version, "-") 229 if rel := upgrade.CompareVersions(prevParts[0], curParts[0]); rel != upgrade.Equal { 230 if prevVersion != "" { 231 l.Infoln("Detected upgrade from", prevVersion, "to", build.Version) 232 } 233 234 if a.cfg.Options().SendFullIndexOnUpgrade { 235 // Drop delta indexes in case we've changed random stuff we 236 // shouldn't have. We will resend our index on next connect. 237 db.DropDeltaIndexIDs(a.ll) 238 } 239 } 240 241 if build.Version != prevVersion { 242 // Remember the new version. 243 miscDB.PutString("prevVersion", build.Version) 244 } 245 246 m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger) 247 248 if a.opts.DeadlockTimeoutS > 0 { 249 m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second) 250 } else if !build.IsRelease || build.IsBeta { 251 m.StartDeadlockDetector(20 * time.Minute) 252 } 253 254 a.mainService.Add(m) 255 256 // The TLS configuration is used for both the listening socket and outgoing 257 // connections. 258 259 var tlsCfg *tls.Config 260 if a.cfg.Options().InsecureAllowOldTLSVersions { 261 l.Infoln("TLS 1.2 is allowed on sync connections. This is less than optimally secure.") 262 tlsCfg = tlsutil.SecureDefaultWithTLS12() 263 } else { 264 tlsCfg = tlsutil.SecureDefaultTLS13() 265 } 266 tlsCfg.Certificates = []tls.Certificate{a.cert} 267 tlsCfg.NextProtos = []string{bepProtocolName} 268 tlsCfg.ClientAuth = tls.RequestClientCert 269 tlsCfg.SessionTicketsDisabled = true 270 tlsCfg.InsecureSkipVerify = true 271 272 // Start discovery and connection management 273 274 // Chicken and egg, discovery manager depends on connection service to tell it what addresses it's listening on 275 // Connection service depends on discovery manager to get addresses to connect to. 276 // Create a wrapper that is then wired after they are both setup. 277 addrLister := &lateAddressLister{} 278 279 discoveryManager := discover.NewManager(a.myID, a.cfg, a.cert, a.evLogger, addrLister) 280 connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, discoveryManager, bepProtocolName, tlsDefaultCommonName, a.evLogger) 281 282 addrLister.AddressLister = connectionsService 283 284 a.mainService.Add(discoveryManager) 285 a.mainService.Add(connectionsService) 286 287 a.cfg.Modify(func(cfg *config.Configuration) { 288 // Candidate builds always run with usage reporting. 289 if build.IsCandidate { 290 l.Infoln("Anonymous usage reporting is always enabled for candidate releases.") 291 if cfg.Options.URAccepted != ur.Version { 292 cfg.Options.URAccepted = ur.Version 293 // Unique ID will be set and config saved below if necessary. 294 } 295 } 296 297 // If we are going to do usage reporting, ensure we have a valid unique ID. 298 if cfg.Options.URAccepted > 0 && cfg.Options.URUniqueID == "" { 299 cfg.Options.URUniqueID = rand.String(8) 300 } 301 }) 302 303 usageReportingSvc := ur.New(a.cfg, m, connectionsService, a.opts.NoUpgrade) 304 a.mainService.Add(usageReportingSvc) 305 306 // GUI 307 308 if err := a.setupGUI(m, defaultSub, diskSub, discoveryManager, connectionsService, usageReportingSvc, errors, systemLog); err != nil { 309 l.Warnln("Failed starting API:", err) 310 return err 311 } 312 313 myDev, _ := a.cfg.Device(a.myID) 314 l.Infof(`My name is "%v"`, myDev.Name) 315 for _, device := range a.cfg.Devices() { 316 if device.DeviceID != a.myID { 317 l.Infof(`Device %s is "%v" at %v`, device.DeviceID, device.Name, device.Addresses) 318 } 319 } 320 321 if isSuperUser() { 322 l.Warnln("Syncthing should not run as a privileged or system user. Please consider using a normal user account.") 323 } 324 325 a.evLogger.Log(events.StartupComplete, map[string]string{ 326 "myID": a.myID.String(), 327 }) 328 329 if a.cfg.Options().SetLowPriority { 330 if err := osutil.SetLowPriority(); err != nil { 331 l.Warnln("Failed to lower process priority:", err) 332 } 333 } 334 335 return nil 336} 337 338func (a *App) wait(errChan <-chan error) { 339 err := <-errChan 340 a.handleMainServiceError(err) 341 342 done := make(chan struct{}) 343 go func() { 344 a.ll.Close() 345 close(done) 346 }() 347 select { 348 case <-done: 349 case <-time.After(10 * time.Second): 350 l.Warnln("Database failed to stop within 10s") 351 } 352 353 l.Infoln("Exiting") 354 355 close(a.stopped) 356} 357 358func (a *App) handleMainServiceError(err error) { 359 if err == nil || errors.Is(err, context.Canceled) { 360 return 361 } 362 var fatalErr *svcutil.FatalErr 363 if errors.As(err, &fatalErr) { 364 a.exitStatus = fatalErr.Status 365 a.err = fatalErr.Err 366 return 367 } 368 a.err = err 369 a.exitStatus = svcutil.ExitError 370} 371 372// Wait blocks until the app stops running. Also returns if the app hasn't been 373// started yet. 374func (a *App) Wait() svcutil.ExitStatus { 375 <-a.stopped 376 return a.exitStatus 377} 378 379// Error returns an error if one occurred while running the app. It does not wait 380// for the app to stop before returning. 381func (a *App) Error() error { 382 select { 383 case <-a.stopped: 384 return a.err 385 default: 386 } 387 return nil 388} 389 390// Stop stops the app and sets its exit status to given reason, unless the app 391// was already stopped before. In any case it returns the effective exit status. 392func (a *App) Stop(stopReason svcutil.ExitStatus) svcutil.ExitStatus { 393 return a.stopWithErr(stopReason, nil) 394} 395 396func (a *App) stopWithErr(stopReason svcutil.ExitStatus, err error) svcutil.ExitStatus { 397 a.stopOnce.Do(func() { 398 a.exitStatus = stopReason 399 a.err = err 400 if shouldDebug() { 401 l.Debugln("Services before stop:") 402 printServiceTree(os.Stdout, a.mainService, 0) 403 } 404 a.mainServiceCancel() 405 }) 406 <-a.stopped 407 return a.exitStatus 408} 409 410func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, errors, systemLog logger.Recorder) error { 411 guiCfg := a.cfg.GUI() 412 413 if !guiCfg.Enabled { 414 return nil 415 } 416 417 if guiCfg.InsecureAdminAccess { 418 l.Warnln("Insecure admin access is enabled.") 419 } 420 421 summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID, a.evLogger) 422 a.mainService.Add(summaryService) 423 424 apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, a.evLogger, discoverer, connectionsService, urService, summaryService, errors, systemLog, a.opts.NoUpgrade) 425 a.mainService.Add(apiSvc) 426 427 if err := apiSvc.WaitForStart(); err != nil { 428 return err 429 } 430 return nil 431} 432 433// checkShortIDs verifies that the configuration won't result in duplicate 434// short ID:s; that is, that the devices in the cluster all have unique 435// initial 64 bits. 436func checkShortIDs(cfg config.Wrapper) error { 437 exists := make(map[protocol.ShortID]protocol.DeviceID) 438 for deviceID := range cfg.Devices() { 439 shortID := deviceID.Short() 440 if otherID, ok := exists[shortID]; ok { 441 return fmt.Errorf("%v in conflict with %v", deviceID, otherID) 442 } 443 exists[shortID] = deviceID 444 } 445 return nil 446} 447 448type supervisor interface{ Services() []suture.Service } 449 450func printServiceTree(w io.Writer, sup supervisor, level int) { 451 printService(w, sup, level) 452 453 svcs := sup.Services() 454 sort.Slice(svcs, func(a, b int) bool { 455 return fmt.Sprint(svcs[a]) < fmt.Sprint(svcs[b]) 456 }) 457 458 for _, svc := range svcs { 459 if sub, ok := svc.(supervisor); ok { 460 printServiceTree(w, sub, level+1) 461 } else { 462 printService(w, svc, level+1) 463 } 464 } 465} 466 467func printService(w io.Writer, svc interface{}, level int) { 468 type errorer interface{ Error() error } 469 470 t := "-" 471 if _, ok := svc.(supervisor); ok { 472 t = "+" 473 } 474 fmt.Fprintln(w, strings.Repeat(" ", level), t, svc) 475 if es, ok := svc.(errorer); ok { 476 if err := es.Error(); err != nil { 477 fmt.Fprintln(w, strings.Repeat(" ", level), " ->", err) 478 } 479 } 480} 481 482type lateAddressLister struct { 483 discover.AddressLister 484} 485