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