1*b0d29bc4SBrooks Davis // Copyright 2015 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/process/executor.ipp"
30*b0d29bc4SBrooks Davis
31*b0d29bc4SBrooks Davis extern "C" {
32*b0d29bc4SBrooks Davis #include <sys/types.h>
33*b0d29bc4SBrooks Davis #include <sys/time.h>
34*b0d29bc4SBrooks Davis #include <sys/wait.h>
35*b0d29bc4SBrooks Davis
36*b0d29bc4SBrooks Davis #include <signal.h>
37*b0d29bc4SBrooks Davis #include <unistd.h>
38*b0d29bc4SBrooks Davis }
39*b0d29bc4SBrooks Davis
40*b0d29bc4SBrooks Davis #include <cerrno>
41*b0d29bc4SBrooks Davis #include <cstdlib>
42*b0d29bc4SBrooks Davis #include <fstream>
43*b0d29bc4SBrooks Davis #include <iostream>
44*b0d29bc4SBrooks Davis #include <vector>
45*b0d29bc4SBrooks Davis
46*b0d29bc4SBrooks Davis #include <atf-c++.hpp>
47*b0d29bc4SBrooks Davis
48*b0d29bc4SBrooks Davis #include "utils/datetime.hpp"
49*b0d29bc4SBrooks Davis #include "utils/defs.hpp"
50*b0d29bc4SBrooks Davis #include "utils/env.hpp"
51*b0d29bc4SBrooks Davis #include "utils/format/containers.ipp"
52*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
53*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp"
54*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
55*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
56*b0d29bc4SBrooks Davis #include "utils/passwd.hpp"
57*b0d29bc4SBrooks Davis #include "utils/process/status.hpp"
58*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
59*b0d29bc4SBrooks Davis #include "utils/signals/exceptions.hpp"
60*b0d29bc4SBrooks Davis #include "utils/stacktrace.hpp"
61*b0d29bc4SBrooks Davis #include "utils/text/exceptions.hpp"
62*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp"
63*b0d29bc4SBrooks Davis
64*b0d29bc4SBrooks Davis namespace datetime = utils::datetime;
65*b0d29bc4SBrooks Davis namespace executor = utils::process::executor;
66*b0d29bc4SBrooks Davis namespace fs = utils::fs;
67*b0d29bc4SBrooks Davis namespace passwd = utils::passwd;
68*b0d29bc4SBrooks Davis namespace process = utils::process;
69*b0d29bc4SBrooks Davis namespace signals = utils::signals;
70*b0d29bc4SBrooks Davis namespace text = utils::text;
71*b0d29bc4SBrooks Davis
72*b0d29bc4SBrooks Davis using utils::none;
73*b0d29bc4SBrooks Davis using utils::optional;
74*b0d29bc4SBrooks Davis
75*b0d29bc4SBrooks Davis
76*b0d29bc4SBrooks Davis /// Large timeout for the processes we spawn.
77*b0d29bc4SBrooks Davis ///
78*b0d29bc4SBrooks Davis /// This number is supposed to be (much) larger than the timeout of the test
79*b0d29bc4SBrooks Davis /// cases that use it so that children processes are not killed spuriously.
80*b0d29bc4SBrooks Davis static const datetime::delta infinite_timeout(1000000, 0);
81*b0d29bc4SBrooks Davis
82*b0d29bc4SBrooks Davis
83*b0d29bc4SBrooks Davis static void do_exit(const int) UTILS_NORETURN;
84*b0d29bc4SBrooks Davis
85*b0d29bc4SBrooks Davis
86*b0d29bc4SBrooks Davis /// Terminates a subprocess without invoking destructors.
87*b0d29bc4SBrooks Davis ///
88*b0d29bc4SBrooks Davis /// This is just a simple wrapper over _exit(2) because we cannot use std::exit
89*b0d29bc4SBrooks Davis /// on exit from a subprocess. The reason is that we do not want to invoke any
90*b0d29bc4SBrooks Davis /// destructors as otherwise we'd clear up the global executor state by mistake.
91*b0d29bc4SBrooks Davis /// This wouldn't be a major problem if it wasn't because doing so deletes
92*b0d29bc4SBrooks Davis /// on-disk files and we want to leave them in place so that the parent process
93*b0d29bc4SBrooks Davis /// can test for them!
94*b0d29bc4SBrooks Davis ///
95*b0d29bc4SBrooks Davis /// \param exit_code Code to exit with.
96*b0d29bc4SBrooks Davis static void
do_exit(const int exit_code)97*b0d29bc4SBrooks Davis do_exit(const int exit_code)
98*b0d29bc4SBrooks Davis {
99*b0d29bc4SBrooks Davis std::cout.flush();
100*b0d29bc4SBrooks Davis std::cerr.flush();
101*b0d29bc4SBrooks Davis ::_exit(exit_code);
102*b0d29bc4SBrooks Davis }
103*b0d29bc4SBrooks Davis
104*b0d29bc4SBrooks Davis
105*b0d29bc4SBrooks Davis /// Subprocess that creates a cookie file in its work directory.
106*b0d29bc4SBrooks Davis class child_create_cookie {
107*b0d29bc4SBrooks Davis /// Name of the cookie to create.
108*b0d29bc4SBrooks Davis const std::string _cookie_name;
109*b0d29bc4SBrooks Davis
110*b0d29bc4SBrooks Davis public:
111*b0d29bc4SBrooks Davis /// Constructor.
112*b0d29bc4SBrooks Davis ///
113*b0d29bc4SBrooks Davis /// \param cookie_name Name of the cookie to create.
child_create_cookie(const std::string & cookie_name)114*b0d29bc4SBrooks Davis child_create_cookie(const std::string& cookie_name) :
115*b0d29bc4SBrooks Davis _cookie_name(cookie_name)
116*b0d29bc4SBrooks Davis {
117*b0d29bc4SBrooks Davis }
118*b0d29bc4SBrooks Davis
119*b0d29bc4SBrooks Davis /// Runs the subprocess.
120*b0d29bc4SBrooks Davis void
operator ()(const fs::path &)121*b0d29bc4SBrooks Davis operator()(const fs::path& /* control_directory */)
122*b0d29bc4SBrooks Davis UTILS_NORETURN
123*b0d29bc4SBrooks Davis {
124*b0d29bc4SBrooks Davis std::cout << "Creating cookie: " << _cookie_name << " (stdout)\n";
125*b0d29bc4SBrooks Davis std::cerr << "Creating cookie: " << _cookie_name << " (stderr)\n";
126*b0d29bc4SBrooks Davis atf::utils::create_file(_cookie_name, "");
127*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
128*b0d29bc4SBrooks Davis }
129*b0d29bc4SBrooks Davis };
130*b0d29bc4SBrooks Davis
131*b0d29bc4SBrooks Davis
132*b0d29bc4SBrooks Davis static void child_delete_all(const fs::path&) UTILS_NORETURN;
133*b0d29bc4SBrooks Davis
134*b0d29bc4SBrooks Davis
135*b0d29bc4SBrooks Davis /// Subprocess that deletes all files in the current directory.
136*b0d29bc4SBrooks Davis ///
137*b0d29bc4SBrooks Davis /// This is intended to validate that the test runs in an empty directory,
138*b0d29bc4SBrooks Davis /// separate from any control files that the executor may have created.
139*b0d29bc4SBrooks Davis ///
140*b0d29bc4SBrooks Davis /// \param control_directory Directory where control files separate from the
141*b0d29bc4SBrooks Davis /// work directory can be placed.
142*b0d29bc4SBrooks Davis static void
child_delete_all(const fs::path & control_directory)143*b0d29bc4SBrooks Davis child_delete_all(const fs::path& control_directory)
144*b0d29bc4SBrooks Davis {
145*b0d29bc4SBrooks Davis const fs::path cookie = control_directory / "exec_was_called";
146*b0d29bc4SBrooks Davis std::ofstream control_file(cookie.c_str());
147*b0d29bc4SBrooks Davis if (!control_file) {
148*b0d29bc4SBrooks Davis std::cerr << "Failed to create " << cookie << '\n';
149*b0d29bc4SBrooks Davis std::abort();
150*b0d29bc4SBrooks Davis }
151*b0d29bc4SBrooks Davis
152*b0d29bc4SBrooks Davis const int exit_code = ::system("rm *") == -1
153*b0d29bc4SBrooks Davis ? EXIT_FAILURE : EXIT_SUCCESS;
154*b0d29bc4SBrooks Davis do_exit(exit_code);
155*b0d29bc4SBrooks Davis }
156*b0d29bc4SBrooks Davis
157*b0d29bc4SBrooks Davis
158*b0d29bc4SBrooks Davis static void child_dump_unprivileged_user(const fs::path&) UTILS_NORETURN;
159*b0d29bc4SBrooks Davis
160*b0d29bc4SBrooks Davis
161*b0d29bc4SBrooks Davis /// Subprocess that dumps user configuration.
162*b0d29bc4SBrooks Davis static void
child_dump_unprivileged_user(const fs::path &)163*b0d29bc4SBrooks Davis child_dump_unprivileged_user(const fs::path& /* control_directory */)
164*b0d29bc4SBrooks Davis {
165*b0d29bc4SBrooks Davis const passwd::user current_user = passwd::current_user();
166*b0d29bc4SBrooks Davis std::cout << F("UID = %s\n") % current_user.uid;
167*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
168*b0d29bc4SBrooks Davis }
169*b0d29bc4SBrooks Davis
170*b0d29bc4SBrooks Davis
171*b0d29bc4SBrooks Davis /// Subprocess that returns a specific exit code.
172*b0d29bc4SBrooks Davis class child_exit {
173*b0d29bc4SBrooks Davis /// Exit code to return.
174*b0d29bc4SBrooks Davis int _exit_code;
175*b0d29bc4SBrooks Davis
176*b0d29bc4SBrooks Davis public:
177*b0d29bc4SBrooks Davis /// Constructor.
178*b0d29bc4SBrooks Davis ///
179*b0d29bc4SBrooks Davis /// \param exit_code Exit code to return.
child_exit(const int exit_code)180*b0d29bc4SBrooks Davis child_exit(const int exit_code) : _exit_code(exit_code)
181*b0d29bc4SBrooks Davis {
182*b0d29bc4SBrooks Davis }
183*b0d29bc4SBrooks Davis
184*b0d29bc4SBrooks Davis /// Runs the subprocess.
185*b0d29bc4SBrooks Davis void
operator ()(const fs::path &)186*b0d29bc4SBrooks Davis operator()(const fs::path& /* control_directory */)
187*b0d29bc4SBrooks Davis UTILS_NORETURN
188*b0d29bc4SBrooks Davis {
189*b0d29bc4SBrooks Davis do_exit(_exit_code);
190*b0d29bc4SBrooks Davis }
191*b0d29bc4SBrooks Davis };
192*b0d29bc4SBrooks Davis
193*b0d29bc4SBrooks Davis
194*b0d29bc4SBrooks Davis static void child_pause(const fs::path&) UTILS_NORETURN;
195*b0d29bc4SBrooks Davis
196*b0d29bc4SBrooks Davis
197*b0d29bc4SBrooks Davis /// Subprocess that just blocks.
198*b0d29bc4SBrooks Davis static void
child_pause(const fs::path &)199*b0d29bc4SBrooks Davis child_pause(const fs::path& /* control_directory */)
200*b0d29bc4SBrooks Davis {
201*b0d29bc4SBrooks Davis sigset_t mask;
202*b0d29bc4SBrooks Davis sigemptyset(&mask);
203*b0d29bc4SBrooks Davis for (;;) {
204*b0d29bc4SBrooks Davis ::sigsuspend(&mask);
205*b0d29bc4SBrooks Davis }
206*b0d29bc4SBrooks Davis std::abort();
207*b0d29bc4SBrooks Davis }
208*b0d29bc4SBrooks Davis
209*b0d29bc4SBrooks Davis
210*b0d29bc4SBrooks Davis static void child_print(const fs::path&) UTILS_NORETURN;
211*b0d29bc4SBrooks Davis
212*b0d29bc4SBrooks Davis
213*b0d29bc4SBrooks Davis /// Subprocess that writes to stdout and stderr.
214*b0d29bc4SBrooks Davis static void
child_print(const fs::path &)215*b0d29bc4SBrooks Davis child_print(const fs::path& /* control_directory */)
216*b0d29bc4SBrooks Davis {
217*b0d29bc4SBrooks Davis std::cout << "stdout: some text\n";
218*b0d29bc4SBrooks Davis std::cerr << "stderr: some other text\n";
219*b0d29bc4SBrooks Davis
220*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
221*b0d29bc4SBrooks Davis }
222*b0d29bc4SBrooks Davis
223*b0d29bc4SBrooks Davis
224*b0d29bc4SBrooks Davis /// Subprocess that sleeps for a period of time before exiting.
225*b0d29bc4SBrooks Davis class child_sleep {
226*b0d29bc4SBrooks Davis /// Seconds to sleep for before termination.
227*b0d29bc4SBrooks Davis int _seconds;
228*b0d29bc4SBrooks Davis
229*b0d29bc4SBrooks Davis public:
230*b0d29bc4SBrooks Davis /// Construtor.
231*b0d29bc4SBrooks Davis ///
232*b0d29bc4SBrooks Davis /// \param seconds Seconds to sleep for before termination.
child_sleep(const int seconds)233*b0d29bc4SBrooks Davis child_sleep(const int seconds) : _seconds(seconds)
234*b0d29bc4SBrooks Davis {
235*b0d29bc4SBrooks Davis }
236*b0d29bc4SBrooks Davis
237*b0d29bc4SBrooks Davis /// Runs the subprocess.
238*b0d29bc4SBrooks Davis void
operator ()(const fs::path &)239*b0d29bc4SBrooks Davis operator()(const fs::path& /* control_directory */)
240*b0d29bc4SBrooks Davis UTILS_NORETURN
241*b0d29bc4SBrooks Davis {
242*b0d29bc4SBrooks Davis ::sleep(_seconds);
243*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
244*b0d29bc4SBrooks Davis }
245*b0d29bc4SBrooks Davis };
246*b0d29bc4SBrooks Davis
247*b0d29bc4SBrooks Davis
248*b0d29bc4SBrooks Davis static void child_spawn_blocking_child(const fs::path&) UTILS_NORETURN;
249*b0d29bc4SBrooks Davis
250*b0d29bc4SBrooks Davis
251*b0d29bc4SBrooks Davis /// Subprocess that spawns a subchild that gets stuck.
252*b0d29bc4SBrooks Davis ///
253*b0d29bc4SBrooks Davis /// Used by the caller to validate that the whole process tree is terminated
254*b0d29bc4SBrooks Davis /// when this subprocess is killed.
255*b0d29bc4SBrooks Davis static void
child_spawn_blocking_child(const fs::path &)256*b0d29bc4SBrooks Davis child_spawn_blocking_child(
257*b0d29bc4SBrooks Davis const fs::path& /* control_directory */)
258*b0d29bc4SBrooks Davis {
259*b0d29bc4SBrooks Davis pid_t pid = ::fork();
260*b0d29bc4SBrooks Davis if (pid == -1) {
261*b0d29bc4SBrooks Davis std::cerr << "Cannot fork subprocess\n";
262*b0d29bc4SBrooks Davis do_exit(EXIT_FAILURE);
263*b0d29bc4SBrooks Davis } else if (pid == 0) {
264*b0d29bc4SBrooks Davis for (;;)
265*b0d29bc4SBrooks Davis ::pause();
266*b0d29bc4SBrooks Davis } else {
267*b0d29bc4SBrooks Davis const fs::path name = fs::path(utils::getenv("CONTROL_DIR").get()) /
268*b0d29bc4SBrooks Davis "pid";
269*b0d29bc4SBrooks Davis std::ofstream pidfile(name.c_str());
270*b0d29bc4SBrooks Davis if (!pidfile) {
271*b0d29bc4SBrooks Davis std::cerr << "Failed to create the pidfile\n";
272*b0d29bc4SBrooks Davis do_exit(EXIT_FAILURE);
273*b0d29bc4SBrooks Davis }
274*b0d29bc4SBrooks Davis pidfile << pid;
275*b0d29bc4SBrooks Davis pidfile.close();
276*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
277*b0d29bc4SBrooks Davis }
278*b0d29bc4SBrooks Davis }
279*b0d29bc4SBrooks Davis
280*b0d29bc4SBrooks Davis
281*b0d29bc4SBrooks Davis static void child_validate_isolation(const fs::path&) UTILS_NORETURN;
282*b0d29bc4SBrooks Davis
283*b0d29bc4SBrooks Davis
284*b0d29bc4SBrooks Davis /// Subprocess that checks if isolate_child() has been called.
285*b0d29bc4SBrooks Davis static void
child_validate_isolation(const fs::path &)286*b0d29bc4SBrooks Davis child_validate_isolation(const fs::path& /* control_directory */)
287*b0d29bc4SBrooks Davis {
288*b0d29bc4SBrooks Davis if (utils::getenv("HOME").get() == "fake-value") {
289*b0d29bc4SBrooks Davis std::cerr << "HOME not reset\n";
290*b0d29bc4SBrooks Davis do_exit(EXIT_FAILURE);
291*b0d29bc4SBrooks Davis }
292*b0d29bc4SBrooks Davis if (utils::getenv("LANG")) {
293*b0d29bc4SBrooks Davis std::cerr << "LANG not unset\n";
294*b0d29bc4SBrooks Davis do_exit(EXIT_FAILURE);
295*b0d29bc4SBrooks Davis }
296*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS);
297*b0d29bc4SBrooks Davis }
298*b0d29bc4SBrooks Davis
299*b0d29bc4SBrooks Davis
300*b0d29bc4SBrooks Davis /// Invokes executor::spawn() with default arguments.
301*b0d29bc4SBrooks Davis ///
302*b0d29bc4SBrooks Davis /// \param handle The executor on which to invoke spawn().
303*b0d29bc4SBrooks Davis /// \param args Arguments to the binary.
304*b0d29bc4SBrooks Davis /// \param timeout Maximum time the program can run for.
305*b0d29bc4SBrooks Davis /// \param unprivileged_user If set, user to switch to when running the child
306*b0d29bc4SBrooks Davis /// program.
307*b0d29bc4SBrooks Davis /// \param stdout_target If not none, file to which to write the stdout of the
308*b0d29bc4SBrooks Davis /// test case.
309*b0d29bc4SBrooks Davis /// \param stderr_target If not none, file to which to write the stderr of the
310*b0d29bc4SBrooks Davis /// test case.
311*b0d29bc4SBrooks Davis ///
312*b0d29bc4SBrooks Davis /// \return The exec handle for the spawned binary.
313*b0d29bc4SBrooks Davis template< class Hook >
314*b0d29bc4SBrooks Davis static executor::exec_handle
do_spawn(executor::executor_handle & handle,Hook hook,const datetime::delta & timeout=infinite_timeout,const optional<passwd::user> unprivileged_user=none,const optional<fs::path> stdout_target=none,const optional<fs::path> stderr_target=none)315*b0d29bc4SBrooks Davis do_spawn(executor::executor_handle& handle, Hook hook,
316*b0d29bc4SBrooks Davis const datetime::delta& timeout = infinite_timeout,
317*b0d29bc4SBrooks Davis const optional< passwd::user > unprivileged_user = none,
318*b0d29bc4SBrooks Davis const optional< fs::path > stdout_target = none,
319*b0d29bc4SBrooks Davis const optional< fs::path > stderr_target = none)
320*b0d29bc4SBrooks Davis {
321*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle = handle.spawn< Hook >(
322*b0d29bc4SBrooks Davis hook, timeout, unprivileged_user, stdout_target, stderr_target);
323*b0d29bc4SBrooks Davis return exec_handle;
324*b0d29bc4SBrooks Davis }
325*b0d29bc4SBrooks Davis
326*b0d29bc4SBrooks Davis
327*b0d29bc4SBrooks Davis /// Checks for a specific exit status in the status of a exit_handle.
328*b0d29bc4SBrooks Davis ///
329*b0d29bc4SBrooks Davis /// \param exit_status The expected exit status.
330*b0d29bc4SBrooks Davis /// \param status The value of exit_handle.status().
331*b0d29bc4SBrooks Davis ///
332*b0d29bc4SBrooks Davis /// \post Terminates the calling test case if the status does not match the
333*b0d29bc4SBrooks Davis /// required value.
334*b0d29bc4SBrooks Davis static void
require_exit(const int exit_status,const optional<process::status> status)335*b0d29bc4SBrooks Davis require_exit(const int exit_status, const optional< process::status > status)
336*b0d29bc4SBrooks Davis {
337*b0d29bc4SBrooks Davis ATF_REQUIRE(status);
338*b0d29bc4SBrooks Davis ATF_REQUIRE(status.get().exited());
339*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_status, status.get().exitstatus());
340*b0d29bc4SBrooks Davis }
341*b0d29bc4SBrooks Davis
342*b0d29bc4SBrooks Davis
343*b0d29bc4SBrooks Davis /// Ensures that a killed process is gone.
344*b0d29bc4SBrooks Davis ///
345*b0d29bc4SBrooks Davis /// The way we do this is by sending an idempotent signal to the given PID
346*b0d29bc4SBrooks Davis /// and checking if the signal was delivered. If it was, the process is
347*b0d29bc4SBrooks Davis /// still alive; if it was not, then it is gone.
348*b0d29bc4SBrooks Davis ///
349*b0d29bc4SBrooks Davis /// Note that this might be inaccurate for two reasons:
350*b0d29bc4SBrooks Davis ///
351*b0d29bc4SBrooks Davis /// 1) The system may have spawned a new process with the same pid as
352*b0d29bc4SBrooks Davis /// our subchild... but in practice, this does not happen because
353*b0d29bc4SBrooks Davis /// most systems do not immediately reuse pid numbers. If that
354*b0d29bc4SBrooks Davis /// happens... well, we get a false test failure.
355*b0d29bc4SBrooks Davis ///
356*b0d29bc4SBrooks Davis /// 2) We ran so fast that even if the process was sent a signal to
357*b0d29bc4SBrooks Davis /// die, it has not had enough time to process it yet. This is why
358*b0d29bc4SBrooks Davis /// we retry this a few times.
359*b0d29bc4SBrooks Davis ///
360*b0d29bc4SBrooks Davis /// \param pid PID of the process to check.
361*b0d29bc4SBrooks Davis static void
ensure_dead(const pid_t pid)362*b0d29bc4SBrooks Davis ensure_dead(const pid_t pid)
363*b0d29bc4SBrooks Davis {
364*b0d29bc4SBrooks Davis int attempts = 30;
365*b0d29bc4SBrooks Davis retry:
366*b0d29bc4SBrooks Davis if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
367*b0d29bc4SBrooks Davis if (attempts > 0) {
368*b0d29bc4SBrooks Davis std::cout << "Subprocess not dead yet; retrying wait\n";
369*b0d29bc4SBrooks Davis --attempts;
370*b0d29bc4SBrooks Davis ::usleep(100000);
371*b0d29bc4SBrooks Davis goto retry;
372*b0d29bc4SBrooks Davis }
373*b0d29bc4SBrooks Davis ATF_FAIL(F("The subprocess %s of our child was not killed") % pid);
374*b0d29bc4SBrooks Davis }
375*b0d29bc4SBrooks Davis }
376*b0d29bc4SBrooks Davis
377*b0d29bc4SBrooks Davis
378*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_one);
ATF_TEST_CASE_BODY(integration__run_one)379*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__run_one)
380*b0d29bc4SBrooks Davis {
381*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
382*b0d29bc4SBrooks Davis
383*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle = do_spawn(handle, child_exit(41));
384*b0d29bc4SBrooks Davis
385*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
386*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
387*b0d29bc4SBrooks Davis require_exit(41, exit_handle.status());
388*b0d29bc4SBrooks Davis exit_handle.cleanup();
389*b0d29bc4SBrooks Davis
390*b0d29bc4SBrooks Davis handle.cleanup();
391*b0d29bc4SBrooks Davis }
392*b0d29bc4SBrooks Davis
393*b0d29bc4SBrooks Davis
394*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_many);
ATF_TEST_CASE_BODY(integration__run_many)395*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__run_many)
396*b0d29bc4SBrooks Davis {
397*b0d29bc4SBrooks Davis static const std::size_t num_children = 30;
398*b0d29bc4SBrooks Davis
399*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
400*b0d29bc4SBrooks Davis
401*b0d29bc4SBrooks Davis std::size_t total_children = 0;
402*b0d29bc4SBrooks Davis std::map< int, int > exp_exit_statuses;
403*b0d29bc4SBrooks Davis std::map< int, datetime::timestamp > exp_start_times;
404*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
405*b0d29bc4SBrooks Davis const datetime::timestamp start_time = datetime::timestamp::from_values(
406*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 40, 0, i);
407*b0d29bc4SBrooks Davis
408*b0d29bc4SBrooks Davis for (std::size_t j = 0; j < 3; j++) {
409*b0d29bc4SBrooks Davis const std::size_t id = i * 3 + j;
410*b0d29bc4SBrooks Davis
411*b0d29bc4SBrooks Davis datetime::set_mock_now(start_time);
412*b0d29bc4SBrooks Davis const int pid = do_spawn(handle, child_exit(id)).pid();
413*b0d29bc4SBrooks Davis exp_exit_statuses.insert(std::make_pair(pid, id));
414*b0d29bc4SBrooks Davis exp_start_times.insert(std::make_pair(pid, start_time));
415*b0d29bc4SBrooks Davis ++total_children;
416*b0d29bc4SBrooks Davis }
417*b0d29bc4SBrooks Davis }
418*b0d29bc4SBrooks Davis
419*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < total_children; ++i) {
420*b0d29bc4SBrooks Davis const datetime::timestamp end_time = datetime::timestamp::from_values(
421*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 50, 10, i);
422*b0d29bc4SBrooks Davis datetime::set_mock_now(end_time);
423*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
424*b0d29bc4SBrooks Davis const int original_pid = exit_handle.original_pid();
425*b0d29bc4SBrooks Davis
426*b0d29bc4SBrooks Davis const int exit_status = exp_exit_statuses.find(original_pid)->second;
427*b0d29bc4SBrooks Davis const datetime::timestamp& start_time = exp_start_times.find(
428*b0d29bc4SBrooks Davis original_pid)->second;
429*b0d29bc4SBrooks Davis
430*b0d29bc4SBrooks Davis require_exit(exit_status, exit_handle.status());
431*b0d29bc4SBrooks Davis
432*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(start_time, exit_handle.start_time());
433*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(end_time, exit_handle.end_time());
434*b0d29bc4SBrooks Davis
435*b0d29bc4SBrooks Davis exit_handle.cleanup();
436*b0d29bc4SBrooks Davis
437*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
438*b0d29bc4SBrooks Davis exit_handle.stdout_file().str()));
439*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
440*b0d29bc4SBrooks Davis exit_handle.stderr_file().str()));
441*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
442*b0d29bc4SBrooks Davis exit_handle.work_directory().str()));
443*b0d29bc4SBrooks Davis }
444*b0d29bc4SBrooks Davis
445*b0d29bc4SBrooks Davis handle.cleanup();
446*b0d29bc4SBrooks Davis }
447*b0d29bc4SBrooks Davis
448*b0d29bc4SBrooks Davis
449*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__parameters_and_output);
ATF_TEST_CASE_BODY(integration__parameters_and_output)450*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__parameters_and_output)
451*b0d29bc4SBrooks Davis {
452*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
453*b0d29bc4SBrooks Davis
454*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle = do_spawn(handle, child_print);
455*b0d29bc4SBrooks Davis
456*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
457*b0d29bc4SBrooks Davis
458*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
459*b0d29bc4SBrooks Davis
460*b0d29bc4SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
461*b0d29bc4SBrooks Davis
462*b0d29bc4SBrooks Davis const fs::path stdout_file = exit_handle.stdout_file();
463*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
464*b0d29bc4SBrooks Davis stdout_file.str(), "stdout: some text\n"));
465*b0d29bc4SBrooks Davis const fs::path stderr_file = exit_handle.stderr_file();
466*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
467*b0d29bc4SBrooks Davis stderr_file.str(), "stderr: some other text\n"));
468*b0d29bc4SBrooks Davis
469*b0d29bc4SBrooks Davis exit_handle.cleanup();
470*b0d29bc4SBrooks Davis ATF_REQUIRE(!fs::exists(stdout_file));
471*b0d29bc4SBrooks Davis ATF_REQUIRE(!fs::exists(stderr_file));
472*b0d29bc4SBrooks Davis
473*b0d29bc4SBrooks Davis handle.cleanup();
474*b0d29bc4SBrooks Davis }
475*b0d29bc4SBrooks Davis
476*b0d29bc4SBrooks Davis
477*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__custom_output_files);
ATF_TEST_CASE_BODY(integration__custom_output_files)478*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__custom_output_files)
479*b0d29bc4SBrooks Davis {
480*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
481*b0d29bc4SBrooks Davis
482*b0d29bc4SBrooks Davis const fs::path stdout_file("custom-stdout.txt");
483*b0d29bc4SBrooks Davis const fs::path stderr_file("custom-stderr.txt");
484*b0d29bc4SBrooks Davis
485*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle = do_spawn(
486*b0d29bc4SBrooks Davis handle, child_print, infinite_timeout, none,
487*b0d29bc4SBrooks Davis utils::make_optional(stdout_file),
488*b0d29bc4SBrooks Davis utils::make_optional(stderr_file));
489*b0d29bc4SBrooks Davis
490*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
491*b0d29bc4SBrooks Davis
492*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
493*b0d29bc4SBrooks Davis
494*b0d29bc4SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
495*b0d29bc4SBrooks Davis
496*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(stdout_file, exit_handle.stdout_file());
497*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(stderr_file, exit_handle.stderr_file());
498*b0d29bc4SBrooks Davis
499*b0d29bc4SBrooks Davis exit_handle.cleanup();
500*b0d29bc4SBrooks Davis
501*b0d29bc4SBrooks Davis handle.cleanup();
502*b0d29bc4SBrooks Davis
503*b0d29bc4SBrooks Davis // Must compare after cleanup to ensure the files did not get deleted.
504*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
505*b0d29bc4SBrooks Davis stdout_file.str(), "stdout: some text\n"));
506*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
507*b0d29bc4SBrooks Davis stderr_file.str(), "stderr: some other text\n"));
508*b0d29bc4SBrooks Davis }
509*b0d29bc4SBrooks Davis
510*b0d29bc4SBrooks Davis
511*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__timestamps);
ATF_TEST_CASE_BODY(integration__timestamps)512*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__timestamps)
513*b0d29bc4SBrooks Davis {
514*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
515*b0d29bc4SBrooks Davis
516*b0d29bc4SBrooks Davis const datetime::timestamp start_time = datetime::timestamp::from_values(
517*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 35, 10, 1000);
518*b0d29bc4SBrooks Davis const datetime::timestamp end_time = datetime::timestamp::from_values(
519*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 35, 20, 2000);
520*b0d29bc4SBrooks Davis
521*b0d29bc4SBrooks Davis datetime::set_mock_now(start_time);
522*b0d29bc4SBrooks Davis do_spawn(handle, child_exit(70));
523*b0d29bc4SBrooks Davis
524*b0d29bc4SBrooks Davis datetime::set_mock_now(end_time);
525*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
526*b0d29bc4SBrooks Davis
527*b0d29bc4SBrooks Davis require_exit(70, exit_handle.status());
528*b0d29bc4SBrooks Davis
529*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(start_time, exit_handle.start_time());
530*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(end_time, exit_handle.end_time());
531*b0d29bc4SBrooks Davis exit_handle.cleanup();
532*b0d29bc4SBrooks Davis
533*b0d29bc4SBrooks Davis handle.cleanup();
534*b0d29bc4SBrooks Davis }
535*b0d29bc4SBrooks Davis
536*b0d29bc4SBrooks Davis
537*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__files);
ATF_TEST_CASE_BODY(integration__files)538*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__files)
539*b0d29bc4SBrooks Davis {
540*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
541*b0d29bc4SBrooks Davis
542*b0d29bc4SBrooks Davis do_spawn(handle, child_create_cookie("cookie.12345"));
543*b0d29bc4SBrooks Davis
544*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
545*b0d29bc4SBrooks Davis
546*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
547*b0d29bc4SBrooks Davis (exit_handle.work_directory() / "cookie.12345").str()));
548*b0d29bc4SBrooks Davis
549*b0d29bc4SBrooks Davis exit_handle.cleanup();
550*b0d29bc4SBrooks Davis
551*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.stdout_file().str()));
552*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.stderr_file().str()));
553*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.work_directory().str()));
554*b0d29bc4SBrooks Davis
555*b0d29bc4SBrooks Davis handle.cleanup();
556*b0d29bc4SBrooks Davis }
557*b0d29bc4SBrooks Davis
558*b0d29bc4SBrooks Davis
559*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__followup);
ATF_TEST_CASE_BODY(integration__followup)560*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__followup)
561*b0d29bc4SBrooks Davis {
562*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
563*b0d29bc4SBrooks Davis
564*b0d29bc4SBrooks Davis (void)handle.spawn(child_create_cookie("cookie.1"), infinite_timeout, none);
565*b0d29bc4SBrooks Davis executor::exit_handle exit_1_handle = handle.wait_any();
566*b0d29bc4SBrooks Davis
567*b0d29bc4SBrooks Davis (void)handle.spawn_followup(child_create_cookie("cookie.2"), exit_1_handle,
568*b0d29bc4SBrooks Davis infinite_timeout);
569*b0d29bc4SBrooks Davis executor::exit_handle exit_2_handle = handle.wait_any();
570*b0d29bc4SBrooks Davis
571*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stdout_file(), exit_2_handle.stdout_file());
572*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stderr_file(), exit_2_handle.stderr_file());
573*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.control_directory(),
574*b0d29bc4SBrooks Davis exit_2_handle.control_directory());
575*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.work_directory(),
576*b0d29bc4SBrooks Davis exit_2_handle.work_directory());
577*b0d29bc4SBrooks Davis
578*b0d29bc4SBrooks Davis (void)handle.spawn_followup(child_create_cookie("cookie.3"), exit_2_handle,
579*b0d29bc4SBrooks Davis infinite_timeout);
580*b0d29bc4SBrooks Davis exit_2_handle.cleanup();
581*b0d29bc4SBrooks Davis exit_1_handle.cleanup();
582*b0d29bc4SBrooks Davis executor::exit_handle exit_3_handle = handle.wait_any();
583*b0d29bc4SBrooks Davis
584*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stdout_file(), exit_3_handle.stdout_file());
585*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stderr_file(), exit_3_handle.stderr_file());
586*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.control_directory(),
587*b0d29bc4SBrooks Davis exit_3_handle.control_directory());
588*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.work_directory(),
589*b0d29bc4SBrooks Davis exit_3_handle.work_directory());
590*b0d29bc4SBrooks Davis
591*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
592*b0d29bc4SBrooks Davis (exit_1_handle.work_directory() / "cookie.1").str()));
593*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
594*b0d29bc4SBrooks Davis (exit_1_handle.work_directory() / "cookie.2").str()));
595*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
596*b0d29bc4SBrooks Davis (exit_1_handle.work_directory() / "cookie.3").str()));
597*b0d29bc4SBrooks Davis
598*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
599*b0d29bc4SBrooks Davis exit_1_handle.stdout_file().str(),
600*b0d29bc4SBrooks Davis "Creating cookie: cookie.1 (stdout)\n"
601*b0d29bc4SBrooks Davis "Creating cookie: cookie.2 (stdout)\n"
602*b0d29bc4SBrooks Davis "Creating cookie: cookie.3 (stdout)\n"));
603*b0d29bc4SBrooks Davis
604*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
605*b0d29bc4SBrooks Davis exit_1_handle.stderr_file().str(),
606*b0d29bc4SBrooks Davis "Creating cookie: cookie.1 (stderr)\n"
607*b0d29bc4SBrooks Davis "Creating cookie: cookie.2 (stderr)\n"
608*b0d29bc4SBrooks Davis "Creating cookie: cookie.3 (stderr)\n"));
609*b0d29bc4SBrooks Davis
610*b0d29bc4SBrooks Davis exit_3_handle.cleanup();
611*b0d29bc4SBrooks Davis
612*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.stdout_file().str()));
613*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.stderr_file().str()));
614*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.work_directory().str()));
615*b0d29bc4SBrooks Davis
616*b0d29bc4SBrooks Davis handle.cleanup();
617*b0d29bc4SBrooks Davis }
618*b0d29bc4SBrooks Davis
619*b0d29bc4SBrooks Davis
620*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__output_files_always_exist);
ATF_TEST_CASE_BODY(integration__output_files_always_exist)621*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__output_files_always_exist)
622*b0d29bc4SBrooks Davis {
623*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
624*b0d29bc4SBrooks Davis
625*b0d29bc4SBrooks Davis // This test is racy: we specify a very short timeout for the subprocess so
626*b0d29bc4SBrooks Davis // that we cause the subprocess to exit before it has had time to set up the
627*b0d29bc4SBrooks Davis // output files. However, for scheduling reasons, the subprocess may
628*b0d29bc4SBrooks Davis // actually run to completion before the timer triggers. Retry this a few
629*b0d29bc4SBrooks Davis // times to attempt to catch a "good test".
630*b0d29bc4SBrooks Davis for (int i = 0; i < 50; i++) {
631*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle =
632*b0d29bc4SBrooks Davis do_spawn(handle, child_exit(0), datetime::delta(0, 100000));
633*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait(exec_handle);
634*b0d29bc4SBrooks Davis ATF_REQUIRE(fs::exists(exit_handle.stdout_file()));
635*b0d29bc4SBrooks Davis ATF_REQUIRE(fs::exists(exit_handle.stderr_file()));
636*b0d29bc4SBrooks Davis exit_handle.cleanup();
637*b0d29bc4SBrooks Davis }
638*b0d29bc4SBrooks Davis
639*b0d29bc4SBrooks Davis handle.cleanup();
640*b0d29bc4SBrooks Davis }
641*b0d29bc4SBrooks Davis
642*b0d29bc4SBrooks Davis
643*b0d29bc4SBrooks Davis ATF_TEST_CASE(integration__timeouts);
ATF_TEST_CASE_HEAD(integration__timeouts)644*b0d29bc4SBrooks Davis ATF_TEST_CASE_HEAD(integration__timeouts)
645*b0d29bc4SBrooks Davis {
646*b0d29bc4SBrooks Davis set_md_var("timeout", "60");
647*b0d29bc4SBrooks Davis }
ATF_TEST_CASE_BODY(integration__timeouts)648*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__timeouts)
649*b0d29bc4SBrooks Davis {
650*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
651*b0d29bc4SBrooks Davis
652*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle1 =
653*b0d29bc4SBrooks Davis do_spawn(handle, child_sleep(30), datetime::delta(2, 0));
654*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle2 =
655*b0d29bc4SBrooks Davis do_spawn(handle, child_sleep(40), datetime::delta(5, 0));
656*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle3 =
657*b0d29bc4SBrooks Davis do_spawn(handle, child_exit(15));
658*b0d29bc4SBrooks Davis
659*b0d29bc4SBrooks Davis {
660*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
661*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle3.pid(), exit_handle.original_pid());
662*b0d29bc4SBrooks Davis require_exit(15, exit_handle.status());
663*b0d29bc4SBrooks Davis exit_handle.cleanup();
664*b0d29bc4SBrooks Davis }
665*b0d29bc4SBrooks Davis
666*b0d29bc4SBrooks Davis {
667*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
668*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle1.pid(), exit_handle.original_pid());
669*b0d29bc4SBrooks Davis ATF_REQUIRE(!exit_handle.status());
670*b0d29bc4SBrooks Davis const datetime::delta duration =
671*b0d29bc4SBrooks Davis exit_handle.end_time() - exit_handle.start_time();
672*b0d29bc4SBrooks Davis ATF_REQUIRE(duration < datetime::delta(10, 0));
673*b0d29bc4SBrooks Davis ATF_REQUIRE(duration >= datetime::delta(2, 0));
674*b0d29bc4SBrooks Davis exit_handle.cleanup();
675*b0d29bc4SBrooks Davis }
676*b0d29bc4SBrooks Davis
677*b0d29bc4SBrooks Davis {
678*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
679*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle2.pid(), exit_handle.original_pid());
680*b0d29bc4SBrooks Davis ATF_REQUIRE(!exit_handle.status());
681*b0d29bc4SBrooks Davis const datetime::delta duration =
682*b0d29bc4SBrooks Davis exit_handle.end_time() - exit_handle.start_time();
683*b0d29bc4SBrooks Davis ATF_REQUIRE(duration < datetime::delta(10, 0));
684*b0d29bc4SBrooks Davis ATF_REQUIRE(duration >= datetime::delta(4, 0));
685*b0d29bc4SBrooks Davis exit_handle.cleanup();
686*b0d29bc4SBrooks Davis }
687*b0d29bc4SBrooks Davis
688*b0d29bc4SBrooks Davis handle.cleanup();
689*b0d29bc4SBrooks Davis }
690*b0d29bc4SBrooks Davis
691*b0d29bc4SBrooks Davis
692*b0d29bc4SBrooks Davis ATF_TEST_CASE(integration__unprivileged_user);
ATF_TEST_CASE_HEAD(integration__unprivileged_user)693*b0d29bc4SBrooks Davis ATF_TEST_CASE_HEAD(integration__unprivileged_user)
694*b0d29bc4SBrooks Davis {
695*b0d29bc4SBrooks Davis set_md_var("require.config", "unprivileged-user");
696*b0d29bc4SBrooks Davis set_md_var("require.user", "root");
697*b0d29bc4SBrooks Davis }
ATF_TEST_CASE_BODY(integration__unprivileged_user)698*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__unprivileged_user)
699*b0d29bc4SBrooks Davis {
700*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
701*b0d29bc4SBrooks Davis
702*b0d29bc4SBrooks Davis const passwd::user unprivileged_user = passwd::find_user_by_name(
703*b0d29bc4SBrooks Davis get_config_var("unprivileged-user"));
704*b0d29bc4SBrooks Davis
705*b0d29bc4SBrooks Davis do_spawn(handle, child_dump_unprivileged_user,
706*b0d29bc4SBrooks Davis infinite_timeout, utils::make_optional(unprivileged_user));
707*b0d29bc4SBrooks Davis
708*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
709*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
710*b0d29bc4SBrooks Davis exit_handle.stdout_file().str(),
711*b0d29bc4SBrooks Davis F("UID = %s\n") % unprivileged_user.uid));
712*b0d29bc4SBrooks Davis exit_handle.cleanup();
713*b0d29bc4SBrooks Davis
714*b0d29bc4SBrooks Davis handle.cleanup();
715*b0d29bc4SBrooks Davis }
716*b0d29bc4SBrooks Davis
717*b0d29bc4SBrooks Davis
718*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__auto_cleanup);
ATF_TEST_CASE_BODY(integration__auto_cleanup)719*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__auto_cleanup)
720*b0d29bc4SBrooks Davis {
721*b0d29bc4SBrooks Davis std::vector< int > pids;
722*b0d29bc4SBrooks Davis std::vector< fs::path > paths;
723*b0d29bc4SBrooks Davis {
724*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
725*b0d29bc4SBrooks Davis
726*b0d29bc4SBrooks Davis pids.push_back(do_spawn(handle, child_exit(10)).pid());
727*b0d29bc4SBrooks Davis pids.push_back(do_spawn(handle, child_exit(20)).pid());
728*b0d29bc4SBrooks Davis
729*b0d29bc4SBrooks Davis // This invocation is never waited for below. This is intentional: we
730*b0d29bc4SBrooks Davis // want the destructor to clean the "leaked" test automatically so that
731*b0d29bc4SBrooks Davis // the clean up of the parent work directory also happens correctly.
732*b0d29bc4SBrooks Davis pids.push_back(do_spawn(handle, child_pause).pid());
733*b0d29bc4SBrooks Davis
734*b0d29bc4SBrooks Davis executor::exit_handle exit_handle1 = handle.wait_any();
735*b0d29bc4SBrooks Davis paths.push_back(exit_handle1.stdout_file());
736*b0d29bc4SBrooks Davis paths.push_back(exit_handle1.stderr_file());
737*b0d29bc4SBrooks Davis paths.push_back(exit_handle1.work_directory());
738*b0d29bc4SBrooks Davis
739*b0d29bc4SBrooks Davis executor::exit_handle exit_handle2 = handle.wait_any();
740*b0d29bc4SBrooks Davis paths.push_back(exit_handle2.stdout_file());
741*b0d29bc4SBrooks Davis paths.push_back(exit_handle2.stderr_file());
742*b0d29bc4SBrooks Davis paths.push_back(exit_handle2.work_directory());
743*b0d29bc4SBrooks Davis }
744*b0d29bc4SBrooks Davis for (std::vector< int >::const_iterator iter = pids.begin();
745*b0d29bc4SBrooks Davis iter != pids.end(); ++iter) {
746*b0d29bc4SBrooks Davis ensure_dead(*iter);
747*b0d29bc4SBrooks Davis }
748*b0d29bc4SBrooks Davis for (std::vector< fs::path >::const_iterator iter = paths.begin();
749*b0d29bc4SBrooks Davis iter != paths.end(); ++iter) {
750*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists((*iter).str()));
751*b0d29bc4SBrooks Davis }
752*b0d29bc4SBrooks Davis }
753*b0d29bc4SBrooks Davis
754*b0d29bc4SBrooks Davis
755*b0d29bc4SBrooks Davis /// Ensures that interrupting an executor cleans things up correctly.
756*b0d29bc4SBrooks Davis ///
757*b0d29bc4SBrooks Davis /// This test scenario is tricky. We spawn a master child process that runs the
758*b0d29bc4SBrooks Davis /// executor code and we send a signal to it externally. The child process
759*b0d29bc4SBrooks Davis /// spawns a bunch of tests that block indefinitely and tries to wait for their
760*b0d29bc4SBrooks Davis /// results. When the signal is received, we expect an interrupt_error to be
761*b0d29bc4SBrooks Davis /// raised, which in turn should clean up all test resources and exit the master
762*b0d29bc4SBrooks Davis /// child process successfully.
763*b0d29bc4SBrooks Davis ///
764*b0d29bc4SBrooks Davis /// \param signo Signal to deliver to the executor.
765*b0d29bc4SBrooks Davis static void
do_signal_handling_test(const int signo)766*b0d29bc4SBrooks Davis do_signal_handling_test(const int signo)
767*b0d29bc4SBrooks Davis {
768*b0d29bc4SBrooks Davis static const char* cookie = "spawned.txt";
769*b0d29bc4SBrooks Davis
770*b0d29bc4SBrooks Davis const pid_t pid = ::fork();
771*b0d29bc4SBrooks Davis ATF_REQUIRE(pid != -1);
772*b0d29bc4SBrooks Davis if (pid == 0) {
773*b0d29bc4SBrooks Davis static const std::size_t num_children = 3;
774*b0d29bc4SBrooks Davis
775*b0d29bc4SBrooks Davis optional< fs::path > root_work_directory;
776*b0d29bc4SBrooks Davis try {
777*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
778*b0d29bc4SBrooks Davis root_work_directory = handle.root_work_directory();
779*b0d29bc4SBrooks Davis
780*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
781*b0d29bc4SBrooks Davis std::cout << "Spawned child number " << i << '\n';
782*b0d29bc4SBrooks Davis do_spawn(handle, child_pause);
783*b0d29bc4SBrooks Davis }
784*b0d29bc4SBrooks Davis
785*b0d29bc4SBrooks Davis std::cout << "Creating " << cookie << " cookie\n";
786*b0d29bc4SBrooks Davis atf::utils::create_file(cookie, "");
787*b0d29bc4SBrooks Davis
788*b0d29bc4SBrooks Davis std::cout << "Waiting for subprocess termination\n";
789*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
790*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
791*b0d29bc4SBrooks Davis // We may never reach this point in the test, but if we do let's
792*b0d29bc4SBrooks Davis // make sure the subprocess was terminated as expected.
793*b0d29bc4SBrooks Davis if (exit_handle.status()) {
794*b0d29bc4SBrooks Davis if (exit_handle.status().get().signaled() &&
795*b0d29bc4SBrooks Davis exit_handle.status().get().termsig() == SIGKILL) {
796*b0d29bc4SBrooks Davis // OK.
797*b0d29bc4SBrooks Davis } else {
798*b0d29bc4SBrooks Davis std::cerr << "Child exited with unexpected code: "
799*b0d29bc4SBrooks Davis << exit_handle.status().get();
800*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE);
801*b0d29bc4SBrooks Davis }
802*b0d29bc4SBrooks Davis } else {
803*b0d29bc4SBrooks Davis std::cerr << "Child timed out\n";
804*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE);
805*b0d29bc4SBrooks Davis }
806*b0d29bc4SBrooks Davis exit_handle.cleanup();
807*b0d29bc4SBrooks Davis }
808*b0d29bc4SBrooks Davis std::cerr << "Terminating without reception of signal\n";
809*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE);
810*b0d29bc4SBrooks Davis } catch (const signals::interrupted_error& unused_error) {
811*b0d29bc4SBrooks Davis std::cerr << "Terminating due to interrupted_error\n";
812*b0d29bc4SBrooks Davis // We never kill ourselves until the cookie is created, so it is
813*b0d29bc4SBrooks Davis // guaranteed that the optional root_work_directory has been
814*b0d29bc4SBrooks Davis // initialized at this point.
815*b0d29bc4SBrooks Davis if (atf::utils::file_exists(root_work_directory.get().str())) {
816*b0d29bc4SBrooks Davis // Some cleanup did not happen; error out.
817*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE);
818*b0d29bc4SBrooks Davis } else {
819*b0d29bc4SBrooks Davis std::exit(EXIT_SUCCESS);
820*b0d29bc4SBrooks Davis }
821*b0d29bc4SBrooks Davis }
822*b0d29bc4SBrooks Davis std::abort();
823*b0d29bc4SBrooks Davis }
824*b0d29bc4SBrooks Davis
825*b0d29bc4SBrooks Davis std::cout << "Waiting for " << cookie << " cookie creation\n";
826*b0d29bc4SBrooks Davis while (!atf::utils::file_exists(cookie)) {
827*b0d29bc4SBrooks Davis // Wait for processes.
828*b0d29bc4SBrooks Davis }
829*b0d29bc4SBrooks Davis ATF_REQUIRE(::unlink(cookie) != -1);
830*b0d29bc4SBrooks Davis std::cout << "Killing process\n";
831*b0d29bc4SBrooks Davis ATF_REQUIRE(::kill(pid, signo) != -1);
832*b0d29bc4SBrooks Davis
833*b0d29bc4SBrooks Davis int status;
834*b0d29bc4SBrooks Davis std::cout << "Waiting for process termination\n";
835*b0d29bc4SBrooks Davis ATF_REQUIRE(::waitpid(pid, &status, 0) != -1);
836*b0d29bc4SBrooks Davis ATF_REQUIRE(WIFEXITED(status));
837*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
838*b0d29bc4SBrooks Davis }
839*b0d29bc4SBrooks Davis
840*b0d29bc4SBrooks Davis
841*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__signal_handling);
ATF_TEST_CASE_BODY(integration__signal_handling)842*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__signal_handling)
843*b0d29bc4SBrooks Davis {
844*b0d29bc4SBrooks Davis // This test scenario is racy so run it multiple times to have higher
845*b0d29bc4SBrooks Davis // chances of exposing problems.
846*b0d29bc4SBrooks Davis const std::size_t rounds = 20;
847*b0d29bc4SBrooks Davis
848*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < rounds; ++i) {
849*b0d29bc4SBrooks Davis std::cout << F("Testing round %s\n") % i;
850*b0d29bc4SBrooks Davis do_signal_handling_test(SIGHUP);
851*b0d29bc4SBrooks Davis do_signal_handling_test(SIGINT);
852*b0d29bc4SBrooks Davis do_signal_handling_test(SIGTERM);
853*b0d29bc4SBrooks Davis }
854*b0d29bc4SBrooks Davis }
855*b0d29bc4SBrooks Davis
856*b0d29bc4SBrooks Davis
857*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__isolate_child_is_called);
ATF_TEST_CASE_BODY(integration__isolate_child_is_called)858*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__isolate_child_is_called)
859*b0d29bc4SBrooks Davis {
860*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
861*b0d29bc4SBrooks Davis
862*b0d29bc4SBrooks Davis utils::setenv("HOME", "fake-value");
863*b0d29bc4SBrooks Davis utils::setenv("LANG", "es_ES");
864*b0d29bc4SBrooks Davis do_spawn(handle, child_validate_isolation);
865*b0d29bc4SBrooks Davis
866*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
867*b0d29bc4SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
868*b0d29bc4SBrooks Davis exit_handle.cleanup();
869*b0d29bc4SBrooks Davis
870*b0d29bc4SBrooks Davis handle.cleanup();
871*b0d29bc4SBrooks Davis }
872*b0d29bc4SBrooks Davis
873*b0d29bc4SBrooks Davis
874*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__process_group_is_terminated);
ATF_TEST_CASE_BODY(integration__process_group_is_terminated)875*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__process_group_is_terminated)
876*b0d29bc4SBrooks Davis {
877*b0d29bc4SBrooks Davis utils::setenv("CONTROL_DIR", fs::current_path().str());
878*b0d29bc4SBrooks Davis
879*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
880*b0d29bc4SBrooks Davis do_spawn(handle, child_spawn_blocking_child);
881*b0d29bc4SBrooks Davis
882*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
883*b0d29bc4SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
884*b0d29bc4SBrooks Davis exit_handle.cleanup();
885*b0d29bc4SBrooks Davis
886*b0d29bc4SBrooks Davis handle.cleanup();
887*b0d29bc4SBrooks Davis
888*b0d29bc4SBrooks Davis if (!fs::exists(fs::path("pid")))
889*b0d29bc4SBrooks Davis fail("The pid file was not created");
890*b0d29bc4SBrooks Davis
891*b0d29bc4SBrooks Davis std::ifstream pidfile("pid");
892*b0d29bc4SBrooks Davis ATF_REQUIRE(pidfile);
893*b0d29bc4SBrooks Davis pid_t pid;
894*b0d29bc4SBrooks Davis pidfile >> pid;
895*b0d29bc4SBrooks Davis pidfile.close();
896*b0d29bc4SBrooks Davis
897*b0d29bc4SBrooks Davis ensure_dead(pid);
898*b0d29bc4SBrooks Davis }
899*b0d29bc4SBrooks Davis
900*b0d29bc4SBrooks Davis
901*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__prevent_clobbering_control_files);
ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)902*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)
903*b0d29bc4SBrooks Davis {
904*b0d29bc4SBrooks Davis executor::executor_handle handle = executor::setup();
905*b0d29bc4SBrooks Davis
906*b0d29bc4SBrooks Davis do_spawn(handle, child_delete_all);
907*b0d29bc4SBrooks Davis
908*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
909*b0d29bc4SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
910*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
911*b0d29bc4SBrooks Davis (exit_handle.control_directory() / "exec_was_called").str()));
912*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
913*b0d29bc4SBrooks Davis (exit_handle.work_directory() / "exec_was_called").str()));
914*b0d29bc4SBrooks Davis exit_handle.cleanup();
915*b0d29bc4SBrooks Davis
916*b0d29bc4SBrooks Davis handle.cleanup();
917*b0d29bc4SBrooks Davis }
918*b0d29bc4SBrooks Davis
919*b0d29bc4SBrooks Davis
ATF_INIT_TEST_CASES(tcs)920*b0d29bc4SBrooks Davis ATF_INIT_TEST_CASES(tcs)
921*b0d29bc4SBrooks Davis {
922*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_one);
923*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_many);
924*b0d29bc4SBrooks Davis
925*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__parameters_and_output);
926*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__custom_output_files);
927*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__timestamps);
928*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__files);
929*b0d29bc4SBrooks Davis
930*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__followup);
931*b0d29bc4SBrooks Davis
932*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__output_files_always_exist);
933*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__timeouts);
934*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__unprivileged_user);
935*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__auto_cleanup);
936*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__signal_handling);
937*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__isolate_child_is_called);
938*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__process_group_is_terminated);
939*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__prevent_clobbering_control_files);
940*b0d29bc4SBrooks Davis }
941