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