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 
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 
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 (unsigned int 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>(
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>(
215  blocked_signals);
216 }
217 
SignalTrap(Scope scope, std::initializer_list< core::posix::Signal > blocked_signals)
Definition: signal.cpp:57
bool has(core::posix::Signal signal) override
Returns true if the given signal is trapped by this instance.
Definition: signal.cpp:96
core::Signal< core::posix::Signal > & signal_raised() override
Emitted whenever a trapped signal is raised by the operating system.
Definition: signal.cpp:187
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
void set_process_signal_mask(::sigset_t *new_mask,::sigset_t *old_mask)
Definition: signal.cpp:37
Signal
The Signal enum collects the most common POSIX signals.
Definition: signal.h:38
The SignalTrap class encapsulates functionality to trap and handle signals.
Definition: signal.h:65
void run() override
Starts observation of incoming signals, relaying them via signal_raised(). The call blocks until stop...
Definition: signal.cpp:101
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
void stop() override
Stops execution of the signal trap.
Definition: signal.cpp:180
void set_thread_signal_mask(::sigset_t *new_mask,::sigset_t *old_mask)
Definition: signal.cpp:32