1 // Copyright 2010 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/child.ipp" 30 31 extern "C" { 32 #include <sys/stat.h> 33 #include <sys/wait.h> 34 35 #include <fcntl.h> 36 #include <signal.h> 37 #include <unistd.h> 38 } 39 40 #include <cstdarg> 41 #include <cerrno> 42 #include <cstdlib> 43 #include <cstring> 44 #include <fstream> 45 #include <iostream> 46 #include <stdexcept> 47 48 #include <atf-c++.hpp> 49 50 #include "utils/defs.hpp" 51 #include "utils/env.hpp" 52 #include "utils/format/macros.hpp" 53 #include "utils/fs/operations.hpp" 54 #include "utils/fs/path.hpp" 55 #include "utils/logging/macros.hpp" 56 #include "utils/process/exceptions.hpp" 57 #include "utils/process/status.hpp" 58 #include "utils/process/system.hpp" 59 #include "utils/sanity.hpp" 60 #include "utils/test_utils.ipp" 61 62 namespace fs = utils::fs; 63 namespace logging = utils::logging; 64 namespace process = utils::process; 65 66 67 namespace { 68 69 70 /// Checks if the current subprocess is in its own session. 71 static void 72 child_check_own_session(void) 73 { 74 std::exit((::getsid(::getpid()) == ::getpid()) ? 75 EXIT_SUCCESS : EXIT_FAILURE); 76 } 77 78 79 /// Body for a process that prints a simple message and exits. 80 /// 81 /// \tparam ExitStatus The exit status for the subprocess. 82 /// \tparam Message A single character that will be prepended to the printed 83 /// messages. This would ideally be a string, but we cannot templatize a 84 /// function with an object nor a pointer. 85 template< int ExitStatus, char Message > 86 static void 87 child_simple_function(void) 88 { 89 std::cout << "To stdout: " << Message << "\n"; 90 std::cerr << "To stderr: " << Message << "\n"; 91 std::exit(ExitStatus); 92 } 93 94 95 /// Functor for the body of a process that prints a simple message and exits. 96 class child_simple_functor { 97 /// The exit status that the subprocess will yield. 98 int _exitstatus; 99 100 /// The message to print on stdout and stderr. 101 std::string _message; 102 103 public: 104 /// Constructs a new functor. 105 /// 106 /// \param exitstatus The exit status that the subprocess will yield. 107 /// \param message The message to print on stdout and stderr. 108 child_simple_functor(const int exitstatus, const std::string& message) : 109 _exitstatus(exitstatus), 110 _message(message) 111 { 112 } 113 114 /// Body for the subprocess. 115 void 116 operator()(void) 117 { 118 std::cout << "To stdout: " << _message << "\n"; 119 std::cerr << "To stderr: " << _message << "\n"; 120 std::exit(_exitstatus); 121 } 122 }; 123 124 125 /// Body for a process that prints many messages to stdout and exits. 126 /// 127 /// The goal of this body is to validate that any buffering performed on the 128 /// parent process to read the output of the subprocess works correctly. 129 static void 130 child_printer_function(void) 131 { 132 for (std::size_t i = 0; i < 100; i++) 133 std::cout << "This is a message to stdout, sequence " << i << "\n"; 134 std::cout.flush(); 135 std::cerr << "Exiting\n"; 136 std::exit(EXIT_SUCCESS); 137 } 138 139 140 /// Functor for the body of a process that runs child_printer_function. 141 class child_printer_functor { 142 public: 143 /// Body for the subprocess. 144 void 145 operator()(void) 146 { 147 child_printer_function(); 148 } 149 }; 150 151 152 /// Body for a child process that throws an exception. 153 static void 154 child_throw_exception(void) 155 { 156 throw std::runtime_error("A loose exception"); 157 } 158 159 160 /// Body for a child process that creates a pidfile. 161 static void 162 child_write_pid(void) 163 { 164 std::ofstream output("pidfile"); 165 output << ::getpid() << "\n"; 166 output.close(); 167 std::exit(EXIT_SUCCESS); 168 } 169 170 171 /// A child process that returns. 172 /// 173 /// The fork() wrappers are supposed to capture this condition and terminate the 174 /// child before the code returns to the fork() call point. 175 static void 176 child_return(void) 177 { 178 } 179 180 181 /// A child process that raises an exception. 182 /// 183 /// The fork() wrappers are supposed to capture this condition and terminate the 184 /// child before the code returns to the fork() call point. 185 /// 186 /// \tparam Type The type of the exception to raise. 187 /// \tparam Value The value passed to the constructor of the exception type. In 188 /// general, this only makes sense if Type is a primitive type so that, in 189 /// the end, the code becomes "throw int(123)". 190 /// 191 /// \throw Type An exception of the provided type. 192 template< class Type, Type Value > 193 void 194 child_raise_exception(void) 195 { 196 throw Type(Value); 197 } 198 199 200 /// Calculates the path to the test helpers binary. 201 /// 202 /// \param tc A pointer to the caller test case, needed to extract the value of 203 /// the "srcdir" property. 204 /// 205 /// \return The path to the helpers binary. 206 static fs::path 207 get_helpers(const atf::tests::tc* tc) 208 { 209 return fs::path(tc->get_config_var("srcdir")) / "helpers"; 210 } 211 212 213 /// Mock fork(2) that just returns an error. 214 /// 215 /// \tparam Errno The value to set as the errno of the failed call. 216 /// 217 /// \return Always -1. 218 template< int Errno > 219 static pid_t 220 fork_fail(void) throw() 221 { 222 errno = Errno; 223 return -1; 224 } 225 226 227 /// Mock open(2) that fails if the 'raise-error' file is opened. 228 /// 229 /// \tparam Errno The value to set as the errno if the known failure triggers. 230 /// \param path The path to the file to be opened. 231 /// \param flags The open flags. 232 /// \param ... The file mode creation, if flags contains O_CREAT. 233 /// 234 /// \return The opened file handle or -1 on error. 235 template< int Errno > 236 static int 237 open_fail(const char* path, const int flags, ...) throw() 238 { 239 if (std::strcmp(path, "raise-error") == 0) { 240 errno = Errno; 241 return -1; 242 } else { 243 va_list ap; 244 va_start(ap, flags); 245 const int mode = va_arg(ap, int); 246 va_end(ap); 247 return ::open(path, flags, mode); 248 } 249 } 250 251 252 /// Mock pipe(2) that just returns an error. 253 /// 254 /// \tparam Errno The value to set as the errno of the failed call. 255 /// 256 /// \return Always -1. 257 template< int Errno > 258 static pid_t 259 pipe_fail(int* /* fildes */) throw() 260 { 261 errno = Errno; 262 return -1; 263 } 264 265 266 /// Helper for child tests to validate inheritance of stdout/stderr. 267 /// 268 /// This function ensures that passing one of /dev/stdout or /dev/stderr to 269 /// the child__fork_files fork method does the right thing. The idea is that we 270 /// call fork with the given parameters and then make our child redirect one of 271 /// its file descriptors to a specific file without going through the process 272 /// library. We then validate if this redirection worked and got the expected 273 /// output. 274 /// 275 /// \param fork_stdout The path to pass to the fork call as the stdout file. 276 /// \param fork_stderr The path to pass to the fork call as the stderr file. 277 /// \param child_file The file to explicitly in the subchild. 278 /// \param child_fd The file descriptor to which to attach child_file. 279 static void 280 do_inherit_test(const char* fork_stdout, const char* fork_stderr, 281 const char* child_file, const int child_fd) 282 { 283 const pid_t pid = ::fork(); 284 ATF_REQUIRE(pid != -1); 285 if (pid == 0) { 286 logging::set_inmemory(); 287 288 const int fd = ::open(child_file, O_CREAT | O_WRONLY | O_TRUNC, 0644); 289 if (fd != child_fd) { 290 if (::dup2(fd, child_fd) == -1) 291 std::abort(); 292 ::close(fd); 293 } 294 295 std::auto_ptr< process::child > child = process::child::fork_files( 296 child_simple_function< 123, 'Z' >, 297 fs::path(fork_stdout), fs::path(fork_stderr)); 298 const process::status status = child->wait(); 299 if (!status.exited() || status.exitstatus() != 123) 300 std::abort(); 301 std::exit(EXIT_SUCCESS); 302 } else { 303 int status; 304 ATF_REQUIRE(::waitpid(pid, &status, 0) != -1); 305 ATF_REQUIRE(WIFEXITED(status)); 306 ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 307 ATF_REQUIRE(atf::utils::grep_file("stdout: Z", "stdout.txt")); 308 ATF_REQUIRE(atf::utils::grep_file("stderr: Z", "stderr.txt")); 309 } 310 } 311 312 313 /// Performs a "child__fork_capture__ok_*" test. 314 /// 315 /// This test basically ensures that the child__fork_capture class spawns a 316 /// process whose output is captured in an input stream. 317 /// 318 /// \tparam Hook The type of the fork hook to use. 319 /// \param hook The hook to the fork call. 320 template< class Hook > 321 static void 322 child__fork_capture__ok(Hook hook) 323 { 324 std::cout << "This unflushed message should not propagate to the child"; 325 std::cerr << "This unflushed message should not propagate to the child"; 326 std::auto_ptr< process::child > child = process::child::fork_capture(hook); 327 std::cout.flush(); 328 std::cerr.flush(); 329 330 std::istream& output = child->output(); 331 for (std::size_t i = 0; i < 100; i++) { 332 std::string line; 333 ATF_REQUIRE(std::getline(output, line).good()); 334 ATF_REQUIRE_EQ((F("This is a message to stdout, " 335 "sequence %s") % i).str(), line); 336 } 337 338 std::string line; 339 ATF_REQUIRE(std::getline(output, line).good()); 340 ATF_REQUIRE_EQ("Exiting", line); 341 342 process::status status = child->wait(); 343 ATF_REQUIRE(status.exited()); 344 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 345 } 346 347 348 } // anonymous namespace 349 350 351 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_function); 352 ATF_TEST_CASE_BODY(child__fork_capture__ok_function) 353 { 354 child__fork_capture__ok(child_printer_function); 355 } 356 357 358 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_functor); 359 ATF_TEST_CASE_BODY(child__fork_capture__ok_functor) 360 { 361 child__fork_capture__ok(child_printer_functor()); 362 } 363 364 365 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__catch_exceptions); 366 ATF_TEST_CASE_BODY(child__fork_capture__catch_exceptions) 367 { 368 std::auto_ptr< process::child > child = process::child::fork_capture( 369 child_throw_exception); 370 371 std::string message; 372 std::istream& output = child->output(); 373 ATF_REQUIRE(std::getline(output, message).good()); 374 375 const process::status status = child->wait(); 376 ATF_REQUIRE(status.signaled()); 377 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 378 379 ATF_REQUIRE_MATCH("Caught.*A loose exception", message); 380 } 381 382 383 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__new_session); 384 ATF_TEST_CASE_BODY(child__fork_capture__new_session) 385 { 386 std::auto_ptr< process::child > child = process::child::fork_capture( 387 child_check_own_session); 388 const process::status status = child->wait(); 389 ATF_REQUIRE(status.exited()); 390 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 391 } 392 393 394 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__pipe_fail); 395 ATF_TEST_CASE_BODY(child__fork_capture__pipe_fail) 396 { 397 process::detail::syscall_pipe = pipe_fail< 23 >; 398 try { 399 process::child::fork_capture(child_simple_function< 1, 'A' >); 400 fail("Expected exception but none raised"); 401 } catch (const process::system_error& e) { 402 ATF_REQUIRE(atf::utils::grep_string("pipe.*failed", e.what())); 403 ATF_REQUIRE_EQ(23, e.original_errno()); 404 } 405 } 406 407 408 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_exit); 409 ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_exit) 410 { 411 const pid_t parent_pid = ::getpid(); 412 atf::utils::create_file("to-not-be-deleted", ""); 413 414 std::auto_ptr< process::child > child = process::child::fork_capture( 415 child_return); 416 if (::getpid() != parent_pid) { 417 // If we enter this clause, it is because the hook returned. 418 ::unlink("to-not-be-deleted"); 419 std::exit(EXIT_SUCCESS); 420 } 421 422 const process::status status = child->wait(); 423 ATF_REQUIRE(status.signaled()); 424 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 425 } 426 427 428 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_unwind); 429 ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_unwind) 430 { 431 const pid_t parent_pid = ::getpid(); 432 atf::utils::create_file("to-not-be-deleted", ""); 433 try { 434 std::auto_ptr< process::child > child = process::child::fork_capture( 435 child_raise_exception< int, 123 >); 436 const process::status status = child->wait(); 437 ATF_REQUIRE(status.signaled()); 438 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 439 } catch (const int i) { 440 // If we enter this clause, it is because an exception leaked from the 441 // hook. 442 INV(parent_pid != ::getpid()); 443 INV(i == 123); 444 ::unlink("to-not-be-deleted"); 445 std::exit(EXIT_SUCCESS); 446 } 447 } 448 449 450 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_fail); 451 ATF_TEST_CASE_BODY(child__fork_capture__fork_fail) 452 { 453 process::detail::syscall_fork = fork_fail< 89 >; 454 try { 455 process::child::fork_capture(child_simple_function< 1, 'A' >); 456 fail("Expected exception but none raised"); 457 } catch (const process::system_error& e) { 458 ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what())); 459 ATF_REQUIRE_EQ(89, e.original_errno()); 460 } 461 } 462 463 464 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_function); 465 ATF_TEST_CASE_BODY(child__fork_files__ok_function) 466 { 467 const fs::path file1("file1.txt"); 468 const fs::path file2("file2.txt"); 469 470 std::auto_ptr< process::child > child = process::child::fork_files( 471 child_simple_function< 15, 'Z' >, file1, file2); 472 const process::status status = child->wait(); 473 ATF_REQUIRE(status.exited()); 474 ATF_REQUIRE_EQ(15, status.exitstatus()); 475 476 ATF_REQUIRE( atf::utils::grep_file("^To stdout: Z$", file1.str())); 477 ATF_REQUIRE(!atf::utils::grep_file("^To stdout: Z$", file2.str())); 478 479 ATF_REQUIRE( atf::utils::grep_file("^To stderr: Z$", file2.str())); 480 ATF_REQUIRE(!atf::utils::grep_file("^To stderr: Z$", file1.str())); 481 } 482 483 484 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_functor); 485 ATF_TEST_CASE_BODY(child__fork_files__ok_functor) 486 { 487 const fs::path filea("fileA.txt"); 488 const fs::path fileb("fileB.txt"); 489 490 atf::utils::create_file(filea.str(), "Initial stdout\n"); 491 atf::utils::create_file(fileb.str(), "Initial stderr\n"); 492 493 std::auto_ptr< process::child > child = process::child::fork_files( 494 child_simple_functor(16, "a functor"), filea, fileb); 495 const process::status status = child->wait(); 496 ATF_REQUIRE(status.exited()); 497 ATF_REQUIRE_EQ(16, status.exitstatus()); 498 499 ATF_REQUIRE( atf::utils::grep_file("^Initial stdout$", filea.str())); 500 ATF_REQUIRE(!atf::utils::grep_file("^Initial stdout$", fileb.str())); 501 502 ATF_REQUIRE( atf::utils::grep_file("^To stdout: a functor$", filea.str())); 503 ATF_REQUIRE(!atf::utils::grep_file("^To stdout: a functor$", fileb.str())); 504 505 ATF_REQUIRE( atf::utils::grep_file("^Initial stderr$", fileb.str())); 506 ATF_REQUIRE(!atf::utils::grep_file("^Initial stderr$", filea.str())); 507 508 ATF_REQUIRE( atf::utils::grep_file("^To stderr: a functor$", fileb.str())); 509 ATF_REQUIRE(!atf::utils::grep_file("^To stderr: a functor$", filea.str())); 510 } 511 512 513 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__catch_exceptions); 514 ATF_TEST_CASE_BODY(child__fork_files__catch_exceptions) 515 { 516 std::auto_ptr< process::child > child = process::child::fork_files( 517 child_throw_exception, 518 fs::path("unused.out"), fs::path("stderr")); 519 520 const process::status status = child->wait(); 521 ATF_REQUIRE(status.signaled()); 522 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 523 524 ATF_REQUIRE(atf::utils::grep_file("Caught.*A loose exception", "stderr")); 525 } 526 527 528 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__new_session); 529 ATF_TEST_CASE_BODY(child__fork_files__new_session) 530 { 531 std::auto_ptr< process::child > child = process::child::fork_files( 532 child_check_own_session, 533 fs::path("unused.out"), fs::path("unused.err")); 534 const process::status status = child->wait(); 535 ATF_REQUIRE(status.exited()); 536 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 537 } 538 539 540 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stdout); 541 ATF_TEST_CASE_BODY(child__fork_files__inherit_stdout) 542 { 543 do_inherit_test("/dev/stdout", "stderr.txt", "stdout.txt", STDOUT_FILENO); 544 } 545 546 547 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stderr); 548 ATF_TEST_CASE_BODY(child__fork_files__inherit_stderr) 549 { 550 do_inherit_test("stdout.txt", "/dev/stderr", "stderr.txt", STDERR_FILENO); 551 } 552 553 554 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_exit); 555 ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_exit) 556 { 557 const pid_t parent_pid = ::getpid(); 558 atf::utils::create_file("to-not-be-deleted", ""); 559 560 std::auto_ptr< process::child > child = process::child::fork_files( 561 child_return, fs::path("out"), fs::path("err")); 562 if (::getpid() != parent_pid) { 563 // If we enter this clause, it is because the hook returned. 564 ::unlink("to-not-be-deleted"); 565 std::exit(EXIT_SUCCESS); 566 } 567 568 const process::status status = child->wait(); 569 ATF_REQUIRE(status.signaled()); 570 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 571 } 572 573 574 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_unwind); 575 ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_unwind) 576 { 577 const pid_t parent_pid = ::getpid(); 578 atf::utils::create_file("to-not-be-deleted", ""); 579 try { 580 std::auto_ptr< process::child > child = process::child::fork_files( 581 child_raise_exception< int, 123 >, fs::path("out"), 582 fs::path("err")); 583 const process::status status = child->wait(); 584 ATF_REQUIRE(status.signaled()); 585 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 586 } catch (const int i) { 587 // If we enter this clause, it is because an exception leaked from the 588 // hook. 589 INV(parent_pid != ::getpid()); 590 INV(i == 123); 591 ::unlink("to-not-be-deleted"); 592 std::exit(EXIT_SUCCESS); 593 } 594 } 595 596 597 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_fail); 598 ATF_TEST_CASE_BODY(child__fork_files__fork_fail) 599 { 600 process::detail::syscall_fork = fork_fail< 1234 >; 601 try { 602 process::child::fork_files(child_simple_function< 1, 'A' >, 603 fs::path("a.txt"), fs::path("b.txt")); 604 fail("Expected exception but none raised"); 605 } catch (const process::system_error& e) { 606 ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what())); 607 ATF_REQUIRE_EQ(1234, e.original_errno()); 608 } 609 ATF_REQUIRE(!fs::exists(fs::path("a.txt"))); 610 ATF_REQUIRE(!fs::exists(fs::path("b.txt"))); 611 } 612 613 614 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stdout_fail); 615 ATF_TEST_CASE_BODY(child__fork_files__create_stdout_fail) 616 { 617 process::detail::syscall_open = open_fail< ENOENT >; 618 std::auto_ptr< process::child > child = process::child::fork_files( 619 child_simple_function< 1, 'A' >, fs::path("raise-error"), 620 fs::path("created")); 621 const process::status status = child->wait(); 622 ATF_REQUIRE(status.signaled()); 623 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 624 ATF_REQUIRE(!fs::exists(fs::path("raise-error"))); 625 ATF_REQUIRE(!fs::exists(fs::path("created"))); 626 } 627 628 629 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stderr_fail); 630 ATF_TEST_CASE_BODY(child__fork_files__create_stderr_fail) 631 { 632 process::detail::syscall_open = open_fail< ENOENT >; 633 std::auto_ptr< process::child > child = process::child::fork_files( 634 child_simple_function< 1, 'A' >, fs::path("created"), 635 fs::path("raise-error")); 636 const process::status status = child->wait(); 637 ATF_REQUIRE(status.signaled()); 638 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 639 ATF_REQUIRE(fs::exists(fs::path("created"))); 640 ATF_REQUIRE(!fs::exists(fs::path("raise-error"))); 641 } 642 643 644 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__absolute_path); 645 ATF_TEST_CASE_BODY(child__spawn__absolute_path) 646 { 647 std::vector< std::string > args; 648 args.push_back("return-code"); 649 args.push_back("12"); 650 651 const fs::path program = get_helpers(this); 652 INV(program.is_absolute()); 653 std::auto_ptr< process::child > child = process::child::spawn_files( 654 program, args, fs::path("out"), fs::path("err")); 655 656 const process::status status = child->wait(); 657 ATF_REQUIRE(status.exited()); 658 ATF_REQUIRE_EQ(12, status.exitstatus()); 659 } 660 661 662 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__relative_path); 663 ATF_TEST_CASE_BODY(child__spawn__relative_path) 664 { 665 std::vector< std::string > args; 666 args.push_back("return-code"); 667 args.push_back("13"); 668 669 ATF_REQUIRE(::mkdir("root", 0755) != -1); 670 ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "root/helpers") != -1); 671 672 std::auto_ptr< process::child > child = process::child::spawn_files( 673 fs::path("root/helpers"), args, fs::path("out"), fs::path("err")); 674 675 const process::status status = child->wait(); 676 ATF_REQUIRE(status.exited()); 677 ATF_REQUIRE_EQ(13, status.exitstatus()); 678 } 679 680 681 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__basename_only); 682 ATF_TEST_CASE_BODY(child__spawn__basename_only) 683 { 684 std::vector< std::string > args; 685 args.push_back("return-code"); 686 args.push_back("14"); 687 688 ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "helpers") != -1); 689 690 std::auto_ptr< process::child > child = process::child::spawn_files( 691 fs::path("helpers"), args, fs::path("out"), fs::path("err")); 692 693 const process::status status = child->wait(); 694 ATF_REQUIRE(status.exited()); 695 ATF_REQUIRE_EQ(14, status.exitstatus()); 696 } 697 698 699 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_path); 700 ATF_TEST_CASE_BODY(child__spawn__no_path) 701 { 702 logging::set_inmemory(); 703 704 std::vector< std::string > args; 705 args.push_back("return-code"); 706 args.push_back("14"); 707 708 const fs::path helpers = get_helpers(this); 709 utils::setenv("PATH", helpers.branch_path().c_str()); 710 std::auto_ptr< process::child > child = process::child::spawn_capture( 711 fs::path(helpers.leaf_name()), args); 712 713 std::string line; 714 ATF_REQUIRE(std::getline(child->output(), line).good()); 715 ATF_REQUIRE_MATCH("Failed to execute", line); 716 ATF_REQUIRE(!std::getline(child->output(), line)); 717 718 const process::status status = child->wait(); 719 ATF_REQUIRE(status.signaled()); 720 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 721 } 722 723 724 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_args); 725 ATF_TEST_CASE_BODY(child__spawn__no_args) 726 { 727 std::vector< std::string > args; 728 std::auto_ptr< process::child > child = process::child::spawn_capture( 729 get_helpers(this), args); 730 731 std::string line; 732 ATF_REQUIRE(std::getline(child->output(), line).good()); 733 ATF_REQUIRE_EQ("Must provide a helper name", line); 734 ATF_REQUIRE(!std::getline(child->output(), line)); 735 736 const process::status status = child->wait(); 737 ATF_REQUIRE(status.exited()); 738 ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus()); 739 } 740 741 742 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__some_args); 743 ATF_TEST_CASE_BODY(child__spawn__some_args) 744 { 745 std::vector< std::string > args; 746 args.push_back("print-args"); 747 args.push_back("foo"); 748 args.push_back(" bar baz "); 749 std::auto_ptr< process::child > child = process::child::spawn_capture( 750 get_helpers(this), args); 751 752 std::string line; 753 ATF_REQUIRE(std::getline(child->output(), line).good()); 754 ATF_REQUIRE_EQ("argv[0] = " + get_helpers(this).str(), line); 755 ATF_REQUIRE(std::getline(child->output(), line).good()); 756 ATF_REQUIRE_EQ("argv[1] = print-args", line); 757 ATF_REQUIRE(std::getline(child->output(), line)); 758 ATF_REQUIRE_EQ("argv[2] = foo", line); 759 ATF_REQUIRE(std::getline(child->output(), line)); 760 ATF_REQUIRE_EQ("argv[3] = bar baz ", line); 761 ATF_REQUIRE(std::getline(child->output(), line)); 762 ATF_REQUIRE_EQ("argv[4] = NULL", line); 763 ATF_REQUIRE(!std::getline(child->output(), line)); 764 765 const process::status status = child->wait(); 766 ATF_REQUIRE(status.exited()); 767 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 768 } 769 770 771 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__missing_program); 772 ATF_TEST_CASE_BODY(child__spawn__missing_program) 773 { 774 std::vector< std::string > args; 775 std::auto_ptr< process::child > child = process::child::spawn_capture( 776 fs::path("a/b/c"), args); 777 778 std::string line; 779 ATF_REQUIRE(std::getline(child->output(), line).good()); 780 const std::string exp = "Failed to execute a/b/c: "; 781 ATF_REQUIRE_EQ(exp, line.substr(0, exp.length())); 782 ATF_REQUIRE(!std::getline(child->output(), line)); 783 784 const process::status status = child->wait(); 785 ATF_REQUIRE(status.signaled()); 786 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 787 } 788 789 790 ATF_TEST_CASE_WITHOUT_HEAD(child__pid); 791 ATF_TEST_CASE_BODY(child__pid) 792 { 793 std::auto_ptr< process::child > child = process::child::fork_capture( 794 child_write_pid); 795 796 const int pid = child->pid(); 797 798 const process::status status = child->wait(); 799 ATF_REQUIRE(status.exited()); 800 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 801 802 std::ifstream input("pidfile"); 803 ATF_REQUIRE(input); 804 int read_pid; 805 input >> read_pid; 806 input.close(); 807 808 ATF_REQUIRE_EQ(read_pid, pid); 809 } 810 811 812 ATF_INIT_TEST_CASES(tcs) 813 { 814 utils::avoid_coredump_on_crash(); 815 816 ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_function); 817 ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_functor); 818 ATF_ADD_TEST_CASE(tcs, child__fork_capture__catch_exceptions); 819 ATF_ADD_TEST_CASE(tcs, child__fork_capture__new_session); 820 ATF_ADD_TEST_CASE(tcs, child__fork_capture__pipe_fail); 821 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_exit); 822 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_unwind); 823 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_fail); 824 825 ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_function); 826 ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_functor); 827 ATF_ADD_TEST_CASE(tcs, child__fork_files__catch_exceptions); 828 ATF_ADD_TEST_CASE(tcs, child__fork_files__new_session); 829 ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stdout); 830 ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stderr); 831 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_exit); 832 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_unwind); 833 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_fail); 834 ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stdout_fail); 835 ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stderr_fail); 836 837 ATF_ADD_TEST_CASE(tcs, child__spawn__absolute_path); 838 ATF_ADD_TEST_CASE(tcs, child__spawn__relative_path); 839 ATF_ADD_TEST_CASE(tcs, child__spawn__basename_only); 840 ATF_ADD_TEST_CASE(tcs, child__spawn__no_path); 841 ATF_ADD_TEST_CASE(tcs, child__spawn__no_args); 842 ATF_ADD_TEST_CASE(tcs, child__spawn__some_args); 843 ATF_ADD_TEST_CASE(tcs, child__spawn__missing_program); 844 845 ATF_ADD_TEST_CASE(tcs, child__pid); 846 } 847