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