1 #include <boost/asio/signal_set.hpp>
2 #include <forward_list>
3 #include <ipmid/api.hpp>
4 #include <memory>
5 #include <phosphor-logging/log.hpp>
6 #include <vector>
7 
8 using namespace phosphor::logging;
9 
10 namespace
11 {
12 
13 class 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
83 std::vector<std::unique_ptr<SignalHandler>> signals;
84 
85 } // namespace
86 
87 void 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 }
108