diff --git a/lib/include/srsran/common/emergency_handlers.h b/lib/include/srsran/common/emergency_handlers.h new file mode 100644 index 000000000..818f1a211 --- /dev/null +++ b/lib/include/srsran/common/emergency_handlers.h @@ -0,0 +1,25 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_EMERGENCY_HANDLERS_H +#define SRSRAN_EMERGENCY_HANDLERS_H + +using emergency_cleanup_callback = void (*)(void*); + +// Add a cleanup function to be called when a kill signal is about to be delivered to the process. The handler may +// optionally pass a pointer to identify what instance of the handler is being called. +void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data); + +// Executes all registered emergency cleanup handlers. +void execute_emergency_cleanup_handlers(); + +#endif // SRSRAN_EMERGENCY_HANDLERS_H diff --git a/lib/include/srsran/common/mac_pcap_base.h b/lib/include/srsran/common/mac_pcap_base.h index f80167853..544df52dc 100644 --- a/lib/include/srsran/common/mac_pcap_base.h +++ b/lib/include/srsran/common/mac_pcap_base.h @@ -28,6 +28,10 @@ class mac_pcap_base : protected srsran::thread { public: mac_pcap_base(); + + mac_pcap_base(const mac_pcap_base& other) = delete; + mac_pcap_base& operator=(const mac_pcap_base& other) = delete; + ~mac_pcap_base(); void enable(bool enable); virtual uint32_t close() = 0; @@ -36,7 +40,7 @@ public: // EUTRA void - write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); + write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); void write_dl_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, bool crc_ok, uint32_t tti, uint8_t cc_idx); void write_dl_ranti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t ranti, bool crc_ok, uint32_t tti, uint8_t cc_idx); diff --git a/lib/include/srsran/common/s1ap_pcap.h b/lib/include/srsran/common/s1ap_pcap.h index f2a0b1164..c62797a2e 100644 --- a/lib/include/srsran/common/s1ap_pcap.h +++ b/lib/include/srsran/common/s1ap_pcap.h @@ -21,7 +21,10 @@ namespace srsran { class s1ap_pcap { public: - s1ap_pcap() = default; + s1ap_pcap(); + s1ap_pcap(const s1ap_pcap& other) = delete; + s1ap_pcap& operator=(const s1ap_pcap& other) = delete; + void enable(); void open(const char* filename_); void close(); diff --git a/lib/include/srsran/common/signal_handler.h b/lib/include/srsran/common/signal_handler.h index fefb23f02..29d4307fb 100644 --- a/lib/include/srsran/common/signal_handler.h +++ b/lib/include/srsran/common/signal_handler.h @@ -18,6 +18,7 @@ #ifndef SRSRAN_SIGNAL_HANDLER_H #define SRSRAN_SIGNAL_HANDLER_H +#include "srsran/common/emergency_handlers.h" #include "srsran/srslog/sink.h" #include "srsran/srslog/srslog.h" #include @@ -30,7 +31,7 @@ extern "C" { #define SRSRAN_TERM_TIMEOUT_S (5) // static vars required by signal handling -static srslog::sink* log_sink = nullptr; +static srslog::sink* log_sink = nullptr; static std::atomic running = {true}; void srsran_dft_exit(); @@ -41,11 +42,12 @@ static void srsran_signal_handler(int signal) case SIGALRM: fprintf(stderr, "Couldn't stop after %ds. Forcing exit.\n", SRSRAN_TERM_TIMEOUT_S); srslog::flush(); - //:TODO: refactor the sighandler, should not depend on log utilities + //: TODO: refactor the sighandler, should not depend on log utilities if (log_sink) { log_sink->flush(); } srsran_dft_exit(); + execute_emergency_cleanup_handlers(); raise(SIGKILL); default: // all other registered signals try to stop the app gracefully diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 2cb75a650..cc8d08470 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES arch_select.cc rlc_pcap.cc s1ap_pcap.cc security.cc + emergency_handlers.cc standard_streams.cc thread_pool.cc threads.c diff --git a/lib/src/common/emergency_handlers.cc b/lib/src/common/emergency_handlers.cc new file mode 100644 index 000000000..3377153ac --- /dev/null +++ b/lib/src/common/emergency_handlers.cc @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/emergency_handlers.h" +#include "srsran/support/srsran_assert.h" + +namespace { + +/// Holds the callback function pointer and the associated user provided data pointer. +struct handler_instance { + std::atomic data{}; + std::atomic callback{}; +}; + +} // namespace + +// Handlers are added in a thread safe manner without using locks to avoid possible issues if a signal is emitted while +// modifying the callback array. +static constexpr unsigned max_handlers = 12; +static handler_instance registered_handlers[max_handlers]; +static std::atomic num_handlers; + +void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data) +{ + // Reserve a slot in the array. + auto pos = num_handlers.fetch_add(1); + + // Check if we have space in the array. + if (pos >= max_handlers) { + srsran_assert(0, "Exceeded the emergency cleanup handler registered limit"); + return; + } + + // Order is important here: write last the callback member as it is used to signal that the handler is valid when + // reading the array. + registered_handlers[pos].data.store(data); + registered_handlers[pos].callback.store(callback); +} + +void execute_emergency_cleanup_handlers() +{ + for (unsigned i = 0, e = num_handlers; i != e; ++i) { + auto callback = registered_handlers[i].callback.load(); + // Test the validity of the callback as it may have not been written yet into the array even if num_callbacks has + // been updated. + if (callback) { + callback(registered_handlers[i].data.load()); + } + } +} diff --git a/lib/src/common/mac_pcap_base.cc b/lib/src/common/mac_pcap_base.cc index c7fe8bd3c..48d825199 100644 --- a/lib/src/common/mac_pcap_base.cc +++ b/lib/src/common/mac_pcap_base.cc @@ -11,13 +11,23 @@ */ #include "srsran/common/mac_pcap_base.h" +#include "srsran/common/emergency_handlers.h" #include "srsran/config.h" #include "srsran/phy/common/phy_common.h" #include namespace srsran { -mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") {} +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") +{ + add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} mac_pcap_base::~mac_pcap_base() {} @@ -45,7 +55,7 @@ void mac_pcap_base::run_thread() } // write remainder of queue - pcap_pdu_t pdu = {}; + pcap_pdu_t pdu = {}; while (queue.try_pop(pdu)) { std::lock_guard lock(mutex); write_pdu(pdu); diff --git a/lib/src/common/s1ap_pcap.cc b/lib/src/common/s1ap_pcap.cc index ce06c5a6e..e87e77ad9 100644 --- a/lib/src/common/s1ap_pcap.cc +++ b/lib/src/common/s1ap_pcap.cc @@ -11,12 +11,24 @@ */ #include "srsran/common/s1ap_pcap.h" +#include "srsran/common/emergency_handlers.h" #include "srsran/common/pcap.h" #include "srsran/srsran.h" #include namespace srsran { +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +s1ap_pcap::s1ap_pcap() +{ + add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + void s1ap_pcap::enable() { enable_write = true; @@ -29,6 +41,9 @@ void s1ap_pcap::open(const char* filename_) } void s1ap_pcap::close() { + if (!enable_write) { + return; + } fprintf(stdout, "Saving S1AP PCAP file (DLT=%d) to %s\n", S1AP_LTE_DLT, filename.c_str()); DLT_PCAP_Close(pcap_file); }