xref: /netbsd-src/external/bsd/kyua-cli/dist/utils/signals/interrupts_test.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 
38 #include <atf-c++.hpp>
39 
40 #include "utils/format/macros.hpp"
41 #include "utils/process/child.ipp"
42 #include "utils/process/status.hpp"
43 #include "utils/signals/exceptions.hpp"
44 #include "utils/signals/programmer.hpp"
45 
46 namespace process = utils::process;
47 namespace signals = utils::signals;
48 
49 
50 namespace {
51 
52 
53 /// Set to the signal that fired; -1 if none.
54 static volatile int fired_signal = -1;
55 
56 
57 /// Test handler for signals.
58 ///
59 /// \post fired_signal is set to the signal that triggered the handler.
60 ///
61 /// \param signo The signal that triggered the handler.
62 static void
signal_handler(const int signo)63 signal_handler(const int signo)
64 {
65     fired_signal = signo;
66 }
67 
68 
69 /// Child process that pauses waiting to be killed.
70 static void
pause_child(void)71 pause_child(void)
72 {
73     sigset_t mask;
74     sigemptyset(&mask);
75     if (sigsuspend(&mask) == -1)
76         ::exit(EXIT_FAILURE);
77     else {
78         // If this happens, it is because we received a non-deadly signal and
79         // the execution resumed.  This is not what we expect, so exit with an
80         // arbitrary code.
81         ::exit(45);
82     }
83 }
84 
85 
86 /// Checks that interrupts_handler() handles a particular signal.
87 ///
88 /// This indirectly checks the check_interrupt() function, which is not part of
89 /// the class but is tightly related.
90 ///
91 /// \param signo The signal to check.
92 static void
check_interrupts_handler(const int signo)93 check_interrupts_handler(const int signo)
94 {
95     signals::programmer test_handler(signo, signal_handler);
96 
97     {
98         signals::interrupts_handler interrupts;
99 
100         signals::check_interrupt();
101         ::kill(getpid(), signo);
102         ATF_REQUIRE_THROW_RE(signals::interrupted_error,
103                              F("Interrupted by signal %s") % signo,
104                              signals::check_interrupt());
105     }
106 
107     ATF_REQUIRE_EQ(-1, fired_signal);
108     ::kill(getpid(), signo);
109     ATF_REQUIRE_EQ(signo, fired_signal);
110 
111     test_handler.unprogram();
112 }
113 
114 
115 /// Checks that interrupts_inhibiter() handles a particular signal.
116 ///
117 /// \param signo The signal to check.
118 static void
check_interrupts_inhibiter(const int signo)119 check_interrupts_inhibiter(const int signo)
120 {
121     signals::programmer test_handler(signo, signal_handler);
122 
123     {
124         signals::interrupts_inhibiter inhibiter;
125         ::kill(::getpid(), signo);
126         ATF_REQUIRE_EQ(-1, fired_signal);
127     }
128     ATF_REQUIRE_EQ(signo, fired_signal);
129 
130     test_handler.unprogram();
131 }
132 
133 
134 }  // anonymous namespace
135 
136 
137 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sighup);
ATF_TEST_CASE_BODY(interrupts_handler__sighup)138 ATF_TEST_CASE_BODY(interrupts_handler__sighup)
139 {
140     check_interrupts_handler(SIGHUP);
141 }
142 
143 
144 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigint);
ATF_TEST_CASE_BODY(interrupts_handler__sigint)145 ATF_TEST_CASE_BODY(interrupts_handler__sigint)
146 {
147     check_interrupts_handler(SIGINT);
148 }
149 
150 
151 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigterm);
ATF_TEST_CASE_BODY(interrupts_handler__sigterm)152 ATF_TEST_CASE_BODY(interrupts_handler__sigterm)
153 {
154     check_interrupts_handler(SIGTERM);
155 }
156 
157 
158 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__kill_children);
ATF_TEST_CASE_BODY(interrupts_handler__kill_children)159 ATF_TEST_CASE_BODY(interrupts_handler__kill_children)
160 {
161     std::unique_ptr< process::child > child1(process::child::fork_capture(
162          pause_child));
163     std::unique_ptr< process::child > child2(process::child::fork_capture(
164          pause_child));
165 
166     signals::interrupts_handler interrupts;
167 
168     // Our children pause until the reception of a signal.  Interrupting
169     // ourselves will cause the signal to be re-delivered to our children due to
170     // the interrupts_handler semantics.  If this does not happen, the wait
171     // calls below would block indefinitely and cause our test to time out.
172     ::kill(::getpid(), SIGHUP);
173 
174     const process::status status1 = child1->wait();
175     ATF_REQUIRE(status1.signaled());
176     ATF_REQUIRE_EQ(SIGHUP, status1.termsig());
177     const process::status status2 = child2->wait();
178     ATF_REQUIRE(status2.signaled());
179     ATF_REQUIRE_EQ(SIGHUP, status2.termsig());
180 }
181 
182 
183 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sighup);
ATF_TEST_CASE_BODY(interrupts_inhibiter__sighup)184 ATF_TEST_CASE_BODY(interrupts_inhibiter__sighup)
185 {
186     check_interrupts_inhibiter(SIGHUP);
187 }
188 
189 
190 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigint);
ATF_TEST_CASE_BODY(interrupts_inhibiter__sigint)191 ATF_TEST_CASE_BODY(interrupts_inhibiter__sigint)
192 {
193     check_interrupts_inhibiter(SIGINT);
194 }
195 
196 
197 ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigterm);
ATF_TEST_CASE_BODY(interrupts_inhibiter__sigterm)198 ATF_TEST_CASE_BODY(interrupts_inhibiter__sigterm)
199 {
200     check_interrupts_inhibiter(SIGTERM);
201 }
202 
203 
ATF_INIT_TEST_CASES(tcs)204 ATF_INIT_TEST_CASES(tcs)
205 {
206     ATF_ADD_TEST_CASE(tcs, interrupts_handler__sighup);
207     ATF_ADD_TEST_CASE(tcs, interrupts_handler__sigint);
208     ATF_ADD_TEST_CASE(tcs, interrupts_handler__sigterm);
209     ATF_ADD_TEST_CASE(tcs, interrupts_handler__kill_children);
210 
211     ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sighup);
212     ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sigint);
213     ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sigterm);
214 }
215