all: Get rid of fatal logging (#5537)

* cleanup Fatal in lib/config/config.go

* cleanup Fatal in lib/config/folderconfiguration.go

* cleanup Fatal in lib/model/model.go

* cleanup Fatal in cmd/syncthing/monitor.go

* cleanup Fatal in cmd/syncthing/main.go

* cleanup Fatal in lib/api

* remove Fatal methods from logger

* lowercase in errors.Wrap

* one less channel
This commit is contained in:
Simon Frei 2019-02-14 21:29:14 +01:00 committed by Audrius Butkevicius
parent 7a40c42e8b
commit d5ff2c41dc
7 changed files with 139 additions and 88 deletions

View File

@ -78,7 +78,8 @@ type apiService struct {
stop chan struct{} // signals intentional stop stop chan struct{} // signals intentional stop
configChanged chan struct{} // signals intentional listener close due to config change configChanged chan struct{} // signals intentional listener close due to config change
started chan string // signals startup complete by sending the listener address, for testing only started chan string // signals startup complete by sending the listener address, for testing only
startedOnce chan struct{} // the service has started successfully at least once startedOnce chan struct{} // the service has started at least once
startupErr error
cpu rater cpu rater
guiErrors logger.Recorder guiErrors logger.Recorder
@ -174,6 +175,11 @@ func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKey
return service return service
} }
func (s *apiService) WaitForStart() error {
<-s.startedOnce
return s.startupErr
}
func (s *apiService) getListener(guiCfg config.GUIConfiguration) (net.Listener, error) { func (s *apiService) getListener(guiCfg config.GUIConfiguration) (net.Listener, error) {
cert, err := tls.LoadX509KeyPair(s.httpsCertFile, s.httpsKeyFile) cert, err := tls.LoadX509KeyPair(s.httpsCertFile, s.httpsKeyFile)
if err != nil { if err != nil {
@ -236,14 +242,15 @@ func (s *apiService) Serve() {
// We let this be a loud user-visible warning as it may be the only // We let this be a loud user-visible warning as it may be the only
// indication they get that the GUI won't be available. // indication they get that the GUI won't be available.
l.Warnln("Starting API/GUI:", err) l.Warnln("Starting API/GUI:", err)
return
default: default:
// This is during initialization. A failure here should be fatal // This is during initialization. A failure here should be fatal
// as there will be no way for the user to communicate with us // as there will be no way for the user to communicate with us
// otherwise anyway. // otherwise anyway.
l.Fatalln("Starting API/GUI:", err) s.startupErr = err
close(s.startedOnce)
} }
return
} }
if listener == nil { if listener == nil {
@ -410,6 +417,19 @@ func (s *apiService) Serve() {
} }
} }
// Complete implements suture.IsCompletable, which signifies to the supervisor
// whether to stop restarting the service.
func (s *apiService) Complete() bool {
select {
case <-s.startedOnce:
return s.startupErr != nil
case <-s.stop:
return true
default:
}
return false
}
func (s *apiService) Stop() { func (s *apiService) Stop() {
close(s.stop) close(s.stop)
} }

View File

@ -9,7 +9,6 @@ package main
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -48,6 +47,7 @@ import (
"github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/tlsutil"
"github.com/syncthing/syncthing/lib/upgrade" "github.com/syncthing/syncthing/lib/upgrade"
"github.com/pkg/errors"
"github.com/thejerf/suture" "github.com/thejerf/suture"
) )
@ -305,7 +305,8 @@ func main() {
// to complain if they set -logfile explicitly, not if it's set to its // to complain if they set -logfile explicitly, not if it's set to its
// default location // default location
if options.noRestart && (options.logFile != "" && options.logFile != "-") { if options.noRestart && (options.logFile != "" && options.logFile != "-") {
l.Fatalln("-logfile may not be used with -no-restart or STNORESTART") l.Warnln("-logfile may not be used with -no-restart or STNORESTART")
os.Exit(exitError)
} }
if options.hideConsole { if options.hideConsole {
@ -318,11 +319,13 @@ func main() {
var err error var err error
options.confDir, err = filepath.Abs(options.confDir) options.confDir, err = filepath.Abs(options.confDir)
if err != nil { if err != nil {
l.Fatalln(err) l.Warnln("Failed to make options path absolute:", err)
os.Exit(exitError)
} }
} }
if err := locations.SetBaseDir(locations.ConfigBaseDir, options.confDir); err != nil { if err := locations.SetBaseDir(locations.ConfigBaseDir, options.confDir); err != nil {
l.Fatalln(err) l.Warnln(err)
os.Exit(exitError)
} }
} }
@ -359,7 +362,8 @@ func main() {
locations.Get(locations.KeyFile), locations.Get(locations.KeyFile),
) )
if err != nil { if err != nil {
l.Fatalln("Error reading device ID:", err) l.Warnln("Error reading device ID:", err)
os.Exit(exitError)
} }
myID = protocol.NewDeviceID(cert.Certificate[0]) myID = protocol.NewDeviceID(cert.Certificate[0])
@ -368,22 +372,32 @@ func main() {
} }
if options.browserOnly { if options.browserOnly {
openGUI() if err := openGUI(); err != nil {
l.Warnln("Failed to open web UI:", err)
os.Exit(exitError)
}
return return
} }
if options.generateDir != "" { if options.generateDir != "" {
generate(options.generateDir) if err := generate(options.generateDir); err != nil {
l.Warnln("Failed to generate config and keys:", err)
os.Exit(exitError)
}
return return
} }
// Ensure that our home directory exists. // Ensure that our home directory exists.
ensureDir(locations.GetBaseDir(locations.ConfigBaseDir), 0700) if err := ensureDir(locations.GetBaseDir(locations.ConfigBaseDir), 0700); err != nil {
l.Warnln("Failure on home directory:", err)
os.Exit(exitError)
}
if options.upgradeTo != "" { if options.upgradeTo != "" {
err := upgrade.ToURL(options.upgradeTo) err := upgrade.ToURL(options.upgradeTo)
if err != nil { if err != nil {
l.Fatalln("Upgrade:", err) // exits 1 l.Warnln("Error while Upgrading:", err)
os.Exit(exitError)
} }
l.Infoln("Upgraded from", options.upgradeTo) l.Infoln("Upgraded from", options.upgradeTo)
return return
@ -402,7 +416,8 @@ func main() {
if options.resetDatabase { if options.resetDatabase {
if err := resetDB(); err != nil { if err := resetDB(); err != nil {
l.Fatalln("Resetting database:", err) l.Warnln("Resetting database:", err)
os.Exit(exitError)
} }
return return
} }
@ -414,23 +429,30 @@ func main() {
} }
} }
func openGUI() { func openGUI() error {
cfg, _ := loadOrDefaultConfig() cfg, err := loadOrDefaultConfig()
if err != nil {
return err
}
if cfg.GUI().Enabled { if cfg.GUI().Enabled {
if err := openURL(cfg.GUI().URL()); err != nil { if err := openURL(cfg.GUI().URL()); err != nil {
l.Fatalln("Open URL:", err) return err
} }
} else { } else {
l.Warnln("Browser: GUI is currently disabled") l.Warnln("Browser: GUI is currently disabled")
} }
return nil
} }
func generate(generateDir string) { func generate(generateDir string) error {
dir, err := fs.ExpandTilde(generateDir) dir, err := fs.ExpandTilde(generateDir)
if err != nil { if err != nil {
l.Fatalln("generate:", err) return err
}
if err := ensureDir(dir, 0700); err != nil {
return err
} }
ensureDir(dir, 0700)
certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem") certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem")
cert, err := tls.LoadX509KeyPair(certFile, keyFile) cert, err := tls.LoadX509KeyPair(certFile, keyFile)
@ -440,11 +462,11 @@ func generate(generateDir string) {
} else { } else {
cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName) cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName)
if err != nil { if err != nil {
l.Fatalln("Create certificate:", err) return errors.Wrap(err, "create certificate")
} }
myID = protocol.NewDeviceID(cert.Certificate[0]) myID = protocol.NewDeviceID(cert.Certificate[0])
if err != nil { if err != nil {
l.Fatalln("Load certificate:", err) return errors.Wrap(err, "load certificate")
} }
if err == nil { if err == nil {
l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0]))
@ -454,13 +476,17 @@ func generate(generateDir string) {
cfgFile := filepath.Join(dir, "config.xml") cfgFile := filepath.Join(dir, "config.xml")
if _, err := os.Stat(cfgFile); err == nil { if _, err := os.Stat(cfgFile); err == nil {
l.Warnln("Config exists; will not overwrite.") l.Warnln("Config exists; will not overwrite.")
return return nil
}
cfg, err := defaultConfig(cfgFile)
if err != nil {
return err
} }
var cfg = defaultConfig(cfgFile)
err = cfg.Save() err = cfg.Save()
if err != nil { if err != nil {
l.Warnln("Failed to save config", err) return errors.Wrap(err, "save config")
} }
return nil
} }
func debugFacilities() string { func debugFacilities() string {
@ -490,7 +516,8 @@ func checkUpgrade() upgrade.Release {
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 {
l.Fatalln("Upgrade:", err) l.Warnln("Upgrade:", err)
os.Exit(exitError)
} }
if upgrade.CompareVersions(release.Tag, build.Version) <= 0 { if upgrade.CompareVersions(release.Tag, build.Version) <= 0 {
@ -509,14 +536,16 @@ func performUpgrade(release upgrade.Release) {
if err == nil { if err == nil {
err = upgrade.To(release) err = upgrade.To(release)
if err != nil { if err != nil {
l.Fatalln("Upgrade:", err) l.Warnln("Upgrade:", err)
os.Exit(exitError)
} }
l.Infof("Upgraded to %q", release.Tag) l.Infof("Upgraded to %q", release.Tag)
} else { } else {
l.Infoln("Attempting upgrade through running Syncthing...") l.Infoln("Attempting upgrade through running Syncthing...")
err = upgradeViaRest() err = upgradeViaRest()
if err != nil { if err != nil {
l.Fatalln("Upgrade:", err) l.Warnln("Upgrade:", err)
os.Exit(exitError)
} }
l.Infoln("Syncthing upgrading") l.Infoln("Syncthing upgrading")
os.Exit(exitUpgrading) os.Exit(exitUpgrading)
@ -615,7 +644,8 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
tlsDefaultCommonName, tlsDefaultCommonName,
) )
if err != nil { if err != nil {
l.Fatalln(err) l.Infoln("Failed to generate certificate:", err)
os.Exit(exitError)
} }
} }
@ -637,10 +667,15 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
"myID": myID.String(), "myID": myID.String(),
}) })
cfg := loadConfigAtStartup() cfg, err := loadConfigAtStartup()
if err != nil {
l.Warnln("Failed to initialize config:", err)
os.Exit(exitError)
}
if err := checkShortIDs(cfg); err != nil { if err := checkShortIDs(cfg); err != nil {
l.Fatalln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err) l.Warnln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err)
os.Exit(exitError)
} }
if len(runtimeOptions.profiler) > 0 { if len(runtimeOptions.profiler) > 0 {
@ -649,7 +684,8 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
runtime.SetBlockProfileRate(1) runtime.SetBlockProfileRate(1)
err := http.ListenAndServe(runtimeOptions.profiler, nil) err := http.ListenAndServe(runtimeOptions.profiler, nil)
if err != nil { if err != nil {
l.Fatalln(err) l.Warnln(err)
os.Exit(exitError)
} }
}() }()
} }
@ -660,10 +696,12 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
dbFile := locations.Get(locations.Database) dbFile := locations.Get(locations.Database)
ldb, err := db.Open(dbFile) ldb, err := db.Open(dbFile)
if err != nil { if err != nil {
l.Fatalln("Error opening database:", err) l.Warnln("Error opening database:", err)
os.Exit(exitError)
} }
if err := db.UpdateSchema(ldb); err != nil { if err := db.UpdateSchema(ldb); err != nil {
l.Fatalln("Database schema:", err) l.Warnln("Database schema:", err)
os.Exit(exitError)
} }
if runtimeOptions.resetDeltaIdxs { if runtimeOptions.resetDeltaIdxs {
@ -799,10 +837,12 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
if runtimeOptions.cpuProfile { if runtimeOptions.cpuProfile {
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid())) f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
if err != nil { if err != nil {
l.Fatalln("Creating profile:", err) l.Warnln("Creating profile:", err)
os.Exit(exitError)
} }
if err := pprof.StartCPUProfile(f); err != nil { if err := pprof.StartCPUProfile(f); err != nil {
l.Fatalln("Starting profile:", err) l.Warnln("Starting profile:", err)
os.Exit(exitError)
} }
} }
@ -921,33 +961,39 @@ func loadOrDefaultConfig() (*config.Wrapper, error) {
cfg, err := config.Load(cfgFile, myID) cfg, err := config.Load(cfgFile, myID)
if err != nil { if err != nil {
cfg = defaultConfig(cfgFile) cfg, err = defaultConfig(cfgFile)
} }
return cfg, err return cfg, err
} }
func loadConfigAtStartup() *config.Wrapper { func loadConfigAtStartup() (*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)
if os.IsNotExist(err) { if os.IsNotExist(err) {
cfg = defaultConfig(cfgFile) cfg, err = defaultConfig(cfgFile)
cfg.Save() if err != nil {
l.Infof("Default config saved. Edit %s to taste or use the GUI\n", cfg.ConfigPath()) return nil, errors.Wrap(err, "failed to generate default config")
}
err = cfg.Save()
if err != nil {
return nil, errors.Wrap(err, "failed to save default config")
}
l.Infof("Default config saved. Edit %s to taste (with Syncthing stopped) or use the GUI", cfg.ConfigPath())
} else if err == io.EOF { } else if err == io.EOF {
l.Fatalln("Failed to load config: unexpected end of file. Truncated or empty configuration?") return nil, errors.New("Failed to load config: unexpected end of file. Truncated or empty configuration?")
} else if err != nil { } else if err != nil {
l.Fatalln("Failed to load config:", err) return nil, errors.Wrap(err, "failed to load config")
} }
if cfg.RawCopy().OriginalVersion != config.CurrentVersion { if cfg.RawCopy().OriginalVersion != config.CurrentVersion {
err = archiveAndSaveConfig(cfg) err = archiveAndSaveConfig(cfg)
if err != nil { if err != nil {
l.Fatalln("Config archive:", err) return nil, errors.Wrap(err, "config archive")
} }
} }
return cfg return cfg, nil
} }
func archiveAndSaveConfig(cfg *config.Wrapper) error { func archiveAndSaveConfig(cfg *config.Wrapper) error {
@ -999,7 +1045,8 @@ func startAuditing(mainService *suture.Supervisor, auditFile string) {
} }
fd, err = os.OpenFile(auditFile, auditFlags, 0600) fd, err = os.OpenFile(auditFile, auditFlags, 0600)
if err != nil { if err != nil {
l.Fatalln("Audit:", err) l.Warnln("Audit:", err)
os.Exit(exitError)
} }
auditDest = auditFile auditDest = auditFile
} }
@ -1032,36 +1079,43 @@ func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Mode
cfg.Subscribe(api) cfg.Subscribe(api)
mainService.Add(api) mainService.Add(api)
if err := api.WaitForStart(); err != nil {
l.Warnln("Failed starting API:", err)
os.Exit(exitError)
}
if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting { if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
// Can potentially block if the utility we are invoking doesn't // Can potentially block if the utility we are invoking doesn't
// fork, and just execs, hence keep it in its own routine. // fork, and just execs, hence keep it in its own routine.
<-api.startedOnce
go func() { _ = openURL(guiCfg.URL()) }() go func() { _ = openURL(guiCfg.URL()) }()
} }
} }
func defaultConfig(cfgFile string) *config.Wrapper { func defaultConfig(cfgFile string) (*config.Wrapper, error) {
newCfg := config.NewWithFreePorts(myID) newCfg, err := config.NewWithFreePorts(myID)
if err != nil {
return nil, err
}
if noDefaultFolder { if noDefaultFolder {
l.Infoln("We will skip creation of a default folder on first start since the proper envvar is set") l.Infoln("We will skip creation of a default folder on first start since the proper envvar is set")
return config.Wrap(cfgFile, newCfg) return config.Wrap(cfgFile, newCfg), 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(cfgFile, newCfg) return config.Wrap(cfgFile, newCfg), nil
} }
func resetDB() error { func resetDB() error {
return os.RemoveAll(locations.Get(locations.Database)) return os.RemoveAll(locations.Get(locations.Database))
} }
func ensureDir(dir string, mode fs.FileMode) { func ensureDir(dir string, mode fs.FileMode) error {
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir) fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
err := fs.MkdirAll(".", mode) err := fs.MkdirAll(".", mode)
if err != nil { if err != nil {
l.Fatalln(err) return err
} }
if fi, err := fs.Stat("."); err == nil { if fi, err := fs.Stat("."); err == nil {
@ -1077,6 +1131,7 @@ func ensureDir(dir string, mode fs.FileMode) {
} }
} }
} }
return nil
} }
func standbyMonitor() { func standbyMonitor() {
@ -1232,6 +1287,7 @@ func setPauseState(cfg *config.Wrapper, paused bool) {
raw.Folders[i].Paused = paused raw.Folders[i].Paused = paused
} }
if _, err := cfg.Replace(raw); err != nil { if _, err := cfg.Replace(raw); err != nil {
l.Fatalln("Cannot adjust paused state:", err) l.Warnln("Cannot adjust paused state:", err)
os.Exit(exitError)
} }
} }

View File

@ -85,18 +85,18 @@ func monitorMain(runtimeOptions RuntimeOptions) {
stderr, err := cmd.StderrPipe() stderr, err := cmd.StderrPipe()
if err != nil { if err != nil {
l.Fatalln("stderr:", err) panic(err)
} }
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
l.Fatalln("stdout:", err) panic(err)
} }
l.Infoln("Starting syncthing") l.Infoln("Starting syncthing")
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
l.Fatalln(err) panic(err)
} }
stdoutMut.Lock() stdoutMut.Lock()

View File

@ -84,18 +84,18 @@ func New(myID protocol.DeviceID) Configuration {
return cfg return cfg
} }
func NewWithFreePorts(myID protocol.DeviceID) Configuration { func NewWithFreePorts(myID protocol.DeviceID) (Configuration, error) {
cfg := New(myID) cfg := New(myID)
port, err := getFreePort("127.0.0.1", DefaultGUIPort) port, err := getFreePort("127.0.0.1", DefaultGUIPort)
if err != nil { if err != nil {
l.Fatalln("get free port (GUI):", err) return Configuration{}, fmt.Errorf("get free port (GUI): %v", err)
} }
cfg.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%d", port) cfg.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%d", port)
port, err = getFreePort("0.0.0.0", DefaultTCPPort) port, err = getFreePort("0.0.0.0", DefaultTCPPort)
if err != nil { if err != nil {
l.Fatalln("get free port (BEP):", err) return Configuration{}, fmt.Errorf("get free port (BEP): %v", err)
} }
if port == DefaultTCPPort { if port == DefaultTCPPort {
cfg.Options.ListenAddresses = []string{"default"} cfg.Options.ListenAddresses = []string{"default"}
@ -106,7 +106,7 @@ func NewWithFreePorts(myID protocol.DeviceID) Configuration {
} }
} }
return cfg return cfg, nil
} }
func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, error) { func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, error) {

View File

@ -106,7 +106,7 @@ func (f FolderConfiguration) Versioner() versioner.Versioner {
} }
versionerFactory, ok := versioner.Factories[f.Versioning.Type] versionerFactory, ok := versioner.Factories[f.Versioning.Type]
if !ok { if !ok {
l.Fatalf("Requested versioning type %q that does not exist", f.Versioning.Type) panic(fmt.Sprintf("Requested versioning type %q that does not exist", f.Versioning.Type))
} }
return versionerFactory(f.ID, f.Filesystem(), f.Versioning.Params) return versionerFactory(f.ID, f.Filesystem(), f.Versioning.Params)

View File

@ -25,7 +25,6 @@ const (
LevelVerbose LevelVerbose
LevelInfo LevelInfo
LevelWarn LevelWarn
LevelFatal
NumLevels NumLevels
) )
@ -49,8 +48,6 @@ type Logger interface {
Infof(format string, vals ...interface{}) Infof(format string, vals ...interface{})
Warnln(vals ...interface{}) Warnln(vals ...interface{})
Warnf(format string, vals ...interface{}) Warnf(format string, vals ...interface{})
Fatalln(vals ...interface{})
Fatalf(format string, vals ...interface{})
ShouldDebug(facility string) bool ShouldDebug(facility string) bool
SetDebug(facility string, enabled bool) SetDebug(facility string, enabled bool)
Facilities() map[string]string Facilities() map[string]string
@ -190,28 +187,6 @@ func (l *logger) Warnf(format string, vals ...interface{}) {
l.callHandlers(LevelWarn, s) l.callHandlers(LevelWarn, s)
} }
// Fatalln logs a line with a FATAL prefix and exits the process with exit
// code 1.
func (l *logger) Fatalln(vals ...interface{}) {
s := fmt.Sprintln(vals...)
l.mut.Lock()
defer l.mut.Unlock()
l.logger.Output(2, "FATAL: "+s)
l.callHandlers(LevelFatal, s)
os.Exit(1)
}
// Fatalf logs a formatted line with a FATAL prefix and exits the process with
// exit code 1.
func (l *logger) Fatalf(format string, vals ...interface{}) {
s := fmt.Sprintf(format, vals...)
l.mut.Lock()
defer l.mut.Unlock()
l.logger.Output(2, "FATAL: "+s)
l.callHandlers(LevelFatal, s)
os.Exit(1)
}
// ShouldDebug returns true if the given facility has debugging enabled. // ShouldDebug returns true if the given facility has debugging enabled.
func (l *logger) ShouldDebug(facility string) bool { func (l *logger) ShouldDebug(facility string) bool {
l.mut.Lock() l.mut.Lock()

View File

@ -923,7 +923,7 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
m.fmut.RUnlock() m.fmut.RUnlock()
if !existing { if !existing {
l.Fatalf("%v for nonexistent folder %q", op, folder) panic(fmt.Sprintf("%v for nonexistent folder %q", op, folder))
} }
if running { if running {
@ -931,7 +931,7 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
} else if update { } else if update {
// Runner may legitimately not be set if this is the "cleanup" Index // Runner may legitimately not be set if this is the "cleanup" Index
// message at startup. // message at startup.
l.Fatalf("%v for not running folder %q", op, folder) panic(fmt.Sprintf("%v for not running folder %q", op, folder))
} }
m.pmut.RLock() m.pmut.RLock()