Files
akvorado/common/daemon/root.go
Vincent Bernat 78fb01c223 chore: fix some small issues detected by golangci-lint
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).
2022-08-10 17:44:32 +02:00

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,
})
}