Vernon Mauery | 3719c2f | 2019-03-20 13:00:20 -0700 | [diff] [blame] | 1 | #include <forward_list> |
| 2 | #include <ipmid/api.hpp> |
| 3 | #include <memory> |
| 4 | #include <phosphor-logging/log.hpp> |
| 5 | #include <vector> |
| 6 | |
| 7 | using namespace phosphor::logging; |
| 8 | |
| 9 | namespace |
| 10 | { |
| 11 | |
| 12 | class SignalHandler |
| 13 | { |
| 14 | public: |
| 15 | SignalHandler(std::shared_ptr<boost::asio::io_context>& io, int sigNum) : |
| 16 | signal(std::make_unique<boost::asio::signal_set>(*io, sigNum)) |
| 17 | { |
| 18 | asyncWait(); |
| 19 | } |
| 20 | |
| 21 | ~SignalHandler() |
| 22 | { |
| 23 | // unregister with asio to unmask the signal |
| 24 | signal->cancel(); |
| 25 | signal->clear(); |
| 26 | } |
| 27 | |
| 28 | void registerHandler(int prio, |
| 29 | const std::function<SignalResponse(int)>& handler) |
| 30 | { |
| 31 | // check for initial placement |
| 32 | if (handlers.empty() || std::get<0>(handlers.front()) < prio) |
| 33 | { |
| 34 | handlers.emplace_front(std::make_tuple(prio, handler)); |
| 35 | return; |
| 36 | } |
| 37 | // walk the list and put it in the right place |
| 38 | auto j = handlers.begin(); |
| 39 | for (auto i = j; i != handlers.end() && std::get<0>(*i) > prio; i++) |
| 40 | { |
| 41 | j = i; |
| 42 | } |
| 43 | handlers.emplace_after(j, std::make_tuple(prio, handler)); |
| 44 | } |
| 45 | |
| 46 | void handleSignal(const boost::system::error_code& ec, int sigNum) |
| 47 | { |
| 48 | if (ec) |
| 49 | { |
| 50 | log<level::ERR>("Error in common signal handler", |
| 51 | entry("SIGNAL=%d", sigNum), |
| 52 | entry("ERROR=%s", ec.message().c_str())); |
| 53 | return; |
| 54 | } |
| 55 | for (auto h = handlers.begin(); h != handlers.end(); h++) |
| 56 | { |
| 57 | std::function<SignalResponse(int)>& handler = std::get<1>(*h); |
| 58 | if (handler(sigNum) == SignalResponse::breakExecution) |
| 59 | { |
| 60 | break; |
| 61 | } |
| 62 | } |
| 63 | // start the wait for the next signal |
| 64 | asyncWait(); |
| 65 | } |
| 66 | |
| 67 | protected: |
| 68 | void asyncWait() |
| 69 | { |
| 70 | signal->async_wait([this](const boost::system::error_code& ec, |
| 71 | int sigNum) { handleSignal(ec, sigNum); }); |
| 72 | } |
| 73 | |
| 74 | std::forward_list<std::tuple<int, std::function<SignalResponse(int)>>> |
| 75 | handlers; |
| 76 | std::unique_ptr<boost::asio::signal_set> signal; |
| 77 | }; |
| 78 | |
| 79 | // SIGRTMAX is defined as a non-constexpr function call and thus cannot be used |
| 80 | // as an array size. Get around this by making a vector and resizing it the |
| 81 | // first time it is needed |
| 82 | std::vector<std::unique_ptr<SignalHandler>> signals; |
| 83 | |
| 84 | } // namespace |
| 85 | |
| 86 | void registerSignalHandler(int priority, int signalNumber, |
| 87 | const std::function<SignalResponse(int)>& handler) |
| 88 | { |
| 89 | if (signalNumber >= SIGRTMAX) |
| 90 | { |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | if (signals.empty()) |
| 95 | { |
| 96 | signals.resize(SIGRTMAX); |
| 97 | } |
| 98 | |
| 99 | if (!signals[signalNumber]) |
| 100 | { |
| 101 | std::shared_ptr<boost::asio::io_context> io = getIoContext(); |
| 102 | signals[signalNumber] = |
| 103 | std::make_unique<SignalHandler>(io, signalNumber); |
| 104 | } |
| 105 | signals[signalNumber]->registerHandler(priority, handler); |
| 106 | } |