Kea 1.5.0
signal_set.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <util/signal_set.h>
10
11#include <cerrno>
12#include <list>
13
14using namespace isc;
15using namespace isc::util;
16
17namespace {
18
28SigIntSetPtr getRegisteredSignals() {
29 static SigIntSetPtr registered_signals(new SigIntSet());
30 return (registered_signals);
31}
32
42SigIntListPtr getSignalStates() {
43 static SigIntListPtr signal_states(new SigIntList());
44 return (signal_states);
45}
46
59void internalHandler(int sig) {
61 // Signal has been handled by the on-receipt handler.
62 return;
63 }
64
65 // Signal is using post-receipt handling, see if we've
66 // already received it.
67 SigIntListPtr states = getSignalStates();
68 for (std::list<int>::const_iterator it = states->begin();
69 it != states->end(); ++it) {
70 if (sig == *it) {
71 return;
72 }
73 }
74
75 // First occurrence, so save it.
76 states->push_back(sig);
77}
78
80BoolSignalHandler onreceipt_handler_ = BoolSignalHandler();
81
82}; // end anon namespace
83
84namespace isc {
85namespace util {
86
87bool
89 if (!onreceipt_handler_) {
90 return (false);
91 }
92
93 // First we set the signal to SIG_IGN. This causes any repeat occurrences
94 // to be discarded, not deferred as they would be if blocked. Note that
95 // we save the current sig action so we can restore it later.
96 struct sigaction sa;
97 struct sigaction prev_sa;
98 memset(&sa, 0, sizeof(sa));
99 sa.sa_handler = SIG_IGN;
100 if (sigaction(sig, &sa, &prev_sa) < 0) {
101 // Highly unlikely we can get here.
102 const char* errmsg = strerror(errno);
103 isc_throw(SignalSetError, "failed to set SIG_IGN for signal "
104 << sig << ": " << errmsg);
105 }
106
107 // Call the registered handler.
108 bool signal_processed = false;
109 try {
110 signal_processed = onreceipt_handler_(sig);
111 } catch (const std::exception& ex) {
112 // Restore the handler. We might fail to restore it, but we likely
113 // have bigger issues anyway: for that reason, the return value is
114 // ignored. To avoid complaints from static code checkers that notice
115 // that the return values from other calls to sigaction() have been
116 // used, the call to sigaction is explicitly cast to void to indicate
117 // that the return value is intentionally being ignored.
118 static_cast<void>(sigaction(sig, &prev_sa, 0));
119 isc_throw(SignalSetError, "onreceipt_handler failed for signal "
120 << sig << ": " << ex.what());
121 }
122
123 // Restore the sig action to re-enable handling this signal.
124 if (sigaction(sig, &prev_sa, 0) < 0) {
125 // Highly unlikely we can get here.
126 const char* errmsg = strerror(errno);
127 isc_throw(SignalSetError, "failed to restore handler for signal "
128 << sig << ": " << errmsg);
129 }
130
131 return (signal_processed);
132}
133
134SignalSet::SignalSet(const int sig0) {
135 // Copy static pointers to ensure they don't lose scope before we do.
136 registered_signals_ = getRegisteredSignals();
137 signal_states_ = getSignalStates();
138 add(sig0);
139}
140
141SignalSet::SignalSet(const int sig0, const int sig1) {
142 registered_signals_ = getRegisteredSignals();
143 signal_states_ = getSignalStates();
144 add(sig0);
145 add(sig1);
146}
147
148SignalSet::SignalSet(const int sig0, const int sig1, const int sig2) {
149 registered_signals_ = getRegisteredSignals();
150 signal_states_ = getSignalStates();
151 add(sig0);
152 add(sig1);
153 add(sig2);
154}
155
157 // Set default signal handlers.
158 try {
159 clear();
160 } catch (...) {
161 // Not a good thing to throw from a destructor. in fact this should
162 // not throw an exception because we just unregister the signals
163 // that we have previously registered. So the signal codes are fine.
164 }
165}
166
167void
168SignalSet::add(const int sig) {
169 insert(sig);
170 struct sigaction sa;
171 memset(&sa, 0, sizeof(sa));
172 sa.sa_handler = internalHandler;
173 sigfillset(&sa.sa_mask);
174 if (sigaction(sig, &sa, 0) < 0) {
175 const char* errmsg = strerror(errno);
176 erase(sig);
177 isc_throw(SignalSetError, "failed to register a signal handler for"
178 " signal " << sig << ": " << errmsg);
179 }
180}
181
182void
183SignalSet::block() const {
184 maskSignals(SIG_BLOCK);
185}
186
187void
189 // Iterate over a copy of the registered signal set because the
190 // remove function is erasing the elements and we don't want to
191 // erase the elements we are iterating over. This would cause
192 // a segfault.
193 std::set<int> all_signals = local_signals_;
194 for (std::set<int>::const_iterator it = all_signals.begin();
195 it != all_signals.end(); ++it) {
196 remove(*it);
197 }
198}
199
200int
202 for (std::list<int>::iterator it = signal_states_->begin();
203 it != signal_states_->end(); ++it) {
204 if (local_signals_.find(*it) != local_signals_.end()) {
205 return (*it);
206 }
207 }
208 return (-1);
209}
210
211void
212SignalSet::erase(const int sig) {
213 if (local_signals_.find(sig) == local_signals_.end()) {
214 isc_throw(SignalSetError, "failed to unregister signal " << sig
215 << " from a signal set: signal is not owned by the"
216 " signal set");
217 }
218 // Remove globally registered signal.
219 registered_signals_->erase(sig);
220 // Remove unhandled signals from the queue.
221 for (std::list<int>::iterator it = signal_states_->begin();
222 it != signal_states_->end(); ++it) {
223 if (*it == sig) {
224 it = signal_states_->erase(it);
225 }
226 }
227
228 // Remove locally registered signal.
229 local_signals_.erase(sig);
230}
231
232void
234 block();
235 int signum = getNext();
236 if (signum >= 0) {
237 popNext();
238 try {
239 signal_handler(signum);
240 } catch (...) {
241 unblock();
242 throw;
243 }
244 }
245 unblock();
246}
247
248void
249SignalSet::insert(const int sig) {
250 if ((registered_signals_->find(sig) != registered_signals_->end()) ||
251 (local_signals_.find(sig) != local_signals_.end())) {
252 isc_throw(SignalSetError, "attempt to register a duplicate signal "
253 << sig);
254 }
255 registered_signals_->insert(sig);
256 local_signals_.insert(sig);
257}
258
259void
260SignalSet::maskSignals(const int mask) const {
261 sigset_t new_set;
262 sigemptyset(&new_set);
263 for (std::set<int>::const_iterator it = registered_signals_->begin();
264 it != registered_signals_->end(); ++it) {
265 sigaddset(&new_set, *it);
266 }
267 pthread_sigmask(mask, &new_set, 0);
268}
269
270void
271SignalSet::popNext() {
272 for (std::list<int>::iterator it = signal_states_->begin();
273 it != signal_states_->end(); ++it) {
274 if (local_signals_.find(*it) != local_signals_.end()) {
275 signal_states_->erase(it);
276 return;
277 }
278 }
279}
280
281void
282SignalSet::remove(const int sig) {
283 // Unregister only if we own this signal.
284 if (local_signals_.find(sig) != local_signals_.end()) {
285 struct sigaction sa;
286 memset(&sa, 0, sizeof(sa));
287 sa.sa_handler = SIG_DFL;
288 sigfillset(&sa.sa_mask);
289 if (sigaction(sig, &sa, 0) < 0) {
290 isc_throw(SignalSetError, "unable to restore original signal"
291 " handler for signal: " << sig);
292 }
293 erase(sig);
294 } else {
295 isc_throw(SignalSetError, "failed to unregister signal " << sig
296 << ": this signal is not owned by the signal set");
297 }
298}
299
300void
301SignalSet::unblock() const {
302 maskSignals(SIG_UNBLOCK);
303}
304
305
306void
308 onreceipt_handler_ = handler;
309}
310
311void
313 onreceipt_handler_ = BoolSignalHandler();
314}
315
316} // end of isc::util
317} // end of isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Exception thrown when the isc::util::SignalSet class experiences an error.
Definition: signal_set.h:24
~SignalSet()
Destructor.
Definition: signal_set.cc:156
static void setOnReceiptHandler(BoolSignalHandler handler)
Registers a handler as the onreceipt signal handler.
Definition: signal_set.cc:307
void handleNext(SignalHandler signal_handler)
Calls a handler for the next received signal.
Definition: signal_set.cc:233
static bool invokeOnReceiptHandler(int sig)
Invokes the onreceipt handler if it exists.
Definition: signal_set.cc:88
int getNext() const
Returns a code of the next received signal.
Definition: signal_set.cc:201
SignalSet(const int sig0)
Constructor installing one signal.
Definition: signal_set.cc:134
void add(const int sig)
Installs the handler for the specified signal.
Definition: signal_set.cc:168
void remove(const int sig)
Uninstalls signal handler for a specified signal.
Definition: signal_set.cc:282
static void clearOnReceiptHandler()
Unregisters the onreceipt signal handler.
Definition: signal_set.cc:312
void clear()
Uninstalls all signals.
Definition: signal_set.cc:188
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: edns.h:19
std::list< int > SigIntList
Defines a list of integer signal identifiers: SIGHUP, SIGTERM...
Definition: signal_set.h:37
boost::shared_ptr< SigIntList > SigIntListPtr
Pointer to a list of signal identifiers.
Definition: signal_set.h:39
boost::shared_ptr< SigIntSet > SigIntSetPtr
Pointer to a set of signal identifiers.
Definition: signal_set.h:34
std::set< int > SigIntSet
Defines a set of integer signal identifiers: SIGHUP, SIGTERM...
Definition: signal_set.h:32
boost::function< bool(int signum)> BoolSignalHandler
Pointer to a signal handling function which returns bool result.
Definition: signal_set.h:54
boost::function< void(int signum)> SignalHandler
Pointer to the signal handling function.
Definition: signal_set.h:47
Defines the logger used by the top-level component of kea-dhcp-ddns.