Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/process/operations_test.cpp
48178 views
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::unique_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::unique_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::unique_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::unique_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
473