Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/signals/interrupts.cpp
48199 views
1
// Copyright 2012 The Kyua Authors.
2
// All rights reserved.
3
//
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are
6
// met:
7
//
8
// * Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above copyright
11
// notice, this list of conditions and the following disclaimer in the
12
// documentation and/or other materials provided with the distribution.
13
// * Neither the name of Google Inc. nor the names of its contributors
14
// may be used to endorse or promote products derived from this software
15
// without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
#include "utils/signals/interrupts.hpp"
30
31
extern "C" {
32
#include <sys/types.h>
33
34
#include <signal.h>
35
#include <unistd.h>
36
}
37
38
#include <cstdlib>
39
#include <cstring>
40
#include <set>
41
42
#include "utils/logging/macros.hpp"
43
#include "utils/process/operations.hpp"
44
#include "utils/sanity.hpp"
45
#include "utils/signals/exceptions.hpp"
46
#include "utils/signals/programmer.hpp"
47
48
namespace signals = utils::signals;
49
namespace process = utils::process;
50
51
52
namespace {
53
54
55
/// The interrupt signal that fired, or -1 if none.
56
static volatile int fired_signal = -1;
57
58
59
/// Collection of PIDs.
60
typedef std::set< pid_t > pids_set;
61
62
63
/// List of processes to kill upon reception of a signal.
64
static pids_set pids_to_kill;
65
66
67
/// Programmer status for the SIGHUP signal.
68
static std::unique_ptr< signals::programmer > sighup_handler;
69
/// Programmer status for the SIGINT signal.
70
static std::unique_ptr< signals::programmer > sigint_handler;
71
/// Programmer status for the SIGTERM signal.
72
static std::unique_ptr< signals::programmer > sigterm_handler;
73
74
75
/// Signal mask to restore after exiting a signal inhibited section.
76
static sigset_t global_old_sigmask;
77
78
79
/// Whether there is an interrupts_handler object in existence or not.
80
static bool interrupts_handler_active = false;
81
82
83
/// Whether there is an interrupts_inhibiter object in existence or not.
84
static std::size_t interrupts_inhibiter_active = 0;
85
86
87
/// Generic handler to capture interrupt signals.
88
///
89
/// From this handler, we record that an interrupt has happened so that
90
/// check_interrupt() can know whether there execution has to be stopped or not.
91
/// We also terminate any of our child processes (started by the
92
/// utils::process::children class) so that any ongoing wait(2) system calls
93
/// terminate.
94
///
95
/// \param signo The signal that caused this handler to be called.
96
static void
97
signal_handler(const int signo)
98
{
99
static const char* message = "[-- Signal caught; please wait for "
100
"cleanup --]\n";
101
if (::write(STDERR_FILENO, message, std::strlen(message)) == -1) {
102
// We are exiting: the message printed here is only for informational
103
// purposes. If we fail to print it (which probably means something
104
// is really bad), there is not much we can do within the signal
105
// handler, so just ignore this.
106
}
107
108
fired_signal = signo;
109
110
for (pids_set::const_iterator iter = pids_to_kill.begin();
111
iter != pids_to_kill.end(); ++iter) {
112
process::terminate_group(*iter);
113
}
114
}
115
116
117
/// Installs signal handlers for potential interrupts.
118
///
119
/// \pre Must not have been called before.
120
/// \post The various sig*_handler global variables are atomically updated.
121
static void
122
setup_handlers(void)
123
{
124
PRE(sighup_handler.get() == NULL);
125
PRE(sigint_handler.get() == NULL);
126
PRE(sigterm_handler.get() == NULL);
127
128
// Create the handlers on the stack first so that, if any of them fails, the
129
// stack unwinding cleans things up.
130
std::unique_ptr< signals::programmer > tmp_sighup_handler(
131
new signals::programmer(SIGHUP, signal_handler));
132
std::unique_ptr< signals::programmer > tmp_sigint_handler(
133
new signals::programmer(SIGINT, signal_handler));
134
std::unique_ptr< signals::programmer > tmp_sigterm_handler(
135
new signals::programmer(SIGTERM, signal_handler));
136
137
// Now, update the global pointers, which is an operation that cannot fail.
138
sighup_handler = std::move(tmp_sighup_handler);
139
sigint_handler = std::move(tmp_sigint_handler);
140
sigterm_handler = std::move(tmp_sigterm_handler);
141
}
142
143
144
/// Uninstalls the signal handlers installed by setup_handlers().
145
static void
146
cleanup_handlers(void)
147
{
148
sighup_handler->unprogram(); sighup_handler.reset();
149
sigint_handler->unprogram(); sigint_handler.reset();
150
sigterm_handler->unprogram(); sigterm_handler.reset();
151
}
152
153
154
155
/// Masks the signals installed by setup_handlers().
156
///
157
/// \param[out] old_sigmask The old signal mask to save via the
158
/// \code oset \endcode argument with sigprocmask(2).
159
static void
160
mask_signals(sigset_t* old_sigmask)
161
{
162
sigset_t mask;
163
sigemptyset(&mask);
164
sigaddset(&mask, SIGALRM);
165
sigaddset(&mask, SIGHUP);
166
sigaddset(&mask, SIGINT);
167
sigaddset(&mask, SIGTERM);
168
const int ret = ::sigprocmask(SIG_BLOCK, &mask, old_sigmask);
169
INV(ret != -1);
170
}
171
172
173
/// Resets the signal masking put in place by mask_signals().
174
///
175
/// \param[in] old_sigmask The old signal mask to restore via the
176
/// \code set \endcode argument with sigprocmask(2).
177
static void
178
unmask_signals(sigset_t* old_sigmask)
179
{
180
const int ret = ::sigprocmask(SIG_SETMASK, old_sigmask, NULL);
181
INV(ret != -1);
182
}
183
184
185
} // anonymous namespace
186
187
188
/// Constructor that sets up the signal handlers.
189
signals::interrupts_handler::interrupts_handler(void) :
190
_programmed(false)
191
{
192
PRE(!interrupts_handler_active);
193
setup_handlers();
194
_programmed = true;
195
interrupts_handler_active = true;
196
}
197
198
199
/// Destructor that removes the signal handlers.
200
///
201
/// Given that this is a destructor and it can't report errors back to the
202
/// caller, the caller must attempt to call unprogram() on its own.
203
signals::interrupts_handler::~interrupts_handler(void)
204
{
205
if (_programmed) {
206
LW("Destroying still-programmed signals::interrupts_handler object");
207
try {
208
unprogram();
209
} catch (const error& e) {
210
UNREACHABLE;
211
}
212
}
213
}
214
215
216
/// Unprograms all signals captured by the interrupts handler.
217
///
218
/// \throw system_error If the unprogramming of any signal fails.
219
void
220
signals::interrupts_handler::unprogram(void)
221
{
222
PRE(_programmed);
223
224
// Modify the control variables first before unprogramming the handlers. If
225
// we fail to do the latter, we do not want to try again because we will not
226
// succeed (and we'll cause a crash due to failed preconditions).
227
_programmed = false;
228
interrupts_handler_active = false;
229
230
cleanup_handlers();
231
fired_signal = -1;
232
}
233
234
235
/// Constructor that sets up signal masking.
236
signals::interrupts_inhibiter::interrupts_inhibiter(void)
237
{
238
sigset_t old_sigmask;
239
mask_signals(&old_sigmask);
240
if (interrupts_inhibiter_active == 0) {
241
global_old_sigmask = old_sigmask;
242
}
243
++interrupts_inhibiter_active;
244
}
245
246
247
/// Destructor that removes signal masking.
248
signals::interrupts_inhibiter::~interrupts_inhibiter(void)
249
{
250
if (interrupts_inhibiter_active > 1) {
251
--interrupts_inhibiter_active;
252
} else {
253
interrupts_inhibiter_active = false;
254
unmask_signals(&global_old_sigmask);
255
}
256
}
257
258
259
/// Checks if an interrupt has fired.
260
///
261
/// Calls to this function should be sprinkled in strategic places through the
262
/// code protected by an interrupts_handler object.
263
///
264
/// Only one call to this function will raise an exception per signal received.
265
/// This is to allow executing cleanup actions without reraising interrupt
266
/// exceptions unless the user has fired another interrupt.
267
///
268
/// \throw interrupted_error If there has been an interrupt.
269
void
270
signals::check_interrupt(void)
271
{
272
if (fired_signal != -1) {
273
const int original_fired_signal = fired_signal;
274
fired_signal = -1;
275
throw interrupted_error(original_fired_signal);
276
}
277
}
278
279
280
/// Registers a child process to be killed upon reception of an interrupt.
281
///
282
/// \pre Must be called with interrupts being inhibited. The caller must ensure
283
/// that the call call to fork() and the addition of the PID happen atomically.
284
///
285
/// \param pid The PID of the child process. Must not have been yet regsitered.
286
void
287
signals::add_pid_to_kill(const pid_t pid)
288
{
289
PRE(interrupts_inhibiter_active);
290
PRE(pids_to_kill.find(pid) == pids_to_kill.end());
291
pids_to_kill.insert(pid);
292
}
293
294
295
/// Unregisters a child process previously registered via add_pid_to_kill().
296
///
297
/// \pre Must be called with interrupts being inhibited. This is not necessary,
298
/// but pushing this to the caller simplifies our logic and provides consistency
299
/// with the add_pid_to_kill() call.
300
///
301
/// \param pid The PID of the child process. Must have been registered
302
/// previously, and the process must have already been awaited for.
303
void
304
signals::remove_pid_to_kill(const pid_t pid)
305
{
306
PRE(interrupts_inhibiter_active);
307
PRE(pids_to_kill.find(pid) != pids_to_kill.end());
308
pids_to_kill.erase(pid);
309
}
310
311