process-cpp: /build/process-cpp/src/process-cpp-3.0.1/src/core/posix/signal.cpp Source File

process-cpp 3.0.0 A simple convenience library for handling processes in C++11.

signal.cpp
Go to the documentation of this file.
1 /*
2 * Copyright © 2013 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19 #include <core/posix/signalable.h>
20
21 #include <poll.h>
22 #include <pthread.h>
23 #include <sys/eventfd.h>
24 #include <sys/signalfd.h>
25
26 #include <unistd.h>
27
28 #include <atomic>
29
30 namespace impl
31 {
32 void set_thread_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask)
33 {
34 ::pthread_sigmask(SIG_BLOCK, new_mask, old_mask);
35 }
36
37 void set_process_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask)
38 {
39 ::sigprocmask(SIG_BLOCK, new_mask, old_mask);
40 }
41
42 class SignalTrap : public core::posix::SignalTrap
43 {
44 public:
45 enum class Scope
46 {
47 process,
48 thread
49 };
50
51 enum class State
52 {
53 not_running,
54 running
55 };
56
57 SignalTrap(Scope scope, std::initializer_list<core::posix::Signal> blocked_signals)
58 : scope(scope),
59 state(State::not_running),
60 event_fd(::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK))
61 {
62 if (event_fd == -1)
63 throw std::system_error(errno, std::system_category());
64
65 ::sigemptyset(&blocked_signals_mask);
66
67 for(auto signal : blocked_signals)
68 ::sigaddset(&blocked_signals_mask, static_cast<int>(signal));
69
70 switch (scope)
71 {
72 case Scope::process:
73 set_process_signal_mask(&blocked_signals_mask, &old_signals_mask);
74 break;
75 case Scope::thread:
76 set_thread_signal_mask(&blocked_signals_mask, &old_signals_mask);
77 break;
78 }
79 }
80
81 ~SignalTrap()
82 {
83 switch (scope)
84 {
85 case Scope::process:
86 set_process_signal_mask(&old_signals_mask, nullptr);
87 break;
88 case Scope::thread:
89 set_thread_signal_mask(&old_signals_mask, nullptr);
90 break;
91 }
92
93 ::close(event_fd);
94 }
95
96 bool has(core::posix::Signal signal) override
97 {
98 return ::sigismember(&blocked_signals_mask, static_cast<int>(signal));
99 }
100
101 void run() override
102 {
103 static constexpr int signal_fd_idx = 0;
104 static constexpr int event_fd_idx = 1;
105
106 static constexpr int signal_info_buffer_size = 5;
107
108 if (state.load() == State::running)
109 throw std::runtime_error("SignalTrap::run can only be run once.");
110
111 state.store(State::running);
112
113 // Make sure we clean up the signal fd whenever
114 // we leave the scope of run.
115 struct Scope
116 {
117 ~Scope()
118 {
119 if (signal_fd != -1)
120 ::close(signal_fd);
121 }
122
123 int signal_fd;
124 } scope{::signalfd(-1, &blocked_signals_mask, SFD_CLOEXEC | SFD_NONBLOCK)};
125
126 if (scope.signal_fd == -1)
127 throw std::system_error(errno, std::system_category());
128
129 pollfd fds[2];
130 signalfd_siginfo signal_info[signal_info_buffer_size];
131
132 for (;;)
133 {
134 fds[signal_fd_idx] = {scope.signal_fd, POLLIN, 0};
135 fds[event_fd_idx] = {event_fd, POLLIN, 0};
136
137 auto rc = ::poll(fds, 2, -1);
138
139 if (rc == -1)
140 {
141 if (errno == EINTR)
142 continue;
143
144 break;
145 }
146
147 if (rc == 0)
148 continue;
149
150 if (fds[signal_fd_idx].revents & POLLIN)
151 {
152 auto result = ::read(scope.signal_fd, signal_info, sizeof(signal_info));
153
154 for (uint i = 0; i < result / sizeof(signalfd_siginfo); i++)
155 {
156 if (has(static_cast<core::posix::Signal>(signal_info[i].ssi_signo)))
157 {
158 on_signal_raised(
159 static_cast<core::posix::Signal>(
160 signal_info[i].ssi_signo));
161 }
162 }
163 }
164
165 if (fds[event_fd_idx].revents & POLLIN)
166 {
167 std::int64_t value{1};
168 // Consciously void-ing the return value here.
169 // Not much we can do about an error.
170 auto result = ::read(event_fd, &value, sizeof(value));
171 (void) result;
172
173 break;
174 }
175 }
176
177 state.store(State::not_running);
178 }
179
180 void stop() override
181 {
182 static const std::int64_t value = {1};
183 if (sizeof(value) != ::write(event_fd, &value, sizeof(value)))
184 throw std::system_error(errno, std::system_category());
185 }
186
187 core::Signal<core::posix::Signal>& signal_raised() override
188 {
189 return on_signal_raised;
190 }
191
192 private:
193 Scope scope;
194 std::atomic<State> state;
195 int event_fd;
196 core::Signal<core::posix::Signal> on_signal_raised;
197 ::sigset_t old_signals_mask;
198 ::sigset_t blocked_signals_mask;
199 };
200 }
201
202 std::shared_ptr<core::posix::SignalTrap> core::posix::trap_signals_for_process(
203 std::initializer_list<core::posix::Signal> blocked_signals)
204 {
205 return std::make_shared<impl::SignalTrap>(
206 impl::SignalTrap::Scope::process,
207 blocked_signals);
208 }
209
210 std::shared_ptr<core::posix::SignalTrap> core::posix::trap_signals_for_all_subsequent_threads(
211 std::initializer_list<core::posix::Signal> blocked_signals)
212 {
213 return std::make_shared<impl::SignalTrap>(
214 impl::SignalTrap::Scope::thread,
215 blocked_signals);
216 }
217
impl::SignalTrap::State::running
@ running
impl::set_process_signal_mask
void set_process_signal_mask(::sigset_t *new_mask, ::sigset_t *old_mask)
Definition: signal.cpp:37
impl::SignalTrap::Scope::thread
@ thread
core::posix::trap_signals_for_process
CORE_POSIX_DLL_PUBLIC std::shared_ptr< SignalTrap > trap_signals_for_process(std::initializer_list< core::posix::Signal > blocked_signals)
Traps the specified signals for the entire process.
Definition: signal.cpp:202
impl::SignalTrap::SignalTrap
SignalTrap(Scope scope, std::initializer_list< core::posix::Signal > blocked_signals)
Definition: signal.cpp:57
impl::SignalTrap::State
State
Definition: signal.cpp:52
impl::SignalTrap
Definition: signal.cpp:43
impl::SignalTrap::signal_raised
core::Signal< core::posix::Signal > & signal_raised() override
Emitted whenever a trapped signal is raised by the operating system.
Definition: signal.cpp:187
impl::SignalTrap::has
bool has(core::posix::Signal signal) override
Returns true if the given signal is trapped by this instance.
Definition: signal.cpp:96
core::posix::Signal
Signal
The Signal enum collects the most common POSIX signals.
Definition: signal.h:39
impl::set_thread_signal_mask
void set_thread_signal_mask(::sigset_t *new_mask, ::sigset_t *old_mask)
Definition: signal.cpp:32
core::posix::SignalTrap
The SignalTrap class encapsulates functionality to trap and handle signals.
Definition: signal.h:66
impl::SignalTrap::run
void run() override
Starts observation of incoming signals, relaying them via signal_raised(). The call blocks until stop...
Definition: signal.cpp:101
impl::SignalTrap::stop
void stop() override
Stops execution of the signal trap.
Definition: signal.cpp:180
impl::SignalTrap::State::not_running
@ not_running
impl
Definition: backtrace.cpp:28
impl::SignalTrap::Scope
Scope
Definition: signal.cpp:46
impl::SignalTrap::Scope::process
@ process
signalable.h
core::posix::trap_signals_for_all_subsequent_threads
CORE_POSIX_DLL_PUBLIC std::shared_ptr< SignalTrap > trap_signals_for_all_subsequent_threads(std::initializer_list< core::posix::Signal > blocked_signals)
Traps the specified signals for the current thread, and inherits the respective signal mask to all ch...
Definition: signal.cpp:210
impl::SignalTrap::~SignalTrap
~SignalTrap()
Definition: signal.cpp:81