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