From a28a6e148ff79397cc1626d8b8803deae2aa1a86 Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Fri, 20 Jan 2023 21:06:54 +0100 Subject: [PATCH 1/2] Add Systemd integration to aes67-daemon This adds integration and especially watchdog support between aes67-daemon and systemd. A example service file is also included. --- daemon/CMakeLists.txt | 8 +++++ daemon/main.cpp | 44 ++++++++++++++++++++++++++ systemd/aes67-daemon.conf | 2 ++ systemd/aes67-daemon.service | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 systemd/aes67-daemon.conf create mode 100644 systemd/aes67-daemon.service diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 2390458..06712b1 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -11,6 +11,8 @@ option(WITH_AVAHI "Include mDNS support via Avahi" OFF) option(FAKE_DRIVER "Use fake driver instead of RAVENNA" OFF) set(CMAKE_CXX_STANDARD 17) +option(WITH_SYSTEMD "Include systemd notify and watchdog support" OFF) + # ravena lkm _should_ be provided by the CLI. Nonetheless, we should be able # to find it in system dirs too... if (NOT RAVENNNA_ALSA_LKM_DIR) @@ -54,3 +56,9 @@ if(WITH_AVAHI) include_directories(aes67-daemon ${AVAHI_INCLUDE_DIRS}) target_link_libraries(aes67-daemon ${AVAHI_LIBRARIES}) endif() + +if(WITH_SYSTEMD) + MESSAGE(STATUS "WITH_SYSTEMD") + add_definitions(-D_USE_SYSTEMD_) + target_link_libraries(aes67-daemon systemd) +endif() diff --git a/daemon/main.cpp b/daemon/main.cpp index e65f7ba..6e7dcba 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -31,6 +31,10 @@ #include "rtsp_server.hpp" #include "session_manager.hpp" +#ifdef _USE_SYSTEMD_ +#include +#endif + namespace po = boost::program_options; namespace postyle = boost::program_options::command_line_style; namespace logging = boost::log; @@ -64,6 +68,11 @@ int main(int argc, char* argv[]) { int unix_style = postyle::unix_style | postyle::short_allow_next; bool driver_restart(true); +#ifdef _USE_SYSTEMD_ + // with which interval we should pet the dog + uint64_t current_watchdog_usec; +#endif + po::variables_map vm; try { po::store(po::command_line_parser(argc, argv) @@ -98,6 +107,17 @@ int main(int argc, char* argv[]) { std::string filename = vm["config"].as(); +#ifdef _USE_SYSTEMD_ + sd_watchdog_enabled(0, ¤t_watchdog_usec); + + if (current_watchdog_usec > 0) { + // Inform systemd that if we're not petting the dog in 5s we're bust. + sd_notify(0, "WATCHDOG_USEC=5000000"); + + current_watchdog_usec = 5000000; + } +#endif + while (!is_terminated() && rc == EXIT_SUCCESS) { /* load configuration from file */ auto config = Config::parse(filename, driver_restart); @@ -112,6 +132,11 @@ int main(int argc, char* argv[]) { log_init(*config); if (config->get_ip_addr_str().empty()) { +#ifdef _USE_SYSTEMD_ + if (current_watchdog_usec > 0) + sd_notify(0, "WATCHDOG=1"); + sd_notify(0, "STATUS=no IP address, waiting ..."); +#endif BOOST_LOG_TRIVIAL(info) << "main:: no IP address, waiting ..."; std::this_thread::sleep_for(std::chrono::seconds(1)); continue; @@ -159,7 +184,22 @@ int main(int argc, char* argv[]) { session_manager->load_status(); BOOST_LOG_TRIVIAL(debug) << "main:: init done, entering loop..."; + +#ifdef _USE_SYSTEMD_ + // To be able to use sd_notify at all have to set service NotifyAccess + // (e.g. to main) + sd_notify(0, "READY=1"); // If service Type=notify the service is only + // considered ready once we send this (this is + // independent of watchdog capability) + sd_notify(0, "STATUS=Working"); +#endif + while (!is_terminated()) { +#ifdef _USE_SYSTEMD_ + if (current_watchdog_usec > 0) + sd_notify(0, "WATCHDOG=1"); +#endif + auto [ip_addr, ip_str] = get_interface_ip(config->get_interface_name()); if (config->get_ip_addr_str() != ip_str) { BOOST_LOG_TRIVIAL(warning) @@ -175,6 +215,10 @@ int main(int argc, char* argv[]) { std::this_thread::sleep_for(std::chrono::seconds(1)); } +#ifdef _USE_SYSTEMD_ + sd_notify(0, "STOPPING=1"); + sd_notify(0, "STATUS=Stopping"); +#endif /* save session status to file */ session_manager->save_status(); diff --git a/systemd/aes67-daemon.conf b/systemd/aes67-daemon.conf new file mode 100644 index 0000000..12ff58e --- /dev/null +++ b/systemd/aes67-daemon.conf @@ -0,0 +1,2 @@ +#Type Name ID GECOS Home directory Shell +u aes67-daemon - "AES67 daemon user" diff --git a/systemd/aes67-daemon.service b/systemd/aes67-daemon.service new file mode 100644 index 0000000..35c4128 --- /dev/null +++ b/systemd/aes67-daemon.service @@ -0,0 +1,60 @@ +[Unit] +Description=AES67 daemon service +Before=multi-user.target +After=network.target + +[Service] +Type=notify +# Will be adjusted by service during startup +WatchdogSec=10 + +# Run as separate user created via sysusers.d +User=aes67-daemon + +ExecStart=/usr/local/bin/aes67-daemon + +# Security filters. +CapabilityBoundingSet= +DevicePolicy=closed +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +PrivateDevices=yes +PrivateMounts=yes +PrivateTmp=yes +PrivateUsers=yes +# interface::get_mac_from_arp_cache() reads from /proc/net/arp +ProcSubset=all +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectProc=invisible +ProtectSystem=strict +RemoveIPC=yes +RestrictAddressFamilies=AF_INET AF_NETLINK AF_UNIX +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +SystemCallFilter=~@clock +SystemCallFilter=~@cpu-emulation +SystemCallFilter=~@debug +SystemCallFilter=~@module +SystemCallFilter=~@mount +SystemCallFilter=~@obsolete +SystemCallFilter=~@privileged +SystemCallFilter=~@raw-io +SystemCallFilter=~@reboot +SystemCallFilter=~@resources +SystemCallFilter=~@swap +UMask=077 +# Paths matching daemon.conf +ReadWritePaths=/etc/daemon.conf +ReadWritePaths=/etc/status.json + +[Install] +WantedBy=multi-user.target From 997fb31d0ea066e34e9e6d93dbf6ded1aceaa25e Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Wed, 25 Jan 2023 15:18:16 +0100 Subject: [PATCH 2/2] Use systemd state RELOADING when ip address changed In systemd when entering state STOPPING, you can't go back to READY. So, we check if we're supposed to stop or just restart, and tells that to systemd. --- daemon/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/daemon/main.cpp b/daemon/main.cpp index 6e7dcba..aa24561 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -216,8 +216,13 @@ int main(int argc, char* argv[]) { std::this_thread::sleep_for(std::chrono::seconds(1)); } #ifdef _USE_SYSTEMD_ - sd_notify(0, "STOPPING=1"); - sd_notify(0, "STATUS=Stopping"); + if (is_terminated()) { + sd_notify(0, "STOPPING=1"); + sd_notify(0, "STATUS=Stopping"); + } else { + sd_notify(0, "RELOADING=1"); + sd_notify(0, "STATUS=Restarting"); + } #endif /* save session status to file */