diff options
-rw-r--r-- | Makefile.am | 28 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | lib/ClockTAI.cpp (renamed from src/ClockTAI.cpp) | 2 | ||||
-rw-r--r-- | lib/ClockTAI.h (renamed from src/ClockTAI.h) | 4 | ||||
-rw-r--r-- | lib/Log.cpp (renamed from src/Log.cpp) | 91 | ||||
-rw-r--r-- | lib/Log.h (renamed from src/Log.h) | 63 | ||||
-rw-r--r-- | lib/ReedSolomon.cpp | 4 | ||||
-rw-r--r-- | lib/RemoteControl.cpp (renamed from src/RemoteControl.cpp) | 132 | ||||
-rw-r--r-- | lib/RemoteControl.h (renamed from src/RemoteControl.h) | 46 | ||||
-rw-r--r-- | lib/crc.c (renamed from src/crc.c) | 0 | ||||
-rw-r--r-- | lib/crc.h (renamed from src/crc.h) | 0 |
11 files changed, 222 insertions, 150 deletions
diff --git a/Makefile.am b/Makefile.am index 6e5aa71..216f7c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,25 +94,17 @@ odr_dabmux_SOURCES =src/DabMux.cpp \ src/dabOutput/edi/TagPacket.h \ src/dabOutput/edi/Transport.cpp \ src/dabOutput/edi/Transport.h \ - src/ClockTAI.h \ - src/ClockTAI.cpp \ src/ConfigParser.cpp \ src/ConfigParser.h \ src/Eti.h \ src/Eti.cpp \ src/Interleaver.h \ src/Interleaver.cpp \ - src/Log.h \ - src/Log.cpp \ src/ManagementServer.h \ src/ManagementServer.cpp \ src/MuxElements.cpp \ src/MuxElements.h \ src/PcDebug.h \ - src/RemoteControl.cpp \ - src/RemoteControl.h \ - src/crc.h \ - src/crc.c \ src/fig/FIG.h \ src/fig/FIG.cpp \ src/fig/FIG0.h \ @@ -160,6 +152,14 @@ odr_dabmux_SOURCES =src/DabMux.cpp \ src/PrbsGenerator.h \ src/utils.cpp \ src/utils.h \ + lib/crc.h \ + lib/crc.c \ + lib/ClockTAI.h \ + lib/ClockTAI.cpp \ + lib/Log.h \ + lib/Log.cpp \ + lib/RemoteControl.cpp \ + lib/RemoteControl.h \ lib/edi/STIDecoder.cpp \ lib/edi/STIDecoder.h \ lib/edi/STIWriter.cpp \ @@ -185,8 +185,8 @@ zmqinput_keygen_CFLAGS = -Wall $(GITVERSION_FLAGS) $(ZMQ_CPPFLAGS) odr_zmq2farsync_SOURCES = src/zmq2farsync/zmq2farsync.cpp \ src/dabOutput/dabOutput.h \ src/dabOutput/dabOutputRaw.cpp \ - src/Log.h \ - src/Log.cpp \ + lib/Log.h \ + lib/Log.cpp \ lib/zmq.hpp odr_zmq2farsync_LDADD = $(ZMQ_LIBS) @@ -212,10 +212,10 @@ odr_zmq2edi_SOURCES = src/zmq2edi/zmq2edi.cpp \ src/dabOutput/edi/TagPacket.h \ src/dabOutput/edi/Transport.cpp \ src/dabOutput/edi/Transport.h \ - src/Log.h \ - src/Log.cpp \ - src/crc.h \ - src/crc.c \ + lib/Log.h \ + lib/Log.cpp \ + lib/crc.h \ + lib/crc.c \ lib/ReedSolomon.h \ lib/ReedSolomon.cpp \ lib/Socket.h \ diff --git a/configure.ac b/configure.ac index 50623a2..405b6f0 100644 --- a/configure.ac +++ b/configure.ac @@ -128,7 +128,7 @@ AX_ZMQ([4.0.0], [], AC_MSG_ERROR(ZeroMQ 4.0.0 is required)) AC_DEFINE([HAVE_INPUT_ZEROMQ], [1], [Define if ZeroMQ input is enabled]) AC_DEFINE([HAVE_OUTPUT_ZEROMQ], [1], [Define if ZeroMQ output is enabled]) -AC_DEFINE([HAVE_RC_ZEROMQ], [1], [Define if ZeroMQ enabled for rc]) +AC_DEFINE([HAVE_ZEROMQ], [1], [Define if ZeroMQ enabled for rc]) # Do not build odr-zmq2farsync if no RAW output AM_CONDITIONAL([HAVE_OUTPUT_RAW_TEST], diff --git a/src/ClockTAI.cpp b/lib/ClockTAI.cpp index c376c07..42497f4 100644 --- a/src/ClockTAI.cpp +++ b/lib/ClockTAI.cpp @@ -477,7 +477,7 @@ 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()); + "' is read-only in controllable " + get_rc_name()); } else { throw ParameterError("Parameter '" + parameter + diff --git a/src/ClockTAI.h b/lib/ClockTAI.h index 4b3c2ff..bb85815 100644 --- a/src/ClockTAI.h +++ b/lib/ClockTAI.h @@ -34,8 +34,8 @@ #pragma once -#include <stdint.h> -#include <stdlib.h> +#include <cstdint> +#include <cstdlib> #include <sstream> #include <chrono> #include <future> diff --git a/src/Log.cpp b/lib/Log.cpp index 6b78fe0..2417f3a 100644 --- a/src/Log.cpp +++ b/lib/Log.cpp @@ -9,31 +9,32 @@ http://www.opendigitalradio.org */ /* - This file is part of ODR-DabMux. + This file is part of the ODR-mmbTools. - ODR-DabMux is free software: you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - ODR-DabMux is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. + along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <list> #include <cstdarg> +#include <cinttypes> #include <chrono> #include "Log.h" using namespace std; -/* etiLog is a singleton used in all parts of ODR-DabMod to output log messages. +/* etiLog is a singleton used in all parts of the program to output log messages. */ Logger etiLog; @@ -74,22 +75,40 @@ 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); - } + log_message_t m(level, move(message)); + m_message_queue.push(move(m)); +} - for (auto &backend : backends) { - backend->log(level, message); - } +void Logger::io_process() +{ + 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); + } - { - std::lock_guard<std::mutex> guard(m_cerr_mutex); - std::cerr << levels_as_str[level] << " " << message << std::endl; + 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; + } } } @@ -112,7 +131,7 @@ LogToFile::LogToFile(const std::string& filename) : name("FILE") void LogToFile::log(log_level_t level, const std::string& message) { - if (level != log_level_t::discard) { + if (not (level == log_level_t::trace or level == log_level_t::discard)) { const char* log_level_text[] = { "DEBUG", "INFO", "WARN", "ERROR", "ALERT", "EMERG"}; @@ -125,7 +144,7 @@ void LogToFile::log(log_level_t level, const std::string& message) void LogToSyslog::log(log_level_t level, const std::string& message) { - if (level != log_level_t::discard) { + if (not (level == log_level_t::trace or level == log_level_t::discard)) { int syslog_level = LOG_EMERG; switch (level) { case debug: syslog_level = LOG_DEBUG; break; @@ -141,3 +160,35 @@ void LogToSyslog::log(log_level_t level, const std::string& message) 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()); + } +} @@ -9,20 +9,20 @@ http://www.opendigitalradio.org */ /* - This file is part of ODR-DabMux. + This file is part of the ODR-mmbTools. - ODR-DabMux is free software: you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - ODR-DabMux is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. + along with this program. If not, see <http://www.gnu.org/licenses/>. */ #pragma once @@ -40,16 +40,19 @@ #include <list> #include <stdexcept> #include <string> +#include <map> #include <mutex> #include <memory> +#include <thread> +#include "ThreadsafeQueue.h" -#define SYSLOG_IDENT "ODR-DabMux" +#define SYSLOG_IDENT PACKAGE_NAME #define SYSLOG_FACILITY LOG_LOCAL0 -enum log_level_t {debug = 0, info, warn, error, alert, emerg, discard}; +enum log_level_t {debug = 0, info, warn, error, alert, emerg, trace, discard}; static const std::string levels_as_str[] = - { " ", " ", "WARN ", "ERROR", "ALERT", "EMERG", "-----"} ; + { " ", " ", "WARN ", "ERROR", "ALERT", "EMERG", "TRACE", "-----"} ; /** Abstract class all backends must inherit from */ class LogBackend { @@ -97,10 +100,50 @@ class LogToFile : public LogBackend { 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 */ @@ -108,6 +151,9 @@ class Logger { 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); @@ -115,6 +161,8 @@ class Logger { 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; }; @@ -154,4 +202,3 @@ class LogLine { Logger* logger_; }; - diff --git a/lib/ReedSolomon.cpp b/lib/ReedSolomon.cpp index 38d8ea8..1bf0b24 100644 --- a/lib/ReedSolomon.cpp +++ b/lib/ReedSolomon.cpp @@ -64,7 +64,9 @@ ReedSolomon::ReedSolomon(int N, int K, bool reverse, int gfpoly, int firstRoot, ReedSolomon::~ReedSolomon() { - free_rs_char(rsData); + if (rsData != nullptr) { + free_rs_char(rsData); + } } diff --git a/src/RemoteControl.cpp b/lib/RemoteControl.cpp index b32c21a..878af59 100644 --- a/src/RemoteControl.cpp +++ b/lib/RemoteControl.cpp @@ -9,31 +9,27 @@ http://www.opendigitalradio.org */ /* - This file is part of ODR-DabMux. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - 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, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. + along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <list> #include <string> #include <iostream> #include <string> -#include <boost/asio.hpp> -#include <boost/thread.hpp> +#include <algorithm> #include "RemoteControl.h" -using boost::asio::ip::tcp; using namespace std; RemoteControllers rcs; @@ -41,7 +37,6 @@ RemoteControllers rcs; RemoteControllerTelnet::~RemoteControllerTelnet() { m_active = false; - m_io_service.stop(); if (m_restarter_thread.joinable()) { m_restarter_thread.join(); @@ -158,7 +153,6 @@ void RemoteControllers::set_param( void RemoteControllerTelnet::restart_thread(long) { m_active = false; - m_io_service.stop(); if (m_child_thread.joinable()) { m_child_thread.join(); @@ -167,52 +161,56 @@ void RemoteControllerTelnet::restart_thread(long) 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) +void RemoteControllerTelnet::handle_accept(Socket::TCPSocket&& socket) { - - const std::string welcome = "ODR-DabMux Remote Control CLI\n" + const std::string welcome = PACKAGE_NAME " 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; + socket.sendall(welcome.data(), welcome.size()); - boost::asio::write(*socket, boost::asio::buffer(welcome), - boost::asio::transfer_all(), - ignored_error); + while (m_active and in_message != "quit") { + socket.sendall(prompt.data(), prompt.size()); - while (m_active && in_message != "quit") { - boost::asio::write(*socket, boost::asio::buffer(prompt), - boost::asio::transfer_all(), - ignored_error); + stringstream in_message_stream; - in_message = ""; - - boost::asio::streambuf buffer; - length = boost::asio::read_until(*socket, buffer, "\n", ignored_error); + char last_char = '\0'; + try { + while (last_char != '\n') { + try { + auto ret = socket.recv(&last_char, 1, 0, 1000); + if (ret == 1) { + in_message_stream << last_char; + } + else { + break; + } + } + catch (const Socket::TCPSocket::Timeout&) { + if (not m_active) { + break; + } + } + } + } + catch (const Socket::TCPSocket::Interrupted&) { + in_message_stream.clear(); + } - std::istream str(&buffer); - std::getline(str, in_message); - if (length == 0) { + if (in_message_stream.str().size() == 0) { etiLog.level(info) << "RC: Connection terminated"; break; } + std::getline(in_message_stream, in_message); + while (in_message.length() > 0 && (in_message[in_message.length()-1] == '\r' || in_message[in_message.length()-1] == '\n')) { @@ -225,44 +223,35 @@ void RemoteControllerTelnet::handle_accept( etiLog.level(info) << "RC: Got message '" << in_message << "'"; - dispatch_command(*socket, in_message); + dispatch_command(socket, in_message); } etiLog.level(info) << "RC: Closing socket"; - socket->close(); + socket.close(); } - catch (const std::exception& e) - { + 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) ); - + try { + m_active = true; - // Add a job to start accepting connections. - boost::shared_ptr<tcp::socket> socket( - new tcp::socket(acceptor.get_io_service())); + m_socket.listen(m_port, "localhost"); - // 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(); + while (m_active) { + auto sock = m_socket.accept(1000); + + if (sock.valid()) { + handle_accept(move(sock)); + etiLog.level(info) << "RC: Connection closed. Waiting for connection on port " << m_port; + } + } + } + catch (const runtime_error& e) { + etiLog.level(warn) << "RC: Encountered error: " << e.what(); } etiLog.level(info) << "RC: Leaving"; @@ -281,7 +270,7 @@ static std::vector<std::string> tokenise(const std::string& message) { } -void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command) +void RemoteControllerTelnet::dispatch_command(Socket::TCPSocket& socket, string command) { vector<string> cmd = tokenise(command); @@ -386,18 +375,15 @@ void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string comman } } -void RemoteControllerTelnet::reply(tcp::socket& socket, string message) +void RemoteControllerTelnet::reply(Socket::TCPSocket& 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); + socket.sendall(message.data(), message.size()); } -#if defined(HAVE_RC_ZEROMQ) +#if defined(HAVE_ZEROMQ) RemoteControllerZmq::~RemoteControllerZmq() { m_active = false; diff --git a/src/RemoteControl.h b/lib/RemoteControl.h index 0726b28..bd88f82 100644 --- a/src/RemoteControl.h +++ b/lib/RemoteControl.h @@ -8,23 +8,21 @@ http://www.opendigitalradio.org - This module adds remote-control capability to some of the dabmux modules. + This module adds remote-control capability to some of the dabmux/dabmod modules. */ /* - This file is part of ODR-DabMux. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - 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, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. + along with this program. If not, see <https://www.gnu.org/licenses/>. */ #pragma once @@ -33,7 +31,7 @@ # include "config.h" #endif -#if defined(HAVE_RC_ZEROMQ) +#if defined(HAVE_ZEROMQ) # include "zmq.hpp" #endif @@ -43,14 +41,11 @@ #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" +#include "Socket.h" #define RC_ADD_PARAMETER(p, desc) { \ std::vector<std::string> p; \ @@ -78,7 +73,7 @@ class RemoteControllable; class BaseRemoteController { public: /* When this returns one, the remote controller cannot be - * used anymore, and must be restarted by dabmux + * used anymore, and must be restarted */ virtual bool fault_detected() = 0; @@ -163,13 +158,11 @@ 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) { @@ -191,31 +184,24 @@ class RemoteControllerTelnet : public BaseRemoteController { 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); + void dispatch_command(Socket::TCPSocket& socket, std::string command); + void reply(Socket::TCPSocket& socket, std::string message); + void handle_accept(Socket::TCPSocket&& socket); 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; + Socket::TCPSocket m_socket; int m_port; }; -#if defined(HAVE_RC_ZEROMQ) -/* Implements a Remote controller using zmq transportlayer +#if defined(HAVE_ZEROMQ) +/* Implements a Remote controller using ZMQ transportlayer * that listens on localhost */ class RemoteControllerZmq : public BaseRemoteController { |