mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-08 02:17:25 +02:00
fix(server): exit with an error code when the server fails to start (#6236)
When a startup step failed (for example, the port was already in use), runNavidrome only logged the error and returned. In service mode, service.Run() kept waiting for a stop signal, so the process stayed up serving nothing and the service manager never restarted it. A plain run exited with code 0. runNavidrome now returns the error, unless its context was cancelled by a normal shutdown. Both the plain run and the service goroutine exit with code 1 on that error. The systemd unit no longer lists 1, 2 and 8 in SuccessExitStatus, so Restart=on-failure restarts the service on exit code 1. Fixes #6235
This commit is contained in:
parent
46c432719f
commit
ce484083bf
2 changed files with 19 additions and 10 deletions
20
cmd/root.go
20
cmd/root.go
|
|
@ -44,7 +44,9 @@ Complete documentation is available at https://www.navidrome.org/docs`,
|
|||
preRun()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runNavidrome(cmd.Context())
|
||||
if err := runNavidrome(cmd.Context()); err != nil {
|
||||
log.Fatal("Fatal error in Navidrome. Aborting", err)
|
||||
}
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
postRun()
|
||||
|
|
@ -76,12 +78,12 @@ func postRun() {
|
|||
}
|
||||
|
||||
// runNavidrome is the main entry point for the Navidrome server. It starts all the services and blocks.
|
||||
// If any of the services returns an error, it will log it and exit. If the process receives a signal to exit,
|
||||
// it will cancel the context and exit gracefully.
|
||||
func runNavidrome(ctx context.Context) {
|
||||
defer db.Init(ctx)()
|
||||
// If any of the services returns an error, it stops the others and returns that error, so the caller can
|
||||
// exit with a non-zero code. If the context is cancelled (a signal or a service stop), it returns nil.
|
||||
func runNavidrome(parentCtx context.Context) error {
|
||||
defer db.Init(parentCtx)()
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
g, ctx := errgroup.WithContext(parentCtx)
|
||||
g.Go(startServer(ctx))
|
||||
g.Go(startSignaller(ctx))
|
||||
g.Go(startScheduler(ctx))
|
||||
|
|
@ -102,9 +104,11 @@ func runNavidrome(ctx context.Context) {
|
|||
log.Warn(ctx, "Automatic Scanning is DISABLED")
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
log.Error("Fatal error in Navidrome. Aborting", err)
|
||||
// Errors caused by a normal shutdown are not failures
|
||||
if err := g.Wait(); err != nil && parentCtx.Err() == nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mainContext returns a context that is cancelled when the process receives a signal to exit.
|
||||
|
|
|
|||
|
|
@ -53,8 +53,13 @@ func (p *svcControl) Start(service.Service) error {
|
|||
p.done = make(chan struct{})
|
||||
p.ctx, p.cancel = context.WithCancel(context.Background())
|
||||
go func() {
|
||||
runNavidrome(p.ctx)
|
||||
err := runNavidrome(p.ctx)
|
||||
close(p.done)
|
||||
// service.Run() only returns when it gets a stop request, so exit here to let the
|
||||
// service manager see the failure and restart the service
|
||||
if err != nil {
|
||||
log.Fatal("Fatal error in Navidrome. Aborting", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -74,7 +79,7 @@ func (p *svcControl) Stop(service.Service) error {
|
|||
var svcInstance = sync.OnceValue(func() service.Service {
|
||||
options := make(service.KeyValue)
|
||||
options["Restart"] = "on-failure"
|
||||
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
|
||||
options["SuccessExitStatus"] = "SIGKILL"
|
||||
options["UserService"] = false
|
||||
options["LogDirectory"] = conf.Server.DataFolder.String()
|
||||
options["SystemdScript"] = systemdScript
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue