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
child_check_own_session(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
child_simple_function(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.
child_simple_functor(const int exitstatus,const std::string & message)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
operator ()(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
child_printer_function(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
operator ()(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
child_throw_exception(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
child_write_pid(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
child_return(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
child_raise_exception(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
get_helpers(const atf::tests::tc * tc)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
fork_fail(void)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
open_fail(const char * path,const int flags,...)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
pipe_fail(int *)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
do_inherit_test(const char * fork_stdout,const char * fork_stderr,const char * child_file,const int child_fd)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
child__fork_capture__ok(Hook hook)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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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
ATF_INIT_TEST_CASES(tcs)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