diff options
-rw-r--r-- | include/ipmid/api.hpp | 35 | ||||
-rw-r--r-- | libipmid/Makefile.am | 1 | ||||
-rw-r--r-- | libipmid/signals.cpp | 106 |
3 files changed, 142 insertions, 0 deletions
diff --git a/include/ipmid/api.hpp b/include/ipmid/api.hpp index b691bed..b825796 100644 --- a/include/ipmid/api.hpp +++ b/include/ipmid/api.hpp @@ -241,3 +241,38 @@ static inline void post_work(WorkFn work) { getIoContext()->post(std::forward<WorkFn>(work)); } + +enum class SignalResponse : int +{ + breakExecution, + continueExecution, +}; + +/** + * @brief add a signal handler + * + * This registers a handler to be called asynchronously via the execution + * queue when the specified signal is received. + * + * Priority allows a signal handler to specify what order in the handler + * chain it gets called. Lower priority numbers will cause the handler to + * be executed later in the chain, while the highest priority numbers will cause + * the handler to be executed first. + * + * In order to facilitate a chain of handlers, each handler in the chain will be + * able to return breakExecution or continueExecution. Returning breakExecution + * will break the chain and no further handlers will execute for that signal. + * Returning continueExecution will allow lower-priority handlers to execute. + * + * By default, the main asio execution loop will register a low priority + * (prioOpenBmcBase) handler for SIGINT and SIGTERM to cause the process to stop + * on either of those signals. To prevent one of those signals from causing the + * process to stop, simply register a higher priority handler that returns + * breakExecution. + * + * @param int - priority of handler + * @param int - signal number to wait for + * @param handler - the callback function to be executed + */ +void registerSignalHandler(int priority, int signalNumber, + const std::function<SignalResponse(int)>& handler); diff --git a/libipmid/Makefile.am b/libipmid/Makefile.am index ac35bbf..e4899e1 100644 --- a/libipmid/Makefile.am +++ b/libipmid/Makefile.am @@ -13,6 +13,7 @@ pkgconfig_DATA = libipmid.pc lib_LTLIBRARIES = libipmid.la libipmid_la_SOURCES = \ sdbus-asio.cpp \ + signals.cpp \ systemintf-sdbus.cpp libipmid_la_LDFLAGS = \ $(SYSTEMD_LIBS) \ diff --git a/libipmid/signals.cpp b/libipmid/signals.cpp new file mode 100644 index 0000000..3838e95 --- /dev/null +++ b/libipmid/signals.cpp @@ -0,0 +1,106 @@ +#include <forward_list> +#include <ipmid/api.hpp> +#include <memory> +#include <phosphor-logging/log.hpp> +#include <vector> + +using namespace phosphor::logging; + +namespace +{ + +class SignalHandler +{ + public: + SignalHandler(std::shared_ptr<boost::asio::io_context>& io, int sigNum) : + signal(std::make_unique<boost::asio::signal_set>(*io, sigNum)) + { + asyncWait(); + } + + ~SignalHandler() + { + // unregister with asio to unmask the signal + signal->cancel(); + signal->clear(); + } + + void registerHandler(int prio, + const std::function<SignalResponse(int)>& handler) + { + // check for initial placement + if (handlers.empty() || std::get<0>(handlers.front()) < prio) + { + handlers.emplace_front(std::make_tuple(prio, handler)); + return; + } + // walk the list and put it in the right place + auto j = handlers.begin(); + for (auto i = j; i != handlers.end() && std::get<0>(*i) > prio; i++) + { + j = i; + } + handlers.emplace_after(j, std::make_tuple(prio, handler)); + } + + void handleSignal(const boost::system::error_code& ec, int sigNum) + { + if (ec) + { + log<level::ERR>("Error in common signal handler", + entry("SIGNAL=%d", sigNum), + entry("ERROR=%s", ec.message().c_str())); + return; + } + for (auto h = handlers.begin(); h != handlers.end(); h++) + { + std::function<SignalResponse(int)>& handler = std::get<1>(*h); + if (handler(sigNum) == SignalResponse::breakExecution) + { + break; + } + } + // start the wait for the next signal + asyncWait(); + } + + protected: + void asyncWait() + { + signal->async_wait([this](const boost::system::error_code& ec, + int sigNum) { handleSignal(ec, sigNum); }); + } + + std::forward_list<std::tuple<int, std::function<SignalResponse(int)>>> + handlers; + std::unique_ptr<boost::asio::signal_set> signal; +}; + +// SIGRTMAX is defined as a non-constexpr function call and thus cannot be used +// as an array size. Get around this by making a vector and resizing it the +// first time it is needed +std::vector<std::unique_ptr<SignalHandler>> signals; + +} // namespace + +void registerSignalHandler(int priority, int signalNumber, + const std::function<SignalResponse(int)>& handler) +{ + if (signalNumber >= SIGRTMAX) + { + return; + } + + if (signals.empty()) + { + signals.resize(SIGRTMAX); + } + + if (!signals[signalNumber]) + { + std::shared_ptr<boost::asio::io_context> io = getIoContext(); + signals[signalNumber] = + std::make_unique<SignalHandler>(io, signalNumber); + } + signals[signalNumber]->registerHandler(priority, handler); +} |