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