diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-08-13 10:48:49 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-08-13 10:48:49 +0200 |
commit | ef536ba0e190df286d273ded5ba93f4a2f39b45e (patch) | |
tree | 5d45c336e2244e3e59e5ae681dd74df037d809ad /src | |
parent | 14f69f9c915cf644147a52b803d79ff8f40a4ea1 (diff) | |
download | dabmux-ef536ba0e190df286d273ded5ba93f4a2f39b45e.tar.gz dabmux-ef536ba0e190df286d273ded5ba93f4a2f39b45e.tar.bz2 dabmux-ef536ba0e190df286d273ded5ba93f4a2f39b45e.zip |
Pull in files from odr-mmbtools-common
Telnet RC is now single-user only.
Diffstat (limited to 'src')
-rw-r--r-- | src/ClockTAI.cpp | 562 | ||||
-rw-r--r-- | src/ClockTAI.h | 102 | ||||
-rw-r--r-- | src/Log.cpp | 143 | ||||
-rw-r--r-- | src/Log.h | 157 | ||||
-rw-r--r-- | src/RemoteControl.cpp | 595 | ||||
-rw-r--r-- | src/RemoteControl.h | 263 | ||||
-rw-r--r-- | src/crc.c | 266 | ||||
-rw-r--r-- | src/crc.h | 59 |
8 files changed, 0 insertions, 2147 deletions
diff --git a/src/ClockTAI.cpp b/src/ClockTAI.cpp deleted file mode 100644 index c376c07..0000000 --- a/src/ClockTAI.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012 Her Majesty the Queen in Right of Canada (Communications - Research Center Canada) - - Copyright (C) 2019 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. -*/ - -/* This file downloads the TAI-UTC bulletins from the from IETF and parses them - * so that correct time can be communicated in EDI timestamps. - * - * This file contains self-test code that can be executed by running - * g++ -g -Wall -DTEST -DHAVE_CURL -std=c++11 -lcurl -pthread \ - * ClockTAI.cpp Log.cpp RemoteControl.cpp -lboost_system -o taitest && ./taitest - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "ClockTAI.h" -#include "Log.h" - -#include <time.h> -#include <stdio.h> -#include <errno.h> -#if SUPPORT_SETTING_CLOCK_TAI -# include <sys/timex.h> -#endif -#ifdef HAVE_CURL -# include <curl/curl.h> -#endif -#include <array> -#include <string> -#include <iostream> -#include <algorithm> -#include <regex> - -using namespace std; - -#ifdef TEST -static bool wait_longer = true; -#endif - -constexpr int download_retry_interval_hours = 1; - -// Offset between NTP time and POSIX time: -// timestamp_unix = timestamp_ntp - ntp_unix_offset -const int64_t ntp_unix_offset = 2208988800L; - -// leap seconds insertion bulletin is available from the IETF and in the TZ -// distribution -static array<const char*, 2> default_tai_urls = { - "https://www.ietf.org/timezones/data/leap-seconds.list", - "https://raw.githubusercontent.com/eggert/tz/master/leap-seconds.list", -}; - -// According to the Filesystem Hierarchy Standard, the data in -// /var/tmp "must not be deleted when the system is booted." -static const char *tai_cache_location = "/var/tmp/odr-dabmux-leap-seconds.cache"; - -// read TAI offset from a valid bulletin in IETF format -static int parse_ietf_bulletin(const std::string& bulletin) -{ - // Example Line: - // 3692217600 37 # 1 Jan 2017 - // - // NTP timestamp<TAB>leap seconds<TAB># some comment - // The NTP timestamp starts at epoch 1.1.1900. - // The difference between NTP timestamps and unix epoch is 70 - // years i.e. 2208988800 seconds - - std::regex regex_bulletin(R"(([0-9]+)\s+([0-9]+)\s+#.*)"); - - time_t now = time(nullptr); - - int tai_utc_offset = 0; - - int tai_utc_offset_valid = false; - - stringstream ss(bulletin); - - /* We cannot just take the last line, because it might - * be in the future, announcing an upcoming leap second. - * - * So we need to look at the current date, and compare it - * with the date of the leap second. - */ - for (string line; getline(ss, line); ) { - - std::smatch bulletin_entry; - - bool is_match = std::regex_search(line, bulletin_entry, regex_bulletin); - if (is_match) { - if (bulletin_entry.size() != 3) { - throw runtime_error( - "Incorrect number of matched TAI IETF bulletin entries"); - } - const string bulletin_ntp_timestamp(bulletin_entry[1]); - const string bulletin_offset(bulletin_entry[2]); - - const int64_t timestamp_unix = - std::atoll(bulletin_ntp_timestamp.c_str()) - ntp_unix_offset; - - const int offset = std::atoi(bulletin_offset.c_str()); - // Ignore entries announcing leap seconds in the future - if (timestamp_unix < now) { - tai_utc_offset = offset; - tai_utc_offset_valid = true; - } -#if TEST - else { - cerr << "IETF Ignoring offset " << bulletin_offset << - " at TS " << bulletin_ntp_timestamp << - " in the future" << endl; - } -#endif - } - } - - if (not tai_utc_offset_valid) { - throw runtime_error("No data in TAI bulletin"); - } - - return tai_utc_offset; -} - - -struct bulletin_state { - bool valid = false; - int64_t expiry = 0; - int offset = 0; - - bool usable() const { return valid and expiry > 0; } -}; - -static bulletin_state parse_bulletin(const string& bulletin) -{ - // The bulletin contains one line that specifies an expiration date - // in NTP time. If that point in time is in the future, we consider - // the bulletin valid. - // - // The entry looks like this: - //#@ 3707596800 - - bulletin_state ret; - - std::regex regex_expiration(R"(#@\s+([0-9]+))"); - - time_t now = time(nullptr); - - stringstream ss(bulletin); - - for (string line; getline(ss, line); ) { - std::smatch bulletin_entry; - - bool is_match = std::regex_search(line, bulletin_entry, regex_expiration); - if (is_match) { - if (bulletin_entry.size() != 2) { - throw runtime_error( - "Incorrect number of matched TAI IETF bulletin expiration"); - } - const string expiry_data_str(bulletin_entry[1]); - const int64_t expiry_unix = - std::atoll(expiry_data_str.c_str()) - ntp_unix_offset; - -#ifdef TEST - etiLog.level(info) << "Bulletin expires in " << expiry_unix - now; -#endif - ret.expiry = expiry_unix - now; - try { - ret.offset = parse_ietf_bulletin(bulletin); - ret.valid = true; - } - catch (const runtime_error& e) { - etiLog.level(warn) << "Bulletin expiry ok but parse error: " << e.what(); - } - break; - } - } - return ret; -} - - -// callback that receives data from cURL -static size_t fill_bulletin(char *ptr, size_t size, size_t nmemb, void *ctx) -{ - auto *bulletin = reinterpret_cast<stringstream*>(ctx); - - size_t len = size * nmemb; - for (size_t i = 0; i < len; i++) { - *bulletin << ptr[i]; - } - return len; -} - -static string download_tai_utc_bulletin(const char* url) -{ - stringstream bulletin; - -#ifdef HAVE_CURL - CURL *curl; - CURLcode res; - - curl = curl_easy_init(); - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, url); - /* Tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fill_bulletin); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &bulletin); - - res = curl_easy_perform(curl); - /* always cleanup ! */ - curl_easy_cleanup(curl); - - if (res != CURLE_OK) { - throw runtime_error( "TAI-UTC bulletin download failed: " + - string(curl_easy_strerror(res))); - } - } - return bulletin.str(); -#else - throw runtime_error("Cannot download TAI Clock information without cURL"); -#endif // HAVE_CURL -} - -static string load_bulletin_from_file(const char* cache_filename) -{ - // Clear the bulletin - ifstream f(cache_filename); - if (not f.good()) { - return {}; - } - - stringstream ss; - ss << f.rdbuf(); - f.close(); - - return ss.str(); -} - -ClockTAI::ClockTAI(const std::vector<std::string>& bulletin_urls) : - RemoteControllable("clocktai") -{ - RC_ADD_PARAMETER(expiry, "Number of seconds until TAI Bulletin expires"); - - if (bulletin_urls.empty()) { - etiLog.level(debug) << "Initialising default TAI Bulletin URLs"; - for (const auto url : default_tai_urls) { - m_bulletin_urls.push_back(url); - } - } - else { - etiLog.level(debug) << "Initialising user-configured TAI Bulletin URLs"; - m_bulletin_urls = bulletin_urls; - } - - for (const auto url : m_bulletin_urls) { - etiLog.level(info) << "TAI Bulletin URL: '" << url << "'"; - } -} - -int ClockTAI::get_valid_offset() -{ - int offset = 0; - bool offset_valid = false; - - std::unique_lock<std::mutex> lock(m_data_mutex); - - const auto state = parse_bulletin(m_bulletin); - if (state.usable()) { -#if TEST - etiLog.level(info) << "Bulletin already valid"; -#endif - offset = state.offset; - offset_valid = true; - } - else { - const auto cache_bulletin = load_bulletin_from_file(tai_cache_location); - const auto cache_state = parse_bulletin(cache_bulletin); - - if (cache_state.usable()) { - m_bulletin = cache_bulletin; - offset = cache_state.offset; - offset_valid = true; -#if TEST - etiLog.level(info) << "Bulletin from cache valid with offset=" << offset; -#endif - } - else { - for (const auto url : m_bulletin_urls) { - try { -#if TEST - etiLog.level(info) << "Load bulletin from " << url; -#endif - const auto new_bulletin = download_tai_utc_bulletin(url.c_str()); - const auto new_state = parse_bulletin(new_bulletin); - if (new_state.usable()) { - m_bulletin = new_bulletin; - offset = new_state.offset; - offset_valid = true; - - etiLog.level(debug) << "Loaded valid TAI Bulletin from " << - url << " giving offset=" << offset; - } - else { - etiLog.level(debug) << "Skipping invalid TAI bulletin from " - << url; - } - } - catch (const runtime_error& e) { - etiLog.level(warn) << - "TAI-UTC offset could not be retrieved from " << - url << " : " << e.what(); - } - - if (offset_valid) { - update_cache(tai_cache_location); - break; - } - } - } - } - - if (offset_valid) { - // With the current evolution of the offset, we're probably going - // to reach 500 long after DAB gets replaced by another standard. - if (offset < 0 or offset > 500) { - stringstream ss; - ss << "TAI offset " << offset << " out of range"; - throw range_error(ss.str()); - } - - return offset; - } - else { - // Try again later - throw download_failed(); - } -} - - -int ClockTAI::get_offset() -{ - using namespace std::chrono; - const auto time_now = system_clock::now(); - - std::unique_lock<std::mutex> lock(m_data_mutex); - - if (not m_offset_valid) { -#ifdef TEST - // Assume we've downloaded it in the past: - - m_offset = 37; // Valid in early 2017 - m_offset_valid = true; - - // Simulate requiring a new download - m_bulletin_download_time = time_now - hours(24 * 40); -#else - // First time we run we must block until we know - // the offset - lock.unlock(); - try { - m_offset = get_valid_offset(); - } - catch (const download_failed&) { - throw runtime_error("Unable to download TAI bulletin"); - } - lock.lock(); - m_offset_valid = true; - m_bulletin_download_time = time_now; -#endif - etiLog.level(info) << - "Initialised TAI-UTC offset to " << m_offset << "s."; - } - - if (time_now - m_bulletin_download_time > hours(24 * 31)) { - // Refresh if it's older than one month. Leap seconds are - // announced several months in advance - etiLog.level(debug) << "Trying to refresh TAI bulletin"; - - if (m_offset_future.valid()) { - auto state = m_offset_future.wait_for(seconds(0)); - switch (state) { - case future_status::ready: - try { - m_offset = m_offset_future.get(); - m_offset_valid = true; - m_bulletin_download_time = time_now; - - etiLog.level(info) << - "Updated TAI-UTC offset to " << m_offset << "s."; - } - catch (const download_failed&) { - etiLog.level(warn) << - "TAI-UTC download failed, will retry in " << - download_retry_interval_hours << " hour(s)"; - - m_bulletin_download_time += hours(download_retry_interval_hours); - } -#ifdef TEST - wait_longer = false; -#endif - break; - - case future_status::deferred: - case future_status::timeout: - // Not ready yet -#ifdef TEST - etiLog.level(debug) << " async not ready yet"; -#endif - break; - } - } - else { -#ifdef TEST - etiLog.level(debug) << " Launch async"; -#endif - m_offset_future = async(launch::async, &ClockTAI::get_valid_offset, this); - } - } - - return m_offset; -} - -#if SUPPORT_SETTING_CLOCK_TAI -int ClockTAI::update_local_tai_clock(int offset) -{ - struct timex timex_request; - timex_request.modes = ADJ_TAI; - timex_request.constant = offset; - - int err = adjtimex(&timex_request); - if (err == -1) { - perror("adjtimex"); - } - - printf("adjtimex: %d, tai %d\n", err, timex_request.tai); - - return err; -} -#endif - -void ClockTAI::update_cache(const char* cache_filename) -{ - ofstream f(cache_filename); - if (not f.good()) { - throw runtime_error("TAI-UTC bulletin open cache for writing"); - } - - f << m_bulletin; - f.close(); -} - - -void ClockTAI::set_parameter(const string& parameter, const string& value) -{ - if (parameter == "expiry") { - throw ParameterError("Parameter '" + parameter + - "' is not read-only in controllable " + get_rc_name()); - } - else { - throw ParameterError("Parameter '" + parameter + - "' is not exported by controllable " + get_rc_name()); - } -} - -const string ClockTAI::get_parameter(const string& parameter) const -{ - if (parameter == "expiry") { - std::unique_lock<std::mutex> lock(m_data_mutex); - const int64_t expiry = parse_bulletin(m_bulletin).expiry; - if (expiry > 0) { - return to_string(expiry); - } - else { - return "Bulletin expired or invalid!"; - } - } - else { - throw ParameterError("Parameter '" + parameter + - "' is not exported by controllable " + get_rc_name()); - } -} - -#if 0 -// Example testing code -void debug_tai_clk() -{ - struct timespec rt_clk; - - int err = clock_gettime(CLOCK_REALTIME, &rt_clk); - if (err) { - perror("REALTIME clock_gettime failed"); - } - - struct timespec tai_clk; - - err = clock_gettime(CLOCK_TAI, &tai_clk); - if (err) { - perror("TAI clock_gettime failed"); - } - - printf("RT - TAI = %ld\n", rt_clk.tv_sec - tai_clk.tv_sec); - - - struct timex timex_request; - timex_request.modes = 0; // Do not set anything - - err = adjtimex(&timex_request); - if (err == -1) { - perror("adjtimex"); - } - - printf("adjtimex: %d, tai %d\n", err, timex_request.tai); -} -#endif - -#if TEST -int main(int argc, char **argv) -{ - using namespace std; - - ClockTAI tai({}); - - while (wait_longer) { - try { - etiLog.level(info) << - "Offset is " << tai.get_offset(); - } - catch (const exception &e) { - etiLog.level(error) << - "Exception " << e.what(); - } - - this_thread::sleep_for(chrono::seconds(2)); - } - - return 0; -} -#endif - diff --git a/src/ClockTAI.h b/src/ClockTAI.h deleted file mode 100644 index 4b3c2ff..0000000 --- a/src/ClockTAI.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012 Her Majesty the Queen in Right of Canada (Communications - Research Center Canada) - - Copyright (C) 2019 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. -*/ - -/* The EDI output needs TAI clock, according to ETSI TS 102 693 Annex F - * "EDI Timestamps". This module can set the local CLOCK_TAI clock by - * setting the TAI-UTC offset using adjtimex. - * - * This functionality requires Linux 3.10 (30 Jun 2013) or newer. - */ - -#pragma once - -#include <stdint.h> -#include <stdlib.h> -#include <sstream> -#include <chrono> -#include <future> -#include <mutex> -#include <string> -#include <vector> -#include "RemoteControl.h" - -// EDI needs to know UTC-TAI, but doesn't need the CLOCK_TAI to be set. -// We can keep this code, maybe for future use -#define SUPPORT_SETTING_CLOCK_TAI 0 - -/* Loads, parses and represents TAI-UTC offset information from the IETF bulletin */ -class ClockTAI : public RemoteControllable { - public: - ClockTAI(const std::vector<std::string>& bulletin_urls); - - // Fetch the bulletin from the IETF website and return the current - // TAI-UTC offset. - // Throws runtime_error on failure. - int get_offset(void); - -#if SUPPORT_SETTING_CLOCK_TAI - // Update the local TAI clock according to the TAI-UTC offset - // return 0 on success - int update_local_tai_clock(int offset); -#endif - - private: - class download_failed {}; - - // Either retrieve the bulletin from the cache or if necessarly - // download it, and calculate the TAI-UTC offset. - // Returns the offset or throws download_failed or a range_error - // if the offset is out of bounds. - int get_valid_offset(void); - - // Download of new bulletin is done asynchronously - std::future<int> m_offset_future; - - // Protect all data members, as RC functions are in another thread - mutable std::mutex m_data_mutex; - - // The currently used TAI-UTC offset - int m_offset = 0; - int m_offset_valid = false; - - std::vector<std::string> m_bulletin_urls; - - std::string m_bulletin; - std::chrono::system_clock::time_point m_bulletin_download_time; - - // Update the cache file with the current m_bulletin - void update_cache(const char* cache_filename); - - - /* Remote control */ - virtual void set_parameter(const std::string& parameter, - const std::string& value); - - /* Getting a parameter always returns a string. */ - virtual const std::string get_parameter(const std::string& parameter) const; -}; - diff --git a/src/Log.cpp b/src/Log.cpp deleted file mode 100644 index 6b78fe0..0000000 --- a/src/Log.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Her Majesty the Queen in Right of Canada (Communications Research - Center Canada) - - Copyright (C) 2018 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <list> -#include <cstdarg> -#include <chrono> - -#include "Log.h" - -using namespace std; - -/* etiLog is a singleton used in all parts of ODR-DabMod to output log messages. - */ -Logger etiLog; - -void Logger::register_backend(std::shared_ptr<LogBackend> backend) -{ - backends.push_back(backend); -} - - -void Logger::log(log_level_t level, const char* fmt, ...) -{ - if (level == discard) { - return; - } - - int size = 100; - std::string str; - va_list ap; - while (1) { - str.resize(size); - va_start(ap, fmt); - int n = vsnprintf((char *)str.c_str(), size, fmt, ap); - va_end(ap); - if (n > -1 && n < size) { - str.resize(n); - break; - } - if (n > -1) - size = n + 1; - else - size *= 2; - } - - logstr(level, move(str)); -} - -void Logger::logstr(log_level_t level, std::string&& message) -{ - if (level == discard) { - return; - } - - /* Remove a potential trailing newline. - * It doesn't look good in syslog - */ - if (message[message.length()-1] == '\n') { - message.resize(message.length()-1); - } - - for (auto &backend : backends) { - backend->log(level, message); - } - - { - std::lock_guard<std::mutex> guard(m_cerr_mutex); - std::cerr << levels_as_str[level] << " " << message << std::endl; - } -} - - -LogLine Logger::level(log_level_t level) -{ - return LogLine(this, level); -} - -LogToFile::LogToFile(const std::string& filename) : name("FILE") -{ - FILE* fd = fopen(filename.c_str(), "a"); - if (fd == nullptr) { - fprintf(stderr, "Cannot open log file !"); - throw std::runtime_error("Cannot open log file !"); - } - - log_file.reset(fd); -} - -void LogToFile::log(log_level_t level, const std::string& message) -{ - if (level != log_level_t::discard) { - const char* log_level_text[] = { - "DEBUG", "INFO", "WARN", "ERROR", "ALERT", "EMERG"}; - - // fprintf is thread-safe - fprintf(log_file.get(), SYSLOG_IDENT ": %s: %s\n", - log_level_text[(size_t)level], message.c_str()); - fflush(log_file.get()); - } -} - -void LogToSyslog::log(log_level_t level, const std::string& message) -{ - if (level != log_level_t::discard) { - int syslog_level = LOG_EMERG; - switch (level) { - case debug: syslog_level = LOG_DEBUG; break; - case info: syslog_level = LOG_INFO; break; - /* we don't have the notice level */ - case warn: syslog_level = LOG_WARNING; break; - case error: syslog_level = LOG_ERR; break; - default: syslog_level = LOG_CRIT; break; - case alert: syslog_level = LOG_ALERT; break; - case emerg: syslog_level = LOG_EMERG; break; - } - - syslog(syslog_level, SYSLOG_IDENT " %s", message.c_str()); - } -} diff --git a/src/Log.h b/src/Log.h deleted file mode 100644 index 18f8c99..0000000 --- a/src/Log.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Her Majesty the Queen in Right of Canada (Communications Research - Center Canada) - - Copyright (C) 2018 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <syslog.h> -#include <cstdarg> -#include <cstdio> -#include <fstream> -#include <sstream> -#include <iostream> -#include <list> -#include <stdexcept> -#include <string> -#include <mutex> -#include <memory> - -#define SYSLOG_IDENT "ODR-DabMux" -#define SYSLOG_FACILITY LOG_LOCAL0 - -enum log_level_t {debug = 0, info, warn, error, alert, emerg, discard}; - -static const std::string levels_as_str[] = - { " ", " ", "WARN ", "ERROR", "ALERT", "EMERG", "-----"} ; - -/** Abstract class all backends must inherit from */ -class LogBackend { - public: - virtual ~LogBackend() {}; - virtual void log(log_level_t level, const std::string& message) = 0; - virtual std::string get_name() const = 0; -}; - -/** A Logging backend for Syslog */ -class LogToSyslog : public LogBackend { - public: - LogToSyslog() : name("SYSLOG") { - openlog(SYSLOG_IDENT, LOG_PID, SYSLOG_FACILITY); - } - - virtual ~LogToSyslog() { - closelog(); - } - - void log(log_level_t level, const std::string& message); - - std::string get_name() const { return name; } - - private: - const std::string name; - - LogToSyslog(const LogToSyslog& other) = delete; - const LogToSyslog& operator=(const LogToSyslog& other) = delete; -}; - -class LogToFile : public LogBackend { - public: - LogToFile(const std::string& filename); - void log(log_level_t level, const std::string& message); - std::string get_name() const { return name; } - - private: - const std::string name; - - struct FILEDeleter{ void operator()(FILE* fd){ if(fd) fclose(fd);}}; - std::unique_ptr<FILE, FILEDeleter> log_file; - - LogToFile(const LogToFile& other) = delete; - const LogToFile& operator=(const LogToFile& other) = delete; -}; - -class LogLine; - -class Logger { - public: - void register_backend(std::shared_ptr<LogBackend> backend); - - /* Log the message to all backends */ - void log(log_level_t level, const char* fmt, ...); - - void logstr(log_level_t level, std::string&& message); - - /* Return a LogLine for the given level - * so that you can write etiLog.level(info) << "stuff = " << 21 */ - LogLine level(log_level_t level); - - private: - std::list<std::shared_ptr<LogBackend> > backends; - - std::mutex m_cerr_mutex; -}; - -extern Logger etiLog; - -// Accumulate a line of logs, using same syntax as stringstream -// The line is logged when the LogLine gets destroyed -class LogLine { - public: - LogLine(const LogLine& logline); - const LogLine& operator=(const LogLine& other) = delete; - LogLine(Logger* logger, log_level_t level) : - logger_(logger) - { - level_ = level; - } - - // Push the new element into the stringstream - template <typename T> - LogLine& operator<<(T s) { - if (level_ != discard) { - os << s; - } - return *this; - } - - ~LogLine() - { - if (level_ != discard) { - logger_->logstr(level_, os.str()); - } - } - - private: - std::ostringstream os; - log_level_t level_; - Logger* logger_; -}; - - diff --git a/src/RemoteControl.cpp b/src/RemoteControl.cpp deleted file mode 100644 index b32c21a..0000000 --- a/src/RemoteControl.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/* - Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 - Her Majesty the Queen in Right of Canada (Communications Research - Center Canada) - - Copyright (C) 2019 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ -#include <list> -#include <string> -#include <iostream> -#include <string> -#include <boost/asio.hpp> -#include <boost/thread.hpp> - -#include "RemoteControl.h" - -using boost::asio::ip::tcp; -using namespace std; - -RemoteControllers rcs; - -RemoteControllerTelnet::~RemoteControllerTelnet() -{ - m_active = false; - m_io_service.stop(); - - if (m_restarter_thread.joinable()) { - m_restarter_thread.join(); - } - - if (m_child_thread.joinable()) { - m_child_thread.join(); - } -} - -void RemoteControllerTelnet::restart() -{ - if (m_restarter_thread.joinable()) { - m_restarter_thread.join(); - } - - m_restarter_thread = std::thread( - &RemoteControllerTelnet::restart_thread, - this, 0); -} - -RemoteControllable::~RemoteControllable() { - rcs.remove_controllable(this); -} - -std::list<std::string> RemoteControllable::get_supported_parameters() const { - std::list<std::string> parameterlist; - for (const auto& param : m_parameters) { - parameterlist.push_back(param[0]); - } - return parameterlist; -} - -void RemoteControllers::add_controller(std::shared_ptr<BaseRemoteController> rc) { - m_controllers.push_back(rc); -} - -void RemoteControllers::enrol(RemoteControllable *rc) { - controllables.push_back(rc); -} - -void RemoteControllers::remove_controllable(RemoteControllable *rc) { - controllables.remove(rc); -} - -std::list< std::vector<std::string> > RemoteControllers::get_param_list_values(const std::string& name) { - RemoteControllable* controllable = get_controllable_(name); - - std::list< std::vector<std::string> > allparams; - for (auto ¶m : controllable->get_supported_parameters()) { - std::vector<std::string> item; - item.push_back(param); - try { - item.push_back(controllable->get_parameter(param)); - } - catch (const ParameterError &e) { - item.push_back(std::string("error: ") + e.what()); - } - - allparams.push_back(item); - } - return allparams; -} - -std::string RemoteControllers::get_param(const std::string& name, const std::string& param) { - RemoteControllable* controllable = get_controllable_(name); - return controllable->get_parameter(param); -} - -void RemoteControllers::check_faults() { - for (auto &controller : m_controllers) { - if (controller->fault_detected()) { - etiLog.level(warn) << - "Detected Remote Control fault, restarting it"; - controller->restart(); - } - } -} - -RemoteControllable* RemoteControllers::get_controllable_(const std::string& name) -{ - auto rc = std::find_if(controllables.begin(), controllables.end(), - [&](RemoteControllable* r) { return r->get_rc_name() == name; }); - - if (rc == controllables.end()) { - throw ParameterError("Module name unknown"); - } - else { - return *rc; - } -} - -void RemoteControllers::set_param( - const std::string& name, - const std::string& param, - const std::string& value) -{ - etiLog.level(info) << "RC: Setting " << name << " " << param - << " to " << value; - RemoteControllable* controllable = get_controllable_(name); - try { - return controllable->set_parameter(param, value); - } - catch (const ios_base::failure& e) { - etiLog.level(info) << "RC: Failed to set " << name << " " << param - << " to " << value << ": " << e.what(); - throw ParameterError("Cannot understand value"); - } -} - -// This runs in a separate thread, because -// it would take too long to be done in the main loop -// thread. -void RemoteControllerTelnet::restart_thread(long) -{ - m_active = false; - m_io_service.stop(); - - if (m_child_thread.joinable()) { - m_child_thread.join(); - } - - m_child_thread = std::thread(&RemoteControllerTelnet::process, this, 0); -} - -void RemoteControllerTelnet::handle_accept( - const boost::system::error_code& boost_error, - boost::shared_ptr< boost::asio::ip::tcp::socket > socket, - boost::asio::ip::tcp::acceptor& acceptor) -{ - - const std::string welcome = "ODR-DabMux Remote Control CLI\n" - "Write 'help' for help.\n" - "**********\n"; - const std::string prompt = "> "; - - std::string in_message; - size_t length; - - if (boost_error) { - etiLog.level(error) << "RC: Error accepting connection"; - return; - } - - try { - etiLog.level(info) << "RC: Accepted"; - - boost::system::error_code ignored_error; - - boost::asio::write(*socket, boost::asio::buffer(welcome), - boost::asio::transfer_all(), - ignored_error); - - while (m_active && in_message != "quit") { - boost::asio::write(*socket, boost::asio::buffer(prompt), - boost::asio::transfer_all(), - ignored_error); - - in_message = ""; - - boost::asio::streambuf buffer; - length = boost::asio::read_until(*socket, buffer, "\n", ignored_error); - - std::istream str(&buffer); - std::getline(str, in_message); - - if (length == 0) { - etiLog.level(info) << "RC: Connection terminated"; - break; - } - - while (in_message.length() > 0 && - (in_message[in_message.length()-1] == '\r' || - in_message[in_message.length()-1] == '\n')) { - in_message.erase(in_message.length()-1, 1); - } - - if (in_message.length() == 0) { - continue; - } - - etiLog.level(info) << "RC: Got message '" << in_message << "'"; - - dispatch_command(*socket, in_message); - } - etiLog.level(info) << "RC: Closing socket"; - socket->close(); - } - catch (const std::exception& e) - { - etiLog.level(error) << "Remote control caught exception: " << e.what(); - } -} - -void RemoteControllerTelnet::process(long) -{ - m_active = true; - - while (m_active) { - m_io_service.reset(); - - tcp::acceptor acceptor(m_io_service, tcp::endpoint( - boost::asio::ip::address::from_string("127.0.0.1"), m_port) ); - - - // Add a job to start accepting connections. - boost::shared_ptr<tcp::socket> socket( - new tcp::socket(acceptor.get_io_service())); - - // Add an accept call to the service. This will prevent io_service::run() - // from returning. - etiLog.level(info) << "RC: Waiting for connection on port " << m_port; - acceptor.async_accept(*socket, - boost::bind(&RemoteControllerTelnet::handle_accept, - this, - boost::asio::placeholders::error, - socket, - boost::ref(acceptor))); - - // Process event loop. - m_io_service.run(); - } - - etiLog.level(info) << "RC: Leaving"; - m_fault = true; -} - -static std::vector<std::string> tokenise(const std::string& message) { - stringstream ss(message); - std::vector<std::string> all_tokens; - std::string item; - - while (std::getline(ss, item, ' ')) { - all_tokens.push_back(move(item)); - } - return all_tokens; -} - - -void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command) -{ - vector<string> cmd = tokenise(command); - - if (cmd[0] == "help") { - reply(socket, - "The following commands are supported:\n" - " list\n" - " * Lists the modules that are loaded and their parameters\n" - " show MODULE\n" - " * Lists all parameters and their values from module MODULE\n" - " get MODULE PARAMETER\n" - " * Gets the value for the specified PARAMETER from module MODULE\n" - " set MODULE PARAMETER VALUE\n" - " * Sets the value for the PARAMETER ofr module MODULE\n" - " quit\n" - " * Terminate this session\n" - "\n"); - } - else if (cmd[0] == "list") { - stringstream ss; - - if (cmd.size() == 1) { - for (auto &controllable : rcs.controllables) { - ss << controllable->get_rc_name() << endl; - - list< vector<string> > params = controllable->get_parameter_descriptions(); - for (auto ¶m : params) { - ss << "\t" << param[0] << " : " << param[1] << endl; - } - } - } - else { - reply(socket, "Too many arguments for command 'list'"); - } - - reply(socket, ss.str()); - } - else if (cmd[0] == "show") { - if (cmd.size() == 2) { - try { - stringstream ss; - list< vector<string> > r = rcs.get_param_list_values(cmd[1]); - for (auto ¶m_val : r) { - ss << param_val[0] << ": " << param_val[1] << endl; - } - reply(socket, ss.str()); - - } - catch (const ParameterError &e) { - reply(socket, e.what()); - } - } - else { - reply(socket, "Incorrect parameters for command 'show'"); - } - } - else if (cmd[0] == "get") { - if (cmd.size() == 3) { - try { - string r = rcs.get_param(cmd[1], cmd[2]); - reply(socket, r); - } - catch (const ParameterError &e) { - reply(socket, e.what()); - } - } - else { - reply(socket, "Incorrect parameters for command 'get'"); - } - } - else if (cmd[0] == "set") { - if (cmd.size() >= 4) { - try { - stringstream new_param_value; - for (size_t i = 3; i < cmd.size(); i++) { - new_param_value << cmd[i]; - - if (i+1 < cmd.size()) { - new_param_value << " "; - } - } - - rcs.set_param(cmd[1], cmd[2], new_param_value.str()); - reply(socket, "ok"); - } - catch (const ParameterError &e) { - reply(socket, e.what()); - } - catch (const exception &e) { - reply(socket, "Error: Invalid parameter value. "); - } - } - else { - reply(socket, "Incorrect parameters for command 'set'"); - } - } - else if (cmd[0] == "quit") { - reply(socket, "Goodbye"); - } - else { - reply(socket, "Message not understood"); - } -} - -void RemoteControllerTelnet::reply(tcp::socket& socket, string message) -{ - boost::system::error_code ignored_error; - stringstream ss; - ss << message << "\r\n"; - boost::asio::write(socket, boost::asio::buffer(ss.str()), - boost::asio::transfer_all(), - ignored_error); -} - - -#if defined(HAVE_RC_ZEROMQ) - -RemoteControllerZmq::~RemoteControllerZmq() { - m_active = false; - m_fault = false; - - if (m_restarter_thread.joinable()) { - m_restarter_thread.join(); - } - - if (m_child_thread.joinable()) { - m_child_thread.join(); - } -} - -void RemoteControllerZmq::restart() -{ - if (m_restarter_thread.joinable()) { - m_restarter_thread.join(); - } - - m_restarter_thread = std::thread(&RemoteControllerZmq::restart_thread, this); -} - -// This runs in a separate thread, because -// it would take too long to be done in the main loop -// thread. -void RemoteControllerZmq::restart_thread() -{ - m_active = false; - - if (m_child_thread.joinable()) { - m_child_thread.join(); - } - - m_child_thread = std::thread(&RemoteControllerZmq::process, this); -} - -void RemoteControllerZmq::recv_all(zmq::socket_t& pSocket, std::vector<std::string> &message) -{ - bool more = true; - do { - zmq::message_t msg; - pSocket.recv(&msg); - std::string incoming((char*)msg.data(), msg.size()); - message.push_back(incoming); - more = msg.more(); - } while (more); -} - -void RemoteControllerZmq::send_ok_reply(zmq::socket_t &pSocket) -{ - zmq::message_t msg(2); - char repCode[2] = {'o', 'k'}; - memcpy ((void*) msg.data(), repCode, 2); - pSocket.send(msg, 0); -} - -void RemoteControllerZmq::send_fail_reply(zmq::socket_t &pSocket, const std::string &error) -{ - zmq::message_t msg1(4); - char repCode[4] = {'f', 'a', 'i', 'l'}; - memcpy ((void*) msg1.data(), repCode, 4); - pSocket.send(msg1, ZMQ_SNDMORE); - - zmq::message_t msg2(error.length()); - memcpy ((void*) msg2.data(), error.c_str(), error.length()); - pSocket.send(msg2, 0); -} - -void RemoteControllerZmq::process() -{ - m_fault = false; - - // create zmq reply socket for receiving ctrl parameters - try { - zmq::socket_t repSocket(m_zmqContext, ZMQ_REP); - - // connect the socket - int hwm = 100; - int linger = 0; - repSocket.setsockopt(ZMQ_RCVHWM, &hwm, sizeof(hwm)); - repSocket.setsockopt(ZMQ_SNDHWM, &hwm, sizeof(hwm)); - repSocket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); - repSocket.bind(m_endpoint.c_str()); - - // create pollitem that polls the ZMQ sockets - zmq::pollitem_t pollItems[] = { {repSocket, 0, ZMQ_POLLIN, 0} }; - while (m_active) { - zmq::poll(pollItems, 1, 100); - std::vector<std::string> msg; - - if (pollItems[0].revents & ZMQ_POLLIN) { - recv_all(repSocket, msg); - - std::string command((char*)msg[0].data(), msg[0].size()); - - if (msg.size() == 1 && command == "ping") { - send_ok_reply(repSocket); - } - else if (msg.size() == 1 && command == "list") { - size_t cohort_size = rcs.controllables.size(); - for (auto &controllable : rcs.controllables) { - std::stringstream ss; - ss << "{ \"name\": \"" << controllable->get_rc_name() << "\"," << - " \"params\": { "; - - list< vector<string> > params = controllable->get_parameter_descriptions(); - size_t i = 0; - for (auto ¶m : params) { - if (i > 0) { - ss << ", "; - } - - ss << "\"" << param[0] << "\": " << - "\"" << param[1] << "\""; - - i++; - } - - ss << " } }"; - - std::string msg_s = ss.str(); - - zmq::message_t zmsg(ss.str().size()); - memcpy ((void*) zmsg.data(), msg_s.data(), msg_s.size()); - - int flag = (--cohort_size > 0) ? ZMQ_SNDMORE : 0; - repSocket.send(zmsg, flag); - } - } - else if (msg.size() == 2 && command == "show") { - std::string module((char*) msg[1].data(), msg[1].size()); - try { - list< vector<string> > r = rcs.get_param_list_values(module); - size_t r_size = r.size(); - for (auto ¶m_val : r) { - std::stringstream ss; - ss << param_val[0] << ": " << param_val[1] << endl; - zmq::message_t zmsg(ss.str().size()); - memcpy(zmsg.data(), ss.str().data(), ss.str().size()); - - int flag = (--r_size > 0) ? ZMQ_SNDMORE : 0; - repSocket.send(zmsg, flag); - } - } - catch (const ParameterError &err) { - send_fail_reply(repSocket, err.what()); - } - } - else if (msg.size() == 3 && command == "get") { - std::string module((char*) msg[1].data(), msg[1].size()); - std::string parameter((char*) msg[2].data(), msg[2].size()); - - try { - std::string value = rcs.get_param(module, parameter); - zmq::message_t zmsg(value.size()); - memcpy ((void*) zmsg.data(), value.data(), value.size()); - repSocket.send(zmsg, 0); - } - catch (const ParameterError &err) { - send_fail_reply(repSocket, err.what()); - } - } - else if (msg.size() == 4 && command == "set") { - std::string module((char*) msg[1].data(), msg[1].size()); - std::string parameter((char*) msg[2].data(), msg[2].size()); - std::string value((char*) msg[3].data(), msg[3].size()); - - try { - rcs.set_param(module, parameter, value); - send_ok_reply(repSocket); - } - catch (const ParameterError &err) { - send_fail_reply(repSocket, err.what()); - } - } - else { - send_fail_reply(repSocket, - "Unsupported command. commands: list, show, get, set"); - } - } - } - repSocket.close(); - } - catch (const zmq::error_t &e) { - etiLog.level(error) << "ZMQ RC error: " << std::string(e.what()); - } - catch (const std::exception& e) { - etiLog.level(error) << "ZMQ RC caught exception: " << e.what(); - m_fault = true; - } -} - -#endif - diff --git a/src/RemoteControl.h b/src/RemoteControl.h deleted file mode 100644 index 0726b28..0000000 --- a/src/RemoteControl.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 - Her Majesty the Queen in Right of Canada (Communications Research - Center Canada) - - Copyright (C) 2019 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://www.opendigitalradio.org - - This module adds remote-control capability to some of the dabmux modules. - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if defined(HAVE_RC_ZEROMQ) -# include "zmq.hpp" -#endif - -#include <list> -#include <map> -#include <memory> -#include <string> -#include <atomic> -#include <iostream> -#include <boost/bind.hpp> -#include <boost/asio.hpp> -#include <boost/foreach.hpp> -#include <boost/tokenizer.hpp> -#include <thread> -#include <stdexcept> - -#include "Log.h" - -#define RC_ADD_PARAMETER(p, desc) { \ - std::vector<std::string> p; \ - p.push_back(#p); \ - p.push_back(desc); \ - m_parameters.push_back(p); \ -} - -class ParameterError : public std::exception -{ - public: - ParameterError(std::string message) : m_message(message) {} - ~ParameterError() throw() {} - const char* what() const throw() { return m_message.c_str(); } - - private: - std::string m_message; -}; - -class RemoteControllable; - -/* Remote controllers (that recieve orders from the user) - * must implement BaseRemoteController - */ -class BaseRemoteController { - public: - /* When this returns one, the remote controller cannot be - * used anymore, and must be restarted by dabmux - */ - virtual bool fault_detected() = 0; - - /* In case of a fault, the remote controller can be - * restarted. - */ - virtual void restart() = 0; - - virtual ~BaseRemoteController() {} -}; - -/* Objects that support remote control must implement the following class */ -class RemoteControllable { - public: - RemoteControllable(const std::string& name) : - m_rc_name(name) {} - - RemoteControllable(const RemoteControllable& other) = delete; - RemoteControllable& operator=(const RemoteControllable& other) = delete; - - virtual ~RemoteControllable(); - - /* return a short name used to identify the controllable. - * It might be used in the commands the user has to type, so keep - * it short - */ - virtual std::string get_rc_name() const { return m_rc_name; } - - /* Return a list of possible parameters that can be set */ - virtual std::list<std::string> get_supported_parameters() const; - - /* Return a mapping of the descriptions of all parameters */ - virtual std::list< std::vector<std::string> > - get_parameter_descriptions() const - { - return m_parameters; - } - - /* Base function to set parameters. */ - virtual void set_parameter( - const std::string& parameter, - const std::string& value) = 0; - - /* Getting a parameter always returns a string. */ - virtual const std::string get_parameter(const std::string& parameter) const = 0; - - protected: - std::string m_rc_name; - std::list< std::vector<std::string> > m_parameters; -}; - -/* Holds all our remote controllers and controlled object. - */ -class RemoteControllers { - public: - void add_controller(std::shared_ptr<BaseRemoteController> rc); - void enrol(RemoteControllable *rc); - void remove_controllable(RemoteControllable *rc); - void check_faults(); - std::list< std::vector<std::string> > get_param_list_values(const std::string& name); - std::string get_param(const std::string& name, const std::string& param); - - void set_param( - const std::string& name, - const std::string& param, - const std::string& value); - - std::list<RemoteControllable*> controllables; - - private: - RemoteControllable* get_controllable_(const std::string& name); - - std::list<std::shared_ptr<BaseRemoteController> > m_controllers; -}; - -extern RemoteControllers rcs; - -/* Implements a Remote controller based on a simple telnet CLI - * that listens on localhost - */ -class RemoteControllerTelnet : public BaseRemoteController { - public: - RemoteControllerTelnet() - : m_active(false), - m_io_service(), - m_fault(false), - m_port(0) { } - - RemoteControllerTelnet(int port) - : m_active(port > 0), - m_io_service(), - m_fault(false), - m_port(port) - { - restart(); - } - - - RemoteControllerTelnet& operator=(const RemoteControllerTelnet& other) = delete; - RemoteControllerTelnet(const RemoteControllerTelnet& other) = delete; - - ~RemoteControllerTelnet(); - - virtual bool fault_detected() { return m_fault; } - - virtual void restart(); - - private: - void restart_thread(long); - - void process(long); - - void dispatch_command(boost::asio::ip::tcp::socket& socket, - std::string command); - - void reply(boost::asio::ip::tcp::socket& socket, std::string message); - - void handle_accept( - const boost::system::error_code& boost_error, - boost::shared_ptr< boost::asio::ip::tcp::socket > socket, - boost::asio::ip::tcp::acceptor& acceptor); - - std::atomic<bool> m_active; - - boost::asio::io_service m_io_service; - - /* This is set to true if a fault occurred */ - std::atomic<bool> m_fault; - std::thread m_restarter_thread; - - std::thread m_child_thread; - - int m_port; -}; - -#if defined(HAVE_RC_ZEROMQ) -/* Implements a Remote controller using zmq transportlayer - * that listens on localhost - */ -class RemoteControllerZmq : public BaseRemoteController { - public: - RemoteControllerZmq() - : m_active(false), m_fault(false), - m_zmqContext(1), - m_endpoint("") { } - - RemoteControllerZmq(const std::string& endpoint) - : m_active(not endpoint.empty()), m_fault(false), - m_zmqContext(1), - m_endpoint(endpoint), - m_child_thread(&RemoteControllerZmq::process, this) { } - - RemoteControllerZmq& operator=(const RemoteControllerZmq& other) = delete; - RemoteControllerZmq(const RemoteControllerZmq& other) = delete; - - ~RemoteControllerZmq(); - - virtual bool fault_detected() { return m_fault; } - - virtual void restart(); - - private: - void restart_thread(); - - void recv_all(zmq::socket_t &pSocket, std::vector<std::string> &message); - void send_ok_reply(zmq::socket_t &pSocket); - void send_fail_reply(zmq::socket_t &pSocket, const std::string &error); - void process(); - - std::atomic<bool> m_active; - - /* This is set to true if a fault occurred */ - std::atomic<bool> m_fault; - std::thread m_restarter_thread; - - zmq::context_t m_zmqContext; - - std::string m_endpoint; - std::thread m_child_thread; -}; -#endif - diff --git a/src/crc.c b/src/crc.c deleted file mode 100644 index cc02473..0000000 --- a/src/crc.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the - Queen in Right of Canada (Communications Research Center Canada) - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "crc.h" -#ifndef _WIN32 -# include <unistd.h> -# include <netinet/in.h> -#endif -#include <stdio.h> -#include <fcntl.h> - -//#define CCITT 0x1021 - -uint8_t crc8tab[256] = { - 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, - 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, - 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, - 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, - 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, - 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, - 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, - 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, - 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, - 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, - 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, - 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, - 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, - 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, - 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, - 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, - 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, - 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, - 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, - 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, - 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, - 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, - 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, - 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, - 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, - 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, - 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, - 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, - 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, - 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, - 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, - 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 -}; - - -uint16_t crc16tab[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - - -uint32_t crc32tab[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, - 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, - 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, - 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, - 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, - 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, - 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, - 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, - 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, - 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, - 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, - 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, - 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, - 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, - 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, - 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, - 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, - 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, - 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, - 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, - 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, - 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, - 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, - 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, - 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, - 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, - 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, - 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, - 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, - 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, - 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, - 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, - 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, - 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, - 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, - 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, - 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, - 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, - 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, - 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, - 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, - 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, - 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 -}; - -// This function can be used to create a new table with a different polynom -void init_crc8tab(uint8_t l_code, uint8_t l_init) -{ - unsigned i, j, msb; - uint8_t nb; - uint8_t crc; - - for (i = 0; i < 256; ++i) { - crc = l_init; - nb = i ^ 0xff; - for (j = 0; j < 8; ++j) { - msb = (nb & (0x80 >> j)) && 1; - msb ^= (crc >> 7); - crc <<= 1; - if (msb) - crc ^= l_code; - } - crc8tab[i] = crc; - } -} - - -void init_crc16tab(uint16_t l_code, uint16_t l_init) -{ - unsigned i, j, msb; - uint8_t nb; - uint16_t crc; - - for (i = 0; i < 256; ++i) { - crc = l_init; - nb = i ^ 0xff; - for (j = 0; j < 8; ++j) { - msb = (nb & (0x80 >> j)) && 1; - msb ^= (crc >> 15); - crc <<= 1; - if (msb) - crc ^= l_code; - } - crc ^= 0xff00; - crc16tab[i] = crc; - } -} - - -void init_crc32tab(uint32_t l_code, uint32_t l_init) -{ - unsigned i, j, msb; - uint8_t nb; - uint32_t crc; - - for (i = 0; i < 256; ++i) { - crc = l_init; - nb = i ^ 0xff; - for (j = 0; j < 8; ++j) { - msb = (nb & (0x80 >> j)) && 1; - msb ^= (crc >> 31); - crc <<= 1; - if (msb) - crc ^= l_code; - } - crc ^= 0xffffff00; - crc32tab[i] = crc; - } -} - - -uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb) -{ - const uint8_t* data = (const uint8_t*)lp_data; - while (l_nb--) { - l_crc = crc8tab[l_crc ^ *(data++)]; - } - return (l_crc); -} - - -uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb) -{ - const uint8_t* data = (const uint8_t*)lp_data; - while (l_nb--) { - l_crc = - (l_crc << 8) ^ crc16tab[(l_crc >> 8) ^ *(data++)]; - } - return (l_crc); -} - - -uint32_t crc32(uint32_t l_crc, const void *lp_data, unsigned l_nb) -{ - const uint8_t* data = (const uint8_t*)lp_data; - while (l_nb--) { - l_crc = - (l_crc << 8) ^ crc32tab[((l_crc >> 24) ^ *(data++)) & 0xff]; - } - return (l_crc); -} diff --git a/src/crc.h b/src/crc.h deleted file mode 100644 index b1785a1..0000000 --- a/src/crc.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the - Queen in Right of Canada (Communications Research Center Canada) - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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. - - ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef _CRC -#define _CRC - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifndef _WIN32 - #include <stdint.h> -#else - #include <winsock2.h> // For types... - typedef BYTE uint8_t; - typedef WORD uint16_t; - typedef DWORD32 uint32_t; -#endif - - -#ifdef __cplusplus -extern "C" { // } -#endif - -void init_crc8tab(uint8_t l_code, uint8_t l_init); -uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb); -extern uint8_t crc8tab[]; - -void init_crc16tab(uint16_t l_code, uint16_t l_init); -uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb); -extern uint16_t crc16tab[]; - -void init_crc32tab(uint32_t l_code, uint32_t l_init); -uint32_t crc32(uint32_t l_crc, const void *lp_data, unsigned l_nb); -extern uint32_t crc32tab[]; - -#ifdef __cplusplus -} -#endif - -#endif //_CRC |