xref: /netbsd-src/external/bsd/kyua-cli/dist/utils/signals/interrupts.cpp (revision 46b85cbbd3d745f264b248ec8702f62b162c7789)
1 // Copyright 2012 Google Inc.
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 <signal.h>
33 #include <unistd.h>
34 }
35 
36 #include <cstdlib>
37 #include <cstring>
38 #include <set>
39 
40 #include "utils/sanity.hpp"
41 #include "utils/signals/exceptions.hpp"
42 #include "utils/signals/programmer.hpp"
43 
44 namespace signals = utils::signals;
45 
46 
47 namespace {
48 
49 
50 /// The interrupt signal that fired, or -1 if none.
51 static volatile int fired_signal = -1;
52 
53 
54 /// Collection of PIDs.
55 typedef std::set< pid_t > pids_set;
56 
57 
58 /// List of processes to kill upon reception of a signal.
59 static pids_set pids_to_kill;
60 
61 
62 /// Programmer status for the SIGHUP signal.
63 static std::unique_ptr< signals::programmer > sighup_handler;
64 /// Programmer status for the SIGINT signal.
65 static std::unique_ptr< signals::programmer > sigint_handler;
66 /// Programmer status for the SIGTERM signal.
67 static std::unique_ptr< signals::programmer > sigterm_handler;
68 
69 
70 /// Signal mask to restore after exiting a signal inhibited section.
71 static sigset_t old_sigmask;
72 
73 
74 /// Whether there is an interrupts_handler object in existence or not.
75 bool interrupts_handler_active = false;
76 
77 
78 /// Whether there is an interrupts_inhibiter object in existence or not.
79 bool interrupts_inhibiter_active = false;
80 
81 
82 /// Generic handler to capture interrupt signals.
83 ///
84 /// From this handler, we record that an interrupt has happened so that
85 /// check_interrupt() can know whether there execution has to be stopped or not.
86 /// We also terminate any of our child processes (started by the
87 /// utils::process::children class) so that any ongoing wait(2) system calls
88 /// terminate.
89 ///
90 /// \param signo The signal that caused this handler to be called.
91 static void
signal_handler(const int signo)92 signal_handler(const int signo)
93 {
94     static const char* message = "[-- Signal caught; please wait for "
95         "cleanup --]\n";
96     if (::write(STDERR_FILENO, message, std::strlen(message)) == -1) {
97         // We are exiting: the message printed here is only for informational
98         // purposes.  If we fail to print it (which probably means something
99         // is really bad), there is not much we can do within the signal
100         // handler, so just ignore this.
101     }
102 
103     fired_signal = signo;
104 
105     for (pids_set::const_iterator iter = pids_to_kill.begin();
106         iter != pids_to_kill.end(); ++iter) {
107         // Redirecting the interrupt signal to our child processes does NOT
108         // guarantee that they also terminate.  For that to happen, we'd need to
109         // SIGKILL them.
110         //
111         // *However*, because we use this code to invoke the kyua-testers only,
112         // and because we assume that such processes are well-behaved and
113         // terminate according to our expectations, we do it this way, which
114         // allows the testers to know which specific signal made them terminate.
115         (void)::kill(*iter, signo);
116     }
117 }
118 
119 
120 /// Installs signal handlers for potential interrupts.
121 ///
122 /// \pre Must not have been called before.
123 /// \post The various sig*_handler global variables are atomically updated.
124 static void
setup_handlers(void)125 setup_handlers(void)
126 {
127     PRE(sighup_handler.get() == NULL);
128     PRE(sigint_handler.get() == NULL);
129     PRE(sigterm_handler.get() == NULL);
130 
131     // Create the handlers on the stack first so that, if any of them fails, the
132     // stack unwinding cleans things up.
133     std::unique_ptr< signals::programmer > tmp_sighup_handler(
134         new signals::programmer(SIGHUP, signal_handler));
135     std::unique_ptr< signals::programmer > tmp_sigint_handler(
136         new signals::programmer(SIGINT, signal_handler));
137     std::unique_ptr< signals::programmer > tmp_sigterm_handler(
138         new signals::programmer(SIGTERM, signal_handler));
139 
140     // Now, update the global pointers, which is an operation that cannot fail.
141     sighup_handler = std::move(tmp_sighup_handler);
142     sigint_handler = std::move(tmp_sigint_handler);
143     sigterm_handler = std::move(tmp_sigterm_handler);
144 }
145 
146 
147 /// Uninstalls the signal handlers installed by setup_handlers().
148 static void
cleanup_handlers(void)149 cleanup_handlers(void)
150 {
151     sighup_handler->unprogram(); sighup_handler.reset(NULL);
152     sigint_handler->unprogram(); sigint_handler.reset(NULL);
153     sigterm_handler->unprogram(); sigterm_handler.reset(NULL);
154 }
155 
156 
157 
158 /// Masks the signals installed by setup_handlers().
159 static void
mask_signals(void)160 mask_signals(void)
161 {
162     sigset_t mask;
163     sigemptyset(&mask);
164     sigaddset(&mask, SIGHUP);
165     sigaddset(&mask, SIGINT);
166     sigaddset(&mask, SIGTERM);
167     const int ret = ::sigprocmask(SIG_BLOCK, &mask, &old_sigmask);
168     INV(ret != -1);
169 }
170 
171 
172 /// Resets the signal masking put in place by mask_signals().
173 static void
unmask_signals(void)174 unmask_signals(void)
175 {
176     const int ret = ::sigprocmask(SIG_SETMASK, &old_sigmask, NULL);
177     INV(ret != -1);
178 }
179 
180 
181 }  // anonymous namespace
182 
183 
184 /// Constructor that sets up the signal handlers.
interrupts_handler(void)185 signals::interrupts_handler::interrupts_handler(void)
186 {
187     PRE(!interrupts_handler_active);
188     setup_handlers();
189     interrupts_handler_active = true;
190 }
191 
192 
193 /// Destructor that removes the signal handlers.
~interrupts_handler(void)194 signals::interrupts_handler::~interrupts_handler(void)
195 {
196     cleanup_handlers();
197     interrupts_handler_active = false;
198 }
199 
200 
201 /// Constructor that sets up signal masking.
interrupts_inhibiter(void)202 signals::interrupts_inhibiter::interrupts_inhibiter(void)
203 {
204     PRE(!interrupts_inhibiter_active);
205     mask_signals();
206     interrupts_inhibiter_active = true;
207 }
208 
209 
210 /// Destructor that removes signal masking.
~interrupts_inhibiter(void)211 signals::interrupts_inhibiter::~interrupts_inhibiter(void)
212 {
213     unmask_signals();
214     interrupts_inhibiter_active = false;
215 }
216 
217 
218 /// Checks if an interrupt has fired.
219 ///
220 /// Calls to this function should be sprinkled in strategic places through the
221 /// code protected by an interrupts_handler object.
222 ///
223 /// \throw interrupted_error If there has been an interrupt.
224 void
check_interrupt(void)225 signals::check_interrupt(void)
226 {
227     if (fired_signal != -1)
228         throw interrupted_error(fired_signal);
229 }
230 
231 
232 /// Registers a child process to be killed upon reception of an interrupt.
233 ///
234 /// \pre Must be called with interrupts being inhibited.  The caller must ensure
235 /// that the call call to fork() and the addition of the PID happen atomically.
236 ///
237 /// \param pid The PID of the child process.  Must not have been yet regsitered.
238 void
add_pid_to_kill(const pid_t pid)239 signals::add_pid_to_kill(const pid_t pid)
240 {
241     PRE(interrupts_inhibiter_active);
242     PRE(pids_to_kill.find(pid) == pids_to_kill.end());
243     pids_to_kill.insert(pid);
244 }
245 
246 
247 /// Unregisters a child process previously registered via add_pid_to_kill().
248 ///
249 /// \pre Must be called with interrupts being inhibited.  This is not necessary,
250 /// but pushing this to the caller simplifies our logic and provides consistency
251 /// with the add_pid_to_kill() call.
252 ///
253 /// \param pid The PID of the child process.  Must have been registered
254 ///     previously, and the process must have already been awaited for.
255 void
remove_pid_to_kill(const pid_t pid)256 signals::remove_pid_to_kill(const pid_t pid)
257 {
258     PRE(interrupts_inhibiter_active);
259     PRE(pids_to_kill.find(pid) != pids_to_kill.end());
260     pids_to_kill.erase(pid);
261 }
262