Skip to content
Snippets Groups Projects
Commit c4a56105 authored by Frédéric Guillot's avatar Frédéric Guillot Committed by fguillot
Browse files

Add Systemd watchdog

parent 1005fb97
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"miniflux.app/service/httpd" "miniflux.app/service/httpd"
"miniflux.app/service/scheduler" "miniflux.app/service/scheduler"
"miniflux.app/storage" "miniflux.app/storage"
"miniflux.app/systemd"
"miniflux.app/worker" "miniflux.app/worker"
) )
...@@ -44,9 +45,35 @@ func startDaemon(store *storage.Storage) { ...@@ -44,9 +45,35 @@ func startDaemon(store *storage.Storage) {
go collector.GatherStorageMetrics() go collector.GatherStorageMetrics()
} }
// Notify systemd that we are ready. if systemd.HasNotifySocket() {
if err := sdNotify(sdNotifyReady); err != nil { logger.Info("Sending readiness notification to Systemd")
logger.Error("Unable to send readiness notification to systemd: %v", err)
if err := systemd.SdNotify(systemd.SdNotifyReady); err != nil {
logger.Error("Unable to send readiness notification to systemd: %v", err)
}
if systemd.HasSystemdWatchdog() {
logger.Info("Activating Systemd watchdog")
go func() {
interval, err := systemd.WatchdogInterval()
if err != nil {
logger.Error("Unable to parse watchdog interval from systemd: %v", err)
return
}
for {
err := store.Ping()
if err != nil {
logger.Error(`Systemd Watchdog: %v`, err)
} else {
systemd.SdNotify(systemd.SdNotifyWatchdog)
}
time.Sleep(interval / 2)
}
}()
}
} }
<-stop <-stop
......
...@@ -5,17 +5,27 @@ ...@@ -5,17 +5,27 @@
# See https://wiki.archlinux.org/index.php/Systemd#Editing_provided_units. # See https://wiki.archlinux.org/index.php/Systemd#Editing_provided_units.
[Unit] [Unit]
Description=Miniflux Feed Reader Description=Miniflux
After=network.target postgresql.service After=network.target postgresql.service
[Service] [Service]
Type=notify
ExecStart=/usr/bin/miniflux ExecStart=/usr/bin/miniflux
Restart=always
EnvironmentFile=/etc/miniflux.conf EnvironmentFile=/etc/miniflux.conf
User=miniflux User=miniflux
# https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
Type=notify
# https://www.freedesktop.org/software/systemd/man/systemd.service.html#WatchdogSec=
WatchdogSec=30s
WatchdogSignal=SIGKILL
# https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=
Restart=always
# https://www.freedesktop.org/software/systemd/man/systemd.service.html#RestartSec=
RestartSec=5
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges= # https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=
NoNewPrivileges=true NoNewPrivileges=true
...@@ -45,13 +55,9 @@ RestrictRealtime=true ...@@ -45,13 +55,9 @@ RestrictRealtime=true
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ReadWritePaths= # https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ReadWritePaths=
ReadWritePaths=/run ReadWritePaths=/run
# Allow miniflux to bind to <1024 ports # Allow miniflux to bind to privileged ports
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities= # https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=
AmbientCapabilities=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE
# Provide a private /tmp
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=
PrivateTmp=true
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
...@@ -2,29 +2,61 @@ ...@@ -2,29 +2,61 @@
// Use of this source code is governed by the Apache 2.0 // Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package cli // import "miniflux.app/cli" package systemd // import "miniflux.app/systemd"
import ( import (
"fmt"
"net" "net"
"os" "os"
"strconv"
"time"
) )
const ( const (
// sdNotifyReady tells the service manager that service startup is // SdNotifyReady tells the service manager that service startup is
// finished, or the service finished loading its configuration. // finished, or the service finished loading its configuration.
sdNotifyReady = "READY=1" // https://www.freedesktop.org/software/systemd/man/sd_notify.html#READY=1
SdNotifyReady = "READY=1"
// SdNotifyWatchdog the service manager to update the watchdog timestamp.
// https://www.freedesktop.org/software/systemd/man/sd_notify.html#WATCHDOG=1
SdNotifyWatchdog = "WATCHDOG=1"
) )
// sdNotify sends a message to systemd using the sd_notify protocol. // HasNotifySocket checks if the process is supervised by Systemd and has the notify socket.
func HasNotifySocket() bool {
return os.Getenv("NOTIFY_SOCKET") != ""
}
// HasSystemdWatchdog checks if the watchdog is configured in Systemd unit file.
func HasSystemdWatchdog() bool {
return os.Getenv("WATCHDOG_USEC") != ""
}
// WatchdogInterval returns the watchdog interval configured in systemd unit file.
func WatchdogInterval() (time.Duration, error) {
s, err := strconv.Atoi(os.Getenv("WATCHDOG_USEC"))
if err != nil {
return 0, fmt.Errorf(`systemd: error converting WATCHDOG_USEC: %v`, err)
}
if s <= 0 {
return 0, fmt.Errorf(`systemd: error WATCHDOG_USEC must be a positive number`)
}
return time.Duration(s) * time.Microsecond, nil
}
// SdNotify sends a message to systemd using the sd_notify protocol.
// See https://www.freedesktop.org/software/systemd/man/sd_notify.html. // See https://www.freedesktop.org/software/systemd/man/sd_notify.html.
func sdNotify(state string) error { func SdNotify(state string) error {
addr := &net.UnixAddr{ addr := &net.UnixAddr{
Net: "unixgram", Net: "unixgram",
Name: os.Getenv("NOTIFY_SOCKET"), Name: os.Getenv("NOTIFY_SOCKET"),
} }
if addr.Name == "" { if addr.Name == "" {
// We're not running under systemd (NOTIFY_SOCKET has not set). // We're not running under systemd (NOTIFY_SOCKET is not set).
return nil return nil
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment