From 742fff174a3f5baaed932afa284334ca2aa1be82 Mon Sep 17 00:00:00 2001
From: Andrew Jackson <46945903+AndrewJackson2020@users.noreply.github.com>
Date: Mon, 18 May 2026 21:37:48 -0500
Subject: [PATCH] Add systemd notify integration.
Allow systemd to have a better understanding as to when pgBackRest has finished starting or is stopping.
This implementation is based off of the existing implementations in
PostgreSQL [1] and PgBouncer [2]. PgBouncer also has an
implementation for `notify-reload` but this is not implemented here
as it is a very recent feature [3] that is unavailable on many Linux
distributions.
[1] https://www.postgresql.org/message-id/flat/CAFj8pRA4%3DhVj-d%3D8O7PSMjopsFUHPcAftd5tLqFC_xb035hNQA%40mail.gmail.com#e346a6189d8b0ed44c745c4aaaef587f
[2] https://github.com/pgbouncer/pgbouncer/commit/3816a0073f09944a6f7eaa278d2226ca4942b911
[3] https://github.com/systemd/systemd/blob/fa6d3bffe30064c4d4092b3daa749465f08d35fb/NEWS#L6176
---
doc/xml/release/2020s/2026/2.59.0.xml | 12 ++++
doc/xml/release/contributor.xml | 5 ++
doc/xml/user-guide.xml | 4 +-
meson.build | 7 +++
meson_options.txt | 1 +
src/command/server/server.c | 13 ++++
src/meson.build | 1 +
test/define.yaml | 1 +
.../pgBackRestTest/Common/ContainerTest.pm | 4 +-
test/src/common/harnessSystemd.c | 63 +++++++++++++++++++
test/src/common/harnessSystemd.h | 17 +++++
test/src/module/command/serverTest.c | 11 ++++
12 files changed, 136 insertions(+), 3 deletions(-)
create mode 100644 test/src/common/harnessSystemd.c
create mode 100644 test/src/common/harnessSystemd.h
diff --git a/doc/xml/release/2020s/2026/2.59.0.xml b/doc/xml/release/2020s/2026/2.59.0.xml
index cf56e58c0..96b103a0d 100644
--- a/doc/xml/release/2020s/2026/2.59.0.xml
+++ b/doc/xml/release/2020s/2026/2.59.0.xml
@@ -39,6 +39,18 @@
Improve seek performance during block incremental delta restore.
+
+
+
+
+
+
+
+
+
+ Add systemd notify integration.
+
+
diff --git a/doc/xml/release/contributor.xml b/doc/xml/release/contributor.xml
index c77edf488..168d4dfe5 100644
--- a/doc/xml/release/contributor.xml
+++ b/doc/xml/release/contributor.xml
@@ -80,6 +80,11 @@
anarazel
+
+ Andrew Jackson
+ AndrewJackson2020
+
+
Andrew L'Ecuyer
andrewlecuyer
diff --git a/doc/xml/user-guide.xml b/doc/xml/user-guide.xml
index 76e6511dc..93201edf4 100644
--- a/doc/xml/user-guide.xml
+++ b/doc/xml/user-guide.xml
@@ -465,7 +465,7 @@
echo 'StartLimitIntervalSec=0' | tee -a /etc/systemd/system/pgbackrest.service &&
echo '' | tee -a /etc/systemd/system/pgbackrest.service &&
echo '[Service]' | tee -a /etc/systemd/system/pgbackrest.service &&
- echo 'Type=simple' | tee -a /etc/systemd/system/pgbackrest.service &&
+ echo 'Type=notify' | tee -a /etc/systemd/system/pgbackrest.service &&
echo 'Restart=always' | tee -a /etc/systemd/system/pgbackrest.service &&
echo 'RestartSec=1' | tee -a /etc/systemd/system/pgbackrest.service &&
echo 'User={[setup-tls-user]}' | tee -a /etc/systemd/system/pgbackrest.service &&
@@ -1011,6 +1011,7 @@
apt-get install python3-distutils meson gcc libpq-dev libssl-dev libxml2-dev
pkg-config liblz4-dev libzstd-dev libbz2-dev libz-dev libyaml-dev libssh2-1-dev
+ libsystemd-dev
-y 2>&1
@@ -1019,6 +1020,7 @@
yum install meson gcc postgresql{[pg-version]}-devel openssl-devel
libxml2-devel lz4-devel libzstd-devel bzip2-devel libyaml-devel libssh2-devel
+ systemd-devel
-y 2>&1
diff --git a/meson.build b/meson.build
index 8600329bc..f858e11e2 100644
--- a/meson.build
+++ b/meson.build
@@ -211,6 +211,13 @@ lib_z = dependency('zlib')
configuration.set('ZLIB_CONST', true, description: 'Require zlib const input buffer')
+# Find optional systemd library
+lib_systemd = dependency('libsystemd', required: get_option('libsystemd'))
+
+if lib_systemd.found()
+ configuration.set('HAVE_LIBSYSTEMD', true, description: 'Is libsystemd present?')
+endif
+
# Find optional libssh2 library
lib_ssh2 = dependency('libssh2', required: get_option('libssh2'))
diff --git a/meson_options.txt b/meson_options.txt
index c304d2d7c..10e28cced 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,3 +2,4 @@ option('configdir', type: 'string', value: '/etc/pgbackrest', description: 'Conf
option('fatal-errors', type: 'boolean', value: false, description: 'Stop compilation on first error')
option('libssh2', type: 'feature', value: 'auto', description: 'Enable SFTP storage support')
option('libzstd', type: 'feature', value: 'auto', description: 'Enable Zstandard compression support')
+option('libsystemd', type: 'feature', value: 'auto', description: 'Enable systemd notify support')
diff --git a/src/command/server/server.c b/src/command/server/server.c
index cb13fa845..d48d85b05 100644
--- a/src/command/server/server.c
+++ b/src/command/server/server.c
@@ -4,6 +4,9 @@ Server Command
#include
#include
+#ifdef HAVE_LIBSYSTEMD
+#include
+#endif
#include "command/exit.h"
#include "command/remote/remote.h"
@@ -134,6 +137,11 @@ cmdServer(const unsigned int argListSize, const char *argList[])
SIGCHLD, &(struct sigaction){.sa_sigaction = cmdServerSigChild, .sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO},
NULL);
+ // Notify systemd that we are ready to accept connections
+#ifdef HAVE_LIBSYSTEMD
+ sd_notify(0, "READY=1");
+#endif
+
// Accept connections indefinitely. The only way to exit this loop is for the process to receive a signal.
do
{
@@ -196,6 +204,11 @@ cmdServer(const unsigned int argListSize, const char *argList[])
}
MEM_CONTEXT_TEMP_END();
+ // Notify systemd that we are shutting down
+#ifdef HAVE_LIBSYSTEMD
+ sd_notify(0, "STOPPING=1");
+#endif
+
// Terminate any remaining children on SIGTERM. Disable the callback so it does not fire in the middle of the loop.
if (serverLocal.sigTerm)
{
diff --git a/src/meson.build b/src/meson.build
index 3aae7b4f6..bfd3eed11 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -282,6 +282,7 @@ executable(
lib_bz2,
lib_openssl,
lib_lz4,
+ lib_systemd,
lib_pq,
lib_ssh2,
lib_xml,
diff --git a/test/define.yaml b/test/define.yaml
index e569660c9..40f3834a3 100644
--- a/test/define.yaml
+++ b/test/define.yaml
@@ -1028,6 +1028,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: server
total: 2
+ harness: systemd
coverage:
- command/server/ping
diff --git a/test/lib/pgBackRestTest/Common/ContainerTest.pm b/test/lib/pgBackRestTest/Common/ContainerTest.pm
index 864a39ed9..df05ced22 100644
--- a/test/lib/pgBackRestTest/Common/ContainerTest.pm
+++ b/test/lib/pgBackRestTest/Common/ContainerTest.pm
@@ -402,7 +402,7 @@ sub containerBuild
" perl perl-Digest-SHA perl-DBD-Pg perl-YAML-LibYAML openssl \\\n" .
" gcc make perl-ExtUtils-MakeMaker perl-Test-Simple openssl-devel perl-ExtUtils-Embed rpm-build \\\n" .
" libyaml-devel zlib-devel libxml2-devel lz4-devel lz4 bzip2-devel bzip2 perl-JSON-PP ccache meson \\\n" .
- " libssh2-devel zstd libzstd-devel";
+ " libssh2-devel zstd libzstd-devel systemd-devel";
}
elsif ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_DEBIAN)
{
@@ -414,7 +414,7 @@ sub containerBuild
" libyaml-libyaml-perl tzdata devscripts lintian libxml-checker-perl txt2man debhelper \\\n" .
" libppi-html-perl libtemplate-perl libtest-differences-perl zlib1g-dev libxml2-dev pkg-config \\\n" .
" libbz2-dev bzip2 libyaml-dev libjson-pp-perl liblz4-dev liblz4-tool gnupg lsb-release ccache meson \\\n" .
- " libssh2-1-dev libcurl4-openssl-dev";
+ " libssh2-1-dev libcurl4-openssl-dev libsystemd-dev";
if ($strOS eq VM_U22)
{
diff --git a/test/src/common/harnessSystemd.c b/test/src/common/harnessSystemd.c
new file mode 100644
index 000000000..5b32bf18c
--- /dev/null
+++ b/test/src/common/harnessSystemd.c
@@ -0,0 +1,63 @@
+/***********************************************************************************************************************************
+Systemd Harness
+***********************************************************************************************************************************/
+#include
+
+#include
+
+#include "common/debug.h"
+#include "common/log.h"
+
+#include "common/harnessDebug.h"
+#include "common/harnessSystemd.h"
+
+static struct
+{
+ bool ready;
+ bool stopping;
+} hrnSystemdStatic;
+
+/***********************************************************************************************************************************
+Shim sd_notify to track notify state
+***********************************************************************************************************************************/
+#ifdef HAVE_LIBSYSTEMD
+
+int
+sd_notify(int unset_environment, const char *state)
+{
+ FUNCTION_HARNESS_BEGIN();
+ FUNCTION_HARNESS_PARAM(INT, unset_environment);
+ FUNCTION_HARNESS_PARAM(STRINGZ, state);
+ FUNCTION_HARNESS_END();
+
+ ASSERT(unset_environment == 0);
+ ASSERT(state != NULL);
+
+ if (strcmp(state, "READY=1") == 0)
+ {
+ ASSERT(!hrnSystemdStatic.stopping);
+ hrnSystemdStatic.ready = true;
+ }
+ else if (strcmp(state, "STOPPING=1") == 0)
+ {
+ ASSERT(hrnSystemdStatic.ready);
+ hrnSystemdStatic.stopping = true;
+ }
+ else
+ THROW_FMT(AssertError, "unknown sd_notify state '%s'", state);
+
+ FUNCTION_HARNESS_RETURN(INT, 1);
+}
+
+#endif
+
+/**********************************************************************************************************************************/
+void
+hrnSystemDCheck(void)
+{
+ FUNCTION_HARNESS_VOID();
+
+ ASSERT(hrnSystemdStatic.ready && hrnSystemdStatic.stopping);
+
+ FUNCTION_HARNESS_RETURN_VOID();
+}
diff --git a/test/src/common/harnessSystemd.h b/test/src/common/harnessSystemd.h
new file mode 100644
index 000000000..97532af00
--- /dev/null
+++ b/test/src/common/harnessSystemd.h
@@ -0,0 +1,17 @@
+/***********************************************************************************************************************************
+Systemd Harness
+***********************************************************************************************************************************/
+#ifndef TEST_COMMON_HARNESS_SYSTEMD_H
+#define TEST_COMMON_HARNESS_SYSTEMD_H
+
+#ifdef HAVE_LIBSYSTEMD
+#include
+#endif
+
+/***********************************************************************************************************************************
+Functions
+***********************************************************************************************************************************/
+// Check that sd_notify calls were made
+void hrnSystemDCheck(void);
+
+#endif
diff --git a/test/src/module/command/serverTest.c b/test/src/module/command/serverTest.c
index ac5678bb4..a86b50682 100644
--- a/test/src/module/command/serverTest.c
+++ b/test/src/module/command/serverTest.c
@@ -9,6 +9,7 @@ Test Server Command
#include "common/harnessFork.h"
#include "common/harnessServer.h"
#include "common/harnessStorage.h"
+#include "common/harnessSystemd.h"
/***********************************************************************************************************************************
Test Run
@@ -168,6 +169,11 @@ testRun(void)
HRN_FORK_CHILD_NOTIFY_PUT();
exit(0);
}
+
+ // Check that sd_notify calls were made
+#ifdef HAVE_LIBSYSTEMD
+ hrnSystemDCheck();
+#endif
}
HRN_FORK_CHILD_END();
@@ -273,6 +279,11 @@ testRun(void)
HRN_FORK_CHILD_NOTIFY_PUT();
exit(0);
}
+
+ // Check that sd_notify calls were made
+#ifdef HAVE_LIBSYSTEMD
+ hrnSystemDCheck();
+#endif
}
HRN_FORK_CHILD_END();