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
19namespace {
20
21pid_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
30pid_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
44namespace brillo {
45
46class ProcessReaperTest : public ::testing::Test {
47 public:
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
63TEST_F(ProcessReaperTest, UnregisterWhenNotRegistered) {
64  ProcessReaper another_process_reaper_;
65  another_process_reaper_.Unregister();
66}
67
68TEST_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
75TEST_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.
88TEST_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
119TEST_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
130TEST_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