feat: update linux service layer implementation, remove SIGUSR1+2 handlers, simpler unified heartbeat/signal routine

This commit is contained in:
2025-06-23 16:27:52 -05:00
parent 15b0e93feb
commit 1f4728cab1

View File

@@ -41,50 +41,53 @@ func (s *linuxService) Run() error {
// Setup signal handling for systemd // Setup signal handling for systemd
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
done := make(chan struct{})
// Notify systemd that we are ready // Setup heartbeat to systemd
daemon.SdNotify(false, "READY=1") ticker := time.NewTicker(30 * time.Second)
daemon.SdNotify(false, "STATUS=HATray running\n") defer ticker.Stop()
// Start the service (backgrounded so that the service can still respond to systemd signals, the app layer is still designed for concurrency)
go func() { go func() {
for { if err := s.app.Resume(); err != nil {
sig := <-sigs s.logger.Error("failed to start (resume) app layer", "error", err)
// TODO: This has no true error handling, retry mechanism, or timeout mechanism. If this fails, then the service will be stuck in the 'StartPending' state.
}
// Notify systemd that we are ready (and running)
daemon.SdNotify(false, "READY=1")
daemon.SdNotify(false, "STATUS=HATray running\n")
}()
for {
select {
case <-ticker.C:
daemon.SdNotify(false, "WATCHDOG=1")
s.logger.Debug("heartbeat") // TODO: add more detailed status information here
case sig := <-sigs:
s.logger.Info("signal received", "signal", sig) s.logger.Info("signal received", "signal", sig)
switch sig { switch sig {
case syscall.SIGINT, syscall.SIGTERM: case syscall.SIGINT, syscall.SIGTERM:
daemon.SdNotify(false, "STOPPING=1") daemon.SdNotify(false, "STOPPING=1")
s.logger.Info("stopping service") s.logger.Info("stopping service")
s.app.Stop()
close(done) if err := s.app.Pause(); err != nil {
return s.logger.Error("failed to pause app layer", "error", err)
}
return nil // exit the service
case syscall.SIGHUP: case syscall.SIGHUP:
s.logger.Info("reloading service") s.logger.Info("reloading service")
daemon.SdNotify(false, "RELOADING=1") daemon.SdNotify(false, "RELOADING=1")
s.app.Reload()
daemon.SdNotify(false, "READY=1")
case syscall.SIGUSR1:
s.logger.Info("pausing service")
s.app.Pause()
case syscall.SIGUSR2:
s.logger.Info("resuming service")
s.app.Resume()
}
}
}()
// Main loop: heartbeat to systemd if err := s.app.Reload(); err != nil {
ticker := time.NewTicker(30 * time.Second) s.logger.Error("failed to reload app layer", "error", err)
defer ticker.Stop() }
for {
select { daemon.SdNotify(false, "READY=1")
case <-done: }
s.logger.Info("service stopped")
return nil
case <-ticker.C:
daemon.SdNotify(false, "WATCHDOG=1")
s.logger.Debug("heartbeat")
} }
} }
} }