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
31extern "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
46namespace process = utils::process;
47namespace signals = utils::signals;
48
49
50namespace {
51
52
53/// Set to the signal that fired; -1 if none.
54static 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.
62static void
63signal_handler(const int signo)
64{
65    fired_signal = signo;
66}
67
68
69/// Child process that pauses waiting to be killed.
70static void
71pause_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.
92static void
93check_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.
118static void
119check_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
137ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sighup);
138ATF_TEST_CASE_BODY(interrupts_handler__sighup)
139{
140    check_interrupts_handler(SIGHUP);
141}
142
143
144ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigint);
145ATF_TEST_CASE_BODY(interrupts_handler__sigint)
146{
147    check_interrupts_handler(SIGINT);
148}
149
150
151ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigterm);
152ATF_TEST_CASE_BODY(interrupts_handler__sigterm)
153{
154    check_interrupts_handler(SIGTERM);
155}
156
157
158ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__kill_children);
159ATF_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
183ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sighup);
184ATF_TEST_CASE_BODY(interrupts_inhibiter__sighup)
185{
186    check_interrupts_inhibiter(SIGHUP);
187}
188
189
190ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigint);
191ATF_TEST_CASE_BODY(interrupts_inhibiter__sigint)
192{
193    check_interrupts_inhibiter(SIGINT);
194}
195
196
197ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigterm);
198ATF_TEST_CASE_BODY(interrupts_inhibiter__sigterm)
199{
200    check_interrupts_inhibiter(SIGTERM);
201}
202
203
204ATF_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