test: update serv daemon to use proper systemd abstract socket

This commit introduces a stripped down sd_notify(), taken from the
systemd man page example, which is used by the serv daemon in lieu
of the previous broken implementation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2025-07-02 16:36:59 +02:00
1 parent 76f8a18816
commit d0d9b83b66
4 files changed
+114 -12

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
noinst_PROGRAMS = serv noinst_PROGRAMS = serv
serv_SOURCES = serv.c serv_SOURCES = serv.c sd-daemon.c sd-daemon.h
serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
+84
View File
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: MIT-0 */
/*
* systemd notify protocol, supporting readiness notification on startup
* and reloading, according to the protocol defined at:
* https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
*
* This protocol is guaranteed to be stable as per:
* https://systemd.io/PORTABILITY_AND_STABILITY/
*/
#define _GNU_SOURCE 1
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
/*
* NOTE: As per spec., this function does not return POSIX OK(0) but
* rather "a positive value", i.e., 1.
*/
int sd_notify(int unset_environment, const char *state)
{
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
};
size_t len, sun_len;
const char *path;
ssize_t written;
int sd;
if (!state)
return -EINVAL;
len = strlen(state);
if (len == 0)
return -EINVAL;
/* If the variable is not set, the protocol is a noop */
path = getenv("NOTIFY_SOCKET");
if (!path)
return 0; /* Not set? Nothing to do */
/* Only AF_UNIX is supported, with path or abstract sockets */
if (path[0] != '/' && path[0] != '@')
return -EAFNOSUPPORT;
/* Ensure there is room for NUL byte */
sun_len = strlen(path);
if (sun_len >= sizeof(sun.sun_path))
return -E2BIG;
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
if (sun.sun_path[0] == '@')
sun.sun_path[0] = 0;
sd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sd == -1)
return -errno;
if (connect(sd, (struct sockaddr *)&sun, offsetof(struct sockaddr_un, sun_path) + sun_len) == -1) {
close(sd);
return -errno;
}
if (unset_environment)
unsetenv("NOTIFY_SOCKET");
written = write(sd, state, len);
if (written != (ssize_t) len) {
close(sd);
return written < 0 ? -errno : -EPROTO;
}
return 1;
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef SD_DAEMON_H_
#define SD_DAEMON_H_
int sd_notify(int unset_environment, const char *state);
#endif /* SD_DAEMON_H_ */
+23 -11
View File
@@ -16,6 +16,7 @@
#include <sysexits.h> #include <sysexits.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "sd-daemon.h"
#define PROGNM "serv" #define PROGNM "serv"
@@ -160,7 +161,7 @@ static int usage(int rc)
" -h Show help text (this)\n" " -h Show help text (this)\n"
" -i IDENT Change process identity, incl. logs, pidfile, etc.\n" " -i IDENT Change process identity, incl. logs, pidfile, etc.\n"
" -n Run in foreground\n" " -n Run in foreground\n"
" -N SOCK Send 'READY=1\\n' on $NOTIFY_SOCKET or SOCK\n" " -N SOCK Send '\\n' on SOCK (integer), for s6 readiness\n"
" -p Create PID file despite running in foreground\n" " -p Create PID file despite running in foreground\n"
" -P FILE Create PID file using FILE\n" " -P FILE Create PID file using FILE\n"
" -r SVC Call initctl to restart service SVC (self)\n" " -r SVC Call initctl to restart service SVC (self)\n"
@@ -170,6 +171,8 @@ static int usage(int rc)
"to let the rest of the system know it's done. When the program runs\n" "to let the rest of the system know it's done. When the program runs\n"
"in the foreground it does not create a PID file by default.\n" "in the foreground it does not create a PID file by default.\n"
"\n" "\n"
"When acting as a systemd daemon, this program expects NOTIFY_SOCKET\n"
"\n"
"Regardless of how This daemon is started it provides a single service\n" "Regardless of how This daemon is started it provides a single service\n"
"to others, spice ... guarded by sandworms.\n", "to others, spice ... guarded by sandworms.\n",
ident); ident);
@@ -256,13 +259,13 @@ int main(int argc, char *argv[])
if (do_pidfile > 0) if (do_pidfile > 0)
pidfile(pidfn); pidfile(pidfn);
if (!do_notify) { if (!do_notify && getenv("NOTIFY_SOCKET"))
const char *sock = getenv("NOTIFY_SOCKET"); do_notify = 1;
/* systemd style */ if (do_notify)
if (sock) inf("Will notify %s ...", notify_s6 ? "s6" : "systemd");
do_notify = atoi(sock); else
} inf("No notify socket ...");
if (do_crash) { if (do_crash) {
inf("Simulating crash, exiting with code %d", EX_SOFTWARE); inf("Simulating crash, exiting with code %d", EX_SOFTWARE);
@@ -279,15 +282,24 @@ int main(int argc, char *argv[])
if (reloading) { if (reloading) {
if (do_notify > 0) { if (do_notify > 0) {
/* Issue #343: see notify.sh test for more details */
inf("Delaying notify by 3 seconds for notify.sh ..."); inf("Delaying notify by 3 seconds for notify.sh ...");
sleep(3); sleep(3);
inf("Notifying Finit on socket %d, READY=1", do_notify);
if (write(do_notify, "READY=1\n", 8) == -1)
err(1, "Failed sending ready notification to Finit");
if (notify_s6) { if (notify_s6) {
inf("Notifying Finit on socket %d", do_notify);
if (write(do_notify, "\n", 1) < 1)
err(1, "Failed sending ready notification to Finit");
inf("s6 notify, closing socket %d ...", do_notify); inf("s6 notify, closing socket %d ...", do_notify);
close(do_notify); if (close(do_notify))
err(1, "Failed closing notify socket");
do_notify = 0; do_notify = 0;
} else {
int rc;
inf("Notifying Finit on NOTIFY_SOCKET");
rc = sd_notify(0, "READY=1");
inf("sd_notify () => %d", rc);
} }
} }
if (do_pidfile > 0) if (do_pidfile > 0)