From d28af58cbbf955f2270babb5c7101b19412919ff Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Mon, 14 Apr 2025 17:38:56 +0200 Subject: [PATCH] agent: implement sd-notify reload correctly (#25636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First of all, we should not send the unix time, but the monotonic time. Second of all, RELOADING= and MONOTONIC_USEC fields should be sent in *single* message not two separate messages. From the man page of [systemd.service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Type=) > 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. [sd_notify](https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html) now has code samples of the protocol to clarify. Without these changes, if you'd set Type=notify-reload on the agen'ts systemd unit, systemd would kill the service due to the service not responding to reload correctly. --- .changelog/25636.txt | 3 +++ command/agent/command.go | 13 +------------ command/agent/sdnotify.go | 12 ++++++++++++ command/agent/sdnotify_default.go | 2 ++ command/agent/sdnotify_linux.go | 9 +++++++++ 5 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 .changelog/25636.txt create mode 100644 command/agent/sdnotify.go diff --git a/.changelog/25636.txt b/.changelog/25636.txt new file mode 100644 index 000000000..fa01e0516 --- /dev/null +++ b/.changelog/25636.txt @@ -0,0 +1,3 @@ +```release-note:bug +agent: Fixed a bug where reloading the agent with systemd notification enabled would cause the agent to be killed by system +``` diff --git a/command/agent/command.go b/command/agent/command.go index 4d72029b0..77054b324 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -978,16 +978,6 @@ func (c *Command) handleRetryJoin(config *Config) error { return nil } -// These constants are for readiness signalling via the systemd notify protocol. -// The functions we send these messages to are no-op on non-Linux systems. See -// also https://www.man7.org/linux/man-pages/man3/sd_notify.3.html -const ( - sdReady = "READY=1" - sdReloading = "RELOADING=1" - sdStopping = "STOPPING=1" - sdMonotonic = "MONOTONIC_USEC=%d" -) - // handleSignals blocks until we get an exit-causing signal func (c *Command) handleSignals() int { signalCh := make(chan os.Signal, 4) @@ -1026,8 +1016,7 @@ WAIT: // Check if this is a SIGHUP if sig == syscall.SIGHUP { - sdNotify(sdSock, sdReloading) - sdNotify(sdSock, fmt.Sprintf(sdMonotonic, time.Now().UnixMicro())) + sdNotifyReloading(sdSock) c.handleReload() sdNotify(sdSock, sdReady) goto WAIT diff --git a/command/agent/sdnotify.go b/command/agent/sdnotify.go new file mode 100644 index 000000000..b977139f6 --- /dev/null +++ b/command/agent/sdnotify.go @@ -0,0 +1,12 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package agent + +// These constants are for readiness signalling via the systemd notify protocol. +// The functions we send these messages to are no-op on non-Linux systems. See +// also https://www.man7.org/linux/man-pages/man3/sd_notify.3.html +const ( + sdReady = "READY=1" + sdStopping = "STOPPING=1" +) diff --git a/command/agent/sdnotify_default.go b/command/agent/sdnotify_default.go index 7ce1f03ee..eb394c750 100644 --- a/command/agent/sdnotify_default.go +++ b/command/agent/sdnotify_default.go @@ -14,3 +14,5 @@ func openNotify() (io.WriteCloser, error) { } func sdNotify(_ io.Writer, _ string) {} + +func sdNotifyReloading(_ io.Writer) {} diff --git a/command/agent/sdnotify_linux.go b/command/agent/sdnotify_linux.go index 707ec15f0..e46c158d7 100644 --- a/command/agent/sdnotify_linux.go +++ b/command/agent/sdnotify_linux.go @@ -6,10 +6,13 @@ package agent import ( + "fmt" "io" "net" "os" "time" + + "golang.org/x/sys/unix" ) const sdNotifySocketEnvVar = "NOTIFY_SOCKET" @@ -38,3 +41,9 @@ func sdNotify(w io.Writer, msg string) { } w.Write([]byte(msg)) } + +func sdNotifyReloading(w io.Writer) { + var ts unix.Timespec + unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts) + sdNotify(w, fmt.Sprintf("RELOADING=1\nMONOTONIC_USEC=%d", ts.Nano()/1000)) +}