• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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