From 35d3bd87ec91f0f958a5596e4aae1877371497ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkki=20Sepp=C3=A4l=C3=A4?= Date: Wed, 29 Nov 2017 22:21:38 +0200 Subject: [PATCH] watchdog: implemented systemd-style watchdog support debian/tvheadend.service has a commented example on enabling it. --- Makefile | 4 ++ debian/tvheadend.service | 7 +++ src/main.c | 5 +++ src/watchdog.c | 93 ++++++++++++++++++++++++++++++++++++++++ src/watchdog.h | 37 ++++++++++++++++ 5 files changed, 146 insertions(+) create mode 100644 src/watchdog.c create mode 100644 src/watchdog.h 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_ */