frostfs-node/pkg/util/sdnotify/sdnotify.go

93 lines
2.5 KiB
Go
Raw Normal View History

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")
)
// InitSocket 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)
}
// ClearStatus resets the current service status previously set by Status.
func ClearStatus() error {
return 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
}