xref: /freebsd/contrib/kyua/utils/signals/interrupts.cpp (revision b077aed33b7b6aefca7b17ddb250cf521f938613)
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::auto_ptr< signals::programmer > sighup_handler;
69 /// Programmer status for the SIGINT signal.
70 static std::auto_ptr< signals::programmer > sigint_handler;
71 /// Programmer status for the SIGTERM signal.
72 static std::auto_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::auto_ptr< signals::programmer > tmp_sighup_handler(
131         new signals::programmer(SIGHUP, signal_handler));
132     std::auto_ptr< signals::programmer > tmp_sigint_handler(
133         new signals::programmer(SIGINT, signal_handler));
134     std::auto_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 = tmp_sighup_handler;
139     sigint_handler = tmp_sigint_handler;
140     sigterm_handler = 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(NULL);
149     sigint_handler->unprogram(); sigint_handler.reset(NULL);
150     sigterm_handler->unprogram(); sigterm_handler.reset(NULL);
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