Skip to content

Commit 7071430

Browse files
committed
Support sd_notify to signal readiness
At this point, it considers it 'ready' when the threads' init functions have run. This should be enhanced with a facility to signal readiness from the plugin, asynchronously.
1 parent d41ce9d commit 7071430

File tree

5 files changed

+136
-1
lines changed

5 files changed

+136
-1
lines changed

CMakeLists.shared

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ set(FLASHMQ_HEADERS
8989
${RELPATH}persistencefunctions.h
9090
${RELPATH}clientacceptqueue.h
9191
${RELPATH}checkedsharedptr.h
92+
${RELPATH}sdnotify.h
9293
)
9394

9495
set(FLASHMQ_IMPLS
@@ -156,4 +157,5 @@ set(FLASHMQ_IMPLS
156157
${RELPATH}fmqssl.cpp
157158
${RELPATH}persistencefunctions.cpp
158159
${RELPATH}clientacceptqueue.cpp
160+
${RELPATH}sdnotify.cpp
159161
)

debian/flashmq.service

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ Description=FlashMQ MQTT server
33
After=network.target
44

55
[Service]
6-
Type=simple
6+
Type=notify
77
User=root
88
Group=root
99
LimitNOFILE=infinity
1010
ExecStart=/usr/bin/flashmq --config-file /etc/flashmq/flashmq.conf
1111
ExecReload=/bin/kill -HUP $MAINPID
1212
Restart=on-failure
1313
RestartSec=5s
14+
TimeoutSec=300
1415

1516
[Install]
1617
WantedBy=multi-user.target

mainapp.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ See LICENSE for license details.
3333
#include "globals.h"
3434
#include "fmqssl.h"
3535
#include "persistencefunctions.h"
36+
#include "sdnotify.h"
3637

3738
MainApp::MainApp(const std::string &configFilePath)
3839
{
@@ -1363,6 +1364,8 @@ void MainApp::queueThreadInitDecrement()
13631364

13641365
reloadTimers(nullptr);
13651366
started = true;
1367+
1368+
notify_ready();
13661369
}
13671370
catch (std::exception &ex)
13681371
{

sdnotify.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "sdnotify.h"
2+
3+
/* SPDX-License-Identifier: MIT-0 */
4+
5+
/* Implement the systemd notify protocol without external dependencies.
6+
* Supports both readiness notification on startup and on reloading,
7+
* according to the protocol defined at:
8+
* https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
9+
* This protocol is guaranteed to be stable as per:
10+
* https://systemd.io/PORTABILITY_AND_STABILITY/ */
11+
12+
#define _GNU_SOURCE 1
13+
#include <errno.h>
14+
#include <inttypes.h>
15+
#include <stdbool.h>
16+
#include <stddef.h>
17+
#include <stdlib.h>
18+
#include <stdio.h>
19+
#include <string.h>
20+
#include <sys/socket.h>
21+
#include <sys/un.h>
22+
#include <time.h>
23+
#include <unistd.h>
24+
25+
#define _cleanup_(f) __attribute__((cleanup(f)))
26+
27+
static void closep(int *fd) {
28+
if (!fd || *fd < 0)
29+
return;
30+
31+
close(*fd);
32+
*fd = -1;
33+
}
34+
35+
static int notify(const char *message) {
36+
union sockaddr_union {
37+
struct sockaddr sa;
38+
struct sockaddr_un sun;
39+
} socket_addr {};
40+
41+
socket_addr.sun.sun_family = AF_UNIX;
42+
size_t path_length, message_length;
43+
_cleanup_(closep) int fd = -1;
44+
const char *socket_path;
45+
46+
/* Verify the argument first */
47+
if (!message)
48+
return -EINVAL;
49+
50+
message_length = strlen(message);
51+
if (message_length == 0)
52+
return -EINVAL;
53+
54+
/* If the variable is not set, the protocol is a noop */
55+
socket_path = getenv("NOTIFY_SOCKET");
56+
if (!socket_path)
57+
return 0; /* Not set? Nothing to do */
58+
59+
/* Only AF_UNIX is supported, with path or abstract sockets */
60+
if (socket_path[0] != '/' && socket_path[0] != '@')
61+
return -EAFNOSUPPORT;
62+
63+
path_length = strlen(socket_path);
64+
/* Ensure there is room for NUL byte */
65+
if (path_length >= sizeof(socket_addr.sun.sun_path))
66+
return -E2BIG;
67+
68+
memcpy(socket_addr.sun.sun_path, socket_path, path_length);
69+
70+
/* Support for abstract socket */
71+
if (socket_addr.sun.sun_path[0] == '@')
72+
socket_addr.sun.sun_path[0] = 0;
73+
74+
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
75+
if (fd < 0)
76+
return -errno;
77+
78+
if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
79+
return -errno;
80+
81+
ssize_t written = write(fd, message, message_length);
82+
if (written != (ssize_t) message_length)
83+
return written < 0 ? -errno : -EPROTO;
84+
85+
return 1; /* Notified! */
86+
}
87+
88+
int notify_ready(void) {
89+
return notify("READY=1");
90+
}
91+
92+
int notify_reloading(void) {
93+
/* A buffer with length sufficient to format the maximum UINT64 value. */
94+
char reload_message[sizeof("RELOADING=1\nMONOTONIC_USEC=18446744073709551615")];
95+
struct timespec ts;
96+
uint64_t now;
97+
98+
/* Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp in usec
99+
* so that the program is compatible with a Type=notify-reload service. */
100+
101+
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
102+
return -errno;
103+
104+
if (ts.tv_sec < 0 || ts.tv_nsec < 0 ||
105+
(uint64_t) ts.tv_sec > (UINT64_MAX - (ts.tv_nsec / 1000ULL)) / 1000000ULL)
106+
return -EINVAL;
107+
108+
now = (uint64_t) ts.tv_sec * 1000000ULL + (uint64_t) ts.tv_nsec / 1000ULL;
109+
110+
if (snprintf(reload_message, sizeof(reload_message), "RELOADING=1\nMONOTONIC_USEC=%" PRIu64, now) < 0)
111+
return -EINVAL;
112+
113+
return notify(reload_message);
114+
}
115+
116+
int notify_stopping(void) {
117+
return notify("STOPPING=1");
118+
}
119+
120+

sdnotify.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef SDNOTIFY_H
2+
#define SDNOTIFY_H
3+
4+
5+
int notify_ready(void);
6+
int notify_reloading(void);
7+
int notify_stopping(void);
8+
9+
#endif // SDNOTIFY_H

0 commit comments

Comments
 (0)