diff --git a/internal/service/linux.go b/internal/service/linux.go index c0994dd..e01e7a0 100644 --- a/internal/service/linux.go +++ b/internal/service/linux.go @@ -41,50 +41,53 @@ func (s *linuxService) Run() error { // Setup signal handling for systemd sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2) - done := make(chan struct{}) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) - // Notify systemd that we are ready - daemon.SdNotify(false, "READY=1") - daemon.SdNotify(false, "STATUS=HATray running\n") + // Setup heartbeat to systemd + ticker := time.NewTicker(30 * time.Second) + 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() { - for { - sig := <-sigs + if err := s.app.Resume(); err != nil { + 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) + switch sig { case syscall.SIGINT, syscall.SIGTERM: daemon.SdNotify(false, "STOPPING=1") s.logger.Info("stopping service") - s.app.Stop() - close(done) - return + + if err := s.app.Pause(); err != nil { + s.logger.Error("failed to pause app layer", "error", err) + } + + return nil // exit the service case syscall.SIGHUP: s.logger.Info("reloading service") 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 - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - for { - select { - case <-done: - s.logger.Info("service stopped") - return nil - case <-ticker.C: - daemon.SdNotify(false, "WATCHDOG=1") - s.logger.Debug("heartbeat") + if err := s.app.Reload(); err != nil { + s.logger.Error("failed to reload app layer", "error", err) + } + + daemon.SdNotify(false, "READY=1") + } } } }