1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/process_reaper.h>
6
7 #include <signal.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10
11 #include <base/bind.h>
12 #include <base/location.h>
13 #include <base/message_loop/message_loop.h>
14 #include <brillo/asynchronous_signal_handler.h>
15 #include <brillo/message_loops/base_message_loop.h>
16 #include <gtest/gtest.h>
17
18 namespace {
19
ForkChildAndExit(int exit_code)20 pid_t ForkChildAndExit(int exit_code) {
21 pid_t pid = fork();
22 PCHECK(pid != -1);
23 if (pid == 0) {
24 _exit(exit_code);
25 }
26 return pid;
27 }
28
ForkChildAndKill(int sig)29 pid_t ForkChildAndKill(int sig) {
30 pid_t pid = fork();
31 PCHECK(pid != -1);
32 if (pid == 0) {
33 if (raise(sig) != 0) {
34 PLOG(ERROR) << "raise(" << sig << ")";
35 }
36 _exit(0); // Not reached. This value will cause the test to fail.
37 }
38 return pid;
39 }
40
41 } // namespace
42
43 namespace brillo {
44
45 class ProcessReaperTest : public ::testing::Test {
46 public:
SetUp()47 void SetUp() override {
48 brillo_loop_.SetAsCurrent();
49 async_signal_handler_.Init();
50 process_reaper_.Register(&async_signal_handler_);
51 }
52
53 protected:
54 base::MessageLoopForIO base_loop_;
55 brillo::BaseMessageLoop brillo_loop_{&base_loop_};
56 brillo::AsynchronousSignalHandler async_signal_handler_;
57
58 // ProcessReaper under test.
59 ProcessReaper process_reaper_;
60 };
61
TEST_F(ProcessReaperTest,UnregisterWhenNotRegistered)62 TEST_F(ProcessReaperTest, UnregisterWhenNotRegistered) {
63 ProcessReaper another_process_reaper_;
64 another_process_reaper_.Unregister();
65 }
66
TEST_F(ProcessReaperTest,UnregisterAndReregister)67 TEST_F(ProcessReaperTest, UnregisterAndReregister) {
68 process_reaper_.Unregister();
69 process_reaper_.Register(&async_signal_handler_);
70 // This checks that we can unregister the ProcessReaper and then destroy it.
71 process_reaper_.Unregister();
72 }
73
TEST_F(ProcessReaperTest,ReapExitedChild)74 TEST_F(ProcessReaperTest, ReapExitedChild) {
75 pid_t pid = ForkChildAndExit(123);
76 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
77 [](MessageLoop* loop, const siginfo_t& info) {
78 EXPECT_EQ(CLD_EXITED, info.si_code);
79 EXPECT_EQ(123, info.si_status);
80 loop->BreakLoop();
81 }, &brillo_loop_)));
82 brillo_loop_.Run();
83 }
84
85 // Test that simultaneous child processes fire their respective callbacks when
86 // exiting.
TEST_F(ProcessReaperTest,ReapedChildrenMatchCallbacks)87 TEST_F(ProcessReaperTest, ReapedChildrenMatchCallbacks) {
88 int running_children = 10;
89 for (int i = 0; i < running_children; ++i) {
90 // Different processes will have different exit values.
91 int exit_value = 1 + i;
92 pid_t pid = ForkChildAndExit(exit_value);
93 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
94 [](MessageLoop* loop, int exit_value, int* running_children,
95 const siginfo_t& info) {
96 EXPECT_EQ(CLD_EXITED, info.si_code);
97 EXPECT_EQ(exit_value, info.si_status);
98 (*running_children)--;
99 if (*running_children == 0)
100 loop->BreakLoop();
101 }, &brillo_loop_, exit_value, &running_children)));
102 }
103 // This sleep is optional. It helps to have more processes exit before we
104 // start watching for them in the message loop.
105 usleep(10 * 1000);
106 brillo_loop_.Run();
107 EXPECT_EQ(0, running_children);
108 }
109
TEST_F(ProcessReaperTest,ReapKilledChild)110 TEST_F(ProcessReaperTest, ReapKilledChild) {
111 pid_t pid = ForkChildAndKill(SIGKILL);
112 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
113 [](MessageLoop* loop, const siginfo_t& info) {
114 EXPECT_EQ(CLD_KILLED, info.si_code);
115 EXPECT_EQ(SIGKILL, info.si_status);
116 loop->BreakLoop();
117 }, &brillo_loop_)));
118 brillo_loop_.Run();
119 }
120
TEST_F(ProcessReaperTest,ReapKilledAndForgottenChild)121 TEST_F(ProcessReaperTest, ReapKilledAndForgottenChild) {
122 pid_t pid = ForkChildAndExit(0);
123 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
124 [](MessageLoop* loop, const siginfo_t& /* info */) {
125 ADD_FAILURE() << "Child process was still tracked.";
126 loop->BreakLoop();
127 }, &brillo_loop_)));
128 EXPECT_TRUE(process_reaper_.ForgetChild(pid));
129
130 // A second call should return failure.
131 EXPECT_FALSE(process_reaper_.ForgetChild(pid));
132
133 // Run the loop with a timeout, as the BreakLoop() above is not expected.
134 brillo_loop_.PostDelayedTask(FROM_HERE,
135 base::Bind(&MessageLoop::BreakLoop,
136 base::Unretained(&brillo_loop_)),
137 base::TimeDelta::FromMilliseconds(100));
138 brillo_loop_.Run();
139 }
140
141 } // namespace brillo
142