1 #include <boost/asio/signal_set.hpp>
2 #include <ipmid/api.hpp>
3 #include <phosphor-logging/log.hpp>
4 
5 #include <forward_list>
6 #include <memory>
7 #include <vector>
8 
9 using namespace phosphor::logging;
10 
11 namespace
12 {
13 
14 class SignalHandler
15 {
16   public:
SignalHandler(std::shared_ptr<boost::asio::io_context> & io,int sigNum)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 
~SignalHandler()23     ~SignalHandler()
24     {
25         // unregister with asio to unmask the signal
26         signal->cancel();
27         signal->clear();
28     }
29 
registerHandler(int prio,const std::function<SignalResponse (int)> & handler)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 
handleSignal(const boost::system::error_code & ec,int sigNum)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:
asyncWait()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 
registerSignalHandler(int priority,int signalNumber,const std::function<SignalResponse (int)> & handler)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();
104         signals[signalNumber] = std::make_unique<SignalHandler>(io,
105                                                                 signalNumber);
106     }
107     signals[signalNumber]->registerHandler(priority, handler);
108 }
109