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();