5#include "systemdnotify.h"
11#include <sys/socket.h>
15#include <QCoreApplication>
16#include <QLoggingCategory>
21#define SD_LISTEN_FDS_START 3
23Q_LOGGING_CATEGORY(C_SERVER_SYSTEMD,
"cutelyst.server.systemd", QtWarningMsg)
32 struct msghdr *notification_object =
nullptr;
33 QTimer *watchdog =
nullptr;
34 int notification_fd = 0;
35 int watchdog_usec = 0;
40systemdNotify::systemdNotify(
QObject *parent)
46 d->notification_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
47 if (d->notification_fd < 0) {
48 qCWarning(C_SERVER_SYSTEMD,
"socket()");
52 auto systemd_socket = getenv(
"NOTIFY_SOCKET");
54 struct sockaddr_un *sd_sun;
55 struct msghdr *msghdr;
57 size_t len = strlen(systemd_socket);
58 sd_sun =
new struct sockaddr_un;
59 memset(sd_sun, 0,
sizeof(
struct sockaddr_un));
60 sd_sun->sun_family = AF_UNIX;
61 strncpy(sd_sun->sun_path, systemd_socket, qMin(len,
sizeof(sd_sun->sun_path)));
62 if (sd_sun->sun_path[0] ==
'@')
63 sd_sun->sun_path[0] = 0;
65 msghdr =
new struct msghdr;
66 memset(msghdr, 0,
sizeof(
struct msghdr));
68 msghdr->msg_iov =
new struct iovec[3];
69 memset(msghdr->msg_iov, 0,
sizeof(
struct iovec) * 3);
71 msghdr->msg_name = sd_sun;
72 msghdr->msg_namelen =
sizeof(
struct sockaddr_un) - (sizeof(sd_sun->sun_path) - len);
74 d->notification_object = msghdr;
78systemdNotify::~systemdNotify()
81 if (d->notification_object) {
82 delete static_cast<struct sockaddr_un *
>(d->notification_object->msg_name);
83 delete[] d->notification_object->msg_iov;
84 delete d->notification_object;
89int systemdNotify::watchdogUSec()
const
92 return d->watchdog_usec;
95bool systemdNotify::setWatchdog(
bool enable,
int usec)
99 d->watchdog_usec = usec;
100 if (d->watchdog_usec > 0) {
103 d->watchdog =
new QTimer(
this);
105 d->watchdog->setInterval(std::chrono::seconds{d->watchdog_usec} / 2);
106 sendWatchdog(QByteArrayLiteral(
"1"));
108 sendWatchdog(QByteArrayLiteral(
"1"));
110 d->watchdog->start();
111 qCInfo(C_SERVER_SYSTEMD)
112 <<
"watchdog enabled" << d->watchdog_usec << d->watchdog->interval();
120 d->watchdog =
nullptr;
125void systemdNotify::sendStatus(
const QByteArray &data)
128 Q_ASSERT(d->notification_fd);
130 struct msghdr *msghdr = d->notification_object;
131 struct iovec *iovec = msghdr->msg_iov;
133 iovec[0].iov_base =
const_cast<char *
>(
"STATUS=");
134 iovec[0].iov_len = 7;
136 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
137 iovec[1].iov_len = data.
size();
139 iovec[2].iov_base =
const_cast<char *
>(
"\n");
140 iovec[2].iov_len = 1;
142 msghdr->msg_iovlen = 3;
144 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
145 qCWarning(C_SERVER_SYSTEMD,
"sendStatus()");
149void systemdNotify::sendWatchdog(
const QByteArray &data)
152 Q_ASSERT(d->notification_fd);
154 struct msghdr *msghdr = d->notification_object;
155 struct iovec *iovec = msghdr->msg_iov;
157 iovec[0].iov_base =
const_cast<char *
>(
"WATCHDOG=");
158 iovec[0].iov_len = 9;
160 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
161 iovec[1].iov_len = data.
size();
163 iovec[2].iov_base =
const_cast<char *
>(
"\n");
164 iovec[2].iov_len = 1;
166 msghdr->msg_iovlen = 3;
168 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
169 qCWarning(C_SERVER_SYSTEMD,
"sendWatchdog()");
173void systemdNotify::sendReady(
const QByteArray &data)
176 Q_ASSERT(d->notification_fd);
178 struct msghdr *msghdr = d->notification_object;
179 struct iovec *iovec = msghdr->msg_iov;
181 iovec[0].iov_base =
const_cast<char *
>(
"READY=");
182 iovec[0].iov_len = 6;
184 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
185 iovec[1].iov_len = data.
size();
187 iovec[2].iov_base =
const_cast<char *
>(
"\n");
188 iovec[2].iov_len = 1;
190 msghdr->msg_iovlen = 3;
192 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
193 qCWarning(C_SERVER_SYSTEMD,
"sendReady()");
197int systemdNotify::sd_watchdog_enabled(
bool unset)
200 auto cleanup = qScopeGuard([unset, &ret] {
201 if (unset && ret > 0) {
202 qunsetenv(
"WATCHDOG_USEC");
203 qunsetenv(
"WATCHDOG_PID");
209 ret = wusec.toInt(&ok);
214 if (qEnvironmentVariableIsSet(
"WATCHDOG_PID")) {
217 if (pid != qApp->applicationPid()) {
225bool systemdNotify::is_systemd_notify_available()
227 return qEnvironmentVariableIsSet(
"NOTIFY_SOCKET");
230int fd_cloexec(
int fd,
bool cloexec)
236 flags = fcntl(fd, F_GETFD, 0);
241 nflags = flags | FD_CLOEXEC;
243 nflags = flags & ~FD_CLOEXEC;
248 if (fcntl(fd, F_SETFD, nflags) < 0)
256 const QByteArray listenPid = qgetenv(
"LISTEN_PID");
258 qint64 pid =
static_cast<pid_t
>(listenPid.
toLongLong(&ok));
268 const QByteArray listenFDS = qgetenv(
"LISTEN_FDS");
269 int n = listenFDS.
toInt(&ok);
274 Q_ASSERT(SD_LISTEN_FDS_START < INT_MAX);
275 if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
279 qCInfo(C_SERVER_SYSTEMD,
"systemd socket activation detected");
282 for (
int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
283 r = fd_cloexec(fd,
true);
294std::vector<int> systemdNotify::listenFds(
bool unsetEnvironment)
296 std::vector<int> ret;
298 if ((maxFD = sd_listen_fds()) > 0) {
299 for (
int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + maxFD; ++fd) {
304 if (unsetEnvironment) {
305 qunsetenv(
"LISTEN_PID");
306 qunsetenv(
"LISTEN_FDS");
307 qunsetenv(
"LISTEN_FDNAMES");
313#include "moc_systemdnotify.cpp"
The Cutelyst namespace holds all public Cutelyst API.
const char * constData() const const
qsizetype size() const const
int toInt(bool *ok, int base) const const
qlonglong toLongLong(bool *ok, int base) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)