blob: 757103a4690271f64e19e7e4a22267dd87b135b3 [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 <ipmid/api.hpp>
Vernon Mauery3719c2f2019-03-20 13:00:20 -07003#include <phosphor-logging/log.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05004
5#include <forward_list>
6#include <memory>
Vernon Mauery3719c2f2019-03-20 13:00:20 -07007#include <vector>
8
9using namespace phosphor::logging;
10
11namespace
12{
13
14class 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
84std::vector<std::unique_ptr<SignalHandler>> signals;
85
86} // namespace
87
88void 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 Williamsfbc6c9d2023-05-10 07:50:16 -0500104 signals[signalNumber] = std::make_unique<SignalHandler>(io,
105 signalNumber);
Vernon Mauery3719c2f2019-03-20 13:00:20 -0700106 }
107 signals[signalNumber]->registerHandler(priority, handler);
108}