diff options
Diffstat (limited to 'libipmid')
-rw-r--r-- | libipmid/Makefile.am | 1 | ||||
-rw-r--r-- | libipmid/signals.cpp | 106 |
2 files changed, 107 insertions, 0 deletions
diff --git a/libipmid/Makefile.am b/libipmid/Makefile.am index ac35bbf..e4899e1 100644 --- a/libipmid/Makefile.am +++ b/libipmid/Makefile.am @@ -13,6 +13,7 @@ pkgconfig_DATA = libipmid.pc lib_LTLIBRARIES = libipmid.la libipmid_la_SOURCES = \ sdbus-asio.cpp \ + signals.cpp \ systemintf-sdbus.cpp libipmid_la_LDFLAGS = \ $(SYSTEMD_LIBS) \ diff --git a/libipmid/signals.cpp b/libipmid/signals.cpp new file mode 100644 index 0000000..3838e95 --- /dev/null +++ b/libipmid/signals.cpp @@ -0,0 +1,106 @@ +#include <forward_list> +#include <ipmid/api.hpp> +#include <memory> +#include <phosphor-logging/log.hpp> +#include <vector> + +using namespace phosphor::logging; + +namespace +{ + +class SignalHandler +{ + public: + SignalHandler(std::shared_ptr<boost::asio::io_context>& io, int sigNum) : + signal(std::make_unique<boost::asio::signal_set>(*io, sigNum)) + { + asyncWait(); + } + + ~SignalHandler() + { + // unregister with asio to unmask the signal + signal->cancel(); + signal->clear(); + } + + void registerHandler(int prio, + const std::function<SignalResponse(int)>& handler) + { + // check for initial placement + if (handlers.empty() || std::get<0>(handlers.front()) < prio) + { + handlers.emplace_front(std::make_tuple(prio, handler)); + return; + } + // walk the list and put it in the right place + auto j = handlers.begin(); + for (auto i = j; i != handlers.end() && std::get<0>(*i) > prio; i++) + { + j = i; + } + handlers.emplace_after(j, std::make_tuple(prio, handler)); + } + + void handleSignal(const boost::system::error_code& ec, int sigNum) + { + if (ec) + { + log<level::ERR>("Error in common signal handler", + entry("SIGNAL=%d", sigNum), + entry("ERROR=%s", ec.message().c_str())); + return; + } + for (auto h = handlers.begin(); h != handlers.end(); h++) + { + std::function<SignalResponse(int)>& handler = std::get<1>(*h); + if (handler(sigNum) == SignalResponse::breakExecution) + { + break; + } + } + // start the wait for the next signal + asyncWait(); + } + + protected: + void asyncWait() + { + signal->async_wait([this](const boost::system::error_code& ec, + int sigNum) { handleSignal(ec, sigNum); }); + } + + std::forward_list<std::tuple<int, std::function<SignalResponse(int)>>> + handlers; + std::unique_ptr<boost::asio::signal_set> signal; +}; + +// SIGRTMAX is defined as a non-constexpr function call and thus cannot be used +// as an array size. Get around this by making a vector and resizing it the +// first time it is needed +std::vector<std::unique_ptr<SignalHandler>> signals; + +} // namespace + +void registerSignalHandler(int priority, int signalNumber, + const std::function<SignalResponse(int)>& handler) +{ + if (signalNumber >= SIGRTMAX) + { + return; + } + + if (signals.empty()) + { + signals.resize(SIGRTMAX); + } + + if (!signals[signalNumber]) + { + std::shared_ptr<boost::asio::io_context> io = getIoContext(); + signals[signalNumber] = + std::make_unique<SignalHandler>(io, signalNumber); + } + signals[signalNumber]->registerHandler(priority, handler); +} |