summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-08-13 10:29:39 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-08-13 10:29:39 +0200
commita5c50a4f262f0a880734623f79d4dc2f1aa8a0a2 (patch)
tree1772ef47d98a68245c3d04d95637e5b9c1040904 /src
parent69aba72f0883c5effb5c3c2991d0c5257deb7409 (diff)
downloaddabmod-a5c50a4f262f0a880734623f79d4dc2f1aa8a0a2.tar.gz
dabmod-a5c50a4f262f0a880734623f79d4dc2f1aa8a0a2.tar.bz2
dabmod-a5c50a4f262f0a880734623f79d4dc2f1aa8a0a2.zip
Pull in files from odr-mmbtools-common
Replace ASIO by simpler implementation, meaning that the telnet RC now only supports a single connection. Move Log, RC to lib/
Diffstat (limited to 'src')
-rw-r--r--src/Log.cpp191
-rw-r--r--src/Log.h200
-rw-r--r--src/RemoteControl.cpp588
-rw-r--r--src/RemoteControl.h259
4 files changed, 0 insertions, 1238 deletions
diff --git a/src/Log.cpp b/src/Log.cpp
deleted file mode 100644
index 4fc7ae3..0000000
--- a/src/Log.cpp
+++ /dev/null
@@ -1,191 +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-DabMod.
-
- ODR-DabMod 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-DabMod 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-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <list>
-#include <cstdarg>
-#include <cinttypes>
-#include <chrono>
-
-#include "Log.h"
-#include "Utils.h"
-
-using namespace std;
-
-/* This is called etiLog because it was copy-pasted from ODR-DabMux, even
- * though it doesn't make any more sense there than here.
- *
- * It 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 lvl, const char* fmt, ...)
-{
- 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(lvl, move(str));
-}
-
-void Logger::logstr(log_level_t lvl, std::string&& message)
-{
- log_message_t m(lvl, move(message));
- m_message_queue.push(move(m));
-}
-
-void Logger::io_process()
-{
- set_thread_name("logger");
- while (1) {
- log_message_t m;
- try {
- m_message_queue.wait_and_pop(m);
- }
- catch (const ThreadsafeQueueWakeup&) {
- break;
- }
-
- auto message = m.message;
-
- /* 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(m.level, message);
- }
-
- if (m.level != log_level_t::trace) {
- std::lock_guard<std::mutex> guard(m_cerr_mutex);
- std::cerr << levels_as_str[m.level] << " " << message << std::endl;
- }
- }
-}
-
-LogLine Logger::level(log_level_t lvl)
-{
- return LogLine(this, lvl);
-}
-
-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::trace) {
- 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::trace) {
- int syslog_level = LOG_EMERG;
- switch (level) {
- case trace: break; // Do not handle TRACE in syslog
- 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());
- }
-}
-
-LogTracer::LogTracer(const string& trace_filename) : name("TRACE")
-{
- etiLog.level(info) << "Setting up TRACE to " << trace_filename;
-
- FILE* fd = fopen(trace_filename.c_str(), "a");
- if (fd == nullptr) {
- fprintf(stderr, "Cannot open trace file !");
- throw std::runtime_error("Cannot open trace file !");
- }
- m_trace_file.reset(fd);
-
- using namespace std::chrono;
- auto now = steady_clock::now().time_since_epoch();
- m_trace_micros_startup = duration_cast<microseconds>(now).count();
-
- fprintf(m_trace_file.get(),
- "0,TRACER,startup at %" PRIu64 "\n", m_trace_micros_startup);
-}
-
-void LogTracer::log(log_level_t level, const std::string& message)
-{
- if (level == log_level_t::trace) {
- using namespace std::chrono;
- const auto now = steady_clock::now().time_since_epoch();
- const auto micros = duration_cast<microseconds>(now).count();
-
- fprintf(m_trace_file.get(), "%" PRIu64 ",%s\n",
- micros - m_trace_micros_startup,
- message.c_str());
- }
-}
diff --git a/src/Log.h b/src/Log.h
deleted file mode 100644
index 1253635..0000000
--- a/src/Log.h
+++ /dev/null
@@ -1,200 +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-DabMod.
-
- ODR-DabMod 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-DabMod 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-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <fstream>
-#include <sstream>
-#include <iostream>
-#include <list>
-#include <stdexcept>
-#include <string>
-#include <map>
-#include <mutex>
-#include <memory>
-#include <thread>
-#include "ThreadsafeQueue.h"
-
-#define SYSLOG_IDENT "ODR-DabMod"
-#define SYSLOG_FACILITY LOG_LOCAL0
-
-enum log_level_t {debug = 0, info, warn, error, alert, emerg, trace};
-
-static const std::string levels_as_str[] =
- { " ", " ", "WARN ", "ERROR", "ALERT", "EMERG", "TRACE"} ;
-
-/** 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 LogTracer : public LogBackend {
- public:
- LogTracer(const std::string& filename);
- void log(log_level_t level, const std::string& message);
- std::string get_name() const { return name; }
- private:
- std::string name;
- uint64_t m_trace_micros_startup = 0;
-
- struct FILEDeleter{ void operator()(FILE* fd){ if(fd) fclose(fd);}};
- std::unique_ptr<FILE, FILEDeleter> m_trace_file;
-
- LogTracer(const LogTracer& other) = delete;
- const LogTracer& operator=(const LogTracer& other) = delete;
-};
-
-class LogLine;
-
-struct log_message_t {
- log_message_t(log_level_t _level, std::string&& _message) :
- level(_level),
- message(move(_message)) {}
-
- log_message_t() :
- level(debug),
- message("") {}
-
- log_level_t level;
- std::string message;
-};
-
-class Logger {
- public:
- Logger() {
- m_io_thread = std::thread(&Logger::io_process, this);
- }
-
- Logger(const Logger& other) = delete;
- const Logger& operator=(const Logger& other) = delete;
- ~Logger() {
- m_message_queue.trigger_wakeup();
- m_io_thread.join();
- }
-
- 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);
-
- /* All logging IO is done in another thread */
- void io_process(void);
-
- /* 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;
-
- ThreadsafeQueue<log_message_t> m_message_queue;
- std::thread m_io_thread;
- 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) {
- os << s;
- return *this;
- }
-
- ~LogLine()
- {
- 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 1065456..0000000
--- a/src/RemoteControl.cpp
+++ /dev/null
@@ -1,588 +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-DabMod.
-
- ODR-DabMod 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-DabMod 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-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-#include <list>
-#include <string>
-#include <iostream>
-#include <string>
-#include <thread>
-#include <functional>
-#include <asio.hpp>
-#include "RemoteControl.h"
-
-using 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 &param : 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)
-{
- 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(
- std::shared_ptr<asio::ip::tcp::socket> socket,
- const asio::error_code& asio_error)
-{
-
- const std::string welcome = "ODR-DabMod Remote Control CLI\n"
- "Write 'help' for help.\n"
- "**********\n";
- const std::string prompt = "> ";
-
- std::string in_message;
- size_t length;
-
- if (asio_error) {
- etiLog.level(error) << "RC: Error accepting connection";
- return;
- }
-
- try {
- etiLog.level(info) << "RC: Accepted";
-
- asio::error_code ignored_error;
-
- asio::write(*socket, asio::buffer(welcome),
- asio::transfer_all(),
- ignored_error);
-
- while (m_active && in_message != "quit") {
- asio::write(*socket, asio::buffer(prompt),
- asio::transfer_all(),
- ignored_error);
-
- in_message = "";
-
- asio::streambuf buffer;
- length = 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(
- asio::ip::address::from_string("127.0.0.1"), m_port) );
-
- // Add a job to start accepting connections.
- auto socket = make_shared<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,
- bind(&RemoteControllerTelnet::handle_accept, this,
- socket,
- std::placeholders::_1));
-
- // 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 &param : 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 &param_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)
-{
- asio::error_code ignored_error;
- stringstream ss;
- ss << message << "\r\n";
- asio::write(socket, asio::buffer(ss.str()),
- asio::transfer_all(),
- ignored_error);
-}
-
-#if defined(HAVE_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 &param : 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 &param_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 087b94a..0000000
--- a/src/RemoteControl.h
+++ /dev/null
@@ -1,259 +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 dabmod modules.
- */
-/*
- This file is part of ODR-DabMod.
-
- ODR-DabMod 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-DabMod 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-DabMod. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#if defined(HAVE_ZEROMQ)
-# include "zmq.hpp"
-#endif
-
-#include <list>
-#include <map>
-#include <memory>
-#include <string>
-#include <atomic>
-#include <iostream>
-#include <thread>
-#include <asio.hpp>
-#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 DabMod
- */
- 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(asio::ip::tcp::socket& socket,
- std::string command);
-
- void reply(asio::ip::tcp::socket& socket, std::string message);
-
- void handle_accept(
- std::shared_ptr<asio::ip::tcp::socket> socket,
- const asio::error_code& asio_error);
-
- std::atomic<bool> m_active;
-
- 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_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
-