5#include "systemdnotify.h"
11#include <sys/socket.h>
15#include <QCoreApplication>
16#include <QLoggingCategory>
22constexpr auto SD_LISTEN_FDS_START = 3;
25Q_LOGGING_CATEGORY(C_SERVER_SYSTEMD,
"cutelyst.server.systemd", QtWarningMsg)
34 struct msghdr *notification_object =
nullptr;
35 QTimer *watchdog =
nullptr;
36 int notification_fd = 0;
37 int watchdog_usec = 0;
42systemdNotify::systemdNotify(
QObject *parent)
48 d->notification_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
49 if (d->notification_fd < 0) {
50 qCWarning(C_SERVER_SYSTEMD,
"socket()");
54 auto systemd_socket = getenv(
"NOTIFY_SOCKET");
56 struct sockaddr_un *sd_sun;
57 struct msghdr *msghdr;
59 size_t len = strlen(systemd_socket);
60 sd_sun =
new struct sockaddr_un;
61 memset(sd_sun, 0,
sizeof(
struct sockaddr_un));
62 sd_sun->sun_family = AF_UNIX;
63 strncpy(sd_sun->sun_path, systemd_socket, qMin(len,
sizeof(sd_sun->sun_path)));
64 if (sd_sun->sun_path[0] ==
'@') {
65 sd_sun->sun_path[0] = 0;
68 msghdr =
new struct msghdr;
69 memset(msghdr, 0,
sizeof(
struct msghdr));
71 msghdr->msg_iov =
new struct iovec[3];
72 memset(msghdr->msg_iov, 0,
sizeof(
struct iovec) * 3);
74 msghdr->msg_name = sd_sun;
75 msghdr->msg_namelen =
sizeof(
struct sockaddr_un) - (sizeof(sd_sun->sun_path) - len);
77 d->notification_object = msghdr;
81systemdNotify::~systemdNotify()
84 if (d->notification_object) {
85 delete static_cast<struct sockaddr_un *
>(d->notification_object->msg_name);
86 delete[] d->notification_object->msg_iov;
87 delete d->notification_object;
92int systemdNotify::watchdogUSec()
const
95 return d->watchdog_usec;
98bool systemdNotify::setWatchdog(
bool enable,
int usec)
102 d->watchdog_usec = usec;
103 if (d->watchdog_usec > 0) {
106 d->watchdog =
new QTimer(
this);
108 d->watchdog->setInterval(std::chrono::seconds{d->watchdog_usec} / 2);
109 sendWatchdog(QByteArrayLiteral(
"1"));
111 sendWatchdog(QByteArrayLiteral(
"1"));
113 d->watchdog->start();
114 qCInfo(C_SERVER_SYSTEMD)
115 <<
"watchdog enabled" << d->watchdog_usec << d->watchdog->interval();
123 d->watchdog =
nullptr;
128void systemdNotify::sendStatus(
const QByteArray &data)
131 Q_ASSERT(d->notification_fd);
133 struct msghdr *msghdr = d->notification_object;
134 struct iovec *iovec = msghdr->msg_iov;
136 iovec[0].iov_base =
const_cast<char *
>(
"STATUS=");
137 iovec[0].iov_len = 7;
139 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
140 iovec[1].iov_len = data.
size();
142 iovec[2].iov_base =
const_cast<char *
>(
"\n");
143 iovec[2].iov_len = 1;
145 msghdr->msg_iovlen = 3;
147 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
148 qCWarning(C_SERVER_SYSTEMD,
"sendStatus()");
152void systemdNotify::sendWatchdog(
const QByteArray &data)
155 Q_ASSERT(d->notification_fd);
157 struct msghdr *msghdr = d->notification_object;
158 struct iovec *iovec = msghdr->msg_iov;
160 iovec[0].iov_base =
const_cast<char *
>(
"WATCHDOG=");
161 iovec[0].iov_len = 9;
163 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
164 iovec[1].iov_len = data.
size();
166 iovec[2].iov_base =
const_cast<char *
>(
"\n");
167 iovec[2].iov_len = 1;
169 msghdr->msg_iovlen = 3;
171 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
172 qCWarning(C_SERVER_SYSTEMD,
"sendWatchdog()");
176void systemdNotify::sendReady(
const QByteArray &data)
179 Q_ASSERT(d->notification_fd);
181 struct msghdr *msghdr = d->notification_object;
182 struct iovec *iovec = msghdr->msg_iov;
184 iovec[0].iov_base =
const_cast<char *
>(
"READY=");
185 iovec[0].iov_len = 6;
187 iovec[1].iov_base =
const_cast<char *
>(data.
constData());
188 iovec[1].iov_len = data.
size();
190 iovec[2].iov_base =
const_cast<char *
>(
"\n");
191 iovec[2].iov_len = 1;
193 msghdr->msg_iovlen = 3;
195 if (sendmsg(d->notification_fd, msghdr, 0) < 0) {
196 qCWarning(C_SERVER_SYSTEMD,
"sendReady()");
200int systemdNotify::sd_watchdog_enabled(
bool unset)
203 auto cleanup = qScopeGuard([unset, &ret] {
204 if (unset && ret > 0) {
205 qunsetenv(
"WATCHDOG_USEC");
206 qunsetenv(
"WATCHDOG_PID");
212 ret = wusec.toInt(&ok);
217 if (qEnvironmentVariableIsSet(
"WATCHDOG_PID")) {
220 if (pid != qApp->applicationPid()) {
228bool systemdNotify::is_systemd_notify_available()
230 return qEnvironmentVariableIsSet(
"NOTIFY_SOCKET");
233int fd_cloexec(
int fd,
bool cloexec)
237 const int flags = fcntl(fd, F_GETFD, 0);
244 nflags = flags | FD_CLOEXEC;
246 nflags = flags & ~FD_CLOEXEC;
249 if (nflags == flags) {
253 if (fcntl(fd, F_SETFD, nflags) < 0) {
262 const QByteArray listenPid = qgetenv(
"LISTEN_PID");
264 qint64 pid =
static_cast<pid_t
>(listenPid.
toLongLong(&ok));
274 const QByteArray listenFDS = qgetenv(
"LISTEN_FDS");
275 int n = listenFDS.
toInt(&ok);
280 Q_ASSERT(SD_LISTEN_FDS_START < INT_MAX);
281 if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
285 qCInfo(C_SERVER_SYSTEMD,
"systemd socket activation detected");
288 for (
int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
289 r = fd_cloexec(fd,
true);
300std::vector<int> systemdNotify::listenFds(
bool unsetEnvironment)
302 std::vector<int> ret;
303 if (
const int maxFD = sd_listen_fds(); maxFD > 0) {
304 for (
int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + maxFD; ++fd) {
309 if (unsetEnvironment) {
310 qunsetenv(
"LISTEN_PID");
311 qunsetenv(
"LISTEN_FDS");
312 qunsetenv(
"LISTEN_FDNAMES");
318#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)