From dbb593120afffbd5480a179bfc38673d28c7df7a Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Thu, 19 May 2022 19:04:26 +0200 Subject: [PATCH] Enhancement of daemon reconfiguration and PTP status update notification via shell script. See #82 The daemon can apply a configuration change to the following parameters without a restart: sap_interval, syslog_proto, syslog_server, log_severity, sample_rate The daemon can apply a configuration change to the following parameters without a light restart: http_port, rtsp_port, http_base_dir, rtp_mcast_base, sap_mcast_addr, rtp_port, rtp_port, status_file, interface_name, mdns_enabled A light restart means that the configuration can be applied without interrupting the playback/capture applications A change to one of following paramters causes a full daemon restart: interface_name, tic_frame_size_at_1fs, max_tic_frame_size On PTP status change the daemon can run in background an external shell script whose path name is specified by the new ptp_status_script Config parameter. If this parameter is empty, no script is invoked. The PTP clock status is passed as first parameter to the script and it can be unlocked, locking or locked. A sample script is provided in daemon/scripts/ptp_status.sh --- daemon/README.md | 9 ++++- daemon/browser.cpp | 6 +-- daemon/config.cpp | 46 ++++++++++++++++------ daemon/config.hpp | 45 ++++++++++++++++++++-- daemon/daemon.conf | 3 +- daemon/driver_handler.cpp | 2 +- daemon/driver_handler.hpp | 4 +- daemon/driver_manager.cpp | 26 ++++++++----- daemon/driver_manager.hpp | 2 +- daemon/http_server.cpp | 26 ++++++++++++- daemon/interface.cpp | 3 -- daemon/json.cpp | 9 ++++- daemon/main.cpp | 24 ++++++------ daemon/mdns_client.cpp | 1 - daemon/mdns_server.cpp | 1 - daemon/rtsp_server.cpp | 1 - daemon/scripts/ptp_status.sh | 11 ++++++ daemon/session_manager.cpp | 75 +++++++++++++++++++++++++++--------- daemon/session_manager.hpp | 4 +- daemon/tests/daemon.conf | 3 +- daemon/tests/daemon_test.cpp | 2 + test/daemon.conf | 3 +- webui/src/Config.jsx | 2 +- 23 files changed, 231 insertions(+), 77 deletions(-) create mode 100755 daemon/scripts/ptp_status.sh diff --git a/daemon/README.md b/daemon/README.md index e442d6c..4415aaf 100644 --- a/daemon/README.md +++ b/daemon/README.md @@ -185,7 +185,8 @@ Example "sap_interval": 30, "mac_addr": "01:00:5e:01:00:01", "ip_addr": "127.0.0.1", - "node_id": "AES67 daemon ubuntu-d9aca383" + "node_id": "AES67 daemon ubuntu-d9aca383", + "ptp_status_script": "./scripts/ptp_status.sh" } where: @@ -221,7 +222,7 @@ where: > The specific multicast RTP address is the base address plus the source id number. > For example if the base address is 239.2.0.1 and source id is 1 the RTP source address used is 239.2.0.2. -> **rtp_port** +> **rtp\_port** > JSON number specifying the RTP port used by the sources. > **ptp\_domain** @@ -267,6 +268,10 @@ where: > JSON string specifying the unique node identifier used to identify mDNS, SAP and SDP services announced by the daemon. > **_NOTE:_** This parameter is read-only and cannot be set. The server will determine the node id at startup time. +> **ptp\_status\_script** +> JSON string specifying the path to the script executed in background when the PTP slave clock status changes. +> The PTP clock status is passed as first parameter to the script and it can be *unlocked*, *locking* or *locked*. + ### JSON PTP Config ### Example diff --git a/daemon/browser.cpp b/daemon/browser.cpp index 3842704..420e01d 100644 --- a/daemon/browser.cpp +++ b/daemon/browser.cpp @@ -101,9 +101,9 @@ bool Browser::worker() { duration_cast(steady_clock::now() - startup_).count(); auto upd_source{*it}; if ((last_seen - upd_source.last_seen) != 0) { - upd_source.announce_period = last_seen - upd_source.last_seen; - upd_source.last_seen = last_seen; - sources_.replace(it, upd_source); + upd_source.announce_period = last_seen - upd_source.last_seen; + upd_source.last_seen = last_seen; + sources_.replace(it, upd_source); } } else { BOOST_LOG_TRIVIAL(info) << "browser:: removing SAP source " << it->id diff --git a/daemon/config.cpp b/daemon/config.cpp index 9889958..c364a93 100644 --- a/daemon/config.cpp +++ b/daemon/config.cpp @@ -36,7 +36,8 @@ using namespace boost::asio; -std::shared_ptr Config::parse(const std::string& filename) { +std::shared_ptr Config::parse(const std::string& filename, + bool driver_restart) { Config config; std::ifstream jsonstream(filename); @@ -104,20 +105,43 @@ std::shared_ptr Config::parse(const std::string& filename) { } config.config_filename_ = filename; - config.need_restart_ = false; + config.daemon_restart_ = false; + config.driver_restart_ = driver_restart; return std::make_shared(config); } -bool Config::save(const Config& config, bool need_restart) { - std::ofstream js(config_filename_); - if (!js) { - BOOST_LOG_TRIVIAL(fatal) - << "Config:: cannot save to file " << config_filename_; - return false; +bool Config::save(const Config& config) { + if (*this != config) { + std::ofstream js(config_filename_); + if (!js) { + BOOST_LOG_TRIVIAL(fatal) + << "Config:: cannot save to file " << config_filename_; + return false; + } + js << config_to_json(config); + + driver_restart_ = + get_tic_frame_size_at_1fs() != config.get_tic_frame_size_at_1fs() || + get_max_tic_frame_size() != config.get_max_tic_frame_size() || + get_interface_name() != config.get_interface_name(); + + daemon_restart_ = driver_restart_ || + get_http_port() != config.get_http_port() || + get_rtsp_port() != config.get_rtsp_port() || + get_http_base_dir() != config.get_http_base_dir() || + get_rtp_mcast_base() != config.get_rtp_mcast_base() || + get_sap_mcast_addr() != config.get_sap_mcast_addr() || + get_rtp_port() != config.get_rtp_port() || + get_status_file() != config.get_status_file() || + get_mdns_enabled() != config.get_mdns_enabled(); + + if (!daemon_restart_) + *this = config; + + BOOST_LOG_TRIVIAL(info) << "Config:: file saved"; + } else { + BOOST_LOG_TRIVIAL(info) << "Config:: unchanged"; } - js << config_to_json(config); - BOOST_LOG_TRIVIAL(info) << "Config:: file saved"; - need_restart_ = need_restart; return true; } diff --git a/daemon/config.hpp b/daemon/config.hpp index 7b74d0c..6502071 100644 --- a/daemon/config.hpp +++ b/daemon/config.hpp @@ -27,9 +27,10 @@ class Config { public: /* save new config to json file */ - bool save(const Config& config, bool need_restart = true); + bool save(const Config& config); /* build config from json file */ - static std::shared_ptr parse(const std::string& filename); + static std::shared_ptr parse(const std::string& filename, + bool driver_restart); /* attributes retrieved from config json */ uint16_t get_http_port() const { return http_port_; }; @@ -57,9 +58,13 @@ class Config { const std::string& get_mac_addr_str() const { return mac_str_; }; uint32_t get_ip_addr() const { return ip_addr_; }; const std::string& get_ip_addr_str() const { return ip_str_; }; - bool get_need_restart() const { return need_restart_; }; + bool get_daemon_restart() const { return daemon_restart_; }; + bool get_driver_restart() const { return driver_restart_; }; bool get_mdns_enabled() const { return mdns_enabled_; }; int get_interface_idx() { return interface_idx_; }; + const std::string& get_ptp_status_script() const { + return ptp_status_script_; + } void set_http_port(uint16_t http_port) { http_port_ = http_port; }; void set_rtsp_port(uint16_t rtsp_port) { rtsp_port_ = rtsp_port; }; @@ -109,6 +114,35 @@ class Config { }; void set_mdns_enabled(bool enabled) { mdns_enabled_ = enabled; }; void set_interface_idx(int index) { interface_idx_ = index; }; + void set_ptp_status_script(const std::string& script) { + ptp_status_script_ = script; + }; + void set_driver_restart(bool restart) { driver_restart_ = restart; } + + friend bool operator!=(const Config& lhs, const Config& rhs) { + return lhs.get_http_port() != rhs.get_http_port() || + lhs.get_rtsp_port() != rhs.get_rtsp_port() || + lhs.get_http_base_dir() != rhs.get_http_base_dir() || + lhs.get_log_severity() != rhs.get_log_severity() || + lhs.get_playout_delay() != rhs.get_playout_delay() || + lhs.get_tic_frame_size_at_1fs() != rhs.get_tic_frame_size_at_1fs() || + lhs.get_max_tic_frame_size() != rhs.get_max_tic_frame_size() || + lhs.get_sample_rate() != rhs.get_sample_rate() || + lhs.get_rtp_mcast_base() != rhs.get_rtp_mcast_base() || + lhs.get_sap_mcast_addr() != rhs.get_sap_mcast_addr() || + lhs.get_rtp_port() != rhs.get_rtp_port() || + lhs.get_ptp_domain() != rhs.get_ptp_domain() || + lhs.get_ptp_dscp() != rhs.get_ptp_dscp() || + lhs.get_sap_interval() != rhs.get_sap_interval() || + lhs.get_syslog_proto() != rhs.get_syslog_proto() || + lhs.get_syslog_server() != rhs.get_syslog_server() || + lhs.get_status_file() != rhs.get_status_file() || + lhs.get_interface_name() != rhs.get_interface_name() || + lhs.get_mdns_enabled() != rhs.get_mdns_enabled(); + }; + friend bool operator==(const Config& lhs, const Config& rhs) { + return !(lhs != rhs); + }; private: /* from json */ @@ -131,6 +165,7 @@ class Config { std::string status_file_{"./status.json"}; std::string interface_name_{"eth0"}; bool mdns_enabled_{true}; + std::string ptp_status_script_; /* set during init */ std::array mac_addr_{0, 0, 0, 0, 0, 0}; @@ -140,8 +175,10 @@ class Config { int interface_idx_; std::string config_filename_; + /* reconfig needs driver restart */ + bool driver_restart_{true}; /* reconfig needs daemon restart */ - bool need_restart_{false}; + bool daemon_restart_{false}; }; #endif diff --git a/daemon/daemon.conf b/daemon/daemon.conf index 95f3343..7a02015 100644 --- a/daemon/daemon.conf +++ b/daemon/daemon.conf @@ -16,6 +16,7 @@ "syslog_proto": "none", "syslog_server": "255.255.255.254:1234", "status_file": "./status.json", + "interface_name": "lo", "mdns_enabled": true, - "interface_name": "lo" + "ptp_status_script": "./scripts/ptp_status.sh" } diff --git a/daemon/driver_handler.cpp b/daemon/driver_handler.cpp index dd6b82a..0ba8690 100644 --- a/daemon/driver_handler.cpp +++ b/daemon/driver_handler.cpp @@ -130,7 +130,7 @@ bool DriverHandler::event_receiver() { return true; } -bool DriverHandler::terminate() { +bool DriverHandler::terminate(const Config& /* config */) { if (running_) { running_ = false; client_u2k_.terminate(); diff --git a/daemon/driver_handler.hpp b/daemon/driver_handler.hpp index b7957f7..501ab05 100644 --- a/daemon/driver_handler.hpp +++ b/daemon/driver_handler.hpp @@ -40,10 +40,10 @@ class DriverHandler { DriverHandler(){}; DriverHandler(const DriverHandler&) = delete; DriverHandler& operator=(const DriverHandler&) = delete; - virtual ~DriverHandler() { terminate(); }; + virtual ~DriverHandler(){}; virtual bool init(const Config& config); - virtual bool terminate(); + virtual bool terminate(const Config& config); protected: virtual void send_command(enum MT_ALSA_msg_id id, diff --git a/daemon/driver_manager.cpp b/daemon/driver_manager.cpp index 9a1be6f..9715db0 100644 --- a/daemon/driver_manager.cpp +++ b/daemon/driver_manager.cpp @@ -81,20 +81,28 @@ bool DriverManager::init(const Config& config) { ptp_config.ui8Domain = config.get_ptp_domain(); ptp_config.ui8DSCP = config.get_ptp_dscp(); - bool res = hello() || start() || reset() || - set_interface_name(config.get_interface_name()) || - set_ptp_config(ptp_config) || - set_tic_frame_size_at_1fs(config.get_tic_frame_size_at_1fs()) || - set_playout_delay(config.get_playout_delay()) || - set_max_tic_frame_size(config.get_max_tic_frame_size()); + if (hello()) + return false; + + bool res(false); + if (config.get_driver_restart()) { + res = start() || reset() || + set_interface_name(config.get_interface_name()) || + set_ptp_config(ptp_config) || + set_tic_frame_size_at_1fs(config.get_tic_frame_size_at_1fs()) || + set_playout_delay(config.get_playout_delay()) || + set_max_tic_frame_size(config.get_max_tic_frame_size()); + } return !res; } -bool DriverManager::terminate() { - stop(); +bool DriverManager::terminate(const Config& config) { + if (config.get_driver_restart()) { + stop(); + } bye(); - return DriverHandler::terminate(); + return DriverHandler::terminate(config); } std::error_code DriverManager::hello() { diff --git a/daemon/driver_manager.hpp b/daemon/driver_manager.hpp index 938c2e2..fda9c8d 100644 --- a/daemon/driver_manager.hpp +++ b/daemon/driver_manager.hpp @@ -33,7 +33,7 @@ class DriverManager : public DriverHandler { // driver interface bool init(const Config& config) override; - bool terminate() override; + bool terminate(const Config& config) override; std::error_code ping(); // unused, return error std::error_code set_ptp_config(const TPTPConfig& config); diff --git a/daemon/http_server.cpp b/daemon/http_server.cpp index b8e36da..2a9e005 100644 --- a/daemon/http_server.cpp +++ b/daemon/http_server.cpp @@ -115,6 +115,30 @@ bool HttpServer::init() { svr_.Post("/api/config", [this](const Request& req, Response& res) { try { Config config = json_to_config(req.body, *config_); + + if (config_->get_syslog_proto() != config.get_syslog_proto() || + config_->get_syslog_server() != config.get_syslog_server() || + config_->get_log_severity() != config.get_log_severity()) { + log_init(config); + } + std::error_code ret; + if (config_->get_playout_delay() != config.get_playout_delay()) { + ret = session_manager_->set_driver_config("playout_delay", + config.get_playout_delay()); + } + if (config_->get_sample_rate() != config.get_sample_rate()) { + ret = session_manager_->set_driver_config("sample_rate", + config.get_sample_rate()); + } + if (config_->get_ptp_domain() != config.get_ptp_domain() || + config_->get_ptp_dscp() != config.get_ptp_dscp()) { + PTPConfig ptpConfig{config.get_ptp_domain(), config.get_ptp_dscp()}; + ret = session_manager_->set_ptp_config(ptpConfig); + } + if (ret) { + set_error(ret, "failed to set config", res); + return; + } if (!config_->save(config)) { set_error(500, "failed to save config", res); return; @@ -153,7 +177,7 @@ bool HttpServer::init() { Config config(*config_); config.set_ptp_domain(ptpConfig.domain); config.set_ptp_dscp(ptpConfig.dscp); - if (!config_->save(config, false)) { + if (!config_->save(config)) { set_error(500, "failed to save config", res); return; } diff --git a/daemon/interface.cpp b/daemon/interface.cpp index 2211621..f5f9fb5 100644 --- a/daemon/interface.cpp +++ b/daemon/interface.cpp @@ -115,7 +115,6 @@ int get_interface_index(const std::string& interface_name) { return ifr.ifr_ifindex; } - std::pair, std::string> get_mac_from_arp_cache( const std::string& interface_name, const std::string& ip) { @@ -155,7 +154,6 @@ std::pair, std::string> get_mac_from_arp_cache( return {mac, ""}; } - bool ping(const std::string& ip) { static uint16_t sequence_number(0); uint16_t identifier(0xABAB); @@ -182,7 +180,6 @@ bool ping(const std::string& ip) { return true; } - bool echo_try_connect(const std::string& ip) { ip::tcp::iostream s; BOOST_LOG_TRIVIAL(debug) << "echo_connect:: connecting to " << ip; diff --git a/daemon/json.cpp b/daemon/json.cpp index 649ef29..68a16c7 100644 --- a/daemon/json.cpp +++ b/daemon/json.cpp @@ -104,6 +104,8 @@ std::string config_to_json(const Config& config) { << ",\n \"ip_addr\": \"" << escape_json(config.get_ip_addr_str()) << "\"" << ",\n \"node_id\": \"" << escape_json(get_node_id(config.get_ip_addr())) << "\"" + << ",\n \"ptp_status_script\": \"" + << escape_json(config.get_ptp_status_script()) << "\"" << "\n}\n"; return ss.str(); } @@ -158,8 +160,8 @@ std::string sink_to_json(const StreamSink& sink) { std::string sink_status_to_json(const SinkStreamStatus& status) { std::stringstream ss; ss << "{"; - ss << " \n \"sink_flags\":\n {" << std::boolalpha - << " \n \"rtp_seq_id_error\": " << status.is_rtp_seq_id_error + ss << "\n \"sink_flags\":\n {" << std::boolalpha + << " \n \"rtp_seq_id_error\": " << status.is_rtp_seq_id_error << ", \n \"rtp_ssrc_error\": " << status.is_rtp_ssrc_error << ", \n \"rtp_payload_type_error\": " << status.is_rtp_payload_type_error @@ -320,6 +322,9 @@ Config json_to_config_(std::istream& js, Config& config) { } else if (key == "syslog_server") { config.set_syslog_server( remove_undesired_chars(val.get_value())); + } else if (key == "ptp_status_script") { + config.set_ptp_status_script( + remove_undesired_chars(val.get_value())); } else if (key == "mac_addr" || key == "ip_addr" || key == "node_id") { /* ignored */ } else { diff --git a/daemon/main.cpp b/daemon/main.cpp index 88299cd..58fa162 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -35,7 +35,7 @@ namespace po = boost::program_options; namespace postyle = boost::program_options::command_line_style; namespace logging = boost::log; -static std::string version("bondagit-1.4"); +static std::string version("bondagit-1.5"); static std::atomic terminate = false; void termination_handler(int signum) { @@ -53,15 +53,16 @@ const std::string& get_version() { } int main(int argc, char* argv[]) { - int rc = EXIT_SUCCESS; + int rc(EXIT_SUCCESS); po::options_description desc("Options"); - desc.add_options() - ("version,v", "Print daemon version and exit") - ("config,c", po::value()->default_value("/etc/daemon.conf"), - "daemon configuration file") - ("http_port,p", po::value(), "HTTP server port") - ("help,h", "Print this help message"); + desc.add_options()("version,v", "Print daemon version and exit")( + "config,c", po::value()->default_value("/etc/daemon.conf"), + "daemon configuration file")("http_port,p", po::value(), + "HTTP server port")("help,h", + "Print this help " + "message"); int unix_style = postyle::unix_style | postyle::short_allow_next; + bool driver_restart(true); po::variables_map vm; try { @@ -98,7 +99,7 @@ int main(int argc, char* argv[]) { while (!is_terminated() && rc == EXIT_SUCCESS) { /* load configuration from file */ - auto config = Config::parse(filename); + auto config = Config::parse(filename, driver_restart); if (config == nullptr) { return EXIT_FAILURE; } @@ -165,7 +166,8 @@ int main(int argc, char* argv[]) { break; } - if (config->get_need_restart()) { + driver_restart = config->get_driver_restart(); + if (config->get_daemon_restart()) { BOOST_LOG_TRIVIAL(warning) << "main:: config changed, restarting ..."; break; } @@ -205,7 +207,7 @@ int main(int argc, char* argv[]) { } /* stop driver manager */ - if (!driver->terminate()) { + if (!driver->terminate(*config)) { throw std::runtime_error( std::string("DriverManager:: terminate failed")); } diff --git a/daemon/mdns_client.cpp b/daemon/mdns_client.cpp index 96e9621..224e7f4 100644 --- a/daemon/mdns_client.cpp +++ b/daemon/mdns_client.cpp @@ -25,7 +25,6 @@ #include "rtsp_client.hpp" #include "mdns_client.hpp" - #ifdef _USE_AVAHI_ void MDNSClient::resolve_callback(AvahiServiceResolver* r, AvahiIfIndex interface, diff --git a/daemon/mdns_server.cpp b/daemon/mdns_server.cpp index 2bf3341..9611d3d 100644 --- a/daemon/mdns_server.cpp +++ b/daemon/mdns_server.cpp @@ -25,7 +25,6 @@ #include "utils.hpp" #include "mdns_server.hpp" - #ifdef _USE_AVAHI_ struct AvahiLockGuard { AvahiLockGuard() = delete; diff --git a/daemon/rtsp_server.cpp b/daemon/rtsp_server.cpp index a35c509..c695245 100644 --- a/daemon/rtsp_server.cpp +++ b/daemon/rtsp_server.cpp @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - #include "utils.hpp" #include "rtsp_server.hpp" diff --git a/daemon/scripts/ptp_status.sh b/daemon/scripts/ptp_status.sh new file mode 100755 index 0000000..679dd62 --- /dev/null +++ b/daemon/scripts/ptp_status.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ $1 == "locked" ]; then + echo "$0 >> PTP locked"; + #speaker-test -D plughw:RAVENNA -r 48000 -c 2 -t sine & +elif [ $1 == "locking" ]; then + echo "$0 >> PTP locking"; +elif [ $1 == "unlocked" ]; then + echo "$0 >> PTP unlocked"; + #killall -9 speaker-test +fi diff --git a/daemon/session_manager.cpp b/daemon/session_manager.cpp index 2c00a0e..f6a5a91 100644 --- a/daemon/session_manager.cpp +++ b/daemon/session_manager.cpp @@ -19,6 +19,7 @@ #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH 4096 // max for SDP file +#include #include #include @@ -479,7 +480,7 @@ std::error_code SessionManager::add_source(const StreamSource& source) { info.stream.m_ui32CRTP_stream_info_sizeof = sizeof(info.stream); strncpy(info.stream.m_cName, source.name.c_str(), sizeof(info.stream.m_cName) - 1); - info.stream.m_ucDSCP = source.dscp; // IPv4 DSCP + info.stream.m_ucDSCP = source.dscp; // IPv4 DSCP info.stream.m_byPayloadType = source.payload_type; info.stream.m_byWordLength = get_codec_word_lenght(source.codec); info.stream.m_byNbOfChannels = source.map.size(); @@ -513,13 +514,16 @@ std::error_code SessionManager::add_source(const StreamSource& source) { info.stream.m_ui8DestMAC); info.stream.m_byTTL = source.ttl; } else { - auto mac_addr = get_mac_from_arp_cache(config_->get_interface_name(), + auto mac_addr = get_mac_from_arp_cache( + config_->get_interface_name(), ip::address_v4(info.stream.m_ui32DestIP).to_string()); int retry = 3; while (!mac_addr.second.length() && retry--) { // if not in cache already try to populate the MAC cache - (void)echo_try_connect(ip::address_v4(info.stream.m_ui32DestIP).to_string()); - mac_addr = get_mac_from_arp_cache(config_->get_interface_name(), + (void)echo_try_connect( + ip::address_v4(info.stream.m_ui32DestIP).to_string()); + mac_addr = get_mac_from_arp_cache( + config_->get_interface_name(), ip::address_v4(info.stream.m_ui32DestIP).to_string()); } if (!mac_addr.second.length()) { @@ -625,8 +629,8 @@ std::string SessionManager::get_source_sdp_(uint32_t id, if (IN_MULTICAST(info.stream.m_ui32DestIP)) { ss << "/" << static_cast(info.stream.m_byTTL); } - ss << "\na=rtpmap:" << static_cast(info.stream.m_byPayloadType) << " " - << info.stream.m_cCodec << "/" << sample_rate << "/" + ss << "\na=rtpmap:" << static_cast(info.stream.m_byPayloadType) + << " " << info.stream.m_cCodec << "/" << sample_rate << "/" << static_cast(info.stream.m_byNbOfChannels) << "\n" << "a=sync-time:0\n" << "a=framecount:" << info.stream.m_ui32MaxSamplesPerPacket << "\n" @@ -902,6 +906,19 @@ std::error_code SessionManager::get_sink_status( return ret; } +std::error_code SessionManager::set_driver_config(const std::string& name, + uint32_t value) const { + if (name == "sample_rate") + return driver_->set_sample_rate(value); + else if (name == "tic_frame_size_at_1fs") + return driver_->set_tic_frame_size_at_1fs(value); + else if (name == "set_max_tic_frame_size") + return driver_->set_max_tic_frame_size(value); + else if (name == "playout_delay") + return driver_->set_playout_delay(value); + return DriverErrc::unknown; +} + std::error_code SessionManager::set_ptp_config(const PTPConfig& config) { TPTPConfig ptp_config; ptp_config.ui8Domain = config.domain; @@ -1002,9 +1019,31 @@ void SessionManager::on_update_sources() { g_session_version++; } -void SessionManager::on_ptp_status_locked() const { - // set sample rate, this may require seconds - (void)driver_->set_sample_rate(driver_->get_current_sample_rate()); +void SessionManager::on_ptp_status_changed(const std::string& status) const { + if (status == "locked") { + // set sample rate, this may require seconds + (void)driver_->set_sample_rate(driver_->get_current_sample_rate()); + } + + static std::string g_ptp_status; + + if (g_ptp_status != status && !config_->get_ptp_status_script().empty()) { + pid_t pid = fork(); + if (pid == 0) { + /* child */ + int fdlimit = (int)sysconf(_SC_OPEN_MAX); + /* close all partent's fds */ + for (int i = STDERR_FILENO + 1; i < fdlimit; i++) + close(i); + + char* argv_list[] = {const_cast(config_->get_ptp_status_script().c_str()), + const_cast(status.c_str()), NULL}; + + execv(config_->get_ptp_status_script().c_str(), argv_list); + exit(0); + } + g_ptp_status = status; + } } using namespace std::chrono; @@ -1043,7 +1082,7 @@ bool SessionManager::worker() { pui64GMID[5], pui64GMID[6], pui64GMID[7]); bool ptp_changed_gmid = false; - bool ptp_changed_to_locked = false; + std::string ptp_status_changed_to; // update PTP clock status ptp_mutex_.lock(); // update status @@ -1069,15 +1108,13 @@ bool SessionManager::worker() { BOOST_LOG_TRIVIAL(info) << "session_manager:: new PTP clock status " << new_ptp_status; ptp_status_.status = new_ptp_status; - if (new_ptp_status == "locked") { - ptp_changed_to_locked = true; - } + ptp_status_changed_to = new_ptp_status; } // end update PTP clock status ptp_mutex_.unlock(); - if (ptp_changed_to_locked) { - on_ptp_status_locked(); + if (!ptp_status_changed_to.empty()) { + on_ptp_status_changed(ptp_status_changed_to); } if (ptp_changed_gmid || @@ -1085,10 +1122,10 @@ bool SessionManager::worker() { /* master clock id changed or sample rate changed * we need to update all the sources */ if (sample_rate != driver_->get_current_sample_rate()) { - sample_rate = driver_->get_current_sample_rate(); - // set driver sample rate - (void)driver_->set_sample_rate(sample_rate); - } + sample_rate = driver_->get_current_sample_rate(); + // set driver sample rate + (void)driver_->set_sample_rate(sample_rate); + } on_update_sources(); } } diff --git a/daemon/session_manager.hpp b/daemon/session_manager.hpp index 7dedd34..b580b14 100644 --- a/daemon/session_manager.hpp +++ b/daemon/session_manager.hpp @@ -151,6 +151,8 @@ class SessionManager { uint8_t get_sink_id(const std::string& name) const; std::error_code set_ptp_config(const PTPConfig& config); + std::error_code set_driver_config(const std::string& name, + uint32_t value) const; void get_ptp_config(PTPConfig& config) const; void get_ptp_status(PTPStatus& status) const; @@ -169,7 +171,7 @@ class SessionManager { void on_add_sink(const StreamSink& sink, const StreamInfo& info); void on_remove_sink(const StreamInfo& info); - void on_ptp_status_locked() const; + void on_ptp_status_changed(const std::string& status) const; void on_update_sources(); diff --git a/daemon/tests/daemon.conf b/daemon/tests/daemon.conf index 9ab940d..f4d2d72 100644 --- a/daemon/tests/daemon.conf +++ b/daemon/tests/daemon.conf @@ -20,5 +20,6 @@ "mdns_enabled": true, "mac_addr": "00:00:00:00:00:00", "ip_addr": "127.0.0.1", - "node_id": "AES67 daemon 007f0100" + "node_id": "AES67 daemon 007f0100", + "ptp_status_script": "" } diff --git a/daemon/tests/daemon_test.cpp b/daemon/tests/daemon_test.cpp index b13568a..d633c13 100644 --- a/daemon/tests/daemon_test.cpp +++ b/daemon/tests/daemon_test.cpp @@ -392,6 +392,7 @@ BOOST_AUTO_TEST_CASE(get_config) { auto syslog_proto = pt.get("syslog_proto"); auto syslog_server = pt.get("syslog_server"); auto status_file = pt.get("status_file"); + auto ptp_status_script = pt.get("ptp_status_script"); auto interface_name = pt.get("interface_name"); auto mac_addr = pt.get("mac_addr"); auto ip_addr = pt.get("ip_addr"); @@ -413,6 +414,7 @@ BOOST_AUTO_TEST_CASE(get_config) { BOOST_CHECK_MESSAGE(interface_name == "lo", "config as excepcted"); BOOST_CHECK_MESSAGE(mac_addr == "00:00:00:00:00:00", "config as excepcted"); BOOST_CHECK_MESSAGE(ip_addr == "127.0.0.1", "config as excepcted"); + BOOST_CHECK_MESSAGE(ptp_status_script == "", "config as excepcted"); } BOOST_AUTO_TEST_CASE(get_ptp_status) { diff --git a/test/daemon.conf b/test/daemon.conf index 0a118dc..3d3131f 100644 --- a/test/daemon.conf +++ b/test/daemon.conf @@ -20,5 +20,6 @@ "mdns_enabled": false, "mac_addr": "00:00:00:00:00:00", "ip_addr": "127.0.0.1", - "node_id": "AES67 daemon 007f0100" + "node_id": "AES67 daemon 007f0100", + "ptp_status_script": "" } diff --git a/webui/src/Config.jsx b/webui/src/Config.jsx index 3bd7f56..2019c9e 100644 --- a/webui/src/Config.jsx +++ b/webui/src/Config.jsx @@ -135,7 +135,7 @@ class Config extends Component { this.state.sapMcastAddr, this.state.sapInterval, this.state.mdnsEnabled) - .then(response => toast.success('Config updated, daemon restart ...')); + .then(response => toast.success('Applying new configuration ...')); } render() {