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 [this](const siginfo_t& info) {
79 EXPECT_EQ(CLD_EXITED, info.si_code);
80 EXPECT_EQ(123, info.si_status);
81 this->brillo_loop_.BreakLoop();
82 })));
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(FROM_HERE, pid, base::Bind(
95 [this, exit_value, &running_childs](const siginfo_t& info) {
96 EXPECT_EQ(CLD_EXITED, info.si_code);
97 EXPECT_EQ(exit_value, info.si_status);
98 running_childs--;
99 if (running_childs == 0)
100 this->brillo_loop_.BreakLoop();
101 })));
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_childs);
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::Bind(
113 [this](const siginfo_t& info) {
114 EXPECT_EQ(CLD_KILLED, info.si_code);
115 EXPECT_EQ(SIGKILL, info.si_status);
116 this->brillo_loop_.BreakLoop();
117 })));
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::Bind(
124 [this](const siginfo_t& /* info */) {
125 ADD_FAILURE() << "Child process was still tracked.";
126 this->brillo_loop_.BreakLoop();
127 })));
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