diff --git a/Makefile b/Makefile
index 9eb1db8b3e..5e5bf11857 100644
--- a/Makefile
+++ b/Makefile
@@ -261,6 +261,7 @@ SRCS-1 = \
src/string_list.c \
src/wizard.c \
src/memoryinfo.c
+
SRCS = $(SRCS-1)
I18N-C = $(SRCS-1)
@@ -622,6 +623,9 @@ SRCS-${CONFIG_SSL} += src/descrambler/algo/libdesdec.c
# DBUS
SRCS-${CONFIG_DBUS_1} += src/dbus.c
+# Watchdog
+SRCS-${CONFIG_LIBSYSTEMD_DAEMON} += src/watchdog.c
+
# File bundles
SRCS-${CONFIG_BUNDLE} += bundle.c
BUNDLES-yes += src/webui/static
diff --git a/debian/tvheadend.service b/debian/tvheadend.service
index 63e424354e..a9374a3d6e 100644
--- a/debian/tvheadend.service
+++ b/debian/tvheadend.service
@@ -10,5 +10,12 @@ Type=forking
Restart=on-failure
RestartSec=54s
+# To enable watchdog functionality, uncomment these, remove Type=forking
+# and compile with --enable-libsystemd_daemon
+#Type=notify
+#WatchdogSec=1m
+#TimeoutStartSec=5m
+#TimeoutStopSec=20
+
[Install]
WantedBy=multi-user.target
diff --git a/src/main.c b/src/main.c
index e617ca88c9..84b8ea1b24 100644
--- a/src/main.c
+++ b/src/main.c
@@ -75,6 +75,7 @@
#include "packet.h"
#include "streaming.h"
#include "memoryinfo.h"
+#include "watchdog.h"
#if CONFIG_LINUXDVB_CA
#include "input/mpegts/en50221/en50221.h"
#endif
@@ -1256,6 +1257,8 @@ main(int argc, char **argv)
pthread_mutex_unlock(&global_lock);
+ tvhftrace(LS_MAIN, watchdog_init);
+
/**
* Wait for SIGTERM / SIGINT, but only in this thread
*/
@@ -1382,6 +1385,8 @@ main(int argc, char **argv)
#endif
tvh_gettext_done();
free((char *)tvheadend_webroot);
+
+ tvhftrace(LS_MAIN, watchdog_done);
return 0;
}
diff --git a/src/watchdog.c b/src/watchdog.c
new file mode 100644
index 0000000000..f51ad46f20
--- /dev/null
+++ b/src/watchdog.c
@@ -0,0 +1,93 @@
+/*
+ * tvheadend, systemd watchdog support
+ * Copyright (C) 2017-2018 Erkki Seppälä
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "watchdog.h"
+#include "tvheadend.h"
+
+#include
+
+static pthread_t watchdog_tid;
+static pthread_mutex_t watchdog_exiting_mutex;
+static tvh_cond_t watchdog_exiting_cond;
+static int watchdog_exiting; /* 1 if exit has been requested */
+static int watchdog_enabled; /* 1 if watchdog was enabled for the systemd unit */
+static uint64_t watchdog_interval_usec; /* value from .service divided by 2 */
+
+static void* watchdog_thread(void* aux)
+{
+ int exiting = 0;
+ (void) aux; /* ignore */
+ sd_notify(0, "READY=1");
+ pthread_mutex_lock(&watchdog_exiting_mutex);
+ while (!exiting) {
+ if (!watchdog_exiting) {
+ /*
+ * Just keep ticking regardless the return value; its intention is
+ * to pace the loop down, and let us exit fast when the time comes
+ */
+ tvh_cond_timedwait(&watchdog_exiting_cond, &watchdog_exiting_mutex, mclk() + watchdog_interval_usec);
+ if (!watchdog_exiting) {
+ pthread_mutex_lock(&global_lock);
+ pthread_mutex_unlock(&global_lock);
+ sd_notify(0, "WATCHDOG=1");
+ }
+ }
+ exiting = watchdog_exiting;
+ }
+ pthread_mutex_unlock(&watchdog_exiting_mutex);
+
+ return NULL;
+}
+
+void watchdog_init(void)
+{
+ /*
+ * I doubt MONOCLOCK_RESOLUTION is going to change, but if it does,
+ * let's break this
+ */
+#if MONOCLOCK_RESOLUTION != 1000000LL
+#error "Watchdog assumes MONOCLOCK_RESOLUTION == 1000000LL"
+#endif
+
+ watchdog_enabled = sd_watchdog_enabled(0, &watchdog_interval_usec) > 0;
+ if (watchdog_enabled) {
+ /* suggested by sd_watchdog_enabled documentation: */
+ watchdog_interval_usec /= 2;
+
+ watchdog_exiting = 0;
+ tvh_cond_init(&watchdog_exiting_cond);
+ pthread_mutex_init(&watchdog_exiting_mutex, NULL);
+
+ tvhthread_create(&watchdog_tid, NULL, watchdog_thread, NULL, "systemd watchdog");
+ }
+}
+
+void watchdog_done(void)
+{
+ if (watchdog_enabled) {
+ pthread_mutex_lock(&watchdog_exiting_mutex);
+ watchdog_exiting = 1;
+ tvh_cond_signal(&watchdog_exiting_cond, 0);
+ pthread_mutex_unlock(&watchdog_exiting_mutex);
+
+ pthread_join(watchdog_tid, NULL);
+
+ tvh_cond_destroy(&watchdog_exiting_cond);
+ pthread_mutex_destroy(&watchdog_exiting_mutex);
+ }
+}
diff --git a/src/watchdog.h b/src/watchdog.h
new file mode 100644
index 0000000000..68af828318
--- /dev/null
+++ b/src/watchdog.h
@@ -0,0 +1,37 @@
+/*
+ * tvheadend, systemd watchdog support
+ * Copyright (C) 2017-2018 Erkki Seppälä
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef WATCHDOG_H_
+#define WATCHDOG_H_
+
+#include "config.h"
+
+#if ENABLE_LIBSYSTEMD_DAEMON
+
+void watchdog_init(void);
+void watchdog_done(void);
+
+#else /* #if ENABLE_LIBSYSTEMD_DAEMON */
+
+static inline void watchdog_init(void) { }
+static inline void watchdog_done(void) { }
+
+#endif /* #if else ENABLE_LIBSYSTEMD_DAEMON */
+
+
+#endif /* WATCHDOG_H_ */