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