/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß */ #include #include #include #include #include #include #include namespace impl { void set_thread_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask) { ::pthread_sigmask(SIG_BLOCK, new_mask, old_mask); } void set_process_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask) { ::sigprocmask(SIG_BLOCK, new_mask, old_mask); } class SignalTrap : public core::posix::SignalTrap { public: enum class Scope { process, thread }; enum class State { not_running, running }; SignalTrap(Scope scope, std::initializer_list blocked_signals) : scope(scope), state(State::not_running), event_fd(::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) { if (event_fd == -1) throw std::system_error(errno, std::system_category()); ::sigemptyset(&blocked_signals_mask); for(auto signal : blocked_signals) ::sigaddset(&blocked_signals_mask, static_cast(signal)); switch (scope) { case Scope::process: set_process_signal_mask(&blocked_signals_mask, &old_signals_mask); break; case Scope::thread: set_thread_signal_mask(&blocked_signals_mask, &old_signals_mask); break; default: break; } } ~SignalTrap() { switch (scope) { case Scope::process: set_process_signal_mask(&old_signals_mask, nullptr); break; case Scope::thread: set_thread_signal_mask(&old_signals_mask, nullptr); break; default: break; } ::close(event_fd); } bool has(core::posix::Signal signal) override { return ::sigismember(&blocked_signals_mask, static_cast(signal)); } void run() override { static constexpr int signal_fd_idx = 0; static constexpr int event_fd_idx = 1; static constexpr int signal_info_buffer_size = 5; if (state.load() == State::running) throw std::runtime_error("SignalTrap::run can only be run once."); state.store(State::running); // Make sure we clean up the signal fd whenever // we leave the scope of run. struct Scope { ~Scope() { if (signal_fd != -1) ::close(signal_fd); } int signal_fd; } scope{::signalfd(-1, &blocked_signals_mask, SFD_CLOEXEC | SFD_NONBLOCK)}; if (scope.signal_fd == -1) throw std::system_error(errno, std::system_category()); pollfd fds[2]; signalfd_siginfo signal_info[signal_info_buffer_size]; for (;;) { fds[signal_fd_idx] = {scope.signal_fd, POLLIN, 0}; fds[event_fd_idx] = {event_fd, POLLIN, 0}; auto rc = ::poll(fds, 2, -1); if (rc == -1) { if (errno == EINTR) continue; break; } if (rc == 0) continue; if (fds[signal_fd_idx].revents & POLLIN) { auto result = ::read(scope.signal_fd, signal_info, sizeof(signal_info)); for (unsigned int i = 0; i < result / sizeof(signalfd_siginfo); i++) { if (has(static_cast(signal_info[i].ssi_signo))) { on_signal_raised( static_cast( signal_info[i].ssi_signo)); } } } if (fds[event_fd_idx].revents & POLLIN) { std::int64_t value{1}; // Consciously void-ing the return value here. // Not much we can do about an error. auto result = ::read(event_fd, &value, sizeof(value)); (void) result; break; } } state.store(State::not_running); } void stop() override { static const std::int64_t value = {1}; if (sizeof(value) != ::write(event_fd, &value, sizeof(value))) throw std::system_error(errno, std::system_category()); } core::Signal& signal_raised() override { return on_signal_raised; } private: Scope scope; std::atomic state; int event_fd; core::Signal on_signal_raised; ::sigset_t old_signals_mask; ::sigset_t blocked_signals_mask; }; } std::shared_ptr core::posix::trap_signals_for_process( std::initializer_list blocked_signals) { return std::make_shared( impl::SignalTrap::Scope::process, blocked_signals); } std::shared_ptr core::posix::trap_signals_for_all_subsequent_threads( std::initializer_list blocked_signals) { return std::make_shared( impl::SignalTrap::Scope::thread, blocked_signals); }