| #include <boost/asio/signal_set.hpp> |
| #include <ipmid/api.hpp> |
| #include <phosphor-logging/log.hpp> |
| |
| #include <forward_list> |
| #include <memory> |
| #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); |
| } |