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 31extern "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 64namespace datetime = utils::datetime; 65namespace executor = utils::process::executor; 66namespace fs = utils::fs; 67namespace passwd = utils::passwd; 68namespace process = utils::process; 69namespace signals = utils::signals; 70namespace text = utils::text; 71 72using utils::none; 73using 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. 80static const datetime::delta infinite_timeout(1000000, 0); 81 82 83static 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. 96static void 97do_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. 106class child_create_cookie { 107 /// Name of the cookie to create. 108 const std::string _cookie_name; 109 110public: 111 /// Constructor. 112 /// 113 /// \param cookie_name Name of the cookie to create. 114 child_create_cookie(const std::string& cookie_name) : 115 _cookie_name(cookie_name) 116 { 117 } 118 119 /// Runs the subprocess. 120 void 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 132static 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. 142static void 143child_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 158static void child_dump_unprivileged_user(const fs::path&) UTILS_NORETURN; 159 160 161/// Subprocess that dumps user configuration. 162static void 163child_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. 172class child_exit { 173 /// Exit code to return. 174 int _exit_code; 175 176public: 177 /// Constructor. 178 /// 179 /// \param exit_code Exit code to return. 180 child_exit(const int exit_code) : _exit_code(exit_code) 181 { 182 } 183 184 /// Runs the subprocess. 185 void 186 operator()(const fs::path& /* control_directory */) 187 UTILS_NORETURN 188 { 189 do_exit(_exit_code); 190 } 191}; 192 193 194static void child_pause(const fs::path&) UTILS_NORETURN; 195 196 197/// Subprocess that just blocks. 198static void 199child_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 210static void child_print(const fs::path&) UTILS_NORETURN; 211 212 213/// Subprocess that writes to stdout and stderr. 214static void 215child_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. 225class child_sleep { 226 /// Seconds to sleep for before termination. 227 int _seconds; 228 229public: 230 /// Construtor. 231 /// 232 /// \param seconds Seconds to sleep for before termination. 233 child_sleep(const int seconds) : _seconds(seconds) 234 { 235 } 236 237 /// Runs the subprocess. 238 void 239 operator()(const fs::path& /* control_directory */) 240 UTILS_NORETURN 241 { 242 ::sleep(_seconds); 243 do_exit(EXIT_SUCCESS); 244 } 245}; 246 247 248static 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. 255static void 256child_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 281static void child_validate_isolation(const fs::path&) UTILS_NORETURN; 282 283 284/// Subprocess that checks if isolate_child() has been called. 285static void 286child_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. 313template< class Hook > 314static executor::exec_handle 315do_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. 334static void 335require_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. 361static void 362ensure_dead(const pid_t pid) 363{ 364 int attempts = 30; 365retry: 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 378ATF_TEST_CASE_WITHOUT_HEAD(integration__run_one); 379ATF_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 394ATF_TEST_CASE_WITHOUT_HEAD(integration__run_many); 395ATF_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 449ATF_TEST_CASE_WITHOUT_HEAD(integration__parameters_and_output); 450ATF_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 477ATF_TEST_CASE_WITHOUT_HEAD(integration__custom_output_files); 478ATF_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 511ATF_TEST_CASE_WITHOUT_HEAD(integration__timestamps); 512ATF_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 537ATF_TEST_CASE_WITHOUT_HEAD(integration__files); 538ATF_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 559ATF_TEST_CASE_WITHOUT_HEAD(integration__followup); 560ATF_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 620ATF_TEST_CASE_WITHOUT_HEAD(integration__output_files_always_exist); 621ATF_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 643ATF_TEST_CASE(integration__timeouts); 644ATF_TEST_CASE_HEAD(integration__timeouts) 645{ 646 set_md_var("timeout", "60"); 647} 648ATF_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 692ATF_TEST_CASE(integration__unprivileged_user); 693ATF_TEST_CASE_HEAD(integration__unprivileged_user) 694{ 695 set_md_var("require.config", "unprivileged-user"); 696 set_md_var("require.user", "root"); 697} 698ATF_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 718ATF_TEST_CASE_WITHOUT_HEAD(integration__auto_cleanup); 719ATF_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. 765static void 766do_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 841ATF_TEST_CASE_WITHOUT_HEAD(integration__signal_handling); 842ATF_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 857ATF_TEST_CASE_WITHOUT_HEAD(integration__isolate_child_is_called); 858ATF_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 874ATF_TEST_CASE_WITHOUT_HEAD(integration__process_group_is_terminated); 875ATF_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 901ATF_TEST_CASE_WITHOUT_HEAD(integration__prevent_clobbering_control_files); 902ATF_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 920ATF_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