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