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> |
George Liu | b4b4091 | 2024-07-17 16:14:17 +0800 | [diff] [blame] | 3 | #include <phosphor-logging/lg2.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 | |
Vernon Mauery | 3719c2f | 2019-03-20 13:00:20 -0700 | [diff] [blame] | 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 | { |
George Liu | b4b4091 | 2024-07-17 16:14:17 +0800 | [diff] [blame] | 50 | lg2::error("Error in common signal handler, " |
| 51 | "signal: {SIGNAL}, error: {ERROR}", |
| 52 | "SIGNAL", sigNum, "ERROR", ec.message()); |
Vernon Mauery | 3719c2f | 2019-03-20 13:00:20 -0700 | [diff] [blame] | 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(); |
Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 102 | signals[signalNumber] = |
| 103 | std::make_unique<SignalHandler>(io, signalNumber); |
Vernon Mauery | 3719c2f | 2019-03-20 13:00:20 -0700 | [diff] [blame] | 104 | } |
| 105 | signals[signalNumber]->registerHandler(priority, handler); |
| 106 | } |