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