summaryrefslogtreecommitdiffstats
path: root/src/systemd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/systemd.c')
-rw-r--r--src/systemd.c134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/systemd.c b/src/systemd.c
new file mode 100644
index 0000000..fb36dd9
--- /dev/null
+++ b/src/systemd.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: MIT-0 */
+
+/* Implement the systemd notify protocol without external dependencies.
+ * Supports both readiness notification on startup and on 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/
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <haproxy/tools.h>
+
+/*
+ * standalone reimplementation of sd_notify from the libsystemd
+ * Return:
+ * -errno in case of error
+ * 0 when ignored
+ * >0 when succeeded
+ *
+ * Will send <message> over the NOTIFY_SOCKET.
+ * When unset_environement is set, unsetenv NOTIFY_SOCKET.
+ */
+int sd_notify(int unset_environment, const char *message)
+{
+ union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } socket_addr = {
+ .sun.sun_family = AF_UNIX,
+ };
+ int ret = 1;
+ int fd = -1;
+ size_t path_length, message_length;
+ const char *socket_path;
+ ssize_t written;
+
+ socket_path = getenv("NOTIFY_SOCKET");
+ if (!socket_path) {
+ ret = 0; /* Not running under systemd? Nothing to do */
+ goto end;
+ }
+
+ if (unset_environment)
+ unsetenv("NOTIFY_SOCKET");
+
+ if (!message) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ message_length = strlen(message);
+ if (message_length == 0) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Only AF_UNIX is supported, with path or abstract sockets */
+ if (socket_path[0] != '/' && socket_path[0] != '@') {
+ ret = -EAFNOSUPPORT;
+ goto end;
+ }
+
+ path_length = strlen(socket_path);
+ /* Ensure there is room for NUL byte */
+ if (path_length >= sizeof(socket_addr.sun.sun_path)) {
+ ret = -E2BIG;
+ goto end;
+ }
+
+ memcpy(socket_addr.sun.sun_path, socket_path, path_length);
+
+ /* Support for abstract socket */
+ if (socket_addr.sun.sun_path[0] == '@')
+ socket_addr.sun.sun_path[0] = 0;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ ret = -errno;
+ goto end;
+ }
+
+ if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0) {
+ ret = -errno;
+ goto end;
+ }
+
+ written = write(fd, message, message_length);
+ if (written != (ssize_t) message_length) {
+ ret = written < 0 ? -errno : -EPROTO;
+ goto end;
+ }
+
+end:
+ if (fd > -1)
+ close(fd);
+ return ret; /* Notified! */
+}
+
+/* va_args variant of sd_notify */
+int sd_notifyf(int unset_environment, const char *format, ...)
+{
+ int r;
+ va_list args;
+ char *strp = NULL;
+
+ va_start(args, format);
+ strp = memvprintf(&strp, format, args);
+ va_end(args);
+
+ if (strp == NULL) {
+ r = -ENOMEM;
+ goto end;
+ }
+
+ r = sd_notify(unset_environment, strp);
+ free(strp);
+end:
+ return r;
+}
+