xref: /freebsd/contrib/kyua/utils/process/operations_test.cpp (revision b392a90ba4e5ea07d8a88a834fd102191d1967bf)
1b0d29bc4SBrooks Davis // Copyright 2014 The Kyua Authors.
2b0d29bc4SBrooks Davis // All rights reserved.
3b0d29bc4SBrooks Davis //
4b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6b0d29bc4SBrooks Davis // met:
7b0d29bc4SBrooks Davis //
8b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer.
10b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
12b0d29bc4SBrooks Davis //   documentation and/or other materials provided with the distribution.
13b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14b0d29bc4SBrooks Davis //   may be used to endorse or promote products derived from this software
15b0d29bc4SBrooks Davis //   without specific prior written permission.
16b0d29bc4SBrooks Davis //
17b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28b0d29bc4SBrooks Davis 
29b0d29bc4SBrooks Davis #include "utils/process/operations.hpp"
30b0d29bc4SBrooks Davis 
31b0d29bc4SBrooks Davis extern "C" {
32b0d29bc4SBrooks Davis #include <sys/types.h>
33b0d29bc4SBrooks Davis #include <sys/wait.h>
34b0d29bc4SBrooks Davis 
35b0d29bc4SBrooks Davis #include <signal.h>
36b0d29bc4SBrooks Davis #include <unistd.h>
37b0d29bc4SBrooks Davis }
38b0d29bc4SBrooks Davis 
39b0d29bc4SBrooks Davis #include <cerrno>
40b0d29bc4SBrooks Davis #include <iostream>
41b0d29bc4SBrooks Davis 
42b0d29bc4SBrooks Davis #include <atf-c++.hpp>
43b0d29bc4SBrooks Davis 
44b0d29bc4SBrooks Davis #include "utils/defs.hpp"
45b0d29bc4SBrooks Davis #include "utils/format/containers.ipp"
46b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
47b0d29bc4SBrooks Davis #include "utils/process/child.ipp"
48b0d29bc4SBrooks Davis #include "utils/process/exceptions.hpp"
49b0d29bc4SBrooks Davis #include "utils/process/status.hpp"
50b0d29bc4SBrooks Davis #include "utils/stacktrace.hpp"
51b0d29bc4SBrooks Davis #include "utils/test_utils.ipp"
52b0d29bc4SBrooks Davis 
53b0d29bc4SBrooks Davis namespace fs = utils::fs;
54b0d29bc4SBrooks Davis namespace process = utils::process;
55b0d29bc4SBrooks Davis 
56b0d29bc4SBrooks Davis 
57b0d29bc4SBrooks Davis namespace {
58b0d29bc4SBrooks Davis 
59b0d29bc4SBrooks Davis 
60b0d29bc4SBrooks Davis /// Type of the process::exec() and process::exec_unsafe() functions.
61b0d29bc4SBrooks Davis typedef void (*exec_function)(const fs::path&, const process::args_vector&);
62b0d29bc4SBrooks Davis 
63b0d29bc4SBrooks Davis 
64b0d29bc4SBrooks Davis /// Calculates the path to the test helpers binary.
65b0d29bc4SBrooks Davis ///
66b0d29bc4SBrooks Davis /// \param tc A pointer to the caller test case, needed to extract the value of
67b0d29bc4SBrooks Davis ///     the "srcdir" property.
68b0d29bc4SBrooks Davis ///
69b0d29bc4SBrooks Davis /// \return The path to the helpers binary.
70b0d29bc4SBrooks Davis static fs::path
get_helpers(const atf::tests::tc * tc)71b0d29bc4SBrooks Davis get_helpers(const atf::tests::tc* tc)
72b0d29bc4SBrooks Davis {
73b0d29bc4SBrooks Davis     return fs::path(tc->get_config_var("srcdir")) / "helpers";
74b0d29bc4SBrooks Davis }
75b0d29bc4SBrooks Davis 
76b0d29bc4SBrooks Davis 
77b0d29bc4SBrooks Davis /// Body for a subprocess that runs exec().
78b0d29bc4SBrooks Davis class child_exec {
79b0d29bc4SBrooks Davis     /// Function to do the exec.
80b0d29bc4SBrooks Davis     const exec_function _do_exec;
81b0d29bc4SBrooks Davis 
82b0d29bc4SBrooks Davis     /// Path to the binary to exec.
83b0d29bc4SBrooks Davis     const fs::path& _program;
84b0d29bc4SBrooks Davis 
85b0d29bc4SBrooks Davis     /// Arguments to the binary, not including argv[0].
86b0d29bc4SBrooks Davis     const process::args_vector& _args;
87b0d29bc4SBrooks Davis 
88b0d29bc4SBrooks Davis public:
89b0d29bc4SBrooks Davis     /// Constructor.
90b0d29bc4SBrooks Davis     ///
91b0d29bc4SBrooks Davis     /// \param do_exec Function to do the exec.
92b0d29bc4SBrooks Davis     /// \param program Path to the binary to exec.
93b0d29bc4SBrooks Davis     /// \param args Arguments to the binary, not including argv[0].
child_exec(const exec_function do_exec,const fs::path & program,const process::args_vector & args)94b0d29bc4SBrooks Davis     child_exec(const exec_function do_exec, const fs::path& program,
95b0d29bc4SBrooks Davis                const process::args_vector& args) :
96b0d29bc4SBrooks Davis         _do_exec(do_exec), _program(program), _args(args)
97b0d29bc4SBrooks Davis     {
98b0d29bc4SBrooks Davis     }
99b0d29bc4SBrooks Davis 
100b0d29bc4SBrooks Davis     /// Body for the subprocess.
101b0d29bc4SBrooks Davis     void
operator ()(void)102b0d29bc4SBrooks Davis     operator()(void)
103b0d29bc4SBrooks Davis     {
104b0d29bc4SBrooks Davis         _do_exec(_program, _args);
105b0d29bc4SBrooks Davis     }
106b0d29bc4SBrooks Davis };
107b0d29bc4SBrooks Davis 
108b0d29bc4SBrooks Davis 
109b0d29bc4SBrooks Davis /// Body for a process that returns a specific exit code.
110b0d29bc4SBrooks Davis ///
111b0d29bc4SBrooks Davis /// \tparam ExitStatus The exit status for the subprocess.
112b0d29bc4SBrooks Davis template< int ExitStatus >
113b0d29bc4SBrooks Davis static void
child_exit(void)114b0d29bc4SBrooks Davis child_exit(void)
115b0d29bc4SBrooks Davis {
116b0d29bc4SBrooks Davis     std::exit(ExitStatus);
117b0d29bc4SBrooks Davis }
118b0d29bc4SBrooks Davis 
119b0d29bc4SBrooks Davis 
120b0d29bc4SBrooks Davis static void suspend(void) UTILS_NORETURN;
121b0d29bc4SBrooks Davis 
122b0d29bc4SBrooks Davis 
123b0d29bc4SBrooks Davis /// Blocks a subprocess from running indefinitely.
124b0d29bc4SBrooks Davis static void
suspend(void)125b0d29bc4SBrooks Davis suspend(void)
126b0d29bc4SBrooks Davis {
127b0d29bc4SBrooks Davis     sigset_t mask;
128b0d29bc4SBrooks Davis     sigemptyset(&mask);
129b0d29bc4SBrooks Davis     for (;;) {
130b0d29bc4SBrooks Davis         ::sigsuspend(&mask);
131b0d29bc4SBrooks Davis     }
132b0d29bc4SBrooks Davis }
133b0d29bc4SBrooks Davis 
134b0d29bc4SBrooks Davis 
135b0d29bc4SBrooks Davis static void write_loop(const int) UTILS_NORETURN;
136b0d29bc4SBrooks Davis 
137b0d29bc4SBrooks Davis 
138b0d29bc4SBrooks Davis /// Provides an infinite stream of data in a subprocess.
139b0d29bc4SBrooks Davis ///
140b0d29bc4SBrooks Davis /// \param fd Descriptor into which to write.
141b0d29bc4SBrooks Davis static void
write_loop(const int fd)142b0d29bc4SBrooks Davis write_loop(const int fd)
143b0d29bc4SBrooks Davis {
144b0d29bc4SBrooks Davis     const int cookie = 0x12345678;
145b0d29bc4SBrooks Davis     for (;;) {
146b0d29bc4SBrooks Davis         std::cerr << "Still alive in PID " << ::getpid() << '\n';
147b0d29bc4SBrooks Davis         if (::write(fd, &cookie, sizeof(cookie)) != sizeof(cookie))
148b0d29bc4SBrooks Davis             std::exit(EXIT_FAILURE);
149b0d29bc4SBrooks Davis         ::sleep(1);
150b0d29bc4SBrooks Davis     }
151b0d29bc4SBrooks Davis }
152b0d29bc4SBrooks Davis 
153b0d29bc4SBrooks Davis 
154b0d29bc4SBrooks Davis }  // anonymous namespace
155b0d29bc4SBrooks Davis 
156b0d29bc4SBrooks Davis 
157b0d29bc4SBrooks Davis /// Tests an exec function with no arguments.
158b0d29bc4SBrooks Davis ///
159b0d29bc4SBrooks Davis /// \param tc The calling test case.
160b0d29bc4SBrooks Davis /// \param do_exec The exec function to test.
161b0d29bc4SBrooks Davis static void
check_exec_no_args(const atf::tests::tc * tc,const exec_function do_exec)162b0d29bc4SBrooks Davis check_exec_no_args(const atf::tests::tc* tc, const exec_function do_exec)
163b0d29bc4SBrooks Davis {
164*b392a90bSJohn Baldwin     std::unique_ptr< process::child > child = process::child::fork_files(
165b0d29bc4SBrooks Davis         child_exec(do_exec, get_helpers(tc), process::args_vector()),
166b0d29bc4SBrooks Davis         fs::path("stdout"), fs::path("stderr"));
167b0d29bc4SBrooks Davis     const process::status status = child->wait();
168b0d29bc4SBrooks Davis     ATF_REQUIRE(status.exited());
169b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus());
170b0d29bc4SBrooks Davis     ATF_REQUIRE(atf::utils::grep_file("Must provide a helper name", "stderr"));
171b0d29bc4SBrooks Davis }
172b0d29bc4SBrooks Davis 
173b0d29bc4SBrooks Davis 
174b0d29bc4SBrooks Davis /// Tests an exec function with some arguments.
175b0d29bc4SBrooks Davis ///
176b0d29bc4SBrooks Davis /// \param tc The calling test case.
177b0d29bc4SBrooks Davis /// \param do_exec The exec function to test.
178b0d29bc4SBrooks Davis static void
check_exec_some_args(const atf::tests::tc * tc,const exec_function do_exec)179b0d29bc4SBrooks Davis check_exec_some_args(const atf::tests::tc* tc, const exec_function do_exec)
180b0d29bc4SBrooks Davis {
181b0d29bc4SBrooks Davis     process::args_vector args;
182b0d29bc4SBrooks Davis     args.push_back("print-args");
183b0d29bc4SBrooks Davis     args.push_back("foo");
184b0d29bc4SBrooks Davis     args.push_back("bar");
185b0d29bc4SBrooks Davis 
186*b392a90bSJohn Baldwin     std::unique_ptr< process::child > child = process::child::fork_files(
187b0d29bc4SBrooks Davis         child_exec(do_exec, get_helpers(tc), args),
188b0d29bc4SBrooks Davis         fs::path("stdout"), fs::path("stderr"));
189b0d29bc4SBrooks Davis     const process::status status = child->wait();
190b0d29bc4SBrooks Davis     ATF_REQUIRE(status.exited());
191b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
192b0d29bc4SBrooks Davis     ATF_REQUIRE(atf::utils::grep_file("argv\\[1\\] = print-args", "stdout"));
193b0d29bc4SBrooks Davis     ATF_REQUIRE(atf::utils::grep_file("argv\\[2\\] = foo", "stdout"));
194b0d29bc4SBrooks Davis     ATF_REQUIRE(atf::utils::grep_file("argv\\[3\\] = bar", "stdout"));
195b0d29bc4SBrooks Davis }
196b0d29bc4SBrooks Davis 
197b0d29bc4SBrooks Davis 
198b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec__no_args);
ATF_TEST_CASE_BODY(exec__no_args)199b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec__no_args)
200b0d29bc4SBrooks Davis {
201b0d29bc4SBrooks Davis     check_exec_no_args(this, process::exec);
202b0d29bc4SBrooks Davis }
203b0d29bc4SBrooks Davis 
204b0d29bc4SBrooks Davis 
205b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec__some_args);
ATF_TEST_CASE_BODY(exec__some_args)206b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec__some_args)
207b0d29bc4SBrooks Davis {
208b0d29bc4SBrooks Davis     check_exec_some_args(this, process::exec);
209b0d29bc4SBrooks Davis }
210b0d29bc4SBrooks Davis 
211b0d29bc4SBrooks Davis 
212b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec__fail);
ATF_TEST_CASE_BODY(exec__fail)213b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec__fail)
214b0d29bc4SBrooks Davis {
215b0d29bc4SBrooks Davis     utils::avoid_coredump_on_crash();
216b0d29bc4SBrooks Davis 
217*b392a90bSJohn Baldwin     std::unique_ptr< process::child > child = process::child::fork_files(
218b0d29bc4SBrooks Davis         child_exec(process::exec, fs::path("non-existent"),
219b0d29bc4SBrooks Davis                    process::args_vector()),
220b0d29bc4SBrooks Davis         fs::path("stdout"), fs::path("stderr"));
221b0d29bc4SBrooks Davis     const process::status status = child->wait();
222b0d29bc4SBrooks Davis     ATF_REQUIRE(status.signaled());
223b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(SIGABRT, status.termsig());
224b0d29bc4SBrooks Davis     ATF_REQUIRE(atf::utils::grep_file("Failed to execute non-existent",
225b0d29bc4SBrooks Davis                                       "stderr"));
226b0d29bc4SBrooks Davis }
227b0d29bc4SBrooks Davis 
228b0d29bc4SBrooks Davis 
229b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec_unsafe__no_args);
ATF_TEST_CASE_BODY(exec_unsafe__no_args)230b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec_unsafe__no_args)
231b0d29bc4SBrooks Davis {
232b0d29bc4SBrooks Davis     check_exec_no_args(this, process::exec_unsafe);
233b0d29bc4SBrooks Davis }
234b0d29bc4SBrooks Davis 
235b0d29bc4SBrooks Davis 
236b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec_unsafe__some_args);
ATF_TEST_CASE_BODY(exec_unsafe__some_args)237b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec_unsafe__some_args)
238b0d29bc4SBrooks Davis {
239b0d29bc4SBrooks Davis     check_exec_some_args(this, process::exec_unsafe);
240b0d29bc4SBrooks Davis }
241b0d29bc4SBrooks Davis 
242b0d29bc4SBrooks Davis 
243b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(exec_unsafe__fail);
ATF_TEST_CASE_BODY(exec_unsafe__fail)244b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(exec_unsafe__fail)
245b0d29bc4SBrooks Davis {
246b0d29bc4SBrooks Davis     ATF_REQUIRE_THROW_RE(
247b0d29bc4SBrooks Davis         process::system_error, "Failed to execute missing-program",
248b0d29bc4SBrooks Davis         process::exec_unsafe(fs::path("missing-program"),
249b0d29bc4SBrooks Davis                              process::args_vector()));
250b0d29bc4SBrooks Davis }
251b0d29bc4SBrooks Davis 
252b0d29bc4SBrooks Davis 
253b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(terminate_group__setpgrp_executed);
ATF_TEST_CASE_BODY(terminate_group__setpgrp_executed)254b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(terminate_group__setpgrp_executed)
255b0d29bc4SBrooks Davis {
256b0d29bc4SBrooks Davis     int first_fds[2], second_fds[2];
257b0d29bc4SBrooks Davis     ATF_REQUIRE(::pipe(first_fds) != -1);
258b0d29bc4SBrooks Davis     ATF_REQUIRE(::pipe(second_fds) != -1);
259b0d29bc4SBrooks Davis 
260b0d29bc4SBrooks Davis     const pid_t pid = ::fork();
261b0d29bc4SBrooks Davis     ATF_REQUIRE(pid != -1);
262b0d29bc4SBrooks Davis     if (pid == 0) {
263b0d29bc4SBrooks Davis         ::setpgid(::getpid(), ::getpid());
264b0d29bc4SBrooks Davis         const pid_t pid2 = ::fork();
265b0d29bc4SBrooks Davis         if (pid2 == -1) {
266b0d29bc4SBrooks Davis             std::exit(EXIT_FAILURE);
267b0d29bc4SBrooks Davis         } else if (pid2 == 0) {
268b0d29bc4SBrooks Davis             ::close(first_fds[0]);
269b0d29bc4SBrooks Davis             ::close(first_fds[1]);
270b0d29bc4SBrooks Davis             ::close(second_fds[0]);
271b0d29bc4SBrooks Davis             write_loop(second_fds[1]);
272b0d29bc4SBrooks Davis         }
273b0d29bc4SBrooks Davis         ::close(first_fds[0]);
274b0d29bc4SBrooks Davis         ::close(second_fds[0]);
275b0d29bc4SBrooks Davis         ::close(second_fds[1]);
276b0d29bc4SBrooks Davis         write_loop(first_fds[1]);
277b0d29bc4SBrooks Davis     }
278b0d29bc4SBrooks Davis     ::close(first_fds[1]);
279b0d29bc4SBrooks Davis     ::close(second_fds[1]);
280b0d29bc4SBrooks Davis 
281b0d29bc4SBrooks Davis     int dummy;
282b0d29bc4SBrooks Davis     std::cerr << "Waiting for children to start\n";
283b0d29bc4SBrooks Davis     while (::read(first_fds[0], &dummy, sizeof(dummy)) <= 0 ||
284b0d29bc4SBrooks Davis            ::read(second_fds[0], &dummy, sizeof(dummy)) <= 0) {
285b0d29bc4SBrooks Davis         // Wait for children to come up.
286b0d29bc4SBrooks Davis     }
287b0d29bc4SBrooks Davis 
288b0d29bc4SBrooks Davis     process::terminate_group(pid);
289b0d29bc4SBrooks Davis     std::cerr << "Waiting for children to die\n";
290b0d29bc4SBrooks Davis     while (::read(first_fds[0], &dummy, sizeof(dummy)) > 0 ||
291b0d29bc4SBrooks Davis            ::read(second_fds[0], &dummy, sizeof(dummy)) > 0) {
292b0d29bc4SBrooks Davis         // Wait for children to terminate.  If they don't, then the test case
293b0d29bc4SBrooks Davis         // will time out.
294b0d29bc4SBrooks Davis     }
295b0d29bc4SBrooks Davis 
296b0d29bc4SBrooks Davis     int status;
297b0d29bc4SBrooks Davis     ATF_REQUIRE(::wait(&status) != -1);
298b0d29bc4SBrooks Davis     ATF_REQUIRE(WIFSIGNALED(status));
299b0d29bc4SBrooks Davis     ATF_REQUIRE(WTERMSIG(status) == SIGKILL);
300b0d29bc4SBrooks Davis }
301b0d29bc4SBrooks Davis 
302b0d29bc4SBrooks Davis 
303b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(terminate_group__setpgrp_not_executed);
ATF_TEST_CASE_BODY(terminate_group__setpgrp_not_executed)304b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(terminate_group__setpgrp_not_executed)
305b0d29bc4SBrooks Davis {
306b0d29bc4SBrooks Davis     const pid_t pid = ::fork();
307b0d29bc4SBrooks Davis     ATF_REQUIRE(pid != -1);
308b0d29bc4SBrooks Davis     if (pid == 0) {
309b0d29bc4SBrooks Davis         // We do not call setgprp() here to simulate the race that happens when
310b0d29bc4SBrooks Davis         // we invoke terminate_group on a process that has not yet had a chance
311b0d29bc4SBrooks Davis         // to run the setpgrp() call.
312b0d29bc4SBrooks Davis         suspend();
313b0d29bc4SBrooks Davis     }
314b0d29bc4SBrooks Davis 
315b0d29bc4SBrooks Davis     process::terminate_group(pid);
316b0d29bc4SBrooks Davis 
317b0d29bc4SBrooks Davis     int status;
318b0d29bc4SBrooks Davis     ATF_REQUIRE(::wait(&status) != -1);
319b0d29bc4SBrooks Davis     ATF_REQUIRE(WIFSIGNALED(status));
320b0d29bc4SBrooks Davis     ATF_REQUIRE(WTERMSIG(status) == SIGKILL);
321b0d29bc4SBrooks Davis }
322b0d29bc4SBrooks Davis 
323b0d29bc4SBrooks Davis 
324b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(terminate_self_with__exitstatus);
ATF_TEST_CASE_BODY(terminate_self_with__exitstatus)325b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(terminate_self_with__exitstatus)
326b0d29bc4SBrooks Davis {
327b0d29bc4SBrooks Davis     const pid_t pid = ::fork();
328b0d29bc4SBrooks Davis     ATF_REQUIRE(pid != -1);
329b0d29bc4SBrooks Davis     if (pid == 0) {
330b0d29bc4SBrooks Davis         const process::status status = process::status::fake_exited(123);
331b0d29bc4SBrooks Davis         process::terminate_self_with(status);
332b0d29bc4SBrooks Davis     }
333b0d29bc4SBrooks Davis 
334b0d29bc4SBrooks Davis     int status;
335b0d29bc4SBrooks Davis     ATF_REQUIRE(::wait(&status) != -1);
336b0d29bc4SBrooks Davis     ATF_REQUIRE(WIFEXITED(status));
337b0d29bc4SBrooks Davis     ATF_REQUIRE(WEXITSTATUS(status) == 123);
338b0d29bc4SBrooks Davis }
339b0d29bc4SBrooks Davis 
340b0d29bc4SBrooks Davis 
341b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(terminate_self_with__termsig);
ATF_TEST_CASE_BODY(terminate_self_with__termsig)342b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(terminate_self_with__termsig)
343b0d29bc4SBrooks Davis {
344b0d29bc4SBrooks Davis     const pid_t pid = ::fork();
345b0d29bc4SBrooks Davis     ATF_REQUIRE(pid != -1);
346b0d29bc4SBrooks Davis     if (pid == 0) {
347b0d29bc4SBrooks Davis         const process::status status = process::status::fake_signaled(
348b0d29bc4SBrooks Davis             SIGKILL, false);
349b0d29bc4SBrooks Davis         process::terminate_self_with(status);
350b0d29bc4SBrooks Davis     }
351b0d29bc4SBrooks Davis 
352b0d29bc4SBrooks Davis     int status;
353b0d29bc4SBrooks Davis     ATF_REQUIRE(::wait(&status) != -1);
354b0d29bc4SBrooks Davis     ATF_REQUIRE(WIFSIGNALED(status));
355b0d29bc4SBrooks Davis     ATF_REQUIRE(WTERMSIG(status) == SIGKILL);
356b0d29bc4SBrooks Davis     ATF_REQUIRE(!WCOREDUMP(status));
357b0d29bc4SBrooks Davis }
358b0d29bc4SBrooks Davis 
359b0d29bc4SBrooks Davis 
360b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(terminate_self_with__termsig_and_core);
ATF_TEST_CASE_BODY(terminate_self_with__termsig_and_core)361b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(terminate_self_with__termsig_and_core)
362b0d29bc4SBrooks Davis {
363b0d29bc4SBrooks Davis     utils::prepare_coredump_test(this);
364b0d29bc4SBrooks Davis 
365b0d29bc4SBrooks Davis     const pid_t pid = ::fork();
366b0d29bc4SBrooks Davis     ATF_REQUIRE(pid != -1);
367b0d29bc4SBrooks Davis     if (pid == 0) {
368b0d29bc4SBrooks Davis         const process::status status = process::status::fake_signaled(
369b0d29bc4SBrooks Davis             SIGABRT, true);
370b0d29bc4SBrooks Davis         process::terminate_self_with(status);
371b0d29bc4SBrooks Davis     }
372b0d29bc4SBrooks Davis 
373b0d29bc4SBrooks Davis     int status;
374b0d29bc4SBrooks Davis     ATF_REQUIRE(::wait(&status) != -1);
375b0d29bc4SBrooks Davis     ATF_REQUIRE(WIFSIGNALED(status));
376b0d29bc4SBrooks Davis     ATF_REQUIRE(WTERMSIG(status) == SIGABRT);
377b0d29bc4SBrooks Davis     ATF_REQUIRE(WCOREDUMP(status));
378b0d29bc4SBrooks Davis }
379b0d29bc4SBrooks Davis 
380b0d29bc4SBrooks Davis 
381b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(wait__ok);
ATF_TEST_CASE_BODY(wait__ok)382b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(wait__ok)
383b0d29bc4SBrooks Davis {
384*b392a90bSJohn Baldwin     std::unique_ptr< process::child > child = process::child::fork_capture(
385b0d29bc4SBrooks Davis         child_exit< 15 >);
386b0d29bc4SBrooks Davis     const pid_t pid = child->pid();
387b0d29bc4SBrooks Davis     child.reset();  // Ensure there is no conflict between destructor and wait.
388b0d29bc4SBrooks Davis 
389b0d29bc4SBrooks Davis     const process::status status = process::wait(pid);
390b0d29bc4SBrooks Davis     ATF_REQUIRE(status.exited());
391b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(15, status.exitstatus());
392b0d29bc4SBrooks Davis }
393b0d29bc4SBrooks Davis 
394b0d29bc4SBrooks Davis 
395b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(wait__fail);
ATF_TEST_CASE_BODY(wait__fail)396b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(wait__fail)
397b0d29bc4SBrooks Davis {
398b0d29bc4SBrooks Davis     ATF_REQUIRE_THROW(process::system_error, process::wait(1));
399b0d29bc4SBrooks Davis }
400b0d29bc4SBrooks Davis 
401b0d29bc4SBrooks Davis 
402b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(wait_any__one);
ATF_TEST_CASE_BODY(wait_any__one)403b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(wait_any__one)
404b0d29bc4SBrooks Davis {
405b0d29bc4SBrooks Davis     process::child::fork_capture(child_exit< 15 >);
406b0d29bc4SBrooks Davis 
407b0d29bc4SBrooks Davis     const process::status status = process::wait_any();
408b0d29bc4SBrooks Davis     ATF_REQUIRE(status.exited());
409b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(15, status.exitstatus());
410b0d29bc4SBrooks Davis }
411b0d29bc4SBrooks Davis 
412b0d29bc4SBrooks Davis 
413b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(wait_any__many);
ATF_TEST_CASE_BODY(wait_any__many)414b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(wait_any__many)
415b0d29bc4SBrooks Davis {
416b0d29bc4SBrooks Davis     process::child::fork_capture(child_exit< 15 >);
417b0d29bc4SBrooks Davis     process::child::fork_capture(child_exit< 30 >);
418b0d29bc4SBrooks Davis     process::child::fork_capture(child_exit< 45 >);
419b0d29bc4SBrooks Davis 
420b0d29bc4SBrooks Davis     std::set< int > exit_codes;
421b0d29bc4SBrooks Davis     for (int i = 0; i < 3; i++) {
422b0d29bc4SBrooks Davis         const process::status status = process::wait_any();
423b0d29bc4SBrooks Davis         ATF_REQUIRE(status.exited());
424b0d29bc4SBrooks Davis         exit_codes.insert(status.exitstatus());
425b0d29bc4SBrooks Davis     }
426b0d29bc4SBrooks Davis 
427b0d29bc4SBrooks Davis     std::set< int > exp_exit_codes;
428b0d29bc4SBrooks Davis     exp_exit_codes.insert(15);
429b0d29bc4SBrooks Davis     exp_exit_codes.insert(30);
430b0d29bc4SBrooks Davis     exp_exit_codes.insert(45);
431b0d29bc4SBrooks Davis     ATF_REQUIRE_EQ(exp_exit_codes, exit_codes);
432b0d29bc4SBrooks Davis }
433b0d29bc4SBrooks Davis 
434b0d29bc4SBrooks Davis 
435b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(wait_any__none_is_failure);
ATF_TEST_CASE_BODY(wait_any__none_is_failure)436b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(wait_any__none_is_failure)
437b0d29bc4SBrooks Davis {
438b0d29bc4SBrooks Davis     try {
439b0d29bc4SBrooks Davis         const process::status status = process::wait_any();
440b0d29bc4SBrooks Davis         fail("Expected exception but none raised");
441b0d29bc4SBrooks Davis     } catch (const process::system_error& e) {
442b0d29bc4SBrooks Davis         ATF_REQUIRE(atf::utils::grep_string("Failed to wait", e.what()));
443b0d29bc4SBrooks Davis         ATF_REQUIRE_EQ(ECHILD, e.original_errno());
444b0d29bc4SBrooks Davis     }
445b0d29bc4SBrooks Davis }
446b0d29bc4SBrooks Davis 
447b0d29bc4SBrooks Davis 
ATF_INIT_TEST_CASES(tcs)448b0d29bc4SBrooks Davis ATF_INIT_TEST_CASES(tcs)
449b0d29bc4SBrooks Davis {
450b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec__no_args);
451b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec__some_args);
452b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec__fail);
453b0d29bc4SBrooks Davis 
454b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec_unsafe__no_args);
455b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec_unsafe__some_args);
456b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, exec_unsafe__fail);
457b0d29bc4SBrooks Davis 
458b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, terminate_group__setpgrp_executed);
459b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, terminate_group__setpgrp_not_executed);
460b0d29bc4SBrooks Davis 
461b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, terminate_self_with__exitstatus);
462b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, terminate_self_with__termsig);
463b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, terminate_self_with__termsig_and_core);
464b0d29bc4SBrooks Davis 
465b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, wait__ok);
466b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, wait__fail);
467b0d29bc4SBrooks Davis 
468b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, wait_any__one);
469b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, wait_any__many);
470b0d29bc4SBrooks Davis     ATF_ADD_TEST_CASE(tcs, wait_any__none_is_failure);
471b0d29bc4SBrooks Davis }
472