1 #include <boost/asio/signal_set.hpp>
2 #include <ipmid/api.hpp>
3 #include <phosphor-logging/lg2.hpp>
4 
5 #include <forward_list>
6 #include <memory>
7 #include <vector>
8 
9 namespace
10 {
11 
12 class SignalHandler
13 {
14   public:
SignalHandler(std::shared_ptr<boost::asio::io_context> & io,int sigNum)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 
~SignalHandler()21     ~SignalHandler()
22     {
23         // unregister with asio to unmask the signal
24         signal->cancel();
25         signal->clear();
26     }
27 
registerHandler(int prio,const std::function<SignalResponse (int)> & handler)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 
handleSignal(const boost::system::error_code & ec,int sigNum)46     void handleSignal(const boost::system::error_code& ec, int sigNum)
47     {
48         if (ec)
49         {
50             lg2::error("Error in common signal handler, "
51                        "signal: {SIGNAL}, error: {ERROR}",
52                        "SIGNAL", sigNum, "ERROR", ec.message());
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:
asyncWait()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 
registerSignalHandler(int priority,int signalNumber,const std::function<SignalResponse (int)> & handler)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 }
107