main: add support for systemd notification

On Linux, if the NOTIFY_SOCKET variable is set, send a "READY=1"
and "STOPPING=1" message to the Unix domain socket after initialization
and before finalization respectively. This is used with the systemd
"notify" service type as documented in the sd_notity(3) man page. It's
a recommended alternative to the "forking" service type, which does not
need the PID file to determine the main process.

Support pathname Unix sockets only. Abstract sockets don't seem to be
used by systemd for notifications since version 212.

Switch the example services to the notify type, but keep the PID
file. It's still useful to prevent start of other chronyd instances.
systemd doesn't seem to care about the content of the file and should
just remove it in case chronyd didn't terminate cleanly.

Suggested-by: Luca Boccassi <bluca@debian.org>
This commit is contained in:
Miroslav Lichvar
2025-02-05 09:30:46 +01:00
parent 8ee725ff18
commit cb1118456b
4 changed files with 43 additions and 4 deletions

View File

@@ -217,6 +217,14 @@ client requests until the service is able to handle them. The service manager
sets the LISTEN_FDS environment variable to the number of passed file
descriptors.
*NOTIFY_SOCKET*::
The systemd service manager sets this variable for services of the *notify*
type. *chronyd* sends a message to this socket when it it is fully initialised
and ready to accept commands (e.g. from *chronyc*), with the clock already set
if using the *-s* option or *initstepslew* directive. It is an alternative to
the *forking* service type, which does not need the PID file. *chronyd* needs
to be started with the *-n* or *-d* option to not fork.
== FILES
_@SYSCONFDIR@/chrony.conf_

View File

@@ -12,10 +12,10 @@ Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
Type=notify
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd -U $OPTIONS
ExecStart=/usr/sbin/chronyd -n -U $OPTIONS
User=chrony
LogsDirectory=chrony

View File

@@ -6,10 +6,10 @@ Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
Type=notify
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
ExecStart=/usr/sbin/chronyd -n $OPTIONS
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE

31
main.c
View File

@@ -107,11 +107,40 @@ delete_pidfile(void)
/* ================================================== */
static void
notify_system_manager(int start)
{
#ifdef LINUX
/* The systemd protocol is documented in the sd_notify(3) man page */
const char *message, *path = getenv("NOTIFY_SOCKET");
int sock_fd;
if (!path)
return;
if (path[0] != '/')
LOG_FATAL("Unsupported notification socket");
message = start ? "READY=1" : "STOPPING=1";
sock_fd = SCK_OpenUnixDatagramSocket(path, NULL, 0);
if (sock_fd < 0 || SCK_Send(sock_fd, message, strlen(message), 0) != strlen(message))
LOG_FATAL("Could not send notification");
SCK_CloseSocket(sock_fd);
#endif
}
/* ================================================== */
void
MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
notify_system_manager(0);
LCL_CancelOffsetCorrection();
SRC_DumpSources();
@@ -215,6 +244,8 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
notify_system_manager(1);
/* Send an empty message to the foreground process so it can exit.
If that fails, indicating the process was killed, exit too. */
if (!LOG_NotifyParent(""))