Ekaterina Lebedeva
ec1509de4e
All checks were successful
DCO action / DCO (pull_request) Successful in 1m40s
Tests and linters / Run gofumpt (pull_request) Successful in 1m37s
Vulncheck / Vulncheck (pull_request) Successful in 2m12s
Build / Build Components (1.22) (pull_request) Successful in 2m33s
Build / Build Components (1.21) (pull_request) Successful in 2m35s
Tests and linters / Tests (1.21) (pull_request) Successful in 2m34s
Pre-commit hooks / Pre-commit (pull_request) Successful in 2m45s
Tests and linters / Tests (1.22) (pull_request) Successful in 3m2s
Tests and linters / Staticcheck (pull_request) Successful in 3m3s
Tests and linters / Lint (pull_request) Successful in 3m27s
Tests and linters / Tests with -race (pull_request) Successful in 3m42s
Tests and linters / gopls check (pull_request) Successful in 3m51s
The synchronized service reload protocol added in systemd version 253 requires that the service provides a MONOTONIC_USEC field alongside the RELOADING=1 notification message for synchronization purposes. The value carried in this field must be the system CLOCK_MONOTONIC timestamp at the time the notification message was generated as systemd compares it to other CLOCK_MONOTONIC timestamps taken by pid1. Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
package sdnotify
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const (
|
|
ReadyEnabled = "READY=1"
|
|
StoppingEnabled = "STOPPING=1"
|
|
ReloadingEnabled = "RELOADING=1"
|
|
)
|
|
|
|
var (
|
|
socket *net.UnixAddr
|
|
|
|
errSocketVariableIsNotPresent = errors.New("\"NOTIFY_SOCKET\" environment variable is not present")
|
|
errSocketIsNotInitialized = errors.New("socket is not initialized")
|
|
)
|
|
|
|
// Initializes socket with provided name of
|
|
// environment variable.
|
|
func InitSocket() error {
|
|
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
|
if notifySocket == "" {
|
|
return errSocketVariableIsNotPresent
|
|
}
|
|
socket = &net.UnixAddr{
|
|
Name: notifySocket,
|
|
Net: "unixgram",
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FlagAndStatus sends systemd a combination of a
|
|
// well-known status and STATUS=%s{status}, separated by newline.
|
|
func FlagAndStatus(status string) error {
|
|
if status == ReloadingEnabled {
|
|
// From https://www.man7.org/linux/man-pages/man5/systemd.service.5.html
|
|
//
|
|
// When initiating the reload process the service is
|
|
// expected to reply with a notification message via
|
|
// sd_notify(3) that contains the "RELOADING=1" field in
|
|
// combination with "MONOTONIC_USEC=" set to the current
|
|
// monotonic time (i.e. CLOCK_MONOTONIC in
|
|
// clock_gettime(2)) in μs, formatted as decimal string.
|
|
// Once reloading is complete another notification message
|
|
// must be sent, containing "READY=1".
|
|
//
|
|
// For MONOTONIC_USEC format refer to https://www.man7.org/linux/man-pages/man3/sd_notify.3.html
|
|
var ts unix.Timespec
|
|
if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts); err != nil {
|
|
return fmt.Errorf("clock_gettime: %w", err)
|
|
}
|
|
status += "\nMONOTONIC_USEC=" + strconv.FormatInt(ts.Nano()/1000, 10)
|
|
}
|
|
status += "\nSTATUS=" + strings.TrimSuffix(status, "=1")
|
|
return Send(status)
|
|
}
|
|
|
|
// Status sends systemd notify STATUS=%s{status}.
|
|
func Status(status string) error {
|
|
return Send("STATUS=" + status)
|
|
}
|
|
|
|
// Send state through the notify socket if any.
|
|
// If the notify socket was not detected, it returns an error.
|
|
func Send(state string) error {
|
|
if socket == nil {
|
|
return errSocketIsNotInitialized
|
|
}
|
|
conn, err := net.DialUnix(socket.Net, nil, socket)
|
|
if err != nil {
|
|
return fmt.Errorf("can't open unix socket: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if _, err = conn.Write([]byte(state)); err != nil {
|
|
return fmt.Errorf("can't write into the unix socket: %v", err)
|
|
}
|
|
return nil
|
|
}
|