mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
But not using it as some linters are either plain incorrect (the one suggesting to not use nil for `c.t.Context()`) or just debatable (checking for err value is a good practice, but there are good reasons to opt out in some cases).
107 lines
2.3 KiB
Go
107 lines
2.3 KiB
Go
// SPDX-FileCopyrightText: 2022 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
// Package daemon will handle daemon-related operations: readiness,
|
|
// watchdog, exit, reexec... Currently, only exit is implemented as
|
|
// other operations do not mean much when running in Docker.
|
|
package daemon
|
|
|
|
import (
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"gopkg.in/tomb.v2"
|
|
|
|
"akvorado/common/reporter"
|
|
)
|
|
|
|
// Component is the interface the daemon component provides.
|
|
type Component interface {
|
|
Start() error
|
|
Stop() error
|
|
Track(t *tomb.Tomb, who string)
|
|
|
|
// Lifecycle
|
|
Terminated() <-chan struct{}
|
|
Terminate()
|
|
}
|
|
|
|
// realComponent is a non-mock implementation of the Component
|
|
// interface.
|
|
type realComponent struct {
|
|
r *reporter.Reporter
|
|
tombs []tombWithOrigin
|
|
|
|
lifecycleComponent
|
|
}
|
|
|
|
// tombWithOrigin stores a reference to a tomb and its origin
|
|
type tombWithOrigin struct {
|
|
tomb *tomb.Tomb
|
|
origin string
|
|
}
|
|
|
|
// New will create a new daemon component.
|
|
func New(r *reporter.Reporter) (Component, error) {
|
|
return &realComponent{
|
|
r: r,
|
|
lifecycleComponent: lifecycleComponent{
|
|
terminateChannel: make(chan struct{}),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Start will make the daemon component active.
|
|
func (c *realComponent) Start() error {
|
|
// Listen for tombs
|
|
for _, t := range c.tombs {
|
|
go func(t tombWithOrigin) {
|
|
<-t.tomb.Dying()
|
|
if t.tomb.Err() == nil {
|
|
c.r.Debug().
|
|
Str("component", t.origin).
|
|
Msg("component shutting down, quitting")
|
|
} else {
|
|
c.r.Err(t.tomb.Err()).
|
|
Str("component", t.origin).
|
|
Msg("component error, quitting")
|
|
}
|
|
c.Terminate()
|
|
}(t)
|
|
}
|
|
// On signal, terminate
|
|
go func() {
|
|
signals := make(chan os.Signal, 1)
|
|
signal.Notify(signals,
|
|
syscall.SIGINT, syscall.SIGTERM)
|
|
select {
|
|
case s := <-signals:
|
|
c.r.Debug().Stringer("signal", s).Msg("signal received")
|
|
switch s {
|
|
case syscall.SIGINT, syscall.SIGTERM:
|
|
c.r.Info().Msg("quitting")
|
|
c.Terminate()
|
|
signal.Stop(signals)
|
|
}
|
|
case <-c.Terminated():
|
|
// Do nothing.
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// Stop will stop the component.
|
|
func (c *realComponent) Stop() error {
|
|
c.Terminate()
|
|
return nil
|
|
}
|
|
|
|
// Add a new tomb to be tracked. This is only used before Start().
|
|
func (c *realComponent) Track(t *tomb.Tomb, who string) {
|
|
c.tombs = append(c.tombs, tombWithOrigin{
|
|
tomb: t,
|
|
origin: who,
|
|
})
|
|
}
|